org.gluu.oxtrust.action.Authenticator.java Source code

Java tutorial

Introduction

Here is the source code for org.gluu.oxtrust.action.Authenticator.java

Source

/*
 * oxTrust is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text.
 *
 * Copyright (c) 2014, Gluu
 */

package org.gluu.oxtrust.action;

import java.io.IOException;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.Principal;
import java.security.acl.Group;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.faces.context.FacesContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;
import org.codehaus.jettison.json.JSONException;
import org.gluu.oxtrust.ldap.service.ApplianceService;
import org.gluu.oxtrust.ldap.service.AuthenticationService;
import org.gluu.oxtrust.ldap.service.PersonService;
import org.gluu.oxtrust.ldap.service.SecurityService;
import org.gluu.oxtrust.model.GluuAppliance;
import org.gluu.oxtrust.model.GluuCustomPerson;
import org.gluu.oxtrust.model.User;
import org.gluu.oxtrust.security.OauthData;
import org.gluu.oxtrust.service.AuthenticationSessionService;
import org.gluu.oxtrust.util.OxTrustConstants;
import org.gluu.oxtrust.util.Utils;
import org.gluu.site.ldap.persistence.exception.AuthenticationException;
import org.jboss.resteasy.client.ClientRequest;
import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Out;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.core.Events;
import org.jboss.seam.faces.FacesMessages;
import org.jboss.seam.faces.Redirect;
import org.jboss.seam.log.Log;
import org.jboss.seam.navigation.Pages;
import org.jboss.seam.security.Credentials;
import org.jboss.seam.security.Identity;
import org.jboss.seam.security.SimplePrincipal;
import org.xdi.config.oxtrust.ApplicationConfiguration;
import org.xdi.ldap.model.GluuStatus;
import org.xdi.model.GluuUserRole;
import org.xdi.oxauth.client.TokenClient;
import org.xdi.oxauth.client.TokenResponse;
import org.xdi.oxauth.client.UserInfoClient;
import org.xdi.oxauth.client.UserInfoResponse;
import org.xdi.oxauth.client.ValidateTokenClient;
import org.xdi.oxauth.client.ValidateTokenResponse;
import org.xdi.oxauth.model.jwt.JwtClaimName;
import org.xdi.util.ArrayHelper;
import org.xdi.util.StringHelper;
import org.xdi.util.security.StringEncrypter;
import org.xdi.util.security.StringEncrypter.EncryptionException;

/**
 * Provides authentication using oAuth
 * 
 * @author Reda Zerrad Date: 05.11.2012
 * @author Yuriy Movchan Date: 02.12.2013
 */
@Name("authenticator")
@Scope(ScopeType.SESSION)
public class Authenticator implements Serializable {
    /**
      *
      */
    private static final long serialVersionUID = -3975272457541385597L;

    @Logger
    private Log log;

    @In
    private Identity identity;

    @In
    private AuthenticationService authenticationService;

    @In
    private Credentials credentials;

    @In
    Redirect redirect;

    @In
    private PersonService personService;

    @In
    private SecurityService securityService;

    @In(create = true)
    private SsoLoginAction ssoLoginAction;

    @In
    private transient ApplianceService applianceService;

    @In
    private FacesMessages facesMessages;

    String viewIdBeforeLoginRedirect;

    @In(create = true)
    @Out(scope = ScopeType.SESSION, required = false)
    private OauthData oauthData;

    @In(value = "#{oxTrustConfiguration.applicationConfiguration}")
    private ApplicationConfiguration applicationConfiguration;

    @In(value = "#{oxTrustConfiguration.cryptoConfigurationSalt}")
    private String cryptoConfigurationSalt;

    public boolean preAuthenticate() throws IOException, Exception {
        boolean result = true;
        if (isOxAuthAuth()) {
            if (!identity.isLoggedIn()) {
                result = oAuthLogin();
            }
        } else {
            result = externalAuthenticate();
        }

        return result;
    }

    public boolean authenticate() {
        String userName = null;
        try {
            if (isBasicAuth()) {
                userName = identity.getCredentials().getUsername();
                log.info("Authenticating user '{0}'", userName);

                boolean authenticated = false;
                try {
                    authenticated = this.authenticationService.authenticate(userName,
                            this.credentials.getPassword());
                } catch (AuthenticationException ex) {
                    this.log.error("Failed to authenticate user: '{0}'", ex, userName);
                }

                if (!authenticated) {
                    return false;
                }
            } else if (isOxAuthAuth()) {
                userName = oauthData.getUserUid();
                identity.getCredentials().setUsername(userName);
                log.info("Authenticating user '{0}'", userName);
            } else {
                return false;
            }

            User user = findUserByUserName(userName);
            if (user == null) {
                log.error("Person '{0}' not found in LDAP", userName);
                return false;
            } else if (GluuStatus.EXPIRED.getValue().equals(user.getAttribute("gluuStatus"))
                    || GluuStatus.REGISTER.getValue().equals(user.getAttribute("gluuStatus"))) {
                redirect.setViewId("/register.xhtml");
                redirect.setParameter("inum", user.getInum());
                redirect.execute();
                return false;
            }

            postLogin(user);
            log.info("User '{0}' authenticated successfully", userName);
        } catch (Exception ex) {
            log.error("Failed to authenticate user '{0}'", ex, userName);
            return false;
        }

        return true;
    }

    public boolean authenticateBasicWebService() {
        String userName = identity.getCredentials().getUsername();
        log.info("Authenticating user '{0}'", userName);

        boolean authenticated = false;
        try {
            authenticated = this.authenticationService.authenticate(userName, this.credentials.getPassword());
        } catch (AuthenticationException ex) {
            this.log.error("Failed to authenticate user: '{0}'", ex, userName);
        }

        if (!authenticated) {
            return false;
        }

        return postAuthenticateWebService(userName);
    }

    public boolean authenticateBearerWebService() {
        String userName = identity.getCredentials().getUsername();
        log.info("Authenticating user '{0}'", userName);

        return postAuthenticateWebService(userName);
    }

    public boolean postAuthenticateWebService(String userName) {
        try {
            User user = findUserByUserName(userName);
            if (user == null) {
                log.error("Person '{0}' not found in LDAP", userName);
                return false;
            }

            Principal principal = new SimplePrincipal(userName);
            identity.acceptExternallyAuthenticatedPrincipal(principal);
            identity.quietLogin();

            postLogin(user);
            log.info("User '{0}' authenticated successfully", userName);

            return true;
        } catch (Exception ex) {
            log.error("Failed to authenticate user '{0}'", ex, userName);
        }

        return false;
    }

    /**
     * Set session variables after user login
     * 
     * @throws Exception
     */
    private void postLogin(User user) {
        log.debug("Configuring application after user '{0}' login", user.getUid());
        GluuCustomPerson person = findPersonByDn(user.getDn());
        Contexts.getSessionContext().set(OxTrustConstants.CURRENT_PERSON, person);

        // Set user roles
        GluuUserRole[] userRoles = securityService.getUserRoles(user);
        if (ArrayHelper.isNotEmpty(userRoles)) {
            log.debug("Get '{0}' user roles", Arrays.toString(userRoles));
        } else {
            log.debug("Get 0 user roles");
        }
        for (GluuUserRole userRole : userRoles) {
            identity.addRole(userRole.getRoleName());
        }

        if (log.isDebugEnabled()) {
            for (Group sg : identity.getSubject().getPrincipals(java.security.acl.Group.class)) {
                if ("Roles".equals(sg.getName())) {
                    log.debug("Using next user roles: '{0}'", sg.members());
                    break;
                }
            }
        }
    }

    private User findUserByUserName(String userName) {
        User user = null;
        try {
            user = personService.getUserByUid(userName);
        } catch (Exception ex) {
            log.error("Failed to find user '{0}' in ldap", ex, userName);
        }

        return user;
    }

    private GluuCustomPerson findPersonByDn(String userDn) {
        GluuCustomPerson person = null;
        try {
            person = personService.getPersonByDn(userDn);
        } catch (Exception ex) {
            log.error("Failed to find person '{0}' in ldap", ex, userDn);
        }

        return person;
    }

    private boolean isBasicAuth() {
        return Utils.isBasicAuth();
    }

    private boolean isOxAuthAuth() {
        return !isBasicAuth();
    }

    public void processLogout() throws Exception {
        ssoLoginAction.logout();
        oAuthlLogout();

        postLogout();
    }

    public String postLogout() {
        if (identity.isLoggedIn()) {
            identity.logout();
        }

        return OxTrustConstants.RESULT_SUCCESS;
    }

    public void oAuthlLogout() throws Exception {
        if (StringHelper.isEmpty(oauthData.getUserUid())) {
            return;
        }

        ClientRequest clientRequest = new ClientRequest(applicationConfiguration.getOxAuthLogoutUrl());

        clientRequest.queryParameter(OxTrustConstants.OXAUTH_ID_TOKEN_HINT, oauthData.getIdToken());
        clientRequest.queryParameter(OxTrustConstants.OXAUTH_POST_LOGOUT_REDIRECT_URI,
                applicationConfiguration.getLogoutRedirectUrl());

        // Clean up OAuth token
        oauthData.setUserUid(null);
        oauthData.setIdToken(null);
        oauthData.setSessionState(null);
        oauthData = null;

        FacesContext.getCurrentInstance().getExternalContext().redirect(clientRequest.getUri());
    }

    /**
     * Authenticate using credentials passed from web request header
     */
    public boolean shibboleth2Authenticate() {
        log.debug("Checking if user authenticated with shibboleth already");
        boolean result = false;
        HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext()
                .getRequest();

        String authType = request.getAuthType();
        String userUid = request.getHeader("REMOTE_USER");
        String userUidlower = request.getHeader("remote_user");
        Enumeration<?> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerName = (String) headerNames.nextElement();
            log.trace(headerName + "-->" + request.getHeader(headerName));
        }
        log.debug("Username is " + userUid);
        log.debug("UsernameLower is " + userUidlower);
        log.debug("AuthType is " + authType);

        Map<String, String[]> headers = FacesContext.getCurrentInstance().getExternalContext()
                .getRequestHeaderValuesMap();
        for (String name : headers.keySet()) {
            log.trace(name + "==>" + StringUtils.join(headers.get(name)));
        }

        if (StringHelper.isEmpty(userUid) || StringHelper.isEmpty(authType) || !authType.equals("shibboleth")) {
            result = false;
            return result;
        }

        Pattern pattern = Pattern.compile(".+@.+\\.[a-z]+");
        Matcher matcher = pattern.matcher(userUid);

        User user = null;
        if (matcher.matches()) {
            // Find user by uid
            user = personService.getPersonByEmail(userUid);
        } else {
            // Find user by uid
            user = personService.getUserByUid(userUid);
        }

        if (user == null) {
            result = false;
            return result;
        }
        log.debug("Person Inum is " + user.getInum());

        if (GluuStatus.ACTIVE.getValue().equals(user.getAttribute("gluuStatus"))) {

            credentials.setUsername(user.getUid());
            // credentials.setPassword("");
            Principal principal = new SimplePrincipal(user.getUid());
            log.debug("Principal is " + principal.toString());

            identity.acceptExternallyAuthenticatedPrincipal(principal);

            log.info("User '{0}' authenticated with shibboleth already", userUid);
            identity.quietLogin();
            postLogin(user);

            Contexts.getSessionContext().set(OxTrustConstants.APPLICATION_AUTHORIZATION_TYPE,
                    OxTrustConstants.APPLICATION_AUTHORIZATION_NAME_SHIBBOLETH2);

            result = true;
            if (Events.exists()) {
                facesMessages.clear();
                Events.instance().raiseEvent(Identity.EVENT_LOGIN_SUCCESSFUL);
            }
        } else {
            result = false;
        }

        return result;
    }

    /**
     * Make attempt to authenticate using parameters passed in header
     */
    public boolean externalAuthenticate() {
        if (identity.isLoggedIn()) {
            return true;
        }

        if (shibboleth2Authenticate()) {

            return true;
        }
        // try {
        // oAuthLogin();
        // } catch (Exception e) {
        // // TODO Auto-generated catch block
        // e.printStackTrace();
        // }
        return false;
    }

    /**
     * Main entry point for oAuth authentication.
     * @throws IOException 
     * 
     * @throws Exception
     */
    public boolean oAuthLogin() throws IOException, Exception {
        ClientRequest clientRequest = new ClientRequest(applicationConfiguration.getOxAuthAuthorizeUrl());
        String clientId = applicationConfiguration.getOxAuthClientId();
        String scope = applicationConfiguration.getOxAuthClientScope();
        String responseType = "code+id_token";

        String nonce = "nonce";

        clientRequest.queryParameter(OxTrustConstants.OXAUTH_CLIENT_ID, clientId);
        clientRequest.queryParameter(OxTrustConstants.OXAUTH_REDIRECT_URI,
                applicationConfiguration.getLoginRedirectUrl());
        clientRequest.queryParameter(OxTrustConstants.OXAUTH_RESPONSE_TYPE, responseType);
        clientRequest.queryParameter(OxTrustConstants.OXAUTH_SCOPE, scope);
        clientRequest.queryParameter(OxTrustConstants.OXAUTH_NONCE, nonce);

        GluuAppliance appliance = applianceService.getAppliance(new String[] { "oxTrustAuthenticationMode" });
        String authenticationMode = appliance.getOxTrustAuthenticationMode();
        if (StringHelper.isNotEmpty(authenticationMode)) {
            clientRequest.queryParameter(OxTrustConstants.OXAUTH_AUTH_MODE, authenticationMode);
        }

        if (viewIdBeforeLoginRedirect != null) {
            clientRequest.queryParameter(OxTrustConstants.OXAUTH_STATE, viewIdBeforeLoginRedirect);
        }

        FacesContext.getCurrentInstance().getExternalContext()
                .redirect(clientRequest.getUri().replaceAll("%2B", "+"));

        return true;
    }

    /**
     * After successful login, oxAuth will redirect user to this method. Obtains
     * access token using authorization code and verifies if access token is
     * valid
     * 
     * @return
     * @throws JSONException
     */
    public String oAuthGetAccessToken() throws JSONException {
        String oxAuthAuthorizeUrl = applicationConfiguration.getOxAuthAuthorizeUrl();
        String oxAuthHost = getOxAuthHost(oxAuthAuthorizeUrl);
        if (StringHelper.isEmpty(oxAuthHost)) {
            log.info("Failed to determine oxAuth host using oxAuthAuthorizeUrl: '{0}'", oxAuthAuthorizeUrl);
            return OxTrustConstants.RESULT_NO_PERMISSIONS;
        }

        Map<String, String> requestParameterMap = FacesContext.getCurrentInstance().getExternalContext()
                .getRequestParameterMap();
        Map<String, Object> requestCookieMap = FacesContext.getCurrentInstance().getExternalContext()
                .getRequestCookieMap();

        String authorizationCode = requestParameterMap.get(OxTrustConstants.OXAUTH_CODE);

        Object sessionStateCookie = requestCookieMap.get(OxTrustConstants.OXAUTH_SESSION_STATE);
        String sessionState = null;
        if (sessionStateCookie != null) {
            sessionState = ((Cookie) sessionStateCookie).getValue();
        }

        String idToken = requestParameterMap.get(OxTrustConstants.OXAUTH_ID_TOKEN);

        if (authorizationCode == null) {
            String error = requestParameterMap.get(OxTrustConstants.OXAUTH_ERROR);
            String errorDescription = requestParameterMap.get(OxTrustConstants.OXAUTH_ERROR_DESCRIPTION);

            log.info("No authorization code sent. Error: " + error + ". Error description: " + errorDescription);
            return OxTrustConstants.RESULT_NO_PERMISSIONS;
        }

        if (viewIdBeforeLoginRedirect != null && !viewIdBeforeLoginRedirect.equals("")) {
            Redirect.instance().setViewId(viewIdBeforeLoginRedirect);
            viewIdBeforeLoginRedirect = "";
        }
        // todo hardcoded for now. Once clients are dynamically registered with
        // oxAuth, change this
        // String credentials = applicationConfiguration.getOxAuthClientId() +
        // ":secret";
        //      String credentials = applicationConfiguration.getOxAuthClientId() + ":5967d41c-ce9c-4137-9068-42578df0c606";
        // String clientCredentials =
        // applicationConfiguration.getOxAuthClientCredentials();
        log.info("authorizationCode : " + authorizationCode);

        String scopes = requestParameterMap.get(OxTrustConstants.OXAUTH_SCOPE);
        log.info(" scopes : " + scopes);

        String clientID = applicationConfiguration.getOxAuthClientId();
        log.info("clientID : " + clientID);

        String clientPassword = applicationConfiguration.getOxAuthClientPassword();
        if (clientPassword != null) {
            try {
                clientPassword = StringEncrypter.defaultInstance().decrypt(clientPassword, cryptoConfigurationSalt);
            } catch (EncryptionException ex) {
                log.error("Failed to decrypt client password", ex);
            }
        }

        log.info("getting accessToken");
        // 1. Request access token using the authorization code.
        log.info("tokenURL : " + applicationConfiguration.getOxAuthTokenUrl());
        String tokenURL = applicationConfiguration.getOxAuthTokenUrl();
        TokenClient tokenClient1 = new TokenClient(tokenURL);

        log.info("Sending request to token endpoint");
        String redirectURL = applicationConfiguration.getLoginRedirectUrl();
        log.info("redirectURI : " + applicationConfiguration.getLoginRedirectUrl());
        TokenResponse tokenResponse = tokenClient1.execAuthorizationCode(authorizationCode, redirectURL, clientID,
                clientPassword);

        log.info(" tokenResponse : " + tokenResponse);
        log.info(" tokenResponse.getErrorType() : " + tokenResponse.getErrorType());

        String accessToken = tokenResponse.getAccessToken();
        log.info(" accessToken : " + accessToken);

        // 2. Validate the access token
        // ValidateTokenClient validateTokenClient = new
        // ValidateTokenClient(applicationConfiguration.getOxAuthValidateTokenUrl());
        // ValidateTokenResponse response3 = validateTokenClient
        // .execValidateToken(accessToken);
        log.info(" validating AccessToken ");

        String validateUrl = applicationConfiguration.getOxAuthTokenValidationUrl();
        ValidateTokenClient validateTokenClient = new ValidateTokenClient(validateUrl);
        ValidateTokenResponse response3 = validateTokenClient.execValidateToken(accessToken);
        log.info(" response3.getStatus() : " + response3.getStatus());

        log.info("validate check session status:" + response3.getStatus());
        if (response3.getErrorDescription() != null) {
            log.debug("validate token status message:" + response3.getErrorDescription());
        }

        if (response3.getStatus() == 200) {
            log.info("Session validation successful. User is logged in");
            String userInfoEndPoint = applicationConfiguration.getOxAuthUserInfo();
            UserInfoClient userInfoClient = new UserInfoClient(userInfoEndPoint);
            UserInfoResponse userInfoResponse = userInfoClient.execUserInfo(accessToken);

            this.oauthData.setHost(oxAuthHost);
            // Determine uid
            List<String> uidValues = userInfoResponse.getClaims().get(JwtClaimName.USER_NAME);
            if ((uidValues == null) || (uidValues.size() == 0)) {
                log.error("User info response doesn't contains uid claim");
                return OxTrustConstants.RESULT_NO_PERMISSIONS;
            }

            this.oauthData.setUserUid(uidValues.get(0));
            this.oauthData.setAccessToken(accessToken);
            this.oauthData.setAccessTokenExpirationInSeconds(response3.getExpiresIn());
            this.oauthData.setScopes(scopes);
            this.oauthData.setIdToken(idToken);
            this.oauthData.setSessionState(sessionState);

            log.info("user uid:" + oauthData.getUserUid());

            // Create session scope authentication service
            Component.getInstance(AuthenticationSessionService.class);

            return OxTrustConstants.RESULT_SUCCESS;
        }
        log.info("Token validation failed. User is NOT logged in");
        return OxTrustConstants.RESULT_NO_PERMISSIONS;
    }

    private String getOxAuthHost(String oxAuthAuthorizeUrl) {
        try {
            URL url = new URL(oxAuthAuthorizeUrl);
            return String.format("%s://%s:%s", url.getProtocol(), url.getHost(), url.getPort());
        } catch (MalformedURLException ex) {
            log.error("Invalid oxAuth authorization URI: '{0}'", ex, oxAuthAuthorizeUrl);
        }

        return null;
    }

    /**
     * Used to remember the view user tried to access prior to login. This is
     * the view user will be redirected after successful login.
     */
    public void captureCurrentView() {
        FacesContext context = FacesContext.getCurrentInstance();

        // If this isn't a faces request then just return
        if (context == null)
            return;

        viewIdBeforeLoginRedirect = Pages.getViewId(context);
    }

    public static Authenticator instance() {
        return (Authenticator) Component.getInstance(Authenticator.class, true);
    }

}