/* * Copyright 2012 david gonzalez. * * 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. */ package com.activecq.samples.loginmodule; import org.apache.jackrabbit.api.security.user.Authorizable; import org.apache.jackrabbit.api.security.user.Group; import org.apache.jackrabbit.api.security.user.User; import org.apache.jackrabbit.api.security.user.UserManager; import org.apache.jackrabbit.core.SessionImpl; import org.apache.jackrabbit.core.security.authentication.AbstractLoginModule; import org.apache.jackrabbit.core.security.authentication.Authentication; import org.apache.jackrabbit.core.security.authentication.token.TokenBasedAuthentication; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.jcr.Credentials; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.login.FailedLoginException; import javax.security.auth.login.LoginException; import java.security.Principal; import java.util.Map; import java.util.UUID; import java.util.logging.Level; public class SampleLoginModule extends AbstractLoginModule { private static final Logger log = LoggerFactory.getLogger(AbstractLoginModule.class); private UserManager userManager; private Session session; @Override protected void doInit(CallbackHandler callbackHandler, Session session, Map options) throws LoginException { if (!(session instanceof SessionImpl)) { throw new LoginException("Unable to initialize SampleLoginModule: SessionImpl expected."); } this.session = session; try { userManager = ((SessionImpl) session).getUserManager(); } catch (RepositoryException e) { throw new LoginException("Unable to initialize SampleLoginModule: " + e.getMessage()); } } /** * Handles the impersonation of the Principal using the provided * Credentials. * <p/> * Impersonation only occurs if the provided Credentials allow for the * impersonation of the Principal. * * @param principalToImpersonate Principal to impersonate * @param impersonatorCredentials Credentials used to create the * impersonation subject. * @return * @throws RepositoryException * @throws LoginException */ @Override protected boolean impersonate(Principal principalToImpersonate, Credentials impersonatorCredentials) throws RepositoryException, LoginException { Authorizable authorizableToImpersonate = userManager.getAuthorizable(principalToImpersonate); if (authorizableToImpersonate == null || authorizableToImpersonate.isGroup()) { return false; } Subject impersonatorSubject = getImpersonatorSubject(impersonatorCredentials); User userToImpersonate = (User) authorizableToImpersonate; if (userToImpersonate.getImpersonation().allows(impersonatorSubject)) { return true; } else { throw new FailedLoginException("attempt to impersonate denied for " + principalToImpersonate.getName()); } } /** * Principal is the CRX Principal that the Credentials should be * authenticated against. * <p/> * Principal is retrieved from getPrincipal(Credentials credentials) using * the Credentials UserId field. * <p/> * Get the Authentication object * * @param principal * @param creds * @return * @throws RepositoryException */ @Override protected Authentication getAuthentication(Principal principal, Credentials creds) throws RepositoryException { if (principal != null) { Authentication authentication = new SampleAuthentication(); if (authentication.canHandle(creds)) { return authentication; } } // No valid user or authentication could not handle the given credentials return null; } /** * Get the CRX Principal the credentials should be authenticated against. * This is NOT the authentication step, and usually involves looking up the * Principal in CRX based on the credentials UserId. * * @param credentials * @return */ @Override protected Principal getPrincipal(Credentials credentials) { System.out.println("SampleLoginModule.getPrincipal()"); final String userId = getUserID(credentials); final SamplePrincipalProvider samplePrincipalProvider = new SamplePrincipalProvider(); final Principal p = samplePrincipalProvider.getPrincipal(userId); log.debug("Principal retrieved from PrincipleProvider: " + p); if (p != null && !(p instanceof Group)) { return p; } return null; } /** * commit() is invoked by login() if LoginContext's overall authentication * succeeded. * <p/> * If authentication has succeeded then this method associates relevant * Principals and Credentials (instance fields) with this objects Subject * (instance field). * <p/> * The login is considers as succeeded if the credentials field is set. If * there is no principal set the login is considered as ignored. * <p/> * The implementation stores the principal associated to the UserID and all * the Groups it is member of with the Subject and in addition adds an * instance of Credentials to the Subject's public credentials. * * @return * @throws LoginException */ @Override public boolean commit() throws LoginException { try { if (!super.commit() || principal == null) { return false; } else if (credentials == null) { abort(); } Authorizable user = userManager.getAuthorizable(principal.getName()); if (user == null) { log.debug("User is null; create user for " + credentials.getUserID()); user = userManager.createUser(credentials.getUserID(), UUID.randomUUID().toString()); if (user == null) { log.debug("Could not create user for " + credentials.getUserID()); abort(); } log.debug("Created user: " + user.getPrincipal().getName()); /** * Optionally add the new user to the appropriate groups. This * may require going back to the PrincipalProvider. **/ principal = user.getPrincipal(); log.debug("Principal name for new user: " + principal.getName()); } else if (user.isGroup()) { log.debug("User is a group: " + user.getPrincipal().getName()); user = null; } // Handle token creation if requested by the given credentials if (user != null) { if (user instanceof User && TokenBasedAuthentication.doCreateToken(credentials)) { log.info("Issue a CRX Login Token for this User."); Session noconflictSession = ((SessionImpl) session).createSession(session.getWorkspace().getName()); try { Credentials tokenCreds = TokenBasedAuthentication.createToken((User) user, credentials, 100000000, noconflictSession); subject.getPublicCredentials().add(tokenCreds); } finally { noconflictSession.logout(); } } return true; } } catch (RepositoryException ex) { java.util.logging.Logger.getLogger(SampleLoginModule.class.getName()).log(Level.SEVERE, null, ex); } return false; } /** * Returns true if this method succeeded or false if this LoginModule should * be ignored * * * @return * @throws LoginException */ @Override public boolean logout() throws LoginException { if (super.logout()) { // LoginModule should not be ignored, proceed with any futher checks // and clear any residual LoginModule state // Return true if logout was successful return true; } // Return false if this LoginModule should be ignored return false; } /** * Returns true if this method succeeded or false if this LoginModule should * be ignored * * @return * @throws LoginException */ @Override public boolean abort() throws LoginException { if (super.abort()) { // LoginModule should not be ignored, proceed with any futher checks // and clear any residual LoginModule state this.principal = null; this.credentials = null; // Return true if the abortion was successful return true; } // Return false if this LoginModule should be ignored return false; } }