/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., 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.jboss.security.plugins.authorization;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import org.jboss.logging.Logger;
import org.jboss.security.SecurityConstants;
import org.jboss.security.authorization.AuthorizationContext;
import org.jboss.security.authorization.AuthorizationException;
import org.jboss.security.authorization.AuthorizationModule;
import org.jboss.security.authorization.Resource;
import org.jboss.security.authorization.ResourceType;
import org.jboss.security.authorization.config.AuthorizationModuleEntry;
import org.jboss.security.authorization.config.AuthorizationModuleEntry.ControlFlag;
import org.jboss.security.config.ApplicationPolicy;
import org.jboss.security.config.AuthorizationInfo;
import org.jboss.security.config.SecurityConfiguration;
//$Id: JBossAuthorizationContext.java 62954 2007-05-10 04:12:18Z anil.saldhana@jboss.com $
/**
* JBAS-3374: Authorization Framework for Policy Decision Modules
* For information on the behavior of the Authorization Modules,
* For Authorization Modules behavior(Required, Requisite, Sufficient and Optional)
* please refer to the javadoc for @see javax.security.auth.login.Configuration
*
* The AuthorizationContext derives the AuthorizationInfo(configuration for the modules)
* in the following way:
* a) If there has been an injection of ApplicationPolicy, then it will be used.
* b) Util.getApplicationPolicy will be used(which relies on SecurityConfiguration static class).
* c) Flag an error that there is no available Application Policy
*
* @author <a href="mailto:Anil.Saldhana@jboss.org">Anil Saldhana</a>
* @since Jun 11, 2006
* @version $Revision: 62954 $
*/
public class JBossAuthorizationContext extends AuthorizationContext
{
private static Logger log = Logger.getLogger(JBossAuthorizationContext.class);
private boolean trace = log.isTraceEnabled();
//Application Policy can be injected
private ApplicationPolicy applicationPolicy = null;
//Control Flag behavior
private boolean encounteredRequiredError = false;
private boolean encounteredOptionalError = false;
private AuthorizationException moduleException = null;
private int overallDecision = DENY;
public JBossAuthorizationContext(String name, Subject subject,
CallbackHandler handler)
{
this.securityDomainName = name;
this.authenticatedSubject = subject;
this.callbackHandler = handler;
}
/**
* Inject an ApplicationPolicy that contains AuthorizationInfo
* @param aPolicy
* @throws IllegalArgumentException if ApplicationPolicy is null or
* does not contain AuthorizationInfo or domain name does not match
*/
public void setApplicationPolicy(ApplicationPolicy aPolicy)
{
if(aPolicy == null)
throw new IllegalArgumentException("Application Policy is null:domain="+this.securityDomainName);
AuthorizationInfo authzInfo = aPolicy.getAuthorizationInfo();
if( authzInfo == null)
throw new IllegalArgumentException("Application Policy has no AuthorizationInfo");
if(!authzInfo.getName().equals(securityDomainName))
throw new IllegalArgumentException("Application Policy ->AuthorizationInfo:" + authzInfo.getName()
+ " does not match required domain name=" + this.securityDomainName);
this.applicationPolicy = aPolicy;
}
/**
* Authorize the Resource
* @param resource
* @return AuthorizationContext.PERMIT or AuthorizationContext.DENY
* @throws AuthorizationException
*/
public int authorize(final Resource resource) throws AuthorizationException
{
try
{
initializeModules(resource);
}
catch (PrivilegedActionException e1)
{
throw new RuntimeException(e1);
}
//Do a PrivilegedAction
try
{
AccessController.doPrivileged(new PrivilegedExceptionAction()
{
public Object run() throws AuthorizationException
{
int result = invokeAuthorize(resource);
if(result == PERMIT)
invokeCommit();
if(result == DENY)
{
invokeAbort();
throw new AuthorizationException("Denied");
}
return null;
}
});
}
catch (PrivilegedActionException e)
{
Exception exc = e.getException();
if(trace)
log.trace("Error in authorize:", exc);
invokeAbort();
throw ((AuthorizationException)exc);
}
return PERMIT;
}
//Private Methods
private void initializeModules(Resource resource) throws PrivilegedActionException
{
AuthorizationInfo authzInfo = getAuthorizationInfo(securityDomainName, resource);
if(authzInfo == null)
authzInfo = getAuthorizationInfo(SecurityConstants.DEFAULT_EJB_APPLICATION_POLICY, resource);
if(authzInfo == null)
throw new IllegalStateException("Authorization Info is null");
AuthorizationModuleEntry[] entries = authzInfo.getAuthorizationModuleEntry();
int len = entries != null ? entries.length : 0;
for(int i = 0 ; i < len; i++)
{
AuthorizationModuleEntry entry = entries[i];
AuthorizationModuleEntry.ControlFlag flag = entry.getControlFlag();
if(flag == null)
{
if(trace)
log.trace("Null Control flag for entry:"+entry+". Defaults to REQUIRED!");
flag = AuthorizationModuleEntry.ControlFlag.REQUIRED;
}
else
if(trace)
log.trace("Control flag for entry:"+entry+"is:["+flag+"]");
this.controlFlags.add(flag);
modules.add(instantiateModule(entry.getPolicyModuleName(), entry.getOptions()));
}
}
private int invokeAuthorize(Resource resource)
throws AuthorizationException
{
int length = modules.size();
for(int i = 0; i < length; i++)
{
AuthorizationModule module = (AuthorizationModule)modules.get(i);
ControlFlag flag = (ControlFlag)this.controlFlags.get(i);
int decision = DENY;
try
{
decision = module.authorize(resource);
}
catch(Exception ae)
{
decision = DENY;
if(this.moduleException == null)
this.moduleException = new AuthorizationException(ae.getMessage());
}
if(decision == PERMIT)
{
this.overallDecision = PERMIT;
//SUFFICIENT case
if(flag == ControlFlag.SUFFICIENT && this.encounteredRequiredError == false)
return PERMIT;
continue; //Continue with the other modules
}
//Go through the failure cases
//REQUISITE case
if(flag == ControlFlag.REQUISITE)
{
if(trace)
log.trace("REQUISITE failed for " + module);
if(this.moduleException == null)
this.moduleException = new AuthorizationException("Authorization failed");
else
throw this.moduleException;
}
//REQUIRED Case
if(flag == ControlFlag.REQUIRED)
{
if(trace)
log.trace("REQUIRED failed for " + module);
if(this.encounteredRequiredError == false)
this.encounteredRequiredError = true;
}
if(flag == ControlFlag.OPTIONAL)
this.encounteredOptionalError = true;
}
//All the authorization modules have been visited.
if(this.encounteredRequiredError)
throw new AuthorizationException("Authorization Failed");
if(this.overallDecision == DENY && this.encounteredOptionalError)
throw new AuthorizationException("Authorization Failed");
if(this.overallDecision == DENY)
throw new AuthorizationException("Authorization Failed:No modules active.");
return PERMIT;
}
private void invokeCommit()
throws AuthorizationException
{
int length = modules.size();
for(int i = 0; i < length; i++)
{
AuthorizationModule module = (AuthorizationModule)modules.get(i);
boolean bool = module.commit();
if(!bool)
throw new AuthorizationException("commit on modules failed");
}
}
private void invokeAbort()
throws AuthorizationException
{
int length = modules.size();
for(int i = 0; i < length; i++)
{
AuthorizationModule module = (AuthorizationModule)modules.get(i);
boolean bool = module.abort();
if(!bool)
throw new AuthorizationException("abort on modules failed");
}
}
private AuthorizationModule instantiateModule(String name, Map map) throws PrivilegedActionException
{
AuthorizationModule am = null;
ClassLoader tcl = SecurityActions.getContextClassLoader();
try
{
Class clazz = tcl.loadClass(name);
am = (AuthorizationModule)clazz.newInstance();
}
catch ( Exception e)
{
log.debug("Error instantiating AuthorizationModule:",e);
}
if(am == null)
throw new IllegalStateException("AuthorizationModule has not " +
"been instantiated");
am.initialize(this.authenticatedSubject, this.callbackHandler,
this.sharedState,map);
return am;
}
private AuthorizationInfo getAuthorizationInfo(String domainName, Resource resource)
{
ResourceType layer = resource.getLayer();
//Check if an instance of ApplicationPolicy is available
if(this.applicationPolicy != null)
return applicationPolicy.getAuthorizationInfo();
ApplicationPolicy aPolicy = SecurityConfiguration.getApplicationPolicy(domainName);
if(aPolicy == null)
{
if(trace)
log.trace("Application Policy not obtained for domain="+ domainName +
". Trying to obtain the App policy for the default domain of the layer:");
if(layer == ResourceType.EJB)
aPolicy = SecurityConfiguration.getApplicationPolicy(SecurityConstants.DEFAULT_EJB_APPLICATION_POLICY);
else
if(layer == ResourceType.WEB)
aPolicy = SecurityConfiguration.getApplicationPolicy(SecurityConstants.DEFAULT_WEB_APPLICATION_POLICY);
}
if(aPolicy == null)
throw new IllegalStateException("Application Policy is null for domain:"+ domainName);
return aPolicy.getAuthorizationInfo();
}
}