/* * oxAuth is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. * * Copyright (c) 2014, Gluu */ package org.xdi.oxauth.service; import org.apache.commons.lang.StringUtils; import org.gluu.jsf2.service.FacesService; import org.gluu.site.ldap.persistence.LdapEntryManager; import org.gluu.site.ldap.persistence.exception.EntryPersistenceException; import org.slf4j.Logger; import org.xdi.ldap.model.CustomAttribute; import org.xdi.ldap.model.CustomEntry; import org.xdi.ldap.model.GluuStatus; import org.xdi.model.SimpleProperty; import org.xdi.model.ldap.GluuLdapConfiguration; import org.xdi.model.metric.MetricType; import org.xdi.model.security.Credentials; import org.xdi.oxauth.model.authorize.AuthorizeRequestParam; import org.xdi.oxauth.model.common.SessionState; import org.xdi.oxauth.model.common.SimpleUser; import org.xdi.oxauth.model.common.User; import org.xdi.oxauth.model.config.Constants; import org.xdi.oxauth.model.configuration.AppConfiguration; import org.xdi.oxauth.model.registration.Client; import org.xdi.oxauth.model.session.SessionClient; import org.xdi.oxauth.model.util.Util; import org.xdi.oxauth.security.Identity; import org.xdi.oxauth.service.external.ExternalAuthenticationService; import org.xdi.util.StringHelper; import javax.annotation.Nonnull; import javax.ejb.Stateless; import javax.faces.context.ExternalContext; import javax.inject.Inject; import javax.inject.Named; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.*; import java.util.Map.Entry; import static org.xdi.oxauth.model.authorize.AuthorizeResponseParam.SESSION_STATE; /** * Authentication service methods * * @author Yuriy Movchan * @author Javier Rojas Blum * @version December 26, 2016 */ @Stateless @Named public class AuthenticationService { // use only "acr" instead of "acr_values" #334 public static final List<String> ALLOWED_PARAMETER = Collections.unmodifiableList(Arrays.asList( AuthorizeRequestParam.SCOPE, AuthorizeRequestParam.RESPONSE_TYPE, AuthorizeRequestParam.CLIENT_ID, AuthorizeRequestParam.REDIRECT_URI, AuthorizeRequestParam.STATE, AuthorizeRequestParam.RESPONSE_MODE, AuthorizeRequestParam.NONCE, AuthorizeRequestParam.DISPLAY, AuthorizeRequestParam.PROMPT, AuthorizeRequestParam.MAX_AGE, AuthorizeRequestParam.UI_LOCALES, AuthorizeRequestParam.ID_TOKEN_HINT, AuthorizeRequestParam.LOGIN_HINT, AuthorizeRequestParam.ACR_VALUES, AuthorizeRequestParam.SESSION_STATE, AuthorizeRequestParam.REQUEST, AuthorizeRequestParam.REQUEST_URI, AuthorizeRequestParam.ORIGIN_HEADERS, AuthorizeRequestParam.CODE_CHALLENGE, AuthorizeRequestParam.CODE_CHALLENGE_METHOD, AuthorizeRequestParam.CUSTOM_RESPONSE_HEADERS)); private static final String EVENT_CONTEXT_AUTHENTICATED_USER = "authenticatedUser"; @Inject private Logger log; @Inject private AppConfiguration appConfiguration; @Inject private Identity identity; @Inject private Credentials credentials; @Inject @Named(AppInitializer.LDAP_AUTH_CONFIG_NAME) private List<GluuLdapConfiguration> ldapAuthConfigs; @Inject private LdapEntryManager ldapEntryManager; @Inject @Named(AppInitializer.LDAP_AUTH_ENTRY_MANAGER_NAME) private List<LdapEntryManager> ldapAuthEntryManagers; @Inject private UserService userService; @Inject private ClientService clientService; @Inject private SessionStateService sessionStateService; @Inject private ExternalAuthenticationService externalAuthenticationService; @Inject private MetricService metricService; @Inject private ExternalContext externalContext; @Inject private FacesService facesService; /** * Authenticate user. * * @param userName The username. * @param password The user's password. * @return <code>true</code> if success, otherwise <code>false</code>. */ public boolean authenticate(String userName, String password) { log.debug("Authenticating user with LDAP: username: '{}', credentials: '{}'", userName, System.identityHashCode(credentials)); boolean authenticated = false; com.codahale.metrics.Timer.Context timerContext = metricService.getTimer(MetricType.OXAUTH_USER_AUTHENTICATION_RATE).time(); try { if ((this.ldapAuthConfigs == null) || (this.ldapAuthConfigs.size() == 0)) { authenticated = localAuthenticate(userName, password); } else { authenticated = externalAuthenticate(userName, password); } } finally { timerContext.stop(); } setAuthenticatedUserSessionAttribute(userName, authenticated); MetricType metricType; if (authenticated) { metricType = MetricType.OXAUTH_USER_AUTHENTICATION_SUCCESS; } else { metricType = MetricType.OXAUTH_USER_AUTHENTICATION_FAILURES; } metricService.incCounter(metricType); return authenticated; } private void setAuthenticatedUserSessionAttribute(String userName, boolean authenticated) { SessionState sessionState = sessionStateService.getSessionState(); if (sessionState != null) { Map<String, String> sessionIdAttributes = sessionState.getSessionAttributes(); if (authenticated) { sessionIdAttributes.put(Constants.AUTHENTICATED_USER, userName); } sessionStateService.updateSessionStateIfNeeded(sessionState, authenticated); } } private boolean localAuthenticate(String userName, String password) { User user = userService.getUser(userName); if (user != null) { if (!checkUserStatus(user)) { return false; } // Use local LDAP server for user authentication boolean authenticated = ldapEntryManager.authenticate(user.getDn(), password); if (authenticated) { configureAuthenticatedUser(user); updateLastLogonUserTime(user); log.trace("Authenticate: credentials: '{}', credentials.userName: '{}', authenticatedUser.userId: '{}'", System.identityHashCode(credentials), credentials.getUsername(), getAuthenticatedUserId()); } return authenticated; } return false; } private boolean externalAuthenticate(String keyValue, String password) { for (int i = 0; i < this.ldapAuthConfigs.size(); i++) { GluuLdapConfiguration ldapAuthConfig = this.ldapAuthConfigs.get(i); LdapEntryManager ldapAuthEntryManager = this.ldapAuthEntryManagers.get(i); String primaryKey = "uid"; if (StringHelper.isNotEmpty(ldapAuthConfig.getPrimaryKey())) { primaryKey = ldapAuthConfig.getPrimaryKey(); } String localPrimaryKey = "uid"; if (StringHelper.isNotEmpty(ldapAuthConfig.getLocalPrimaryKey())) { localPrimaryKey = ldapAuthConfig.getLocalPrimaryKey(); } boolean authenticated = authenticate(ldapAuthConfig, ldapAuthEntryManager, keyValue, password, primaryKey, localPrimaryKey); if (authenticated) { return authenticated; } } return false; } public boolean authenticate(String keyValue, String password, String primaryKey, String localPrimaryKey) { if (this.ldapAuthConfigs == null) { return authenticate(null, ldapEntryManager, keyValue, password, primaryKey, localPrimaryKey); } boolean authenticated = false; com.codahale.metrics.Timer.Context timerContext = metricService.getTimer(MetricType.OXAUTH_USER_AUTHENTICATION_RATE).time(); try { for (int i = 0; i < this.ldapAuthConfigs.size(); i++) { GluuLdapConfiguration ldapAuthConfig = this.ldapAuthConfigs.get(i); LdapEntryManager ldapAuthEntryManager = this.ldapAuthEntryManagers.get(i); authenticated = authenticate(ldapAuthConfig, ldapAuthEntryManager, keyValue, password, primaryKey, localPrimaryKey); if (authenticated) { break; } } } finally { timerContext.stop(); } MetricType metricType; if (authenticated) { metricType = MetricType.OXAUTH_USER_AUTHENTICATION_SUCCESS; } else { metricType = MetricType.OXAUTH_USER_AUTHENTICATION_FAILURES; } metricService.incCounter(metricType); return authenticated; } /* * Utility method which can be used in custom scripts */ public boolean authenticate(GluuLdapConfiguration ldapAuthConfig, LdapEntryManager ldapAuthEntryManager, String keyValue, String password, String primaryKey, String localPrimaryKey) { log.debug("Attempting to find userDN by primary key: '{}' and key value: '{}', credentials: '{}'", primaryKey, keyValue, System.identityHashCode(credentials)); List<?> baseDNs; if (ldapAuthConfig == null) { baseDNs = Arrays.asList(userService.getDnForUser(null)); } else { baseDNs = ldapAuthConfig.getBaseDNs(); } if (baseDNs != null && !baseDNs.isEmpty()) { for (Object baseDnProperty : baseDNs) { String baseDn; if (baseDnProperty instanceof SimpleProperty) { baseDn = ((SimpleProperty) baseDnProperty).getValue(); } else { baseDn = baseDnProperty.toString(); } User user = getUserByAttribute(ldapAuthEntryManager, baseDn, primaryKey, keyValue); if (user != null) { String userDn = user.getDn(); log.debug("Attempting to authenticate userDN: {}", userDn); if (ldapAuthEntryManager.authenticate(userDn, password)) { log.debug("User authenticated: {}", userDn); log.debug("Attempting to find userDN by local primary key: {}", localPrimaryKey); User localUser = userService.getUserByAttribute(localPrimaryKey, keyValue); if (localUser != null) { if (!checkUserStatus(localUser)) { return false; } configureAuthenticatedUser(localUser); updateLastLogonUserTime(localUser); log.trace("authenticate_external: credentials: '{}', credentials.userName: '{}', authenticatedUser.userId: '{}'", System.identityHashCode(credentials), credentials.getUsername(), getAuthenticatedUserId()); return true; } } } } } else { log.error("There are no baseDns specified in authentication configuration."); } return false; } public boolean authenticate(String userName) { log.debug("Authenticating user with LDAP: username: '{}', credentials: '{}'", userName, System.identityHashCode(credentials)); boolean authenticated = false; com.codahale.metrics.Timer.Context timerContext = metricService.getTimer(MetricType.OXAUTH_USER_AUTHENTICATION_RATE).time(); try { User user = userService.getUser(userName); if ((user != null) && checkUserStatus(user)) { credentials.setUsername(user.getUserId()); configureAuthenticatedUser(user); updateLastLogonUserTime(user); log.trace("Authenticate: credentials: '{}', credentials.userName: '{}', authenticatedUser.userId: '{}'", System.identityHashCode(credentials), credentials.getUsername(), getAuthenticatedUserId()); authenticated = true; } } finally { timerContext.stop(); } setAuthenticatedUserSessionAttribute(userName, authenticated); MetricType metricType; if (authenticated) { metricType = MetricType.OXAUTH_USER_AUTHENTICATION_SUCCESS; } else { metricType = MetricType.OXAUTH_USER_AUTHENTICATION_FAILURES; } metricService.incCounter(metricType); return authenticated; } private User getUserByAttribute(LdapEntryManager ldapAuthEntryManager, String baseDn, String attributeName, String attributeValue) { log.debug("Getting user information from LDAP: attributeName = '{}', attributeValue = '{}'", attributeName, attributeValue); if (StringHelper.isEmpty(attributeValue)) { return null; } SimpleUser sampleUser = new SimpleUser(); sampleUser.setDn(baseDn); List<CustomAttribute> customAttributes = new ArrayList<CustomAttribute>(); customAttributes.add(new CustomAttribute(attributeName, attributeValue)); sampleUser.setCustomAttributes(customAttributes); log.debug("Searching user by attributes: '{}', baseDn: '{}'", customAttributes, baseDn); List<User> entries = ldapAuthEntryManager.findEntries(sampleUser, 1); log.debug("Found '{}' entries", entries.size()); if (entries.size() > 0) { SimpleUser foundUser = entries.get(0); return ldapAuthEntryManager.find(User.class, foundUser.getDn()); } else { return null; } } private boolean checkUserStatus(User user) { CustomAttribute userStatus = userService.getCustomAttribute(user, "gluuStatus"); if ((userStatus != null) && GluuStatus.ACTIVE.equals(GluuStatus.getByValue(userStatus.getValue()))) { return true; } log.warn("User '{}' was disabled", user.getUserId()); return false; } private void updateLastLogonUserTime(User user) { if (!appConfiguration.getUpdateUserLastLogonTime()) { return; } CustomEntry customEntry = new CustomEntry(); customEntry.setDn(user.getDn()); customEntry.setCustomObjectClasses(UserService.USER_OBJECT_CLASSES); CustomAttribute customAttribute = new CustomAttribute("oxLastLogonTime", new Date()); customEntry.getCustomAttributes().add(customAttribute); try { ldapEntryManager.merge(customEntry); } catch (EntryPersistenceException epe) { log.error("Failed to update oxLastLoginTime of user '{}'", user.getUserId()); } } public SessionState configureSessionUser(SessionState sessionState, Map<String, String> sessionIdAttributes) { log.trace("configureSessionUser: credentials: '{}', sessionState: '{}', credentials.userName: '{}', authenticatedUser.userId: '{}'", System.identityHashCode(credentials), sessionState, credentials.getUsername(), getAuthenticatedUserId()); User user = getAuthenticatedUser(); SessionState newSessionState; if (sessionState == null) { newSessionState = sessionStateService.generateAuthenticatedSessionState(user.getDn(), sessionIdAttributes); } else { // TODO: Remove after 2.4.5 String sessionAuthUser = sessionIdAttributes.get(Constants.AUTHENTICATED_USER); log.trace("configureSessionUser sessionState: '{}', sessionState.auth_user: '{}'", sessionState, sessionAuthUser); newSessionState = sessionStateService.setSessionStateAuthenticated(sessionState, user.getDn()); } configureEventUserContext(newSessionState); return newSessionState; } public SessionState configureEventUser() { User user = getAuthenticatedUser(); if (user == null) { return null; } log.debug("ConfigureEventUser: username: '{}', credentials: '{}'", user.getUserId(), System.identityHashCode(credentials)); SessionState sessionState = sessionStateService.generateAuthenticatedSessionState(user.getDn()); configureEventUserContext(sessionState); return sessionState; } public void configureEventUser(SessionState sessionState) { sessionStateService.updateSessionState(sessionState); configureEventUserContext(sessionState); } private void configureEventUserContext(SessionState sessionState) { identity.setSessionState(sessionState); } private void configureAuthenticatedUser(User user) { identity.setUser(user); } public User getAuthenticatedUser() { if (identity.getUser() != null) { return identity.getUser(); } else { SessionState sessionState = sessionStateService.getSessionState(); if (sessionState != null) { Map<String, String> sessionIdAttributes = sessionState.getSessionAttributes(); String userId = sessionIdAttributes.get(Constants.AUTHENTICATED_USER); if (StringHelper.isNotEmpty(userId)) { User user = userService.getUser(userId); identity.setUser(user); return user; } } } return null; } private String getAuthenticatedUserId() { User authenticatedUser = getAuthenticatedUser(); if (authenticatedUser != null) { return authenticatedUser.getUserId(); } return null; } public void configureSessionClient() { String clientInum = credentials.getUsername(); log.debug("ConfigureSessionClient: username: '{}', credentials: '{}'", clientInum, System.identityHashCode(credentials)); Client client = clientService.getClient(clientInum); configureSessionClient(client); } public void configureSessionClient(Client client) { SessionClient sessionClient = new SessionClient(); sessionClient.setClient(client); identity.setSessionClient(sessionClient); clientService.updatAccessTime(client, true); } @SuppressWarnings({"unchecked", "rawtypes"}) public void onSuccessfulLogin(SessionState sessionUser) { log.info("Attempting to redirect user: SessionUser: {}", sessionUser); if ((sessionUser == null) || StringUtils.isBlank(sessionUser.getUserDn())) { return; } User user = userService.getUserByDn(sessionUser.getUserDn()); log.info("Attempting to redirect user: User: {}", user); if (user != null) { final Map<String, String> result = sessionUser.getSessionAttributes(); Map<String, String> allowedParameters = getAllowedParameters(result); result.put(SESSION_STATE, sessionUser.getId()); log.trace("Logged in successfully! User: {}, page: /authorize.xhtml, map: {}", user, allowedParameters); facesService.redirect("/authorize.xhtml", (Map) allowedParameters); } } public static Map<String, String> getAllowedParameters(@Nonnull final Map<String, String> requestParameterMap) { final Map<String, String> result = new HashMap<String, String>(); if (!requestParameterMap.isEmpty()) { final Set<Map.Entry<String, String>> set = requestParameterMap.entrySet(); for (Map.Entry<String, String> entry : set) { if (ALLOWED_PARAMETER.contains(entry.getKey())) { result.put(entry.getKey(), entry.getValue()); } } } return result; } public User getUserOrRemoveSession(SessionState p_sessionState) { if (p_sessionState != null) { try { if (StringUtils.isNotBlank(p_sessionState.getUserDn())) { final User user = userService.getUserByDn(p_sessionState.getUserDn()); if (user != null) { return user; } else { // if there is no user than session is invalid sessionStateService.remove(p_sessionState); } } else { // if there is no user than session is invalid sessionStateService.remove(p_sessionState); } } catch (Exception e) { log.trace(e.getMessage(), e); } } return null; } public String parametersAsString() throws UnsupportedEncodingException { final Map<String, String> parameterMap = getParametersMap(null); return parametersAsString(parameterMap); } public String parametersAsString(final Map<String, String> parameterMap) throws UnsupportedEncodingException { final StringBuilder sb = new StringBuilder(); final Set<Entry<String, String>> set = parameterMap.entrySet(); for (Map.Entry<String, String> entry : set) { final String value = (String) entry.getValue(); if (StringUtils.isNotBlank(value)) { sb.append(entry.getKey()).append("=").append(URLEncoder.encode(value, Util.UTF8_STRING_ENCODING)).append("&"); } } String result = sb.toString(); if (result.endsWith("&")) { result = result.substring(0, result.length() - 1); } return result; } public Map<String, String> getParametersMap(List<String> extraParameters) { final Map<String, String> parameterMap = new HashMap<String, String>(externalContext .getRequestParameterMap()); return getParametersMap(extraParameters, parameterMap); } public Map<String, String> getParametersMap(List<String> extraParameters, final Map<String, String> parameterMap) { final List<String> allowedParameters = new ArrayList<String>(ALLOWED_PARAMETER); if (extraParameters != null) { for (String extraParameter : extraParameters) { putInMap(parameterMap, extraParameter); } allowedParameters.addAll(extraParameters); } for (Iterator<Entry<String, String>> it = parameterMap.entrySet().iterator(); it.hasNext(); ) { Entry<String, String> entry = it.next(); if (!allowedParameters.contains(entry.getKey())) { it.remove(); } } return parameterMap; } private void putInMap(Map<String, String> map, String p_name) { if (map == null) { return; } String value = getParameterValue(p_name); map.put(p_name, value); } public String getParameterValue(String p_name) { final Object o = identity.getWorkingParameter(p_name); if (o instanceof String) { final String s = (String) o; return s; } else if (o instanceof Integer) { final Integer i = (Integer) o; return i.toString(); } else if (o instanceof Boolean) { final Boolean b = (Boolean) o; return b.toString(); } return null; } public boolean isParameterExists(String p_name) { return identity.isSetWorkingParameter(p_name); } }