/*
 * Copyright 2002-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.security.intercept.method.aopalliance;

import static org.assertj.core.api.Assertions.*;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.AbstractXmlApplicationContext;
import org.springframework.security.ITargetObject;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.config.util.InMemoryXmlApplicationContext;
import org.springframework.security.core.context.SecurityContextHolder;

/**
 * Tests for SEC-428 (and SEC-1204).
 *
 * @author Luke Taylor
 * @author Ben Alex
 */
public class MethodSecurityInterceptorWithAopConfigTests {
	static final String AUTH_PROVIDER_XML = "<authentication-manager>"
			+ "    <authentication-provider>"
			+ "        <user-service>"
			+ "            <user name='bob' password='bobspassword' authorities='ROLE_USER,ROLE_ADMIN' />"
			+ "            <user name='bill' password='billspassword' authorities='ROLE_USER' />"
			+ "        </user-service>" + "    </authentication-provider>"
			+ "</authentication-manager>";

	static final String ACCESS_MANAGER_XML = "<b:bean id='accessDecisionManager' class='org.springframework.security.access.vote.AffirmativeBased'>"
			+ "   <b:constructor-arg>"
			+ "       <b:list><b:bean class='org.springframework.security.access.vote.RoleVoter'/></b:list>"
			+ "   </b:constructor-arg>" + "</b:bean>";

	static final String TARGET_BEAN_AND_INTERCEPTOR = "<b:bean id='target' class='org.springframework.security.TargetObject'/>"
			+ "<b:bean id='securityInterceptor' class='org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor' autowire='byType' >"
			+ "     <b:property name='securityMetadataSource'>"
			+ "         <method-security-metadata-source>"
			+ "             <protect method='org.springframework.security.ITargetObject.makeLower*' access='ROLE_A'/>"
			+ "             <protect method='org.springframework.security.ITargetObject.makeUpper*' access='ROLE_A'/>"
			+ "             <protect method='org.springframework.security.ITargetObject.computeHashCode*' access='ROLE_B'/>"
			+ "         </method-security-metadata-source>"
			+ "     </b:property>"
			+ "</b:bean>";

	private AbstractXmlApplicationContext appContext;

	@Before
	public void clearContext() {
		SecurityContextHolder.clearContext();
	}

	@After
	public void closeAppContext() {
		SecurityContextHolder.clearContext();
		if (appContext != null) {
			appContext.close();
			appContext = null;
		}
	}

	@Test(expected = AuthenticationCredentialsNotFoundException.class)
	public void securityInterceptorIsAppliedWhenUsedWithAopConfig() {
		setContext("<aop:config>"
				+ "     <aop:pointcut id='targetMethods' expression='execution(* org.springframework.security.TargetObject.*(..))'/>"
				+ "     <aop:advisor advice-ref='securityInterceptor' pointcut-ref='targetMethods' />"
				+ "</aop:config>" + TARGET_BEAN_AND_INTERCEPTOR + AUTH_PROVIDER_XML
				+ ACCESS_MANAGER_XML);

		ITargetObject target = (ITargetObject) appContext.getBean("target");

		// Check both against interface and class
		try {
			target.makeLowerCase("TEST");
			fail("AuthenticationCredentialsNotFoundException expected");
		}
		catch (AuthenticationCredentialsNotFoundException expected) {
		}

		target.makeUpperCase("test");
	}

	@Test(expected = AuthenticationCredentialsNotFoundException.class)
	public void securityInterceptorIsAppliedWhenUsedWithBeanNameAutoProxyCreator() {
		setContext("<b:bean id='autoProxyCreator' class='org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator'>"
				+ "   <b:property name='interceptorNames'>"
				+ "       <b:list>"
				+ "          <b:value>securityInterceptor</b:value>"
				+ "       </b:list>"
				+ "   </b:property>"
				+ "   <b:property name='beanNames'>"
				+ "       <b:list>"
				+ "          <b:value>target</b:value>"
				+ "       </b:list>"
				+ "   </b:property>"
				+ "   <b:property name='proxyTargetClass' value='false'/>"
				+ "</b:bean>"
				+ TARGET_BEAN_AND_INTERCEPTOR + AUTH_PROVIDER_XML + ACCESS_MANAGER_XML);

		ITargetObject target = (ITargetObject) appContext.getBean("target");

		try {
			target.makeLowerCase("TEST");
			fail("AuthenticationCredentialsNotFoundException expected");
		}
		catch (AuthenticationCredentialsNotFoundException expected) {
		}

		target.makeUpperCase("test");

	}

	private void setContext(String context) {
		appContext = new InMemoryXmlApplicationContext(context);
	}
}
