/* * (C) Copyright 2006-2008 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$ */ package org.nuxeo.ecm.platform.login; import java.security.Principal; import java.security.acl.Group; import java.util.Enumeration; import java.util.Map; import java.util.Set; import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.login.LoginException; import javax.security.auth.spi.LoginModule; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Abstract implementation of the {@link LoginModule} SPI from {@code javax.security.auth.spi}. */ public abstract class NuxeoAbstractServerLoginModule implements LoginModule { private static final Log log = LogFactory.getLog(NuxeoAbstractServerLoginModule.class); protected Subject subject; protected Map sharedState; protected Map options; protected boolean loginOk; /** An optional custom Principal class implementation */ protected String principalClassName; /** the principal to use when a null username and password are seen */ protected Principal unauthenticatedIdentity; protected CallbackHandler callbackHandler; /** Flag indicating if the shared credential should be used */ protected boolean useFirstPass; protected abstract Principal getIdentity(); protected abstract Group[] getRoleSets() throws LoginException; protected abstract Principal createIdentity(String username) throws LoginException; public boolean abort() throws LoginException { log.trace("abort"); return true; } public boolean commit() throws LoginException { log.trace("commit, loginOk=" + loginOk); if (!loginOk) { return false; } Set<Principal> principals = subject.getPrincipals(); Principal identity = getIdentity(); principals.add(identity); Group[] roleSets = getRoleSets(); for (Group group : roleSets) { String name = group.getName(); Group subjectGroup = createGroup(name, principals); /* * if( subjectGroup instanceof NestableGroup ) { SimpleGroup tmp = new SimpleGroup("Roles"); * subjectGroup.addMember(tmp); subjectGroup = tmp; } */ // Copy the group members to the Subject group Enumeration<? extends Principal> members = group.members(); while (members.hasMoreElements()) { Principal role = members.nextElement(); subjectGroup.addMember(role); } } return true; } public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) { this.subject = subject; this.callbackHandler = callbackHandler; this.sharedState = sharedState; this.options = options; if (log.isTraceEnabled()) { log.trace("initialize, instance=@" + System.identityHashCode(this)); } /* * Check for password sharing options. Any non-null value for password_stacking sets useFirstPass as this module * has no way to validate any shared password. */ String passwordStacking = (String) options.get("password-stacking"); if (passwordStacking != null && passwordStacking.equalsIgnoreCase("useFirstPass")) { useFirstPass = true; } // Check for a custom Principal implementation principalClassName = (String) options.get("principalClass"); // Check for unauthenticatedIdentity option. String name = (String) options.get("unauthenticatedIdentity"); if (name != null) { try { unauthenticatedIdentity = createIdentity(name); log.trace("Saw unauthenticatedIdentity=" + name); } catch (LoginException e) { log.warn("Failed to create custom unauthenticatedIdentity", e); } } } public boolean logout() throws LoginException { log.trace("logout"); // Remove the user identity Principal identity = getIdentity(); Set<Principal> principals = subject.getPrincipals(); principals.remove(identity); // Remove any added Groups... return true; } /** * Finds or creates a Group with the given name. Subclasses should use this method to locate the 'Roles' group or * create additional types of groups. * * @return A named Group from the principals set. */ protected Group createGroup(String name, Set<Principal> principals) { Group roles = null; for (Principal principal : principals) { if (!(principal instanceof Group)) { continue; } Group grp = (Group) principal; if (grp.getName().equals(name)) { roles = grp; break; } } // If we did not find a group, create one if (roles == null) { roles = new GroupImpl(name); principals.add(roles); } return roles; } }