Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jboss.security.SecurityAssociation Maven / Gradle / Ivy
/*
* 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;
import java.security.Principal;
import java.util.ArrayList;
import java.util.HashMap;
import javax.security.auth.Subject;
import org.jboss.logging.Logger;
/**
* The SecurityAssociation class maintains the security principal and
* credentials. This can be done on either a singleton basis or a thread local
* basis depending on the server property. When the server property has been set
* to true, the security information is maintained in thread local storage. The
* type of thread local storage depends on the org.jboss.security.SecurityAssociation.ThreadLocal
* property. If this property is true, then the thread local storage object is
* of type java.lang.ThreadLocal which results in the current thread's security
* information NOT being propagated to child threads.
*
* When the property is false or does not exist, the thread local storage object
* is of type java.lang.InheritableThreadLocal, and any threads spawned by the
* current thread will inherit the security information of the current thread.
* Subseqent changes to the current thread's security information are NOT
* propagated to any previously spawned child threads.
*
* When the server property is false, security information is maintained in
* class variables which makes the information available to all threads within
* the current VM.
*
* Note that this is not a public API class. Its an implementation detail that
* is subject to change without notice.
*
* @author Daniel O'Connor ([email protected] )
* @author [email protected]
* @author [email protected]
* @version $Revision: 107 $
*/
public final class SecurityAssociation
{
private static Logger log = Logger.getLogger(SecurityAssociation.class);
/**
* A flag indicating if trace level logging should be performed
*/
private static boolean trace;
/**
* A flag indicating if security information is global or thread local
*/
private static boolean server;
/**
* The SecurityAssociation principal used when the server flag is false
*/
private static Principal principal;
/**
* The SecurityAssociation credential used when the server flag is false
*/
private static Object credential;
/**
* The SecurityAssociation principal used when the server flag is true
*/
private static ThreadLocal threadPrincipal;
/**
* The SecurityAssociation credential used when the server flag is true
*/
private static ThreadLocal threadCredential;
/**
* The SecurityAssociation HashMap
*/
private static ThreadLocal> threadContextMap;
/**
* Thread local stacks of run-as principal roles used to implement J2EE
* run-as identity propagation
*/
private static RunAsThreadLocalStack threadRunAsStacks;
/**
* Thread local stacks of authenticated subject used to control the current
* caller security context
*/
private static SubjectThreadLocalStack threadSubjectStacks;
/**
* The permission required to access getPrincpal, getCredential
*/
private static final RuntimePermission getPrincipalInfoPermission =
new RuntimePermission("org.jboss.security.SecurityAssociation.getPrincipalInfo");
/**
* The permission required to access getSubject
*/
private static final RuntimePermission getSubjectPermission =
new RuntimePermission("org.jboss.security.SecurityAssociation.getSubject");
/**
* The permission required to access setPrincpal, setCredential, setSubject
* pushSubjectContext, popSubjectContext
*/
private static final RuntimePermission setPrincipalInfoPermission =
new RuntimePermission("org.jboss.security.SecurityAssociation.setPrincipalInfo");
/**
* The permission required to access setServer
*/
private static final RuntimePermission setServerPermission =
new RuntimePermission("org.jboss.security.SecurityAssociation.setServer");
/**
* The permission required to access pushRunAsIdentity/popRunAsIdentity
*/
private static final RuntimePermission setRunAsIdentity =
new RuntimePermission("org.jboss.security.SecurityAssociation.setRunAsRole");
/**
* The permission required to get the current security context info
*/
private static final RuntimePermission getContextInfo =
new RuntimePermission("org.jboss.security.SecurityAssociation.accessContextInfo", "get");
/**
* The permission required to set the current security context info
*/
private static final RuntimePermission setContextInfo =
new RuntimePermission("org.jboss.security.SecurityAssociation.accessContextInfo", "set");
static
{
String flag = SecurityActions.getProperty("org.jboss.security.SecurityAssociation.ThreadLocal", "false");
boolean useThreadLocal = Boolean.valueOf(flag).booleanValue();
log.debug("Using ThreadLocal: "+useThreadLocal);
trace = log.isTraceEnabled();
if (useThreadLocal)
{
threadPrincipal = new ThreadLocal();
threadCredential = new ThreadLocal();
threadContextMap = new ThreadLocal>()
{
protected HashMap initialValue()
{
return new HashMap();
}
};
}
else
{
threadPrincipal = new InheritableThreadLocal();
threadCredential = new InheritableThreadLocal();
threadContextMap = new HashMapInheritableLocal>();
}
threadRunAsStacks = new RunAsThreadLocalStack(useThreadLocal);
threadSubjectStacks = new SubjectThreadLocalStack(useThreadLocal);
}
/**
* Get the current authentication principal information. If a security
* manager is present, then this method calls the security manager's
* checkPermission
method with a
* RuntimePermission("org.jboss.security.SecurityAssociation.getPrincipalInfo")
*
permission to ensure it's ok to access principal information. If
* not, a SecurityException
will be thrown.
* @return Principal, the current principal identity.
*/
public static Principal getPrincipal()
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(getPrincipalInfoPermission);
Principal thePrincipal = principal;
if(!server)
return principal;
if( trace )
log.trace("getPrincipal, principal="+thePrincipal);
SecurityContext sc = SecurityContextAssociation.getSecurityContext();
if(sc != null)
{
if( trace )
log.warn("You are using deprecated api to getPrincipal. Use security context based approach");
thePrincipal = sc.getUtil().getUserPrincipal();
}
return thePrincipal;
}
/**
* Get the caller's principal. If a security manager is present,
* then this method calls the security manager's checkPermission
* method with a RuntimePermission("org.jboss.security.SecurityAssociation.getPrincipalInfo")
*
permission to ensure it's ok to access principal information. If
* not, a SecurityException
will be thrown.
*
* @return Principal, the current principal identity.
*/
public static Principal getCallerPrincipal()
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(getPrincipalInfoPermission);
/*Principal thePrincipal = peekRunAsIdentity(1);
if( thePrincipal == null )
{
if (server)
thePrincipal = (Principal) threadPrincipal.get();
else
thePrincipal = principal;
}*/
if(!server)
return principal;
//Just pluck it from the current security context
SecurityContext sc = SecurityContextAssociation.getSecurityContext();
Principal thePrincipal = null;
if(sc != null)
{
//Check for runas
RunAs ras = sc.getIncomingRunAs();
if(ras != null)
thePrincipal = new SimplePrincipal(ras.getName());
else
thePrincipal = sc.getUtil().getUserPrincipal();
}
if( trace )
log.trace("getCallerPrincipal, principal="+thePrincipal);
return thePrincipal;
}
/**
* Get the current authentication credential information. This can be of any type
* including: a String password, a char[] password, an X509 cert, etc. If a
* security manager is present, then this method calls the security manager's
* checkPermission
method with a RuntimePermission("org.jboss.security.SecurityAssociation.getPrincipalInfo")
*
permission to ensure it's ok to access principal information. If
* not, a SecurityException
will be thrown.
* @return Object, the credential that proves the principal identity.
*/
public static Object getCredential()
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(getPrincipalInfoPermission);
if(!server)
return credential;
SecurityContext sc = SecurityContextAssociation.getSecurityContext();
if(sc != null)
{
if(trace)
log.warn("You are using deprecated api to getCredential. Use security context based approach");
credential = sc.getUtil().getCredential();
}
return credential;
}
/**
* Get the current Subject information. If a security manager is present,
* then this method calls the security manager's checkPermission method with
* a RuntimePermission("org.jboss.security.SecurityAssociation.getSubject")
* permission to ensure it's ok to access principal information. If not, a
* SecurityException will be thrown. Note that this method does not consider
* whether or not a run-as identity exists. For access to this information
* see the JACC PolicyContextHandler registered under the key
* "javax.security.auth.Subject.container"
* @return Subject, the current Subject identity.
* @see javax.security.jacc.PolicyContext#getContext(String)
*/
public static Subject getSubject()
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(getSubjectPermission);
SubjectContext sc = threadSubjectStacks.peek();
if( trace )
log.trace("getSubject, sc="+sc);
Subject subject = null;
/*if( sc != null )
subject = sc.getSubject();
return subject;*/
SecurityContext secContext = SecurityAssociationActions.getSecurityContext();
if(secContext != null)
{
if(trace)
log.warn("You are using deprecated api to getSubject. Use security context based approach");
subject = secContext.getUtil().getSubject();
}
return subject;
}
/**
* Set the current principal information. If a security manager is present,
* then this method calls the security manager's checkPermission
* method with a RuntimePermission("org.jboss.security.SecurityAssociation.setPrincipalInfo")
*
permission to ensure it's ok to access principal information. If
* not, a SecurityException
will be thrown.
* @param principal - the current principal identity.
*/
public static void setPrincipal(Principal principal)
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(setPrincipalInfoPermission);
if (trace)
log.trace("setPrincipal, p=" + principal + ", server=" + server);
// Integrate with the new SubjectContext
SubjectContext sc = threadSubjectStacks.peek();
if( sc == null )
{
// There is no active security context
sc = new SubjectContext();
threadSubjectStacks.push(sc);
}
else if( (sc.getFlags() & SubjectContext.PRINCIPAL_WAS_SET) != 0 )
{
// The current security context has its principal set
sc = new SubjectContext();
threadSubjectStacks.push(sc);
}
sc.setPrincipal(principal);
if(!server)
{
SecurityContextAssociation.setClient();
SecurityAssociation.principal = principal;
return;
}
SecurityContext securityContext = SecurityContextAssociation.getSecurityContext();
//Clients code that may have set directly (Legacy)
if(securityContext == null)
{
try
{
securityContext = SecurityContextFactory.createSecurityContext("CLIENT_SIDE");
}
catch (Exception e)
{
throw new RuntimeException(e);
}
SecurityContextAssociation.setSecurityContext(securityContext);
}
if(trace)
log.warn("Using deprecated API. Move to a security context based approach");
Object cred = securityContext.getUtil().getCredential();
Subject subj = securityContext.getUtil().getSubject();
securityContext.getUtil().createSubjectInfo(principal,cred, subj);
if (trace)
log.trace("setPrincipal, sc="+sc);
}
/**
* Set the current principal credential information. This can be of any type
* including: a String password, a char[] password, an X509 cert, etc.
*
* If a security manager is present, then this method calls the security
* manager's checkPermission
method with a
* RuntimePermission("org.jboss.security.SecurityAssociation.setPrincipalInfo")
*
permission to ensure it's ok to access principal information. If
* not, a SecurityException
will be thrown.
* @param credential - the credential that proves the principal identity.
*/
public static void setCredential(Object credential)
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(setPrincipalInfoPermission);
// Integrate with the new SubjectContext
SubjectContext sc = threadSubjectStacks.peek();
if( sc == null )
{
// There is no active security context
sc = new SubjectContext();
threadSubjectStacks.push(sc);
}
else if( (sc.getFlags() & SubjectContext.CREDENTIAL_WAS_SET) != 0 )
{
// The current security context has its principal set
sc = new SubjectContext();
threadSubjectStacks.push(sc);
}
sc.setCredential(credential);
if (trace)
log.trace("setCredential, sc="+sc);
if(!server)
{
SecurityContextAssociation.setClient();
SecurityAssociation.credential = credential;
return;
}
SecurityContext securityContext = SecurityContextAssociation.getSecurityContext();
//Clients code that may have set directly (Legacy)
if(securityContext == null)
{
try
{
securityContext = SecurityContextFactory.createSecurityContext("CLIENT_SIDE");
}
catch (Exception e)
{
throw new RuntimeException(e);
}
SecurityContextAssociation.setSecurityContext(securityContext);
}
if(trace)
log.warn("Using deprecated API. Move to a security context based approach");
Principal principal = securityContext.getUtil().getUserPrincipal();
Subject subj = securityContext.getUtil().getSubject();
securityContext.getUtil().createSubjectInfo(principal,credential, subj);
}
/**
* Set the current Subject information. If a security manager is present,
* then this method calls the security manager's checkPermission
* method with a RuntimePermission("org.jboss.security.SecurityAssociation.setPrincipalInfo")
*
permission to ensure it's ok to access principal information. If
* not, a SecurityException
will be thrown.
* @param subject - the current identity.
*/
public static void setSubject(Subject subject)
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(setPrincipalInfoPermission);
if (trace)
log.trace("setSubject, s=" + subject + ", server=" + server);
// Integrate with the new SubjectContext
SubjectContext sc = threadSubjectStacks.peek();
if( sc == null )
{
// There is no active security context
sc = new SubjectContext();
threadSubjectStacks.push(sc);
}
else if( (sc.getFlags() & SubjectContext.SUBJECT_WAS_SET) != 0 )
{
// The current security context has its subject set
sc = new SubjectContext();
threadSubjectStacks.push(sc);
}
sc.setSubject(subject);
if (trace)
log.trace("setSubject, sc="+sc);
SecurityContext sctx = SecurityContextAssociation.getSecurityContext();
if(sctx != null)
{
SubjectInfo si = sctx.getSubjectInfo();
if(si != null)
{
si.setAuthenticatedSubject(subject);
}
else
sctx.getUtil().createSubjectInfo(null, null, subject);
}
}
/**
* Introduced for backward compatibility with older versions of security
* @deprecated
* @see {@code SecurityAssociation#getContextInfo(String)}
* @param key
* @return
* @throws IllegalArgumentException if the passed key is not of type String
*/
public static Object getContextInfo(Object key)
{
if(key instanceof String == false)
throw new IllegalArgumentException("Key should be of type String");
return getContextInfo((String)key);
}
/**
* Get the current thread context info. If a security manager is present,
* then this method calls the security manager's checkPermission
* method with a RuntimePermission("org.jboss.security.SecurityAssociation.accessContextInfo",
* "get")
permission to ensure it's ok to access context information.
* If not, a SecurityException
will be thrown.
* @param key - the context key
* @return the mapping for the key in the current thread context
*/
public static Object getContextInfo(String key)
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(getContextInfo);
if(key == null)
throw new IllegalArgumentException("key is null");
//SECURITY-459 get it from the current security context
SecurityContext sc = SecurityAssociationActions.getSecurityContext();
if(sc != null)
return sc.getData().get(key);
//fall back
HashMap contextInfo = (HashMap) threadContextMap.get();
return contextInfo != null ? contextInfo.get(key) : null;
}
/**
* Maintain backwards compatibility
* @deprecated
* @see {@code SecurityAssociation#setContextInfo(String, Object)}
* @param key
* @param value
* @return
* @throws IllegalArgumentException if the passed key is not of type String
*/
public static Object setContextInfo(Object key, Object value)
{
if(key instanceof String == false)
throw new IllegalArgumentException("key should be of type String");
String keyStr = (String) key;
return setContextInfo(keyStr, value);
}
/**
* Set the current thread context info. If a security manager is present,
* then this method calls the security manager's checkPermission
* method with a RuntimePermission("org.jboss.security.SecurityAssociation.accessContextInfo",
* "set")
permission to ensure it's ok to access context information.
* If not, a SecurityException
will be thrown.
* @param key - the context key
* @param value - the context value to associate under key
* @return the previous mapping for the key if one exists
*/
public static Object setContextInfo(String key, Object value)
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(setContextInfo);
SecurityContext sc = SecurityAssociationActions.getSecurityContext();
if(sc != null)
return sc.getData().put(key, value);
HashMap contextInfo = (HashMap) threadContextMap.get();
return contextInfo.put(key, value);
}
/**
* Push the current authenticated context. This sets the authenticated subject
* along with the principal and proof of identity that was used to validate
* the subject. This context is used for authorization checks. Typically
* just the subject as seen by getSubject() is input into the authorization.
* When run under a security manager this requires the
* RuntimePermission("org.jboss.security.SecurityAssociation.setPrincipalInfo")
* permission.
* @param subject - the authenticated subject
* @param principal - the principal that was input into the authentication
* @param credential - the credential that was input into the authentication
* @deprecated
*/
public static void pushSubjectContext(Subject subject,
Principal principal, Object credential)
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(setPrincipalInfoPermission);
// Set the legacy single-value access points
if (server)
{
threadPrincipal.set(principal);
threadCredential.set(credential);
}
else
{
SecurityAssociation.principal = principal;
SecurityAssociation.credential = credential;
}
// Push the subject context
SubjectContext sc = new SubjectContext(subject, principal, credential);
threadSubjectStacks.push(sc);
if(server)
{
if (trace)
log.trace("pushSubjectContext, subject=" + subject + ", sc="+sc);
//Use the new method
SecurityContext sctx = SecurityContextAssociation.getSecurityContext();
if(sctx == null)
{
if(trace)
log.trace("WARN::Deprecated usage of SecurityAssociation. Use SecurityContext");
try
{
sctx = SecurityAssociationActions.createSecurityContext("FROM_SECURITY_ASSOCIATION");
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
sctx.getUtil().createSubjectInfo(principal, credential,subject);
SecurityAssociationActions.setSecurityContext(sctx);
}
}
/**
* Push a duplicate of the current SubjectContext if one exists.
* When run under a security manager this requires the
* RuntimePermission("org.jboss.security.SecurityAssociation.setPrincipalInfo")
* permission.
*/
public static void dupSubjectContext()
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(setPrincipalInfoPermission);
SubjectContext sc = threadSubjectStacks.dup();
if (trace)
log.trace("dupSubjectContext, sc="+sc);
}
/**
* Pop the current SubjectContext from the previous pushSubjectContext call
* and return the pushed SubjectContext ig there was one.
* When run under a security manager this requires the
* RuntimePermission("org.jboss.security.SecurityAssociation.setPrincipalInfo")
* permission.
* @return the SubjectContext pushed previously by a pushSubjectContext call
* @deprecated
*/
public static SubjectContext popSubjectContext()
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(setPrincipalInfoPermission);
SubjectContext sc = threadSubjectStacks.pop();
if (trace)
{
log.trace("popSubjectContext, sc="+sc);
}
Principal principal = null;
Object credential = null;
SubjectContext top = threadSubjectStacks.peek();
if (top != null)
{
principal = top.getPrincipal();
credential = top.getCredential();
}
if (server)
{
threadPrincipal.set(principal);
threadCredential.set(credential);
}
else
{
SecurityAssociation.principal = principal;
SecurityAssociation.credential = credential;
}
if(server)
{
if(trace)
log.trace("WARN::Deprecated usage of SecurityAssociation. Use SecurityContext");
SecurityContext sctx = SecurityContextAssociation.getSecurityContext();
if(sc == null)
{
if(sctx != null)
{
sc = new SubjectContext(sctx.getUtil().getSubject(),
sctx.getUtil().getUserPrincipal(),
sctx.getUtil().getCredential());
}
}
//Now pop the subject context on the security context
if(sctx != null)
{
sctx.getUtil().createSubjectInfo(null, null, null);
}
return sc;
}
return top;
}
/**
* Look at the current thread of control's authenticated identity on the top
* of the stack.
* When run under a security manager this requires the
* RuntimePermission("org.jboss.security.SecurityAssociation.getPrincipalInfo")
* permission.
* @return the SubjectContext pushed previously by a pushSubjectContext call
*/
public static SubjectContext peekSubjectContext()
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(getPrincipalInfoPermission);
if(server)
{
//Get the subject context from the security context
SecurityContext sc = SecurityContextAssociation.getSecurityContext();
SubjectContext subjectCtx = null;
if( sc != null)
{
SecurityContextUtil util = sc.getUtil();
subjectCtx = new SubjectContext(util.getSubject(), util.getUserPrincipal(), util.getCredential());
}
return subjectCtx;
}
return threadSubjectStacks.peek();
}
/**
* Clear all principal information. If a security manager is present, then
* this method calls the security manager's checkPermission
* method with a RuntimePermission("org.jboss.security.SecurityAssociation.setPrincipalInfo")
*
permission to ensure it's ok to access principal information. If
* not, a SecurityException
will be thrown.
*/
public static void clear()
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(setPrincipalInfoPermission);
if (trace)
log.trace("clear, server=" + server);
if (server == true)
{
threadPrincipal.set(null);
threadCredential.set(null);
}
else
{
SecurityAssociation.principal = null;
SecurityAssociation.credential = null;
}
// Remove all subject contexts
threadSubjectStacks.clear();
//Clear the security context
SecurityContextAssociation.clearSecurityContext();
}
/**
* Push the current thread of control's run-as identity.
*/
public static void pushRunAsIdentity(RunAsIdentity runAs)
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(setRunAsIdentity);
if (trace)
log.trace("pushRunAsIdentity, runAs=" + runAs);
threadRunAsStacks.push(runAs);
SecurityContext sc = SecurityContextAssociation.getSecurityContext();
if( sc != null)
{
sc.setOutgoingRunAs(runAs);
}
}
/**
* Pop the current thread of control's run-as identity.
*/
public static RunAsIdentity popRunAsIdentity()
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(setRunAsIdentity);
/*RunAsIdentity runAs = threadRunAsStacks.pop();
if (trace)
log.trace("popRunAsIdentity, runAs=" + runAs);
return runAs;*/
SecurityContext sc = SecurityContextAssociation.getSecurityContext();
RunAsIdentity ra = null;
if( sc != null)
{
ra = (RunAsIdentity) sc.getOutgoingRunAs();
sc.setOutgoingRunAs(null);
}
return ra;
}
/**
* Look at the current thread of control's run-as identity on the top of the
* stack.
*/
public static RunAsIdentity peekRunAsIdentity()
{
//return peekRunAsIdentity(0);
RunAsIdentity ra = null;
SecurityContext sc = SecurityContextAssociation.getSecurityContext();
if( sc != null)
{
ra = (RunAsIdentity) sc.getOutgoingRunAs();
}
return ra;
}
/**
* Look at the current thread of control's run-as identity at the indicated
* depth. Typically depth is either 0 for the identity the current caller
* run-as that will be assumed, or 1 for the active run-as the previous
* caller has assumed.
* @return RunAsIdentity depth frames up.
*/
public static RunAsIdentity peekRunAsIdentity(int depth)
{
//RunAsIdentity runAs = threadRunAsStacks.peek(depth);
//return runAs;
if(depth > 1)
throw new IllegalArgumentException("Security Context approach needs to be used. Depth upto 1");
if(depth == 0)
return peekRunAsIdentity();
else
{
SecurityContext sc = SecurityContextAssociation.getSecurityContext();
RunAsIdentity ra = null;
if( sc != null)
{
RunAs ras = sc.getIncomingRunAs();
if(ras instanceof RunAsIdentity)
ra = (RunAsIdentity) ras;
}
return ra;
}
}
/**
* Indicate whether we are server side
* @return flag set by a {@link #setServer()} call
*/
public static boolean isServer()
{
return server;
}
/**
* Set the server mode of operation. When the server property has been set to
* true, the security information is maintained in thread local storage. This
* should be called to enable property security semantics in any
* multi-threaded environment where more than one thread requires that
* security information be restricted to the thread's flow of control.
*
* If a security manager is present, then this method calls the security
* manager's checkPermission
method with a
* RuntimePermission("org.jboss.security.SecurityAssociation.setServer")
*
permission to ensure it's ok to access principal information. If
* not, a SecurityException
will be thrown.
*/
public static void setServer()
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(setServerPermission);
server = true;
}
/**
* A subclass of ThreadLocal that implements a value stack using an ArrayList
* and implements push, pop and peek stack operations on the thread local
* ArrayList.
*/
@SuppressWarnings( {"unchecked","unused", "rawtypes" } )
private static class RunAsThreadLocalStack
{
ThreadLocal local;
RunAsThreadLocalStack(boolean threadLocal)
{
if( threadLocal == true )
local = new ArrayListLocal();
else
local = new ArrayListInheritableLocal();
}
int size()
{
ArrayList stack = (ArrayList) local.get();
return stack.size();
}
void push(RunAsIdentity runAs)
{
ArrayList stack = (ArrayList) local.get();
stack.add(runAs);
}
RunAsIdentity pop()
{
ArrayList stack = (ArrayList) local.get();
RunAsIdentity runAs = null;
int lastIndex = stack.size() - 1;
if (lastIndex >= 0)
runAs = (RunAsIdentity) stack.remove(lastIndex);
return runAs;
}
/**
* Look for the first non-null run-as identity on the stack starting
* with the value at depth.
* @return The run-as identity if one exists, null otherwise.
*/
RunAsIdentity peek(int depth)
{
ArrayList stack = (ArrayList) local.get();
RunAsIdentity runAs = null;
final int stackSize = stack.size();
do
{
int index = stackSize - 1 - depth;
if( index >= 0 )
runAs = (RunAsIdentity) stack.get(index);
depth ++;
}
while (runAs == null && depth <= stackSize - 1);
return runAs;
}
}
/**
* The encapsulation of the authenticated subject
*/
public static class SubjectContext
{
public static final int SUBJECT_WAS_SET = 1;
public static final int PRINCIPAL_WAS_SET = 2;
public static final int CREDENTIAL_WAS_SET = 4;
private Subject subject;
private Principal principal;
private Object credential;
private int flags;
public SubjectContext()
{
this.flags = 0;
}
public SubjectContext(Subject s, Principal p, Object cred)
{
this.subject = s;
this.principal = p;
this.credential = cred;
this.flags = SUBJECT_WAS_SET | PRINCIPAL_WAS_SET | CREDENTIAL_WAS_SET;
}
public Subject getSubject()
{
return subject;
}
public void setSubject(Subject subject)
{
this.subject = subject;
this.flags |= SUBJECT_WAS_SET;
}
public Principal getPrincipal()
{
return principal;
}
public void setPrincipal(Principal principal)
{
this.principal = principal;
this.flags |= PRINCIPAL_WAS_SET;
}
public Object getCredential()
{
return credential;
}
public void setCredential(Object credential)
{
this.credential = credential;
this.flags |= CREDENTIAL_WAS_SET;
}
public int getFlags()
{
return this.flags;
}
public String toString()
{
StringBuffer tmp = new StringBuffer(super.toString());
tmp.append("{principal=");
tmp.append(principal);
tmp.append(",subject=");
if( subject != null )
tmp.append(System.identityHashCode(subject));
else
tmp.append("null");
tmp.append("}");
return tmp.toString();
}
}
@SuppressWarnings({"unchecked", "rawtypes", "unused"})
private static class SubjectThreadLocalStack
{
ThreadLocal local;
SubjectThreadLocalStack(boolean threadLocal)
{
if( threadLocal == true )
local = new ArrayListLocal();
else
local = new ArrayListInheritableLocal();
}
int size()
{
ArrayList stack = (ArrayList) local.get();
return stack.size();
}
void push(SubjectContext context)
{
ArrayList stack = (ArrayList) local.get();
stack.add(context);
}
SubjectContext dup()
{
ArrayList stack = (ArrayList) local.get();
SubjectContext context = null;
int lastIndex = stack.size() - 1;
if (lastIndex >= 0)
{
context = (SubjectContext) stack.get(lastIndex);
stack.add(context);
}
return context;
}
SubjectContext pop()
{
ArrayList stack = (ArrayList) local.get();
SubjectContext context = null;
int lastIndex = stack.size() - 1;
if (lastIndex >= 0)
context = (SubjectContext) stack.remove(lastIndex);
return context;
}
/**
* Look for the first non-null run-as identity on the stack starting
* with the value at depth.
* @return The run-as identity if one exists, null otherwise.
*/
SubjectContext peek()
{
ArrayList stack = (ArrayList) local.get();
SubjectContext context = null;
int lastIndex = stack.size() - 1;
if (lastIndex >= 0)
context = (SubjectContext) stack.get(lastIndex);
return context;
}
/**
* Remove all SubjectContext from the current thread stack
*/
void clear()
{
ArrayList stack = (ArrayList) local.get();
stack.clear();
}
}
@SuppressWarnings("rawtypes")
private static class ArrayListLocal extends ThreadLocal
{
protected Object initialValue()
{
return new ArrayList();
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
private static class ArrayListInheritableLocal extends InheritableThreadLocal
{
/**
* Override to make a copy of the parent as not doing so results in multiple
* threads sharing the unsynchronized list of the parent thread.
* @param parentValue - the parent ArrayList
* @return a copy of the parent thread list
*/
protected Object childValue(Object parentValue)
{
ArrayList list = (ArrayList) parentValue;
/* It seems there are scenarios where the size can change during the copy so there is
a fallback to an empty list here.
*/
ArrayList copy = null;
try
{
copy = new ArrayList(list);
}
catch(Throwable t)
{
log.debug("Failed to copy parent list, using new list");
copy = new ArrayList();
}
return copy;
}
protected Object initialValue()
{
return new ArrayList();
}
}
private static class HashMapInheritableLocal
extends InheritableThreadLocal>
{
/**
* Override to make a copy of the parent as not doing so results in multiple
* threads sharing the unsynchronized map of the parent thread.
* @param parentValue - the parent HashMap
* @return a copy of the parent thread map
*/
@Override
protected HashMap childValue(HashMap parentValue)
{
HashMap map = (HashMap) parentValue;
/* It seems there are scenarios where the size can change during the copy so there is
a fallback to an empty map here.
*/
HashMap copy = null;
try
{
copy = new HashMap(map);
}
catch(Throwable t)
{
log.debug("Failed to copy parent map, using new map");
copy = new HashMap();
}
return copy;
}
protected HashMap initialValue()
{
return new HashMap();
}
}
}