org.sakaiproject.nakamura.lite.jackrabbit.SparseLoginModule.java Source code

Java tutorial

Introduction

Here is the source code for org.sakaiproject.nakamura.lite.jackrabbit.SparseLoginModule.java

Source

/**
 * Licensed to the Sakai Foundation (SF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The SF licenses this file
 * to you 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.
 */
package org.sakaiproject.nakamura.lite.jackrabbit;

import org.apache.commons.lang.StringUtils;
import org.apache.jackrabbit.core.SessionImpl;
import org.apache.jackrabbit.core.security.AnonymousPrincipal;
import org.apache.jackrabbit.core.security.SystemPrincipal;
import org.apache.jackrabbit.core.security.authentication.AbstractLoginModule;
import org.apache.jackrabbit.core.security.authentication.Authentication;
import org.apache.jackrabbit.core.security.authentication.CredentialsCallback;
import org.apache.jackrabbit.core.security.principal.AdminPrincipal;
import org.apache.sling.jcr.jackrabbit.server.impl.Activator;
import org.apache.sling.jcr.jackrabbit.server.impl.security.AdministrativeCredentials;
import org.apache.sling.jcr.jackrabbit.server.impl.security.AnonCredentials;
import org.apache.sling.jcr.jackrabbit.server.impl.security.AuthenticationPluginWrapper;
import org.apache.sling.jcr.jackrabbit.server.impl.security.CallbackHandlerWrapper;
import org.apache.sling.jcr.jackrabbit.server.impl.security.TrustedCredentials;
import org.apache.sling.jcr.jackrabbit.server.security.AuthenticationPlugin;
import org.apache.sling.jcr.jackrabbit.server.security.LoginModulePlugin;
import org.sakaiproject.nakamura.api.lite.StorageClientException;
import org.sakaiproject.nakamura.api.lite.accesscontrol.Authenticator;
import org.sakaiproject.nakamura.api.lite.authorizable.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.security.Principal;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.jcr.AccessDeniedException;
import javax.jcr.Credentials;
import javax.jcr.GuestCredentials;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
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.UnsupportedCallbackException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;

public class SparseLoginModule extends AbstractLoginModule {

    private static final Logger LOGGER = LoggerFactory.getLogger(SparseLoginModule.class);
    private Authenticator authenticator;
    private User user;

    @Override
    protected void doInit(CallbackHandler callbackHandler, Session session,
            @SuppressWarnings("rawtypes") Map options) throws LoginException {
        try {
            SessionImpl sessionImpl = (SessionImpl) session;
            SparseMapUserManager userManager = (SparseMapUserManager) sessionImpl.getUserManager();
            org.sakaiproject.nakamura.api.lite.Session sparseSession = userManager.getSession();

            LoginModulePlugin[] modules = Activator.getLoginModules();
            for (int i = 0; i < modules.length; i++) {
                modules[i].doInit(callbackHandler, session, options);
            }

            CredentialsCallback cb = new CredentialsCallback();
            try {
                callbackHandler.handle(new Callback[] { cb });
            } catch (IOException e1) {
                LOGGER.warn(e1.getMessage(), e1);
            } catch (UnsupportedCallbackException e1) {
                LOGGER.warn(e1.getMessage(), e1);
            }
            authenticator = sparseSession.getAuthenticator();
        } catch (StorageClientException e) {
            throw new LoginException(e.getMessage());
        } catch (AccessDeniedException e) {
            throw new LoginException(e.getMessage());
        } catch (RepositoryException e) {
            throw new LoginException(e.getMessage());
        }

    }

    /**
     * {@inheritDoc}
     * 
     * @see org.apache.jackrabbit.core.security.authentication.AbstractLoginModule#initialize(javax.security.auth.Subject,
     *      javax.security.auth.callback.CallbackHandler, java.util.Map, java.util.Map)
     */
    @Override
    public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState,
            Map<String, ?> options) {
        CallbackHandlerWrapper wrappedCallbackHandler = new CallbackHandlerWrapper(subject, callbackHandler);

        super.initialize(subject, wrappedCallbackHandler, sharedState, options);
    }

    @Override
    protected boolean impersonate(Principal principal, Credentials credentials)
            throws RepositoryException, LoginException {
        if (credentials instanceof AdministrativeCredentials) {
            return true;
        }
        if (credentials instanceof AnonCredentials) {
            return false;
        }

        LoginModulePlugin[] modules = Activator.getLoginModules();
        for (int i = 0; i < modules.length; i++) {
            if (modules[i].canHandle(credentials)) {
                int result = modules[i].impersonate(principal, credentials);
                if (result != LoginModulePlugin.IMPERSONATION_DEFAULT) {
                    return result == LoginModulePlugin.IMPERSONATION_SUCCESS;
                }
            }
        }

        User user = authenticator.systemAuthenticate(principal.getName());
        if (user != null) {
            Subject impersSubject = getImpersonatorSubject(credentials);

            if (!impersSubject.getPrincipals(AdminPrincipal.class).isEmpty()
                    || !impersSubject.getPrincipals(SystemPrincipal.class).isEmpty()) {
                return true;
            }

            String impersonators = (String) user.getProperty(User.IMPERSONATORS_FIELD);
            if (impersonators != null) {
                Set<String> imp = new HashSet<String>();
                Collections.addAll(imp, StringUtils.split(impersonators, ';'));
                for (Principal p : subject.getPrincipals()) {
                    if (imp.contains(p.getName())) {
                        return true;
                    }
                }
            }
            throw new FailedLoginException("attempt by user " + principal.getName() + " with subjects "
                    + impersSubject.getPrincipals() + " to impersonate " + credentials);
        }
        return false;
    }

    @Override
    protected Authentication getAuthentication(Principal principal, Credentials creds) throws RepositoryException {
        if (creds instanceof TrustedCredentials) {
            return new Authentication() {

                public boolean canHandle(Credentials credentials) {
                    return (credentials instanceof AdministrativeCredentials)
                            || (credentials instanceof AnonCredentials);
                }

                public boolean authenticate(Credentials credentials) throws RepositoryException {
                    return (credentials instanceof AdministrativeCredentials)
                            || (credentials instanceof AnonCredentials);
                }
            };
        }

        LoginModulePlugin[] modules = Activator.getLoginModules();
        for (int i = 0; i < modules.length; i++) {
            if (modules[i].canHandle(creds)) {
                AuthenticationPlugin pa = modules[i].getAuthentication(principal, creds);
                if (pa != null) {
                    return new AuthenticationPluginWrapper(pa, modules[i]);
                }
            }
        }

        if (user != null) {
            Authentication authentication = new SparseCredentialsAuthentication(user, authenticator);
            if (authentication.canHandle(creds)) {
                return authentication;
            }
        } else {
            LOGGER.debug("User is null, no login being performed ");
        }
        return null;
    }

    @Override
    protected Principal getPrincipal(Credentials credentials) {
        if (credentials instanceof TrustedCredentials) {
            return ((TrustedCredentials) credentials).getPrincipal();
        }
        LoginModulePlugin[] modules = Activator.getLoginModules();
        for (int i = 0; i < modules.length; i++) {
            if (modules[i].canHandle(credentials)) {
                Principal p = modules[i].getPrincipal(credentials);
                if (p != null) {
                    return p;
                }
            }
        }
        String userId = getUserID(credentials);
        LOGGER.debug("Got User ID as [{}]", userId);
        User auser = authenticator.systemAuthenticate(userId);
        if (auser != null) {
            this.user = auser;
            if (User.ADMIN_USER.equals(userId)) {
                return new AdminPrincipal(userId);
            } else if (User.SYSTEM_USER.equals(userId)) {
                return new SystemPrincipal();
            } else if (User.ANON_USER.equals(userId)) {
                return new AnonymousPrincipal();
            }
            LOGGER.debug("Sparse User Principal {}", userId);
            return new SparsePrincipal(userId,
                    this.getClass().getName() + " credentials were "
                            + ((credentials == null) ? null : credentials.getClass().getName()),
                    SparseMapUserManager.USERS_PATH);
        } else {
            LOGGER.debug("No User Found in credentials  UserID[{}] Credentials[{}]", userId, credentials);
        }
        return null;
    }

    /**
     * Since the AbstractLoginModule getCredentials does not know anything about
     * TrustedCredentials we have to re-try here.
     */
    @Override
    protected Credentials getCredentials() {
        Credentials creds = super.getCredentials();
        if (creds == null) {
            CredentialsCallback callback = new CredentialsCallback();
            try {
                callbackHandler.handle(new Callback[] { callback });
                Credentials callbackCreds = callback.getCredentials();
                if (callbackCreds instanceof TrustedCredentials) {
                    creds = callbackCreds;
                }
            } catch (UnsupportedCallbackException e) {
                LOGGER.warn("Credentials-Callback not supported try Name-Callback");
            } catch (IOException e) {
                LOGGER.error("Credentials-Callback failed: " + e.getMessage() + ": try Name-Callback");
            }
        }
        return creds;
    }

    /**
     * @see org.apache.jackrabbit.core.security.authentication.AbstractLoginModule#getPrincipals
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Override
    protected Set getPrincipals() {
        Set principals = super.getPrincipals();
        LoginModulePlugin[] modules = Activator.getLoginModules();
        for (int i = 0; i < modules.length; i++) {
            modules[i].addPrincipals(principals);
        }
        return principals;
    }

    private static final String KEY_LOGIN_NAME = "javax.security.auth.login.name";

    protected String getUserID(Credentials credentials) {
        String userId = null;
        if (credentials != null) {
            if (credentials instanceof GuestCredentials) {
                userId = anonymousId;
            } else if (credentials instanceof SimpleCredentials) {
                userId = ((SimpleCredentials) credentials).getUserID();
            } else {
                try {
                    NameCallback callback = new NameCallback("User-ID: ");
                    callbackHandler.handle(new Callback[] { callback });
                    userId = callback.getName();
                } catch (UnsupportedCallbackException e) {
                    LOGGER.warn("Credentials- or NameCallback must be supported");
                } catch (IOException e) {
                    LOGGER.error("Name-Callback failed: " + e.getMessage());
                }
            }
        }
        if (userId == null && sharedState.containsKey(KEY_LOGIN_NAME)) {
            userId = (String) sharedState.get(KEY_LOGIN_NAME);
        }

        // still no userId -> anonymousID if its has been defined.
        // TODO: check again if correct when used with 'extendedAuth'
        if (userId == null) {
            userId = anonymousId;
        }
        return userId;
    }
}