/*
* JBoss, a division of Red Hat
* Copyright 2012, Red Hat Middleware, LLC, and individual
* contributors as indicated by the @authors tag. See the
* copyright.txt 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.gatein.sso.agent.login;
import org.exoplatform.container.ExoContainer;
import org.exoplatform.container.ExoContainerContext;
import org.exoplatform.container.PortalContainer;
import org.exoplatform.container.RootContainer;
import org.exoplatform.services.security.Authenticator;
import org.exoplatform.services.security.Identity;
import org.exoplatform.services.security.UsernameCredential;
import org.picketlink.identity.federation.bindings.jboss.auth.SAML2LoginModule;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;
import java.security.Principal;
import java.security.acl.Group;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
/**
* Login module for integration with GateIn. It's running on GateIn (SAML SP) side.
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class SAML2IntegrationLoginModule extends SAML2LoginModule
{
// Name of security-domain (actually not used by this impl)
private static final String OPTION_REALM_NAME = "realmName";
// Name of portalContainer
private static final String OPTION_PORTAL_CONTAINER_NAME = "portalContainerName";
// If this boolean property is true, then final principal will use roles from SAML.
// If false, then we don't use roles from SAML, but we will delegate filling of "Roles" principal to next login module in stack
// (actually it is JbossLoginModule, which uses JAAS roles from GateIn database)
// Default value is false, so we are preferring delegation to JbossLoginModule and using roles from portal DB.
private static final String OPTION_USE_SAML_ROLES = "useSAMLRoles";
private static final String[] ALL_VALID_OPTIONS =
{
OPTION_PORTAL_CONTAINER_NAME,
OPTION_REALM_NAME,
OPTION_USE_SAML_ROLES
};
private String portalContainerName;
private boolean useSAMLRoles;
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState,
Map<String, ?> options)
{
try
{
addValidOptions(ALL_VALID_OPTIONS);
}
catch (NoSuchMethodError ignore)
{
// Method addValidOptions is new in picketbox. Not supported on older AS 7.1.1
}
super.initialize(subject, callbackHandler, sharedState, options);
// GateIn integration
this.portalContainerName = getPortalContainerName(options);
String useSAMLRoles = (String)options.get(OPTION_USE_SAML_ROLES);
this.useSAMLRoles = useSAMLRoles != null && "true".equals(useSAMLRoles);
if (log.isTraceEnabled())
{
log.trace("Using options: "
+ OPTION_PORTAL_CONTAINER_NAME + "=" + this.portalContainerName
+ ", " + OPTION_USE_SAML_ROLES + "=" + this.useSAMLRoles);
}
}
@Override
public boolean login() throws javax.security.auth.login.LoginException
{
if (super.login())
{
// Username is already in sharedState thanks to superclass
String username = getUsernameFromSharedState();
if (log.isTraceEnabled())
{
log.trace("Found user " + username + " in shared state.");
}
try
{
//Perform authentication by setting up the proper Application State
Authenticator authenticator = (Authenticator) getContainer().getComponentInstanceOfType(Authenticator.class);
Identity identity = authenticator.createIdentity(username);
sharedState.put("exo.security.identity", identity);
subject.getPublicCredentials().add(new UsernameCredential(username));
return true;
}
catch (Exception e)
{
log.debug("Exception during login process: " + e.getMessage(), e);
throw new LoginException(e.getMessage());
}
}
else
{
return false;
}
}
protected String getUsernameFromSharedState()
{
Object tmp = sharedState.get("javax.security.auth.login.name");
if (tmp == null)
{
return null;
}
else if (tmp instanceof Principal)
{
return ((Principal) tmp).getName();
}
else
{
return (String)tmp;
}
}
@Override
protected Group[] getRoleSets() throws LoginException
{
if (useSAMLRoles)
{
return super.getRoleSets();
}
else
{
// Delegate creation of Group principal to next login module
return new Group[] {};
}
}
// *********************** Helper private methods for GateIn integration *****************************
private String getPortalContainerName(Map options)
{
if (options != null)
{
String optionValue = (String) options.get(OPTION_PORTAL_CONTAINER_NAME);
if (optionValue != null && optionValue.length() > 0)
{
return optionValue;
}
}
return PortalContainer.DEFAULT_PORTAL_CONTAINER_NAME;
}
private ExoContainer getContainer() throws Exception
{
ExoContainer container = ExoContainerContext.getCurrentContainer();
if (container instanceof RootContainer)
{
container = RootContainer.getInstance().getPortalContainer(portalContainerName);
}
return container;
}
}