/**
* JAS
*/
package jeffaschenk.commons.container.security;
import jeffaschenk.commons.container.security.mapper.SecuritySessionObjectRowMapper;
import jeffaschenk.commons.container.security.mapper.SecuritySessionPermissionRowMapper;
import jeffaschenk.commons.container.security.mapper.SecuritySessionProfileRowMapper;
import jeffaschenk.commons.container.security.mapper.SecuritySessionRoleRowMapper;
import jeffaschenk.commons.container.security.object.SecuritySessionPermissionObject;
import jeffaschenk.commons.container.security.object.SecuritySessionProfileObject;
import jeffaschenk.commons.container.security.object.SecuritySessionRoleObject;
import jeffaschenk.commons.container.security.object.SecuritySessionUserObject;
import jeffaschenk.commons.types.UserStatus;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserCache;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.sql.DataSource;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* Security Service JDBC Implementation Provides Implementation interface to Spring
* Security services and Session information for the Authenticated User.
*
* @author jeffaschenk@gmail.com
* @version $Id: $
*/
@SuppressWarnings("unchecked")
@Component("securityServiceJdbcDao")
public class SecurityServiceJdbcDaoImpl extends JdbcUserDetailsManager implements SecurityServiceJdbcDao {
/**
* DataSource
*/
@Autowired
private DataSource dataSource;
/**
* Initialized Indicator
*/
private boolean initialized = false;
/**
* Constant <code>log</code>
*/
protected static Log log = LogFactory.getLog(SecurityServiceJdbcDaoImpl.class);
/**
* SQL Constants
*/
private static final String UPPER_BEGIN = "trim(upper(";
private static final String UPPER_END = ")) ";
private static final String AND = " and ";
private static final String OR = " or ";
private static final String WHERE = " where ";
/**
* Registered User Session Query
* Lookup by either User's Email Address or Screen Name.
* Obtains additional information to populate to our Session Object for use within the Views.
*/
private static final String REGISTEREDUSER_QUERY =
"select u.ID, u.REGISTEREDUSERPROFILEID, u.EMAILADDRESS, u.PASSWORD, u.REGISTEREDUSERSID, u.SCREENNAME, u.STATUS FROM REGISTEREDUSER u "
+ WHERE + "(" + UPPER_BEGIN + "u.EMAILADDRESS" + UPPER_END + "=" + UPPER_BEGIN + "?" + UPPER_END + " )"
+ OR
+ "(" + UPPER_BEGIN + "u.SCREENNAME" + UPPER_END + "=" + UPPER_BEGIN + "?" + UPPER_END + " )";
/**
* Registered User Session Query
* Lookup by Facebook UUID
* Obtains additional information to populate to our Session Object for use within the Views.
*/
private static final String REGISTEREDUSER_FACEBOOK_UUID_QUERY =
"select u.ID, u.REGISTEREDUSERPROFILEID, u.EMAILADDRESS, u.PASSWORD, u.REGISTEREDUSERSID, u.SCREENNAME, u.STATUS FROM REGISTEREDUSER u, REGISTEREDUSERPROFILE p "
+ WHERE + "( u.REGISTEREDUSERPROFILEID = p.ID)" + AND + "( p.FBUUID" + "=" + " ? )";
/**
* Role User Session Query
* Lookup by either User's Email Address or Screen Name.
* Obtains additional information to populate to our Session Object for use within the Views.
*/
private static final String REGISTEREDUSER_ROLE_QUERY =
"select r.ID, r.NAME FROM REGISTEREDUSER_ROLE ur, ROLE r "
+ WHERE + "( ur.REGISTEREDUSERID = ? and ur.ROLEID = r.ID )";
/**
* Permission User Session Query
* Lookup by either User's Email Address or Screen Name.
* Obtains additional information to populate to our Session Object for use within the Views.
*/
private static final String REGISTEREDUSER_PERMISSION_QUERY =
"select distinct p.ID, p.NAME FROM REGISTEREDUSER_ROLE ur, ROLE r, PERMISSION p, ROLE_PERMISSION rp "
+ WHERE + "( ur.REGISTEREDUSERID = ? and ur.ROLEID = r.ID and r.ID = rp.ROLEID and rp.PERMISSIONID = p.ID )";
/**
* Registered User Profile Session Query
* Obtain additional Information Regarding the RegisteredUser for Session Object if Available.
*/
private static final String REGISTEREDUSER_PROFILE_QUERY =
"select p.FIRSTNAME, p.LASTNAME, p.ALTHOVERTEXT, p.AVATARURL, p.USERLOCALE, p.USERTIMEZONE FROM REGISTEREDUSERPROFILE p "
+ WHERE + "( p.ID = ? )";
/**
* Registered User Login Time Update
* Once authenticated, the Users Last Login Time will be Trapped and Updated on the DB.
*/
private static final String REGISTEREDUSER_LOGIN_TIME_UPDATE =
"update REGISTEREDUSER SET LASTLOGINTIMESTAMP = ? WHERE ID = ?";
/**
* Initialize the Singleton Cache. This method is Inject by the Spring
* Configuration.
*/
@PostConstruct
public synchronized void init() {
// ************************************
// Initialization
log.info("Security Service JDBC DAO starting to Initialize.");
if (this.dataSource == null) {
String emsg = "No DataSource or Jdbc Template has been Configured for this Bean, will be unable to perform functions.";
log.error(emsg);
throw new IllegalStateException(emsg);
}
// *****************************************
// Continue with Initialization
this.setJdbcTemplate(this.createJdbcTemplate(this.dataSource));
this.setEnableGroups(true);
this.setEnableAuthorities(false);
this.setRolePrefix(SecurityServiceProviderImpl.ROLE_PREFIX);
log.info("Security Service JDBC DAO has been Initialized.");
// ***************************************
// Proceed with additional Initialization
this.initialized = true;
}
/**
* <p/>
* Destroy Implemented Spring Interface Method, invoked when bean is removed
* from container.
*/
@PreDestroy
public void destroy() {
log
.info("Security Session Service has been Removed from the Container.");
}
/**
* Obtain the Registered User For Authentication using Principal.
*
* @param principal
* @return List<SecuritySessionUserObject> - Normally will contain Null or One Entry.
*/
@Override
public List<SecuritySessionUserObject> getRegisteredUserForAuthentication(String principal) {
return
(this.initialized) ? this.getJdbcTemplate().query(REGISTEREDUSER_QUERY, new Object[]{principal, principal},
new SecuritySessionObjectRowMapper()) : null;
}
/**
* Obtain the Optional Regiustered User Profile For Authentication using the Registered User's Row Identifier.
*
* @param registeredUserProfileId
* @return List<SecuritySessionProfileObject> - Normally will contain Null or One Entry.
*/
@Override
public List<SecuritySessionProfileObject> getRegisteredUserProfileForAuthentication(BigInteger registeredUserProfileId) {
return
(this.initialized) ? this.getJdbcTemplate().query(REGISTEREDUSER_PROFILE_QUERY, new Object[]{new BigDecimal(registeredUserProfileId)},
new SecuritySessionProfileRowMapper()) : null;
}
/**
* Obtain the Regiustered User Roles For Authentication using the Registered User's Row Identifier.
*
* @param registeredUserId
* @return List<SecuritySessionRoleObject> - Roles Assigned to this User.
*/
@Override
public List<SecuritySessionRoleObject> getRegisteredUserRolesForAuthentication(BigInteger registeredUserId) {
return
(this.initialized) ? this.getJdbcTemplate().query(REGISTEREDUSER_ROLE_QUERY, new Object[]{new BigDecimal(registeredUserId)},
new SecuritySessionRoleRowMapper()) : null;
}
/**
* Obtain the Registered User Permissions For Authentication using the Registered User's Row Identifier.
*
* @param registeredUserId
* @return List<SecuritySessionPermissionObject> - Permissions Assigned to this User.
*/
@Override
public List<SecuritySessionPermissionObject> getRegisteredUserPermissionsForAuthentication(BigInteger registeredUserId) {
return
(this.initialized) ? this.getJdbcTemplate().query(REGISTEREDUSER_PERMISSION_QUERY, new Object[]{new BigDecimal(registeredUserId)},
new SecuritySessionPermissionRowMapper()) : null;
}
/**
* Helper method to perform a Database Query to populate
* the Session Object so that specific information regarding the User is available
* especially for the view.
*
* @param principal
* @param sessionId
* @return SecuritySessionUserObject
*/
@Override
public SecuritySessionUserObject createSecuritySessionObject(String principal, String sessionId) {
// ******************************************
// Obtain the Result List
List<SecuritySessionUserObject> results = this.getRegisteredUserForAuthentication(principal);
if (results.size() > 0) {
SecuritySessionUserObject securitySessionUserObject = results.get(0);
securitySessionUserObject.setSessionId(sessionId);
securitySessionUserObject.setPrincipal(principal);
// *********************************************
// Obtain the optional Registered User Profile
if (securitySessionUserObject.getRegisteredUserProfileId() != null) {
List<SecuritySessionProfileObject> profiles =
this.getRegisteredUserProfileForAuthentication(securitySessionUserObject.getRegisteredUserProfileId());
if ((profiles != null) && (profiles.size() == 1)) {
securitySessionUserObject.setSecuritySessionProfileObject(profiles.get(0));
}
}
// ******************************************
// Obtain the Roles for this RegisteredUser
securitySessionUserObject.setSecuritySessionRoles(
this.getRegisteredUserRolesForAuthentication(securitySessionUserObject.getRegisteredUserId()));
// ******************************************
// Obtain the Permissions for this RegisteredUser
securitySessionUserObject.setSecuritySessionPermissions(
this.getRegisteredUserPermissionsForAuthentication(securitySessionUserObject.getRegisteredUserId()));
// ******************************************
// return the Security session object
return securitySessionUserObject;
} else {
return null;
}
}
/**
* Log the Login Time of the Registered User
*
* @param registeredUserId
* @param loginTime
*/
@Override
public void setLogInTimeOfRegisteredUser(BigInteger registeredUserId, Date loginTime) {
if (this.initialized) {
try {
this.getJdbcTemplate().update(REGISTEREDUSER_LOGIN_TIME_UPDATE,
new Object[]{new java.sql.Timestamp(loginTime.getTime()), new BigDecimal(registeredUserId)},
new int[]{java.sql.Types.TIMESTAMP, java.sql.Types.DECIMAL});
} catch (Exception e) {
log.error("Exception occurred while updating the Last Login Time for a Registered User, Ignoring, Login Allowed.", e);
}
}
}
@Override
public void createUser(UserDetails user) {
if (log.isDebugEnabled()) {
log.debug("Invoking createUser");
}
super.createUser(user);
}
@Override
public void setUserCache(UserCache userCache) {
if (log.isDebugEnabled()) {
log.debug("Invoking setuserCache");
}
super.setUserCache(userCache);
}
@Override
public void setFindAllGroupsSql(String findAllGroupsSql) {
if (log.isDebugEnabled()) {
log.debug("Invoking setFindAllGroupsSql");
}
super.setFindAllGroupsSql(findAllGroupsSql);
}
@Override
public void setChangePasswordSql(String changePasswordSql) {
if (log.isDebugEnabled()) {
log.debug("Invoking setChangePasswordSql");
}
super.setChangePasswordSql(changePasswordSql);
}
@Override
public void setUserExistsSql(String userExistsSql) {
if (log.isDebugEnabled()) {
log.debug("Invoking setUserExistsSql");
}
super.setUserExistsSql(userExistsSql);
}
@Override
public void setDeleteUserAuthoritiesSql(String deleteUserAuthoritiesSql) {
if (log.isDebugEnabled()) {
log.debug("Invoking setDeleteUserAuthoritiesSql");
}
super.setDeleteUserAuthoritiesSql(deleteUserAuthoritiesSql);
}
@Override
public void setCreateAuthoritySql(String createAuthoritySql) {
if (log.isDebugEnabled()) {
log.debug("Invoking setCreateAuthoritySql");
}
super.setCreateAuthoritySql(createAuthoritySql);
}
@Override
public void setUpdateUserSql(String updateUserSql) {
if (log.isDebugEnabled()) {
log.debug("Invoking setUpdateUserSql");
}
super.setUpdateUserSql(updateUserSql);
}
@Override
public void setDeleteUserSql(String deleteUserSql) {
if (log.isDebugEnabled()) {
log.debug("Invoking setDeleteUserSql");
}
super.setDeleteUserSql(deleteUserSql);
}
@Override
public void setCreateUserSql(String createUserSql) {
if (log.isDebugEnabled()) {
log.debug("Invoking setCreateUserSql");
}
super.setCreateUserSql(createUserSql);
}
@Override
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
if (log.isDebugEnabled()) {
log.debug("Invoking setAuthenticationManager");
}
super.setAuthenticationManager(authenticationManager);
}
@Override
public void addGroupAuthority(String groupName, GrantedAuthority authority) {
if (log.isDebugEnabled()) {
log.debug("Invoking addGroupAuthority");
}
super.addGroupAuthority(groupName, authority);
}
@Override
public void removeGroupAuthority(String groupName, GrantedAuthority authority) {
if (log.isDebugEnabled()) {
log.debug("Invoking removeGroupAuthority");
}
super.removeGroupAuthority(groupName, authority);
}
@Override
public List<GrantedAuthority> findGroupAuthorities(String groupName) {
if (log.isDebugEnabled()) {
log.debug("Invoking findGroupAuthorities");
}
return super.findGroupAuthorities(groupName);
}
@Override
public void removeUserFromGroup(String username, String groupName) {
if (log.isDebugEnabled()) {
log.debug("Invoking removeUserFromGroup");
}
super.removeUserFromGroup(username, groupName);
}
@Override
public void addUserToGroup(String username, String groupName) {
if (log.isDebugEnabled()) {
log.debug("Invoking addUserToGroup");
}
super.addUserToGroup(username, groupName);
}
@Override
public void renameGroup(String oldName, String newName) {
if (log.isDebugEnabled()) {
log.debug("Invoking renameGroup");
}
super.renameGroup(oldName, newName);
}
@Override
public void deleteGroup(String groupName) {
if (log.isDebugEnabled()) {
log.debug("Invoking deleteGroup");
}
super.deleteGroup(groupName);
}
@Override
public void createGroup(String groupName, List<GrantedAuthority> authorities) {
if (log.isDebugEnabled()) {
log.debug("Invoking createGroup");
}
super.createGroup(groupName, authorities);
}
@Override
public List<String> findUsersInGroup(String groupName) {
if (log.isDebugEnabled()) {
log.debug("Invoking findUsersInGroup");
}
return super.findUsersInGroup(groupName);
}
@Override
public List<String> findAllGroups() {
if (log.isDebugEnabled()) {
log.debug("Invoking findAllGroups");
}
return super.findAllGroups();
}
@Override
public boolean userExists(String username) {
if (log.isDebugEnabled()) {
log.debug("Invoking userExists");
}
return super.userExists(username);
}
@Override
protected Authentication createNewAuthentication(Authentication currentAuth, String newPassword) {
if (log.isDebugEnabled()) {
log.debug("Invoking createNewAuthentication");
}
return super.createNewAuthentication(currentAuth, newPassword);
}
@Override
public void updateUser(UserDetails user) {
if (log.isDebugEnabled()) {
log.debug("Invoking updateUser");
}
super.updateUser(user);
}
@Override
public void deleteUser(String username) {
if (log.isDebugEnabled()) {
log.debug("Invoking deleteUser");
}
super.deleteUser(username);
}
@Override
public void changePassword(String oldPassword, String newPassword) throws org.springframework.security.core.AuthenticationException {
if (log.isDebugEnabled()) {
log.debug("Invoking changePassword");
}
super.changePassword(oldPassword, newPassword);
}
@Override
protected void addCustomAuthorities(String username, List<GrantedAuthority> authorities) {
if (log.isDebugEnabled()) {
log.debug("Invoking addCustomAuthorities");
}
super.addCustomAuthorities(username, authorities);
}
@Override
public String getUsersByUsernameQuery() {
if (log.isDebugEnabled()) {
log.debug("Invoking getUsersByUsernameQuery");
}
return super.getUsersByUsernameQuery();
}
/**
* load User By UserName
*
* @param username
* @return UserDetails
* @throws UsernameNotFoundException
* @throws DataAccessException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
List<SecuritySessionUserObject> results =
this.getRegisteredUserForAuthentication(username);
// ******************************************
// Check for nothing found.
if ((results == null) || (results.size() <= 0)) {
String emsg = "Authenticating Principal:[" + username + "], Failed due to invalid (Not Found) Principal, please Register";
log.warn(emsg);
throw new UsernameNotFoundException(emsg);
}
// ******************************************
// Only Accept an Exact Hit.
if (results.size() > 1) {
String emsg = "Authenticating Principal:[" + username + "], lookup yield more than one hit, can not reliably authenticate User";
log.warn(emsg);
throw new AuthenticationException(emsg);
}
// ******************************************
// Only Accept an Exact Hit.
SecuritySessionUserObject securitySessionUserObject = results.get(0);
if (securitySessionUserObject == null) {
String emsg = "Authenticating Principal:[" + username + "], lookup yield exactly one hit, but Session Object is null";
log.warn(emsg);
throw new AuthenticationException(emsg);
}
// ***********************************
// Check the User Status
// Can not Login if the User is New,
// Suspended or Terminated.
// Provide specific messages for each
// state.
securitySessionUserObject.setPrincipal(username);
if (securitySessionUserObject.getUserStatus().equalsIgnoreCase(UserStatus.SUSPENDED.code())) {
String emsg = "Authenticating Principal:[" + username + "], Account has been Suspended, Please Call Customer Support.";
log.warn(emsg);
throw new AuthenticationException(emsg);
} else if (securitySessionUserObject.getUserStatus().equalsIgnoreCase(UserStatus.TERMINATED.code())) {
String emsg = "Authenticating Principal:[" + username + "], Account has been Terminated, Please Call Customer Support.";
log.warn(emsg);
throw new AuthenticationException(emsg);
} else if (securitySessionUserObject.getUserStatus().equalsIgnoreCase(UserStatus.NEW.code())) {
String emsg = "Authenticating Principal:[" + username + "], Account has not been verified yet, Please Check your Email Inbox for a Registration Email.";
log.warn(emsg);
throw new AuthenticationException(emsg);
} else if (securitySessionUserObject.getUserStatus().equalsIgnoreCase(UserStatus.DEACTIVATED.code())) {
String emsg = "Authenticating Principal:[" + username + "], Account has been Deactivated, you will need your Account Reactivated.";
log.warn(emsg);
throw new AuthenticationException(emsg);
}
// *****************************************
// Return the Object, never allow an object
// unless Activated?(new|Validated)
// Nothing authticated yet, just
// we found our User Object.
//
return securitySessionUserObject;
}
/**
* Load All Applicable Users by User Name.
*
* @param username
* @return List<UserDetails>
*/
@Override
protected List<UserDetails> loadUsersByUsername(String username) {
List<SecuritySessionUserObject> results =
this.getRegisteredUserForAuthentication(username);
List<UserDetails> filteredResults = new ArrayList<UserDetails>(results.size());
for (SecuritySessionUserObject securitySessionUserObject : results) {
if (securitySessionUserObject.getUserStatus().equalsIgnoreCase(UserStatus.VERIFIED.code())) {
filteredResults.add(securitySessionUserObject);
}
}
// ************************
// Return Filtered Results
return filteredResults;
}
@Override
public void setEnableGroups(boolean enableGroups) {
if (log.isDebugEnabled()) {
log.debug("Invoking setEnableGroups");
}
super.setEnableGroups(enableGroups);
}
@Override
protected boolean getEnableGroups() {
if (log.isDebugEnabled()) {
log.debug("Invoking getEnableGroups");
}
return super.getEnableGroups();
}
@Override
public void setEnableAuthorities(boolean enableAuthorities) {
if (log.isDebugEnabled()) {
log.debug("Invoking setEnableAuthorities");
}
super.setEnableAuthorities(enableAuthorities);
}
@Override
protected boolean getEnableAuthorities() {
if (log.isDebugEnabled()) {
log.debug("Invoking getEnableAuthorities");
}
return super.getEnableAuthorities();
}
@Override
public void setUsersByUsernameQuery(String usersByUsernameQueryString) {
if (log.isDebugEnabled()) {
log.debug("Invoking setUsersByUsernameQuery");
}
super.setUsersByUsernameQuery(usersByUsernameQueryString);
}
@Override
protected boolean isUsernameBasedPrimaryKey() {
if (log.isDebugEnabled()) {
log.debug("Invoking isUsernameBasedPrimaryKey");
}
return super.isUsernameBasedPrimaryKey();
}
@Override
public void setUsernameBasedPrimaryKey(boolean usernameBasedPrimaryKey) {
if (log.isDebugEnabled()) {
log.debug("Invoking setUsernameBasedPrimaryKey");
}
super.setUsernameBasedPrimaryKey(usernameBasedPrimaryKey);
}
@Override
protected String getRolePrefix() {
if (log.isDebugEnabled()) {
log.debug("Invoking getRolePrefix");
}
return super.getRolePrefix();
}
@Override
public void setRolePrefix(String rolePrefix) {
if (log.isDebugEnabled()) {
log.debug("Invoking setRolePrefix");
}
super.setRolePrefix(rolePrefix);
}
@Override
public void setGroupAuthoritiesByUsernameQuery(String queryString) {
if (log.isDebugEnabled()) {
log.debug("Invoking setGroupAuthoritiesByUsernameQuery");
}
super.setGroupAuthoritiesByUsernameQuery(queryString);
}
@Override
protected String getAuthoritiesByUsernameQuery() {
if (log.isDebugEnabled()) {
log.debug("Invoking getAuthoritiesByUsernameQuery");
}
return super.getAuthoritiesByUsernameQuery();
}
@Override
public void setAuthoritiesByUsernameQuery(String queryString) {
if (log.isDebugEnabled()) {
log.debug("Invoking setAuthoritiesByUsernameQuery");
}
super.setAuthoritiesByUsernameQuery(queryString);
}
@Override
protected UserDetails createUserDetails(String username, UserDetails userFromUserQuery, List<GrantedAuthority> combinedAuthorities) {
if (log.isDebugEnabled()) {
log.debug("Invoking createUserDetails");
}
return super.createUserDetails(username, userFromUserQuery, combinedAuthorities);
}
@Override
protected List<GrantedAuthority> loadGroupAuthorities(String username) {
if (log.isDebugEnabled()) {
log.debug("Invoking loadGroupAuthorities");
}
return super.loadGroupAuthorities(username);
}
@Override
protected List<GrantedAuthority> loadUserAuthorities(String username) {
if (log.isDebugEnabled()) {
log.debug("Invoking loadUserAuthorities");
}
return super.loadUserAuthorities(username);
}
}