/* * JBoss, Home of Professional Open Source. * Copyright 2012, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.picketlink.internal; import org.picketlink.Identity; import org.picketlink.annotations.PicketLink; import org.picketlink.authentication.AuthenticationException; import org.picketlink.authentication.Authenticator; import org.picketlink.authentication.LockedAccountException; import org.picketlink.authentication.UnexpectedCredentialException; import org.picketlink.authentication.UserAlreadyLoggedInException; import org.picketlink.authentication.event.AlreadyLoggedInEvent; import org.picketlink.authentication.event.LockedAccountEvent; import org.picketlink.authentication.event.LoggedInEvent; import org.picketlink.authentication.event.LoginFailedEvent; import org.picketlink.authentication.event.PostAuthenticateEvent; import org.picketlink.authentication.event.PostLoggedOutEvent; import org.picketlink.authentication.event.PreAuthenticateEvent; import org.picketlink.authentication.event.PreLoggedOutEvent; import org.picketlink.authentication.internal.IdmAuthenticator; import org.picketlink.authentication.levels.DifferentUserLoggedInExcpetion; import org.picketlink.authentication.levels.Level; import org.picketlink.authentication.levels.SecurityLevelManager; import org.picketlink.common.properties.Property; import org.picketlink.common.properties.query.AnnotatedPropertyCriteria; import org.picketlink.common.properties.query.PropertyQueries; import org.picketlink.credential.DefaultLoginCredentials; import org.picketlink.idm.IDMMessages; import org.picketlink.idm.model.Account; import org.picketlink.idm.model.annotation.StereotypeProperty; import org.picketlink.idm.permission.spi.PermissionResolver; import javax.enterprise.inject.Instance; import javax.inject.Inject; import java.io.Serializable; import java.util.List; import static org.picketlink.log.BaseLog.AUTHENTICATION_LOGGER; /** * <p>Base implementation for {@link org.picketlink.Identity} types.</p> * * @author Shane Bryzak * @author Pedro Igor */ public abstract class AbstractIdentity implements Identity { private static final long serialVersionUID = 8655816330461907668L; @Inject private CDIEventBridge eventBridge; @Inject private DefaultLoginCredentials loginCredential; @Inject @PicketLink private Instance<Authenticator> authenticatorInstance; @Inject private Instance<IdmAuthenticator> idmAuthenticatorInstance; @Inject private Instance<PermissionResolver> permissionResolver; @Inject private Instance<SecurityLevelManager> securityLevelManager; /** * Flag indicating whether we are currently authenticating */ private boolean authenticating; private Account account; private Level securityLevel; public boolean isLoggedIn() { // If there is an account set, then the account is logged in. return this.account != null; } @Override public Account getAccount() { return this.account; } @Override public Level getLevel() { if(securityLevel == null){ securityLevel = getSecurityLevelManager().resolveSecurityLevel(); } return securityLevel; } @Override public AuthenticationResult login() { try { if (AUTHENTICATION_LOGGER.isDebugEnabled()) { AUTHENTICATION_LOGGER.debugf("Performing authentication using credentials [%s]. User id is [%s].", this.loginCredential .getCredential(), this.loginCredential.getUserId()); } Account validatedAccount = null; if (isLoggedIn()) { if (getSecurityLevelManager().resolveSecurityLevel().compareTo(securityLevel) <= 0) { throw new UserAlreadyLoggedInException("active agent: " + this.account.toString()); } else { validatedAccount = authenticate(); if(validatedAccount != null){ //obtain username of the current user and the user which is trying to rise level Property firstdeclaredField = getDefaultLoginNameProperty(validatedAccount.getClass()); Object firstUsername = firstdeclaredField.getValue(validatedAccount); Property seconddeclaredField = getDefaultLoginNameProperty(this.account.getClass()); Object secondUsername = seconddeclaredField.getValue(this.account); //if the second user does not have username or they does not match, then exception is thrown if(secondUsername == null || !secondUsername.equals(firstUsername)){ throw new DifferentUserLoggedInExcpetion("active agent: "+ this.account.toString() + " but agent: "+ validatedAccount.toString() + " is trying to log in"); } } } } else{ validatedAccount = authenticate(); } if (validatedAccount != null) { if (!validatedAccount.isEnabled()) { throw new LockedAccountException("Account [" + validatedAccount + "] is disabled."); } handleSuccessfulLoginAttempt(validatedAccount); return AuthenticationResult.SUCCESS; } handleUnsuccesfulLoginAttempt(null); return AuthenticationResult.FAILED; } catch (Exception e) { handleUnsuccesfulLoginAttempt(e); if (AuthenticationException.class.isInstance(e)) { throw (AuthenticationException) e; } throw new AuthenticationException("Login failed with a unexpected error.", e); } finally { if (AUTHENTICATION_LOGGER.isDebugEnabled()) { AUTHENTICATION_LOGGER .debugf("Authentication is finished using credentials [%s]. User id is [%s].", this.loginCredential .getCredential(), this.loginCredential.getUserId()); } } } protected void handleSuccessfulLoginAttempt(Account validatedAccount) { AUTHENTICATION_LOGGER.debugf("Authentication was successful for credentials [%s]. User id is [%s].", this.loginCredential.getCredential(), this.loginCredential.getUserId()); this.account = validatedAccount; securityLevel = getSecurityLevelManager().resolveSecurityLevel(); eventBridge.fireEvent(new LoggedInEvent()); } protected void handleUnsuccesfulLoginAttempt(Throwable e) { if (e != null) { if (UnexpectedCredentialException.class.isInstance(e)) { //X TODO discuss special handling of UnexpectedCredentialException } else if (UserAlreadyLoggedInException.class.isInstance(e)) { eventBridge.fireEvent(new AlreadyLoggedInEvent()); } else if (LockedAccountException.class.isInstance(e)) { eventBridge.fireEvent(new LockedAccountEvent()); } } if (AUTHENTICATION_LOGGER.isDebugEnabled()) { AUTHENTICATION_LOGGER.authenticationFailed(this.loginCredential.getUserId(), e); } eventBridge.fireEvent(new LoginFailedEvent(e)); } protected Account authenticate() throws AuthenticationException { Account validatedAccount = null; if (authenticating) { authenticating = false; //X TODO discuss it throw new IllegalStateException("Authentication already in progress."); } try { authenticating = true; eventBridge.fireEvent(new PreAuthenticateEvent()); Authenticator authenticator = getAuthenticator(); if (AUTHENTICATION_LOGGER.isDebugEnabled()) { AUTHENTICATION_LOGGER.debugf("Authentication is going to be performed by authenticator [%s]", authenticator); } authenticator.authenticate(); if (authenticator.getStatus() == null) { throw new AuthenticationException("Authenticator must return a valid authentication status"); } if (authenticator.getStatus() == Authenticator.AuthenticationStatus.SUCCESS) { validatedAccount = authenticator.getAccount(); postAuthenticate(authenticator); } } catch (AuthenticationException e) { throw e; } catch (Throwable ex) { throw new AuthenticationException("Authentication failed.", ex); } finally { authenticating = false; } return validatedAccount; } private Authenticator getAuthenticator() throws AuthenticationException { Authenticator authenticator = authenticatorInstance.isUnsatisfied() ? idmAuthenticatorInstance.get() : authenticatorInstance.get(); if (authenticator == null) { throw new AuthenticationException("No Authenticator has been configured."); } return authenticator; } protected void postAuthenticate(Authenticator authenticator) { authenticator.postAuthenticate(); if (!authenticator.getStatus().equals(Authenticator.AuthenticationStatus.SUCCESS)) { return; } eventBridge.fireEvent(new PostAuthenticateEvent()); } @Override public void logout() { logout(true); } protected void logout(boolean invalidateLoginCredential) { if (isLoggedIn()) { eventBridge.fireEvent(new PreLoggedOutEvent(this.account)); PostLoggedOutEvent postLoggedOutEvent = new PostLoggedOutEvent(this.account); unAuthenticate(invalidateLoginCredential); eventBridge.fireEvent(postLoggedOutEvent); } } /** * Resets all security state and loginCredential */ private void unAuthenticate(boolean invalidateLoginCredential) { this.account = null; this.securityLevel = getSecurityLevelManager().resolveSecurityLevel(); if (invalidateLoginCredential) { loginCredential.invalidate(); } } public boolean hasPermission(Object resource, String operation) { return isLoggedIn() && getPermissionResolver().resolvePermission(this.account, resource, operation); } public boolean hasPermission(Class<?> resourceClass, Serializable identifier, String operation) { return isLoggedIn() && getPermissionResolver().resolvePermission(this.account, resourceClass, identifier, operation); } protected Property getDefaultLoginNameProperty(Class<? extends Account> accountType) { List<Property<Object>> properties = PropertyQueries .createQuery(accountType) .addCriteria(new AnnotatedPropertyCriteria(StereotypeProperty.class)).getResultList(); for (Property property : properties) { StereotypeProperty stereotypeProperty = property.getAnnotatedElement().getAnnotation(StereotypeProperty.class); if (StereotypeProperty.Property.IDENTITY_USER_NAME.equals(stereotypeProperty.value())) { return property; } } throw IDMMessages.MESSAGES.credentialUnknownUserNameProperty(accountType); } private SecurityLevelManager getSecurityLevelManager() { return securityLevelManager.get(); } private PermissionResolver getPermissionResolver() { return permissionResolver.get(); } }