org.nuxeo.ecm.platform.login.NuxeoLoginModule.java Source code

Java tutorial

Introduction

Here is the source code for org.nuxeo.ecm.platform.login.NuxeoLoginModule.java

Source

/*
 * (C) Copyright 2006-2007 Nuxeo SA (http://nuxeo.com/) and others.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Contributors:
 *     Nuxeo - initial API and implementation
 *
 * $Id: JOOoConvertPluginImpl.java 18651 2007-05-13 20:28:53Z sfermigier $
 */

package org.nuxeo.ecm.platform.login;

import java.io.IOException;
import java.security.Principal;
import java.security.acl.Group;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.core.api.NuxeoPrincipal;
import org.nuxeo.ecm.core.api.SystemPrincipal;
import org.nuxeo.ecm.core.api.security.SecurityConstants;
import org.nuxeo.ecm.directory.DirectoryException;
import org.nuxeo.ecm.platform.api.login.RestrictedLoginHelper;
import org.nuxeo.ecm.platform.api.login.UserIdentificationInfo;
import org.nuxeo.ecm.platform.api.login.UserIdentificationInfoCallback;
import org.nuxeo.ecm.platform.usermanager.NuxeoPrincipalImpl;
import org.nuxeo.ecm.platform.usermanager.UserManager;
import org.nuxeo.runtime.RuntimeService;
import org.nuxeo.runtime.api.Framework;
import org.nuxeo.runtime.api.login.LoginComponent;

public class NuxeoLoginModule extends NuxeoAbstractServerLoginModule {

    private static final Log log = LogFactory.getLog(NuxeoLoginModule.class);

    private UserManager manager;

    private Random random;

    private NuxeoPrincipal identity;

    private LoginPluginRegistry loginPluginManager;

    private boolean useUserIdentificationInfoCB = false;

    @Override
    public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState,
            Map<String, ?> options) {
        // explicit cast to match the direct superclass method declaration
        // (JBoss implementation)
        // rather than the newer (jdk1.5) LoginModule (... Map<String,?>...)
        // This is needed to avoid compilation errors when the linker wants to
        // bind
        // with the (interface) LoginModule method (which is abstract of course
        // and cannot be called)
        String useUIICB = (String) options.get("useUserIdentificationInfoCB");
        if (useUIICB != null && useUIICB.equalsIgnoreCase("true")) {
            useUserIdentificationInfoCB = true;
        }

        super.initialize(subject, callbackHandler, sharedState, options);
        random = new Random(System.currentTimeMillis());

        manager = Framework.getService(UserManager.class);
        log.debug("NuxeoLoginModule initialized");

        final RuntimeService runtime = Framework.getRuntime();
        loginPluginManager = (LoginPluginRegistry) runtime.getComponent(LoginPluginRegistry.NAME);
    }

    /**
     * Gets the roles the user belongs to.
     */
    @Override
    protected Group[] getRoleSets() throws LoginException {
        log.debug("getRoleSets");
        if (manager == null) {
            // throw new LoginException("UserManager implementation not found");
        }
        String username = identity.getName();
        List<String> roles = identity.getRoles();

        Group roleSet = new GroupImpl("Roles");
        log.debug("Getting roles for user=" + username);
        for (String roleName : roles) {
            Principal role = new PrincipalImpl(roleName);
            log.debug("Found role=" + roleName);
            roleSet.addMember(role);
        }
        Group callerPrincipal = new GroupImpl("CallerPrincipal");
        callerPrincipal.addMember(identity);

        return new Group[] { roleSet, callerPrincipal };
    }

    @SuppressWarnings({ "unchecked" })
    protected NuxeoPrincipal getPrincipal() throws LoginException {
        UserIdentificationInfo userIdent = null;

        // **** init the callbacks
        // Std login/password callbacks
        NameCallback nc = new NameCallback("Username: ", SecurityConstants.ANONYMOUS);
        PasswordCallback pc = new PasswordCallback("Password: ", false);

        // Nuxeo specific cb : handle LoginPlugin initialization
        UserIdentificationInfoCallback uic = new UserIdentificationInfoCallback();

        // JBoss specific cb : handle web=>ejb propagation
        // SecurityAssociationCallback ac = new SecurityAssociationCallback();
        // ObjectCallback oc = new ObjectCallback("UserInfo:");

        // **** handle callbacks
        // We can't check the callback handler class to know what will be
        // supported
        // because the cbh is wrapped by JAAS
        // => just try and swalow exceptions
        // => will be externalised to plugins via EP to avoid JBoss dependency
        boolean cb_handled = false;

        try {
            // only try this cbh when called from the web layer
            if (useUserIdentificationInfoCB) {
                callbackHandler.handle(new Callback[] { uic });
                // First check UserInfo CB return
                userIdent = uic.getUserInfo();
                cb_handled = true;
            }
        } catch (UnsupportedCallbackException e) {
            log.debug("UserIdentificationInfoCallback is not supported");
        } catch (IOException e) {
            log.warn("Error calling callback handler with UserIdentificationInfoCallback : " + e.getMessage());
        }

        Principal principal = null;
        Object credential = null;

        if (!cb_handled) {
            CallbackResult result = loginPluginManager.handleSpecifcCallbacks(callbackHandler);

            if (result != null && result.cb_handled) {
                if (result.userIdent != null && result.userIdent.containsValidIdentity()) {
                    userIdent = result.userIdent;
                    cb_handled = true;
                } else {
                    principal = result.principal;
                    credential = result.credential;
                    if (principal != null) {
                        cb_handled = true;
                    }
                }
            }
        }

        if (!cb_handled) {
            try {
                // Std CBH : will only works for L/P
                callbackHandler.handle(new Callback[] { nc, pc });
                cb_handled = true;
            } catch (UnsupportedCallbackException e) {
                LoginException le = new LoginException("Authentications Failure - " + e.getMessage());
                le.initCause(e);
            } catch (IOException e) {
                LoginException le = new LoginException("Authentications Failure - " + e.getMessage());
                le.initCause(e);
            }
        }

        // Login via the Web Interface : may be using a plugin
        if (userIdent != null && userIdent.containsValidIdentity()) {
            NuxeoPrincipal nxp = validateUserIdentity(userIdent);

            if (nxp != null) {
                sharedState.put("javax.security.auth.login.name", nxp.getName());
                sharedState.put("javax.security.auth.login.password", userIdent);
            }
            return nxp;
        }

        if (LoginComponent.isSystemLogin(principal)) {
            return new SystemPrincipal(principal.getName());
        }
        // if (principal instanceof NuxeoPrincipal) { // a nuxeo principal
        // return validatePrincipal((NuxeoPrincipal) principal);
        // } else
        if (principal != null) { // a non null principal
            String password = null;
            if (credential instanceof char[]) {
                password = new String((char[]) credential);
            } else if (credential != null) {
                password = credential.toString();
            }
            return validateUsernamePassword(principal.getName(), password);
        } else { // we don't have a principal - try the username &
            // password
            String username = nc.getName();
            if (username == null) {
                return null;
            }
            char[] password = pc.getPassword();
            return validateUsernamePassword(username, password != null ? new String(password) : null);
        }
    }

    public boolean login() throws LoginException {
        if (manager == null) {
            // throw new LoginException("UserManager implementation not found");
        }

        loginOk = false;

        identity = getPrincipal();
        if (identity == null) { // auth failed
            throw new LoginException("Authentication Failed");
        }

        if (RestrictedLoginHelper.isRestrictedModeActivated()) {
            if (!identity.isAdministrator()) {
                throw new LoginException("Only Administrators can login when restricted mode is activated");
            }
        }

        loginOk = true;
        log.trace("User '" + identity + "' authenticated");

        /*
         * if( getUseFirstPass() == true ) { // Add the username and password to the shared state map // not sure it's
         * needed sharedState.put("javax.security.auth.login.name", identity.getName());
         * sharedState.put("javax.security.auth.login.password", identity.getPassword()); }
         */

        return true;
    }

    @Override
    public Principal getIdentity() {
        return identity;
    }

    @Override
    public Principal createIdentity(String username) throws LoginException {
        log.debug("createIdentity: " + username);
        try {
            NuxeoPrincipal principal;
            if (manager == null) {
                principal = new NuxeoPrincipalImpl(username);
            } else {
                principal = manager.getPrincipal(username);
                if (principal == null) {
                    throw new LoginException(String.format("principal %s does not exist", username));
                }
            }

            String principalId = String.valueOf(random.nextLong());
            principal.setPrincipalId(principalId);
            return principal;
        } catch (LoginException e) {
            log.error("createIdentity failed", e);
            LoginException le = new LoginException("createIdentity failed for user " + username);
            le.initCause(e);
            throw le;
        }
    }

    protected NuxeoPrincipal validateUserIdentity(UserIdentificationInfo userIdent) throws LoginException {
        String loginPluginName = userIdent.getLoginPluginName();
        if (loginPluginName == null) {
            // we don't use a specific plugin
            boolean authenticated;
            try {
                authenticated = manager.checkUsernamePassword(userIdent.getUserName(), userIdent.getPassword());
            } catch (DirectoryException e) {
                throw (LoginException) new LoginException("Unable to validate identity").initCause(e);
            }
            if (authenticated) {
                return (NuxeoPrincipal) createIdentity(userIdent.getUserName());
            } else {
                return null;
            }
        } else {
            LoginPlugin lp = loginPluginManager.getPlugin(loginPluginName);
            if (lp == null) {
                log.error("Can't authenticate against a null loginModul plugin");
                return null;
            }
            // set the parameters and reinit if needed
            LoginPluginDescriptor lpd = loginPluginManager.getPluginDescriptor(loginPluginName);
            if (!lpd.getInitialized()) {
                Map<String, String> existingParams = lp.getParameters();
                if (existingParams == null) {
                    existingParams = new HashMap<String, String>();
                }
                Map<String, String> loginParams = userIdent.getLoginParameters();
                if (loginParams != null) {
                    existingParams.putAll(loginParams);
                }
                boolean init = lp.initLoginModule();
                if (init) {
                    lpd.setInitialized(true);
                } else {
                    log.error("Unable to initialize LoginModulePlugin " + lp.getName());
                    return null;
                }
            }

            String username = lp.validatedUserIdentity(userIdent);
            if (username == null) {
                return null;
            } else {
                return (NuxeoPrincipal) createIdentity(username);
            }
        }
    }

    protected NuxeoPrincipal validateUsernamePassword(String username, String password) throws LoginException {
        if (!manager.checkUsernamePassword(username, password)) {
            return null;
        }
        return (NuxeoPrincipal) createIdentity(username);
    }

}