/* * JBoss, Home of Professional Open Source. * Copyright 2012, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.picketlink.identity.federation.bindings.tomcat; import java.io.IOException; import java.security.AccessController; import java.security.Principal; import java.security.PrivilegedAction; import java.util.Set; import java.util.UUID; import javax.security.auth.Subject; import org.apache.catalina.Realm; import org.apache.catalina.Session; import org.apache.catalina.authenticator.AuthenticatorBase; import org.apache.catalina.authenticator.Constants; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; import org.apache.catalina.deploy.LoginConfig; import org.picketlink.identity.federation.PicketLinkLogger; import org.picketlink.identity.federation.PicketLinkLoggerFactory; /** * <p>An authenticator that delegates actual authentication to a realm, and in turn to a security manager, by presenting a * "conventional" identity. The security manager must accept the conventional identity and generate the real identity for the * authenticated principal.</p> * <p>Subclasses should override some methods to provide especific implementation according with the binding/environment.</p> * * @author <a href="mailto:ovidiu@novaordis.com">Ovidiu Feodorov</a> * @author Anil.Saldhana@redhat.com * @author <a href="mailto:psilva@redhat.com">Pedro Silva</a> * */ public abstract class AbstractPicketLinkAuthenticator extends AuthenticatorBase { protected static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger(); /** * This is the auth method used in the register method */ protected String authMethod = "SECURITY_DOMAIN"; /** * The authenticator may not be aware of the user name until after the underlying security exercise is complete. The Subject * will have the proper user name. Hence we may need to perform an additional authentication now with the user name we have * obtained. */ protected boolean needSubjectPrincipalSubstitution = true; protected SubjectSecurityInteraction subjectInteraction = null; protected String subjectInteractionClassName = "org.picketlink.identity.federation.bindings.jboss.subject.PicketLinkJBossSubjectInteraction"; /** * Set the auth method via WEB-INF/context.xml (JBoss AS) * * @param authMethod */ public void setAuthMethod(String authMethod) { this.authMethod = authMethod; } public void setNeedSubjectPrincipalSubstitution(String needSubjectPrincipalSubstitutionVal) { this.needSubjectPrincipalSubstitution = Boolean.valueOf(needSubjectPrincipalSubstitutionVal); } /** * Set this if you want to override the default {@link SubjectSecurityInteraction} * * @param subjectRetrieverClassName */ public void setSubjectInteractionClassName(String subjectRetrieverClassName) { this.subjectInteractionClassName = subjectRetrieverClassName; } /** * <p>Actually performs the authentication. Subclasses should call this method when implementing the <code>AuthenticatorBase.authenticate</code> method.</p> * <p>This method was created to allow different signatures for the <code>AuthenticatorBase.authenticate</code> method according with the catalina version.</p> * * @param request * @param response * @param loginConfig * @return * @throws IOException */ protected boolean performAuthentication(Request request, Response response, LoginConfig loginConfig) throws IOException { logger.trace("Authenticating user"); Principal principal = request.getUserPrincipal(); if (principal != null) { logger.trace("Already authenticated '" + principal.getName() + "'"); return true; } Session session = request.getSessionInternal(true); String userName = UUID.randomUUID().toString(); String password = userName; Realm realm = context.getRealm(); principal = realm.authenticate(userName, password); Principal originalPrincipal = principal; if (principal != null) { if (needSubjectPrincipalSubstitution) { principal = getSubjectPrincipal(); if (principal == null) throw new RuntimeException("Principal from subject is null"); principal = realm.authenticate(principal.getName(), password); } session.setNote(Constants.SESS_USERNAME_NOTE, principal.getName()); session.setNote(Constants.SESS_PASSWORD_NOTE, password); request.setUserPrincipal(principal); doRegister(request, response, principal, password); if (originalPrincipal != null && needSubjectPrincipalSubstitution) { subjectInteraction.cleanup(originalPrincipal); } return true; } return false; } /** * <p>Subclasses should override this method to register an authenticated Principal.</p> * * @param request * @param response * @param principal * @param password */ protected abstract void doRegister(Request request, Response response, Principal principal, String password); protected Principal getSubjectPrincipal() { if (subjectInteraction == null) { Class<?> clazz = loadClass(getClass(), subjectInteractionClassName); try { subjectInteraction = (SubjectSecurityInteraction) clazz.newInstance(); subjectInteraction.setSecurityDomain(context.getRealm().getContainer().getName()); } catch (Exception e) { throw new RuntimeException(e); } } Subject subject = subjectInteraction.get(); if (subject != null) { Set<Principal> principals = subject.getPrincipals(); if (!principals.isEmpty()) { return subject.getPrincipals().iterator().next(); } } return null; } Class<?> loadClass(final Class<?> theClass, final String fqn) { return AccessController.doPrivileged(new PrivilegedAction<Class<?>>() { public Class<?> run() { ClassLoader classLoader = theClass.getClassLoader(); Class<?> clazz = loadClass(classLoader, fqn); if (clazz == null) { classLoader = Thread.currentThread().getContextClassLoader(); clazz = loadClass(classLoader, fqn); } return clazz; } }); } Class<?> loadClass(final ClassLoader cl, final String fqn) { return AccessController.doPrivileged(new PrivilegedAction<Class<?>>() { public Class<?> run() { try { return cl.loadClass(fqn); } catch (ClassNotFoundException e) { } return null; } }); } }