/*
* Copyright 2000-2013 Enonic AS
* http://www.enonic.com/license
*/
package com.enonic.cms.core.security;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.enonic.cms.core.admin.AdminConsoleAccessDeniedException;
import com.enonic.cms.core.log.LogService;
import com.enonic.cms.core.log.LogType;
import com.enonic.cms.core.log.StoreNewLogEntryCommand;
import com.enonic.cms.core.security.group.GroupEntity;
import com.enonic.cms.core.security.group.GroupKey;
import com.enonic.cms.core.security.group.GroupType;
import com.enonic.cms.core.security.group.QualifiedGroupname;
import com.enonic.cms.core.security.user.QualifiedUsername;
import com.enonic.cms.core.security.user.User;
import com.enonic.cms.core.security.user.UserEntity;
import com.enonic.cms.core.security.user.UserKey;
import com.enonic.cms.core.security.user.UserNotFoundException;
import com.enonic.cms.core.security.user.UserSpecification;
import com.enonic.cms.core.security.userstore.UserStoreEntity;
import com.enonic.cms.core.security.userstore.UserStoreKey;
import com.enonic.cms.core.security.userstore.UserStoreNotFoundException;
import com.enonic.cms.core.security.userstore.UserStoreService;
import com.enonic.cms.core.servlet.ServletRequestAccessor;
import com.enonic.cms.core.structure.SiteContext;
import com.enonic.cms.core.structure.SiteKey;
import com.enonic.cms.core.structure.SiteService;
import com.enonic.cms.store.dao.GroupDao;
import com.enonic.cms.store.dao.GroupQuery;
import com.enonic.cms.store.dao.SiteDao;
import com.enonic.cms.store.dao.UserDao;
import com.enonic.cms.store.dao.UserStoreDao;
@Service("securityService")
public class SecurityServiceImpl
implements SecurityService
{
@Autowired
private GroupDao groupDao;
@Autowired
private LogService logService;
@Autowired
private SiteDao siteDao;
@Autowired
private SiteService siteService;
@Autowired
private UserDao userDao;
@Autowired
private UserStoreDao userStoreDao;
@Autowired
private UserStoreService userStoreService;
@Autowired
protected AdminConsoleLoginAccessResolver adminConsoleLoginAccessResolver;
@Value("${cms.security.session.forceNewOnLogin}")
private Boolean forceNewSessionOnLogin;
@Value("${cms.admin.password}")
private String adminUserPassword;
private void initializeSecurityHolder()
{
if ( PortalSecurityHolder.getAnonUser() == null )
{
PortalSecurityHolder.setAnonUser( userDao.findBuiltInAnonymousUser().getKey() );
}
}
/**
* @inheritDoc
*/
public UserKey getAnonymousUserKey()
{
return userDao.findBuiltInAnonymousUser().getKey();
}
public UserEntity getAnonymousUser()
{
return userDao.findBuiltInAnonymousUser();
}
/**
* @inheritDoc
*/
public GroupKey getEnterpriseAdministratorGroup()
{
return groupDao.findBuiltInEnterpriseAdministrator().getGroupKey();
}
public GroupEntity getAuthenticatedUsersGroup( UserStoreEntity userStore )
{
return groupDao.findSingleByGroupTypeAndUserStore( GroupType.AUTHENTICATED_USERS, userStore.getKey() );
}
public UserEntity getUser( UserKey userKey )
{
final UserEntity user = userDao.findByKey( userKey );
if ( user != null && user.isDeleted() )
{
return null;
}
return user;
}
public UserEntity getUser( QualifiedUsername qname )
{
final UserEntity user = userDao.findByQualifiedUsername( qname );
if ( user != null && user.isDeleted() )
{
return null;
}
return user;
}
/**
* @inheritDoc
*/
public UserEntity getUserFromDefaultUserStore( String username )
{
UserStoreEntity defaultUserStore = userStoreService.getDefaultUserStore();
UserSpecification userSpecification = new UserSpecification();
userSpecification.setUserStoreKey( defaultUserStore.getKey() );
userSpecification.setName( username );
userSpecification.setDeletedStateNotDeleted();
return userDao.findSingleBySpecification( userSpecification );
}
public UserEntity getUser( User oldUserObject )
{
return userDao.findByKey( oldUserObject.getKey() );
}
/**
* @inheritDoc
*/
public List<UserEntity> getUsers( UserStoreKey userStoreKey, Integer index, Integer count, boolean includeDeleted )
{
UserStoreEntity userStore = userStoreDao.findByKey( userStoreKey );
if ( userStore == null )
{
throw new UserStoreNotFoundException( userStoreKey );
}
return userDao.findByUserStoreKey( userStore.getKey(), index, count, includeDeleted );
}
/**
* @inheritDoc
*/
public GroupEntity getGroup( QualifiedGroupname qname )
{
if ( qname.isGlobal() )
{
return groupDao.findGlobalGroupByName( qname.getGroupname(), false );
}
else
{
return groupDao.findSingleUndeletedByUserStoreKeyAndGroupname( qname.getUserStoreKey(), qname.getGroupname() );
}
}
/**
* @inheritDoc
*/
public GroupEntity getGroup( GroupKey key )
{
return groupDao.findByKey( key );
}
public List<GroupEntity> getGroups( GroupQuery spec )
{
spec.validate();
return groupDao.findByQuery( spec );
}
public List<UserEntity> findUsersByQuery( UserStoreKey userStoreKey, String queryStr, String orderBy, boolean orderAscending )
{
return userDao.findByQuery( userStoreKey, queryStr, orderBy, orderAscending );
}
public List<UserStoreEntity> getUserStores()
{
return userStoreDao.findAll();
}
/**
* @inheritDoc
*/
public User getLoggedInAdminConsoleUser()
{
final UserKey userKey = doGetUserKeyForLoggedInAdminConsoleUser();
if ( userKey == null )
{
return null;
}
return userStoreService.getUserByKey( userKey );
}
public User getLoggedInClientApiUser()
{
return userStoreService.getUserByKey( doGetUserKeyForLoggedInPortalUser() );
}
public UserEntity getLoggedInClientApiUserAsEntity()
{
return userDao.findByKey( doGetUserKeyForLoggedInPortalUser() );
}
public UserEntity getLoggedInAdminConsoleUserAsEntity()
{
final UserKey userKey = doGetUserKeyForLoggedInAdminConsoleUser();
if ( userKey == null )
{
return null;
}
return userDao.findByKey( userKey );
}
public User getLoggedInPortalUser()
{
return userStoreService.getUserByKey( doGetUserKeyForLoggedInPortalUser() );
}
public UserEntity getLoggedInPortalUserAsEntity()
{
return userDao.findByKey( doGetUserKeyForLoggedInPortalUser() );
}
public UserEntity getImpersonatedPortalUser()
{
UserSpecification userSpecification = new UserSpecification();
userSpecification.setKey( doGetUserKeyForImpersonatedPortalUser() );
userSpecification.setDeletedStateNotDeleted();
return userDao.findSingleBySpecification( userSpecification );
}
public boolean autoLoginPortalUser( QualifiedUsername qualifiedUsername, String remoteIp, SiteKey siteKey )
{
try
{
doLoginPortalUser( qualifiedUsername, null, false );
SiteContext siteContext = this.siteService.getSiteContext( siteKey );
if ( siteContext.isAuthenticationLoggingEnabled() )
{
logAutoLogin( qualifiedUsername, remoteIp, siteKey );
}
return true;
}
catch ( InvalidCredentialsException e )
{
return false;
}
}
public User loginAdminUser( final LoginAdminUserCommand command )
{
return doLoginAdminUser( command.getQualifiedUsername(), command.getPassword(), command.isVerifyPassword() );
}
public boolean autoLoginAdminUser( final QualifiedUsername qualifiedUsername, String remoteIp )
{
try
{
doLoginAdminUser( qualifiedUsername, null, false );
logAutoLogin( qualifiedUsername, remoteIp, null );
return true;
}
catch ( InvalidCredentialsException e )
{
return false;
}
catch ( AdminConsoleAccessDeniedException e )
{
return false;
}
}
public User doLoginAdminUser( final QualifiedUsername qualifiedUsername, final String password, final boolean verifyPassword )
{
final String uid = qualifiedUsername.getUsername();
UserEntity user;
if ( UserEntity.isBuiltInUser( uid ) )
{
UserSpecification userSpec = new UserSpecification();
userSpec.setDeletedStateNotDeleted();
UserKey userKey = authenticateBuiltInUser( uid, password, verifyPassword );
userSpec.setKey( userKey );
user = userDao.findSingleBySpecification( userSpec );
}
else
{
UserStoreEntity userStore;
if ( qualifiedUsername.hasUserStoreSet() )
{
userStore = doResolveUserStore( qualifiedUsername );
}
else
{
userStore = doGetDefaultUserStore();
}
if ( userStore == null )
{
throw new InvalidCredentialsException( qualifiedUsername );
}
if ( verifyPassword )
{
userStoreService.authenticateUser( userStore.getKey(), uid, password );
}
userStoreService.synchronizeUser( userStore.getKey(), uid );
UserSpecification userSpec = new UserSpecification();
userSpec.setDeletedStateNotDeleted();
userSpec.setUserStoreKey( userStore.getKey() );
userSpec.setName( uid );
user = userDao.findSingleBySpecification( userSpec );
}
if ( (user == null) || !adminConsoleLoginAccessResolver.hasAccess( user ) )
{
throw new AdminConsoleAccessDeniedException( qualifiedUsername );
}
AdminSecurityHolder.setUser( user.getKey() );
return userStoreService.getUserByKey( user.getKey() );
}
public void loginPortalUser( final QualifiedUsername qualifiedUsername, final String password )
{
doLoginPortalUser( qualifiedUsername, password, true );
}
public void loginClientApiUser( final QualifiedUsername qualifiedUsername, final String password )
{
doLoginPortalUser( qualifiedUsername, password, true );
}
public void loginDavUser( final QualifiedUsername qualifiedUsername, final String password )
{
doLoginPortalUser( qualifiedUsername, password, true );
}
public void loginInstantTraceUser( final QualifiedUsername qualifiedUsername, final String password )
{
doLoginInstantTraceUser( qualifiedUsername, password );
}
private void doLoginPortalUser( final QualifiedUsername qualifiedUsername, final String password, final boolean verifyPassword )
{
final String uid = qualifiedUsername.getUsername();
if ( this.forceNewSessionOnLogin )
{
createNewSession();
}
if ( UserEntity.isBuiltInUser( uid ) )
{
UserKey userKey = authenticateBuiltInUser( uid, password, verifyPassword );
PortalSecurityHolder.setLoggedInUser( userKey );
}
else
{
UserStoreEntity userStore;
if ( qualifiedUsername.hasUserStoreSet() )
{
userStore = doResolveUserStore( qualifiedUsername );
}
else
{
userStore = doGetDefaultUserStore();
}
if ( userStore == null )
{
throw new InvalidCredentialsException( qualifiedUsername );
}
if ( verifyPassword )
{
userStoreService.authenticateUser( userStore.getKey(), uid, password );
}
userStoreService.synchronizeUser( userStore.getKey(), uid );
UserSpecification userSpec = new UserSpecification();
userSpec.setDeletedStateNotDeleted();
userSpec.setUserStoreKey( userStore.getKey() );
userSpec.setName( uid );
UserEntity user = userDao.findSingleBySpecification( userSpec );
PortalSecurityHolder.setLoggedInUser( user.getKey() );
PortalSecurityHolder.setImpersonatedUser( user.getKey() );
}
}
private void doLoginInstantTraceUser( final QualifiedUsername qualifiedUsername, final String password )
{
final String uid = qualifiedUsername.getUsername();
if ( UserEntity.isBuiltInUser( uid ) )
{
UserKey userKey = authenticateBuiltInUser( uid, password, true );
InstantTraceSecurityHolder.setUser( userKey );
}
else
{
UserStoreEntity userStore;
if ( qualifiedUsername.hasUserStoreSet() )
{
userStore = doResolveUserStore( qualifiedUsername );
}
else
{
userStore = doGetDefaultUserStore();
}
if ( userStore == null )
{
throw new InvalidCredentialsException( qualifiedUsername );
}
userStoreService.authenticateUser( userStore.getKey(), uid, password );
userStoreService.synchronizeUser( userStore.getKey(), uid );
UserSpecification userSpec = new UserSpecification();
userSpec.setDeletedStateNotDeleted();
userSpec.setUserStoreKey( userStore.getKey() );
userSpec.setName( uid );
UserEntity user = userDao.findSingleBySpecification( userSpec );
InstantTraceSecurityHolder.setUser( user.getKey() );
}
}
public UserEntity impersonatePortalUser( final ImpersonateCommand command )
{
Preconditions.checkNotNull( command.getUser() );
if ( command.isRequireAccessCheck() )
{
final User current = getLoggedInPortalUser();
if ( !current.isEnterpriseAdmin() )
{
throw new IllegalArgumentException( "Impersonate not allowed" );
}
}
final UserEntity user = userDao.findByKey( command.getUser() );
if ( user == null )
{
throw new UserNotFoundException( command.getUser() );
}
else if ( user.isAnonymous() )
{
throw new IllegalArgumentException( "Not allowed to impersonate anonymous user, use method removePortalImpersonation instead" );
}
else if ( user.isRoot() )
{
throw new IllegalArgumentException( "Not allowed to impersonate the admin user" );
}
PortalSecurityHolder.setImpersonatedUser( user.getKey() );
return user;
}
@Override
public void removePortalImpersonation()
{
PortalSecurityHolder.removeImpersonatedUser();
}
public void logoutAdminUser()
{
doLogoutAdminUser();
}
public void logoutPortalUser()
{
doLogoutPortalUser( false );
}
public void logoutClientApiUser( boolean invalidateSession )
{
doLogoutPortalUser( invalidateSession );
}
public void changePassword( final QualifiedUsername qualifiedUsername, final String newPassword )
{
final String uid = qualifiedUsername.getUsername();
final UserEntity user = userDao.findByQualifiedUsername( qualifiedUsername );
if ( user == null )
{
throw new InvalidCredentialsException( "Could not find user: " + qualifiedUsername );
}
final UserStoreKey userStoreKey = user.getUserStore() == null ? null : user.getUserStore().getKey();
userStoreService.changePassword( userStoreKey, uid, newPassword );
}
private void doLogoutAdminUser()
{
AdminSecurityHolder.setUser( null );
// Only invalidate session if logged out of both "portal" and "admin". Check portal user!
if ( PortalSecurityHolder.getLoggedInUser() == null )
{
invalidateSession();
}
}
private void doLogoutPortalUser( boolean invalidateSession )
{
PortalSecurityHolder.setLoggedInUser( null );
PortalSecurityHolder.setImpersonatedUser( null );
// Only invalidate session if logged out of both "portal" and "admin". Check admin user!
if ( invalidateSession && AdminSecurityHolder.getUser() == null )
{
invalidateSession();
}
}
private void invalidateSession()
{
HttpServletRequest request = ServletRequestAccessor.getRequest();
HttpSession session = request.getSession( false );
if ( null != session )
{
clearSession( session );
session.invalidate();
}
}
private void createNewSession()
{
HttpServletRequest request = ServletRequestAccessor.getRequest();
if ( request == null )
{
return;
}
HttpSession existingSession = request.getSession( false );
if ( null != existingSession )
{
Map<String, Object> existingAttributes = getCurrentAttributes( existingSession );
clearSession( existingSession );
existingSession.invalidate();
final HttpSession newSession = request.getSession( true );
for ( final String attributeName : existingAttributes.keySet() )
{
newSession.setAttribute( attributeName, existingAttributes.get( attributeName ) );
}
}
}
private Map<String, Object> getCurrentAttributes( final HttpSession existingSession )
{
Map<String, Object> existingAttributes = Maps.newHashMap();
final Enumeration<String> attributeNames = existingSession.getAttributeNames();
while ( attributeNames.hasMoreElements() )
{
final String attributeName = attributeNames.nextElement();
existingAttributes.put( attributeName, existingSession.getAttribute( attributeName ) );
}
return existingAttributes;
}
private void clearSession( HttpSession session )
{
Enumeration attributeNames = session.getAttributeNames();
while ( attributeNames.hasMoreElements() )
{
String attributeName = (String) attributeNames.nextElement();
if ( !attributeName.startsWith( "Instant-Trace-" ) )
{
session.removeAttribute( attributeName );
}
}
}
private UserStoreEntity doGetDefaultUserStore()
{
UserStoreEntity defaultUserStore = userStoreDao.findDefaultUserStore();
if ( defaultUserStore == null )
{
throw new IllegalStateException( "Expected default user store to be set" );
}
return defaultUserStore;
}
private UserStoreEntity doResolveUserStore( final QualifiedUsername qualifiedUsername )
{
UserStoreKey userStoreKey = qualifiedUsername.getUserStoreKey();
if ( userStoreKey != null )
{
/* Key passed in as a part of the qualifiedUsername. Use it. */
return userStoreDao.findByKey( userStoreKey );
}
if ( qualifiedUsername.hasUserStoreNameSet() )
{
/* Key not passed in as a part of the qualifiedUsername. But name is. Trying to find the key from name. */
return userStoreDao.findByName( qualifiedUsername.getUserStoreName() );
}
throw new IllegalArgumentException( "Given qualified username has no user store set" );
}
private UserKey authenticateBuiltInUser( final String uid, final String password, final boolean verifyPassword )
{
final UserEntity user = userDao.findBuiltInGlobalByName( uid );
if ( user == null )
{
throw new IllegalArgumentException( "Could not retrieve built-in user: " + uid );
}
if ( user.isRoot() )
{
if ( !verifyPassword || adminUserPassword.equals( password ) )
{
return user.getKey();
}
throw new InvalidCredentialsException( uid );
}
if ( user.isAnonymous() )
{
return user.getKey();
}
throw new IllegalArgumentException( "Cannot authenticate built in user: " + uid );
}
private UserKey doGetUserKeyForLoggedInPortalUser()
{
initializeSecurityHolder();
return PortalSecurityHolder.getLoggedInUser();
}
private UserKey doGetUserKeyForImpersonatedPortalUser()
{
initializeSecurityHolder();
return PortalSecurityHolder.getImpersonatedUser();
}
private UserKey doGetUserKeyForLoggedInAdminConsoleUser()
{
return AdminSecurityHolder.getUser();
}
private void logAutoLogin( final QualifiedUsername user, final String remoteIp, SiteKey siteKey )
{
UserStoreEntity userStore;
if ( user.hasUserStoreSet() )
{
userStore = doResolveUserStore( user );
}
else
{
userStore = doGetDefaultUserStore();
}
UserEntity userEntity = userDao.findByUserStoreKeyAndUsername( userStore.getKey(), user.getUsername() );
final StoreNewLogEntryCommand command = new StoreNewLogEntryCommand();
command.setType( LogType.AUTO_LOGIN );
command.setInetAddress( remoteIp );
command.setUser( userEntity.getKey() );
command.setTitle( userEntity.getDisplayName() + " (" + userEntity.getName() + ")" );
command.setXmlData( SecurityLoggingXml.createUserStoreDataDoc( user ) );
if ( siteKey != null )
{
command.setSite( siteDao.findByKey( siteKey ) );
}
this.logService.storeNew( command );
}
@Override
public UserEntity findUserByEmail( final String userStoreName, final String email )
{
UserStoreEntity userStore;
if ( Strings.isNullOrEmpty( userStoreName ) )
{
userStore = doGetDefaultUserStore();
}
else
{
userStore = this.userStoreDao.findByName( userStoreName );
}
if ( userStore == null )
{
return null;
}
final UserSpecification spec = new UserSpecification();
spec.setEmail( email );
spec.setUserStoreKey( userStore.getKey() );
spec.setDeletedStateNotDeleted();
final List<UserEntity> users = this.userDao.findBySpecification( spec );
return users.isEmpty() ? null : users.get( 0 );
}
}