package ca.corefacility.bioinformatics.irida.config;

import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
import org.springframework.security.core.userdetails.UserDetailsChecker;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import ca.corefacility.bioinformatics.irida.repositories.UserRepository;
import ca.corefacility.bioinformatics.irida.security.IgnoreExpiredCredentialsForPasswordChangeChecker;
import ca.corefacility.bioinformatics.irida.security.permissions.BasePermission;
import ca.corefacility.bioinformatics.irida.security.permissions.IridaPermissionEvaluator;

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@ComponentScan(basePackages = "ca.corefacility.bioinformatics.irida.security")
public class IridaApiSecurityConfig extends GlobalMethodSecurityConfiguration {

	private static final String[] ROLE_HIERARCHIES = new String[] { "ROLE_ADMIN > ROLE_MANAGER",
			"ROLE_MANAGER > ROLE_USER" };

	private static final String ROLE_HIERARCHY = StringUtils.join(ROLE_HIERARCHIES, "\n");

	@Autowired
	private List<BasePermission<?>> basePermissions;

	@Autowired
	private UserRepository userRepository;

	@Override
	protected MethodSecurityExpressionHandler createExpressionHandler() {
		DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
		IridaPermissionEvaluator permissionEvaluator = new IridaPermissionEvaluator(basePermissions);
		permissionEvaluator.init();
		handler.setPermissionEvaluator(permissionEvaluator);
		RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
		roleHierarchy.setHierarchy(ROLE_HIERARCHY);
		handler.setRoleHierarchy(roleHierarchy);
		return handler;
	}

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(userRepository).passwordEncoder(passwordEncoder());
		auth.authenticationProvider(authenticationProvider());
	}

	@Bean
	public AuthenticationProvider authenticationProvider() {
		DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
		authenticationProvider.setUserDetailsService(userRepository);
		authenticationProvider.setPasswordEncoder(passwordEncoder());
		authenticationProvider.setPostAuthenticationChecks(postAuthenticationChecker());
		return authenticationProvider;
	}

	@Bean
	public UserDetailsChecker postAuthenticationChecker() {
		return new IgnoreExpiredCredentialsForPasswordChangeChecker();
	}

	@Bean
	public AuthenticationManager authenticationManager() throws Exception {
		return super.authenticationManager();
	}

	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
}

@@ -1743,16 +1743,81 @@
 aluator;
+%0Aimport ca.corefacility.bioinformatics.irida.service.UserService;
 %0A%0A@Confi
@@ -2253,16 +2253,311 @@
 %22%5Cn%22);%0A%0A
+%09/**%0A%09 * Loads all of the %7B@link BasePermission%7D sub-classes found in the security%0A%09 * package during component scan. %7B@link BasePermission%7D classes are used in%0A%09 * %7B@link @PreAuthorize%7D annotations for verifying that a user has%0A%09 * permission to invoke a method by the expression handler.%0A%09 */%0A
 %09@Autowi
@@ -3821,24 +3821,365 @@
 ovider;%0A%09%7D%0A%0A
+%09/**%0A%09 * After a user has been authenticated, we want to allow them to change%0A%09 * their password if the password is expired. The%0A%09 * %7B@link IgnoreExpiredCredentialsForPasswordChangeChecker%7D allows%0A%09 * authenticated users with expired credentials to invoke one method, the%0A%09 * %7B@link UserService.changePassword%7D method.%0A%09 * %0A%09 * @return%0A%09 */%0A
 %09@Bean%0A%09publ

Add comments about what's going on with the security config.