package jeffaschenk.commons.container.security;
import jeffaschenk.commons.container.security.constants.SecurityConstants;
import jeffaschenk.commons.container.security.object.*;
import jeffaschenk.commons.environment.SystemEnvironmentBean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.MessageSource;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserCache;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsChecker;
import org.springframework.security.core.userdetails.UserDetailsService;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
/**
* Security Service Implementation Provides Implementation interface to Spring
* Security services and Session information for the Authenticated User.
*
* @author jeffaschenk@gmail.com
* @version $Id: $
*/
@Qualifier("securityService")
public class SecurityServiceProviderImpl extends DaoAuthenticationProvider
implements AuthenticationProvider, SecurityServiceProvider, SecurityConstants {
/**
* Constant <code>log</code>
*/
protected static Log log = LogFactory.getLog(SecurityServiceProviderImpl.class);
/**
* AUTHENTICATION FAILURE Messages
*/
private static final String FAILURE_MESSAGE_PRINCIPAL_NULL = "Authenticating Principal is null, unable to perform Authentication";
/**
* Autowired Services
*/
//@Autowired
//private FacebookServiceProvider facebookServiceProvider;
//@Autowired
//RegisteredUserDAO registeredUserDAO;
@Autowired
private SystemEnvironmentBean systemEnvironment;
/**
* Initialize the Singleton Session Cache.
*/
@PostConstruct
public synchronized void init() {
// ************************************
// Initialization
log.info("AuthenticationProvider,SecurityServiceProvider starting to Initialize.");
log.info("AuthenticationProvider,SecurityServiceProvider has been Initialized");
// ***************************************
// Proceed with additional Initialization
}
/**
* <p/>
* Destroy Implemented Spring Interface Method, invoked when bean is removed
* from container.
*/
@PreDestroy
public void destroy() {
log
.info("AuthenticationProvider,SecurityServiceProvider have been Removed from the Container.");
}
/**
* Overriding Authentication Method within Spring Security
*
* @param authentication
* @return uthentication
* @throws AuthenticationException
*
*/
@Override
public final Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (log.isDebugEnabled()) {
log.debug("Performing Authentication:[" + authentication + "]");
}
// *****************************************
// Initialize
boolean authorized = false;
String principal = null;
SecuritySessionUserObject securitySessionUserObject = null;
Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
// *********************************************
// Process Normal WEB Form Based Login
// *********************************************
// Obtain Registered User via String Principal
principal = authentication.getPrincipal().toString();
log.warn("Processing a Normal Authentication Request from Principal:[" + principal + "].");
if ((principal == null) || (principal.isEmpty())) {
log.warn(FAILURE_MESSAGE_PRINCIPAL_NULL);
throw new AuthenticationException(FAILURE_MESSAGE_PRINCIPAL_NULL);
}
// **************************************
// Obtain Session User Object.
securitySessionUserObject
= (SecuritySessionUserObject) this.getUserDetailsService().loadUserByUsername(principal);
// *******************************************
// Validate either non-Encrypted Password or
// a Fully Encrypted Password.
if (securitySessionUserObject.getPassword().length() <= SecurityServiceProviderUtility.MAX_UNENCRYPTED_CREDENTIALS_LENGTH) {
if (securitySessionUserObject.getPassword().equals(authentication.getCredentials().toString())) {
authorized = true;
}
} else if (SecurityServiceProviderUtility.checkPassword(authentication.getCredentials().toString(), securitySessionUserObject.getPassword())) {
authorized = true;
}
// ******************************************
// If credentials are Authorized Access
// go deeper to obtain Roles and Profile
// for this User.
if (authorized)
{
// *********************************************
// Obtain the optional Registered User Profile
if (securitySessionUserObject.getRegisteredUserProfileId() != null) {
List<SecuritySessionProfileObject> profiles =
((SecurityServiceJdbcDao) this.getUserDetailsService()).getRegisteredUserProfileForAuthentication(securitySessionUserObject.getRegisteredUserProfileId());
if ((profiles != null) && (profiles.size() == 1)) {
securitySessionUserObject.setSecuritySessionProfileObject(profiles.get(0));
if (log.isInfoEnabled()) {
log.info("Authenticating Principal:[" + principal + "], has a Profile Assigned.");
}
}
}
// ******************************************
// Obtain the Roles for this RegisteredUser
securitySessionUserObject.setSecuritySessionRoles(
((SecurityServiceJdbcDao) this.getUserDetailsService()).getRegisteredUserRolesForAuthentication(securitySessionUserObject.getRegisteredUserId()));
// ******************************************
// Obtain the Permissions for this RegisteredUser
securitySessionUserObject.setSecuritySessionPermissions(
((SecurityServiceJdbcDao) this.getUserDetailsService()).getRegisteredUserPermissionsForAuthentication(securitySessionUserObject.getRegisteredUserId()));
// ******************************************
// Provide a Default User Role.
//
// TODO
// This should go away in Production!
// No Role, Then they are should be considered Anonymous.
// TODO
if (securitySessionUserObject.getSecuritySessionRoles().size() <= 0) {
securitySessionUserObject.getSecuritySessionRoles().add(
new SecuritySessionRoleObject(
new BigInteger("0"), ROLE_USER));
}
// *****************************************
// Build Up Granted Authorities List
//
log.info("Building Authorities for Authenticating Principal:[" + principal + "], has [" +
securitySessionUserObject.getSecuritySessionRoles().size() + "] Roles Assigned.");
for (SecuritySessionRoleObject role : securitySessionUserObject.getSecuritySessionRoles()) {
String security_roleName = (role.getRoleName().toUpperCase().startsWith(ROLE_PREFIX)
? "" : ROLE_PREFIX) + role.getRoleName().toUpperCase();
if (log.isInfoEnabled()) {
log.info("Authenticating Principal:[" + principal + "], has a Security Role of:[" + security_roleName + "] Assigned.");
}
authorities.add(new GrantedAuthority(security_roleName));
}
// *****************************************
// Build Up Granted Authorities List
//
log.info("Building Authorities for Authenticating Principal:[" + principal + "], has [" +
securitySessionUserObject.getSecuritySessionPermissions().size() + "] Permissions Assigned.");
for (SecuritySessionPermissionObject permission : securitySessionUserObject.getSecuritySessionPermissions()) {
String security_permissionName = (permission.getPermissionName().toUpperCase().startsWith(PERMISSION_PREFIX)
? "" : PERMISSION_PREFIX) + permission.getPermissionName().toUpperCase();
if (log.isInfoEnabled()) {
log.info("Authenticating Principal:[" + principal + "], has a Security Permission of:[" + security_permissionName + "] Assigned.");
}
authorities.add(new GrantedAuthority(security_permissionName));
}
} // End of Authorized Additional Methods for Profile and Roles.
// ***************************************
// Finish the Validation
if (authorized)
{
String emsg = "Authentication of Principal:[" + principal + "], was Successful.";
log.info(emsg);
((SecurityServiceJdbcDao) this.getUserDetailsService()).setLogInTimeOfRegisteredUser(securitySessionUserObject.getRegisteredUserId(), new Date());
//RegisteredUser user =
//registeredUserDAO.getRegisteredUserById(securitySessionUserObject.getRegisteredUserId(),
//"registeredUserProfile");
return new AuthenticationToken(principal, authorities, securitySessionUserObject);
} else
{
String emsg = "Authentication of Principal:[" + principal + "], has Failed due to invalid Credentials";
log.warn(emsg);
throw new AuthenticationException(emsg);
}
}
/**
* Indicate this class can return a UserName and Password Token.
*
* @param aClass
* @return boolean indicating if specified class is supported or not.
*/
@Override
public boolean supports(Class aClass) {
if (UsernamePasswordAuthenticationToken.class.isAssignableFrom(aClass)) {
return true;
}
return (AuthenticationToken.class.isAssignableFrom(aClass));
}
/**
* {@inheritDoc}
*/
@Override
public SecuritySessionUserObject getLoggedInUser() {
// **************************
// Obtain Security Context
SecurityContext securityCtx = SecurityContextHolder.getContext();
if (securityCtx == null) {
log.error("Security Context is Null, possible Security Breach Attempt!");
return null;
}
// **************************
// Obtain Authentication
// Object, if no Authentication
// Object (null), then we are
// an "Anonymous" User.
//
Authentication auth = securityCtx.getAuthentication();
if (auth == null) {
return null;
}
if (auth instanceof AuthenticationToken) {
if (((AuthenticationToken) auth).getSecuritySessionUserObject() == null) {
} else {
return ((AuthenticationToken) auth).getSecuritySessionUserObject();
}
} else if (auth instanceof AnonymousAuthenticationToken) {
return null;
} else {
log.warn("Unknown Security Context Authentication of " + auth.getClass().getName());
}
return null;
}
// New with Spring Security 3.0.3
@Override
protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) {
if (log.isDebugEnabled()) {
log.debug("Invoking createSuccessAuthentication");
}
return super.createSuccessAuthentication(principal, authentication, user);
}
@Override
protected void doAfterPropertiesSet() throws Exception {
if (log.isDebugEnabled()) {
log.debug("Invoking doAfterPropertiesSet");
}
super.doAfterPropertiesSet();
}
@Override
public UserCache getUserCache() {
if (log.isDebugEnabled()) {
log.debug("Invoking getUserCache");
}
return super.getUserCache();
}
@Override
public boolean isForcePrincipalAsString() {
if (log.isDebugEnabled()) {
log.debug("Invoking isForcePrincipalAsString");
}
return super.isForcePrincipalAsString();
}
@Override
public boolean isHideUserNotFoundExceptions() {
if (log.isDebugEnabled()) {
log.debug("Invoking isHideUserNotFoundExceptions");
}
return super.isHideUserNotFoundExceptions();
}
@Override
public void setForcePrincipalAsString(boolean forcePrincipalAsString) {
if (log.isDebugEnabled()) {
log.debug("Invoking setForcePrincipalAsString");
}
super.setForcePrincipalAsString(forcePrincipalAsString);
}
@Override
public void setHideUserNotFoundExceptions(boolean hideUserNotFoundExceptions) {
if (log.isDebugEnabled()) {
log.debug("Invoking setHideUserNotFoundExceptions");
}
super.setHideUserNotFoundExceptions(hideUserNotFoundExceptions);
}
@Override
public void setMessageSource(MessageSource messageSource) {
if (log.isDebugEnabled()) {
log.debug("Invoking setMessageSource");
}
super.setMessageSource(messageSource);
}
@Override
public void setUserCache(UserCache userCache) {
if (log.isDebugEnabled()) {
log.debug("Invoking setUserCache");
}
super.setUserCache(userCache);
}
@Override
protected UserDetailsChecker getPreAuthenticationChecks() {
if (log.isDebugEnabled()) {
log.debug("Invoking getPreAuthenticationChecks");
}
return super.getPreAuthenticationChecks();
}
@Override
public void setPreAuthenticationChecks(UserDetailsChecker preAuthenticationChecks) {
if (log.isDebugEnabled()) {
log.debug("Invoking setPreAuthenticationChecks");
}
super.setPreAuthenticationChecks(preAuthenticationChecks);
}
@Override
protected UserDetailsChecker getPostAuthenticationChecks() {
if (log.isDebugEnabled()) {
log.debug("Invoking getPostAuthenticationChecks");
}
return super.getPostAuthenticationChecks();
}
@Override
public void setPostAuthenticationChecks(UserDetailsChecker postAuthenticationChecks) {
if (log.isDebugEnabled()) {
log.debug("Invoking setPostAuthenticationChecks");
}
super.setPostAuthenticationChecks(postAuthenticationChecks);
}
@Override
protected boolean isIncludeDetailsObject() {
if (log.isDebugEnabled()) {
log.debug("Invoking isIncludeDetailsObject");
}
return super.isIncludeDetailsObject();
}
/**
* References our Configuration User Details Service.
*
* @return UserDetailsService
*/
@Override
protected UserDetailsService getUserDetailsService() {
return super.getUserDetailsService();
}
/**
* Configures our User Details Service.
*
* @param userDetailsService
*/
@Override
public void setUserDetailsService(UserDetailsService userDetailsService) {
if (log.isDebugEnabled()) {
log.debug("Invoking setUserDetailsService");
}
super.setUserDetailsService(userDetailsService);
}
@Override
protected PasswordEncoder getPasswordEncoder() {
if (log.isDebugEnabled()) {
log.debug("Invoking getPasswordEncoder");
}
return super.getPasswordEncoder();
}
@Override
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
if (log.isDebugEnabled()) {
log.debug("Invoking setPasswordEncoder");
}
super.setPasswordEncoder(passwordEncoder);
}
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws org.springframework.security.core.AuthenticationException {
if (log.isDebugEnabled()) {
log.debug("Invoking additionalAuthenticationChecks");
}
super.additionalAuthenticationChecks(userDetails, authentication);
}
}