/*
* JBoss, Home of Professional Open Source Copyright 2008, Red Hat Middleware
* LLC, and individual contributors 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.jboss.internal.soa.esb.services.security;
import java.security.Principal;
import java.util.List;
import java.util.Set;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import org.jboss.internal.soa.esb.assertion.AssertArgument;
import org.jboss.soa.esb.ConfigurationException;
import org.jboss.soa.esb.common.Configuration;
import org.jboss.soa.esb.services.security.SecurityConfig;
import org.jboss.soa.esb.services.security.SecurityContext;
import org.jboss.soa.esb.services.security.SecurityService;
import org.jboss.soa.esb.services.security.SecurityServiceException;
import org.jboss.soa.esb.services.security.auth.AuthenticationRequest;
import org.jboss.soa.esb.services.security.principals.Group;
import org.jboss.soa.esb.services.security.principals.Role;
import org.jboss.soa.esb.util.ClassUtil;
/**
* Concrete impl of a SecurityService in JBoss ESB that uses JAAS.
* <p/>
* This class is indented to be specified as the security implementation
* to be used with JBoss ESB. <br>
* This would be specified in jbossesb-properties.xml:
* <pre>
* {@literal
* <properties name="security">
* <property name="org.jboss.soa.esb.services.security.implementationClass" value="org.jboss.internal.soa.esb.services.security.JaasSecurityService"/>
* <property name="org.jboss.soa.esb.services.security.callbackHandler" value="org.jboss.internal.soa.esb.services.security.UserPassCallbackHandler"/>
* </properties>
* </pre>}
*
* @author <a href="mailto:dbevenius@jboss.com">Daniel Bevenius</a>
* @Since 4.4
*/
public final class JaasSecurityService implements SecurityService
{
/*
* Callback handler implementation name
*/
private String callbackHandlerClassName;
/**
* Performs authentication of the passed in SecurityContext.
* </p>
*
* @param config - the security configuration. Properties from jboss-esb.xml
* @param securityContext - the security context to be used.
* @param authRequest - the authentication request to be processed.
* @throws SecurityServiceException
* @throws LoginException if the authentication fails
*/
public void authenticate(final SecurityConfig config, SecurityContext securityContext, final AuthenticationRequest authRequest) throws SecurityServiceException
{
AssertArgument.isNotNull(config, "config");
LoginContext loginContext;
try
{
final EsbCallbackHandler callbackHandler = createCallbackHandler(config, authRequest);
if (callbackHandler != null)
{
loginContext = new LoginContext(config.getModuleName(), securityContext.getSubject(), callbackHandler);
}
else
{
loginContext = new LoginContext(config.getModuleName(), securityContext.getSubject());
}
loginContext.login();
// add a runAs group if specified
addRunAs(config.getRunAs(), securityContext.getSubject());
}
catch (final LoginException e)
{
throw new SecurityServiceException("Exception while trying to login:", e);
}
}
public boolean checkRolesAllowed(final List<String> rolesAllowed, SecurityContext securityContext)
{
if (rolesAllowed.isEmpty())
{
return true;
}
for (String roleName : rolesAllowed)
{
boolean isInRole = securityContext.isCallerInRole(roleName);
if (isInRole)
{
return true;
}
}
return false;
}
/**
* Determines if the passed in Subject has the role specified in the context.
* @return true - if the callers has the role
*/
public boolean isCallerInRole( final Subject subject, final Principal role)
{
final Set<java.security.acl.Group> principals = subject.getPrincipals(java.security.acl.Group.class);
for (java.security.acl.Group group : principals)
{
if ( group.isMember(role) )
return true;
}
return false;
}
/**
* Configures by reading the value of the property 'org.jboss.soa.esb.services.security.configUrl'
* from jbossesb-properties, is one exists.
*/
public void configure() throws ConfigurationException
{
// get a EsbCallbackHandler if one is configured in jbossesb-properties.xml
callbackHandlerClassName = Configuration.getSecurityServiceCallbackHandlerImplClass();
}
public void logout(SecurityConfig config)
{
// NoOp
}
public void refreshSecurityConfig()
{
// NoOp
}
/**
* Creates an instance of EsbCallbackHandler specified in either jboss-esb.xml:
* <pre>
* {@literal
* <security moduleName="OpenSSOLogin" runAs="adminRole" callbackHandler="org.jboss.internal.soa.esb.services.security.UserPassCallbackHandler"/>
* }</pre><br>
* or if one was not specified in jboss-esb.xml but one has been specified in jbossesb-properties.xml:
* <pre>
* {@literal
* <property name="org.jboss.soa.esb.services.security.callbackHandler" value="org.jboss.internal.soa.esb.services.security.UserPassCallbackHandler"/>
* }</pre><br>
* After an instance has been created its {@link EsbCallbackHandler#setAuthenticationRequest(AuthenticationRequest)} method is called
* which gives the callback handler access to the authentication information.
*
* @param config - the security configuration information(from jboss-esb.xml)
* @param authRequest - the authentication request information
* @return EsbCallbackHandler - new instance with authReqeust set or null if no callback handler has been specified in either jboss-esb.xml or jbossesb-properties.xml
* @throws SecurityServiceException - if an EsbCallbackHandler has specified in the configuration but the implementation cannot be created.
*/
private EsbCallbackHandler createCallbackHandler( final SecurityConfig config, final AuthenticationRequest authRequest ) throws SecurityServiceException
{
EsbCallbackHandler callbackHandler = null;
// check if a callbackhandler was specified in jboss-esb.xml
String callbackImpl = config.getCallbackHandler();
if ( callbackImpl == null )
{
// use the global callbackhandler that can be specified in jbossesb-properties.xml(optional)
callbackImpl = callbackHandlerClassName;
}
if ( callbackImpl != null )
{
callbackHandler = createNewInstance(callbackImpl);
// set the authReqeust so that the callback handler has access to the authentication information(Principals, Credentials)
callbackHandler.setAuthenticationRequest(authRequest);
// set the SecurityConfig so that the callbakc handler has access to the configuration information
callbackHandler.setSecurityConfig(config);
}
return callbackHandler;
}
private void addRunAs( final String runAs, final Subject subject )
{
if ( runAs != null )
{
final Role runAsRole = new Role(runAs);
final Set<Group> principals = subject.getPrincipals(Group.class);
if ( principals.isEmpty() )
{
final Group group = new Group("Roles");
group.addMember(runAsRole);
subject.getPrincipals().add(group);
}
else
{
for (Group groups : principals)
{
if ( "Roles".equals(groups.getName()) )
{
groups.addMember(runAsRole);
}
}
}
}
}
private <T extends EsbCallbackHandler> T createNewInstance( final String className ) throws SecurityServiceException
{
try
{
@SuppressWarnings("unchecked")
final Class<T> forName = ClassUtil.forName(className, getClass());
return forName.newInstance();
}
catch (final ClassNotFoundException e)
{
throw new SecurityServiceException("ClassNotFoundException while trying to create an impl of [" + className + "]", e);
}
catch (final InstantiationException e)
{
throw new SecurityServiceException("InstantiationException while trying to create an impl of [" + className + "]", e);
}
catch (final IllegalAccessException e)
{
throw new SecurityServiceException("IllegalAccess while trying to create an impl of [" + className + "]", e);
}
}
}