001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.security;
023:
024: import java.security.Principal;
025: import java.util.ArrayList;
026: import java.util.HashMap;
027: import javax.security.auth.Subject;
028:
029: import org.jboss.logging.Logger;
030:
031: /**
032: * The SecurityAssociation class maintains the security principal and
033: * credentials. This can be done on either a singleton basis or a thread local
034: * basis depending on the server property. When the server property has been set
035: * to true, the security information is maintained in thread local storage. The
036: * type of thread local storage depends on the org.jboss.security.SecurityAssociation.ThreadLocal
037: * property. If this property is true, then the thread local storage object is
038: * of type java.lang.ThreadLocal which results in the current thread's security
039: * information NOT being propagated to child threads.
040: *
041: * When the property is false or does not exist, the thread local storage object
042: * is of type java.lang.InheritableThreadLocal, and any threads spawned by the
043: * current thread will inherit the security information of the current thread.
044: * Subseqent changes to the current thread's security information are NOT
045: * propagated to any previously spawned child threads.
046: *
047: * When the server property is false, security information is maintained in
048: * class variables which makes the information available to all threads within
049: * the current VM.
050: *
051: * Note that this is not a public API class. Its an implementation detail that
052: * is subject to change without notice.
053: *
054: * @author Daniel O'Connor (docodan@nycap.rr.com)
055: * @author Scott.Stark@jboss.org
056: * @version $Revision: 57203 $
057: */
058: public final class SecurityAssociation {
059: private static Logger log = Logger
060: .getLogger(SecurityAssociation.class);
061: /**
062: * A flag indicating if trace level logging should be performed
063: */
064: private static boolean trace;
065: /**
066: * A flag indicating if security information is global or thread local
067: */
068: private static boolean server;
069: /**
070: * The SecurityAssociation principal used when the server flag is false
071: */
072: private static Principal principal;
073: /**
074: * The SecurityAssociation credential used when the server flag is false
075: */
076: private static Object credential;
077:
078: /**
079: * The SecurityAssociation principal used when the server flag is true
080: */
081: private static ThreadLocal threadPrincipal;
082: /**
083: * The SecurityAssociation credential used when the server flag is true
084: */
085: private static ThreadLocal threadCredential;
086: /**
087: * The SecurityAssociation HashMap<String, Object>
088: */
089: private static ThreadLocal threadContextMap;
090:
091: /**
092: * Thread local stacks of run-as principal roles used to implement J2EE
093: * run-as identity propagation
094: */
095: private static RunAsThreadLocalStack threadRunAsStacks;
096: /**
097: * Thread local stacks of authenticated subject used to control the current
098: * caller security context
099: */
100: private static SubjectThreadLocalStack threadSubjectStacks;
101:
102: /**
103: * The permission required to access getPrincpal, getCredential
104: */
105: private static final RuntimePermission getPrincipalInfoPermission = new RuntimePermission(
106: "org.jboss.security.SecurityAssociation.getPrincipalInfo");
107: /**
108: * The permission required to access getSubject
109: */
110: private static final RuntimePermission getSubjectPermission = new RuntimePermission(
111: "org.jboss.security.SecurityAssociation.getSubject");
112: /**
113: * The permission required to access setPrincpal, setCredential, setSubject
114: * pushSubjectContext, popSubjectContext
115: */
116: private static final RuntimePermission setPrincipalInfoPermission = new RuntimePermission(
117: "org.jboss.security.SecurityAssociation.setPrincipalInfo");
118: /**
119: * The permission required to access setServer
120: */
121: private static final RuntimePermission setServerPermission = new RuntimePermission(
122: "org.jboss.security.SecurityAssociation.setServer");
123: /**
124: * The permission required to access pushRunAsIdentity/popRunAsIdentity
125: */
126: private static final RuntimePermission setRunAsIdentity = new RuntimePermission(
127: "org.jboss.security.SecurityAssociation.setRunAsRole");
128: /**
129: * The permission required to get the current security context info
130: */
131: private static final RuntimePermission getContextInfo = new RuntimePermission(
132: "org.jboss.security.SecurityAssociation.accessContextInfo",
133: "get");
134: /**
135: * The permission required to set the current security context info
136: */
137: private static final RuntimePermission setContextInfo = new RuntimePermission(
138: "org.jboss.security.SecurityAssociation.accessContextInfo",
139: "set");
140:
141: static {
142: String flag = SecurityActions.getProperty(
143: "org.jboss.security.SecurityAssociation.ThreadLocal",
144: "false");
145: boolean useThreadLocal = Boolean.valueOf(flag).booleanValue();
146: log.debug("Using ThreadLocal: " + useThreadLocal);
147:
148: trace = log.isTraceEnabled();
149: if (useThreadLocal) {
150: threadPrincipal = new ThreadLocal();
151: threadCredential = new ThreadLocal();
152: threadContextMap = new ThreadLocal() {
153: protected Object initialValue() {
154: return new HashMap();
155: }
156: };
157: } else {
158: threadPrincipal = new InheritableThreadLocal();
159: threadCredential = new InheritableThreadLocal();
160: threadContextMap = new HashMapInheritableLocal();
161: }
162: threadRunAsStacks = new RunAsThreadLocalStack(useThreadLocal);
163: threadSubjectStacks = new SubjectThreadLocalStack(
164: useThreadLocal);
165: }
166:
167: /**
168: * Get the current authentication principal information. If a security
169: * manager is present, then this method calls the security manager's
170: * <code>checkPermission</code> method with a
171: * <code>RuntimePermission("org.jboss.security.SecurityAssociation.getPrincipalInfo")
172: * </code> permission to ensure it's ok to access principal information. If
173: * not, a <code>SecurityException</code> will be thrown.
174: * @return Principal, the current principal identity.
175: */
176: public static Principal getPrincipal() {
177: SecurityManager sm = System.getSecurityManager();
178: if (sm != null)
179: sm.checkPermission(getPrincipalInfoPermission);
180:
181: Principal thePrincipal = principal;
182:
183: if (server)
184: thePrincipal = (Principal) threadPrincipal.get();
185:
186: if (trace)
187: log.trace("getPrincipal, principal=" + thePrincipal);
188:
189: return thePrincipal;
190: }
191:
192: /**
193: * Get the caller's principal. If a security manager is present,
194: * then this method calls the security manager's <code>checkPermission</code>
195: * method with a <code> RuntimePermission("org.jboss.security.SecurityAssociation.getPrincipalInfo")
196: * </code> permission to ensure it's ok to access principal information. If
197: * not, a <code>SecurityException</code> will be thrown.
198: *
199: * @return Principal, the current principal identity.
200: */
201: public static Principal getCallerPrincipal() {
202: SecurityManager sm = System.getSecurityManager();
203: if (sm != null)
204: sm.checkPermission(getPrincipalInfoPermission);
205:
206: Principal thePrincipal = peekRunAsIdentity(1);
207: if (thePrincipal == null) {
208: if (server)
209: thePrincipal = (Principal) threadPrincipal.get();
210: else
211: thePrincipal = principal;
212: }
213: if (trace)
214: log.trace("getCallerPrincipal, principal=" + thePrincipal);
215: return thePrincipal;
216: }
217:
218: /**
219: * Get the current authentication credential information. This can be of any type
220: * including: a String password, a char[] password, an X509 cert, etc. If a
221: * security manager is present, then this method calls the security manager's
222: * <code>checkPermission</code> method with a <code> RuntimePermission("org.jboss.security.SecurityAssociation.getPrincipalInfo")
223: * </code> permission to ensure it's ok to access principal information. If
224: * not, a <code>SecurityException</code> will be thrown.
225: * @return Object, the credential that proves the principal identity.
226: */
227: public static Object getCredential() {
228: SecurityManager sm = System.getSecurityManager();
229: if (sm != null)
230: sm.checkPermission(getPrincipalInfoPermission);
231:
232: if (server)
233: return threadCredential.get();
234: else
235: return credential;
236: }
237:
238: /**
239: * Get the current Subject information. If a security manager is present,
240: * then this method calls the security manager's checkPermission method with
241: * a RuntimePermission("org.jboss.security.SecurityAssociation.getSubject")
242: * permission to ensure it's ok to access principal information. If not, a
243: * SecurityException will be thrown. Note that this method does not consider
244: * whether or not a run-as identity exists. For access to this information
245: * see the JACC PolicyContextHandler registered under the key
246: * "javax.security.auth.Subject.container"
247: * @return Subject, the current Subject identity.
248: * @see javax.security.jacc.PolicyContext#getContext(String)
249: */
250: public static Subject getSubject() {
251: SecurityManager sm = System.getSecurityManager();
252: if (sm != null)
253: sm.checkPermission(getSubjectPermission);
254:
255: SubjectContext sc = threadSubjectStacks.peek();
256: if (trace)
257: log.trace("getSubject, sc=" + sc);
258: Subject subject = null;
259: if (sc != null)
260: subject = sc.getSubject();
261: return subject;
262: }
263:
264: /**
265: * Set the current principal information. If a security manager is present,
266: * then this method calls the security manager's <code>checkPermission</code>
267: * method with a <code> RuntimePermission("org.jboss.security.SecurityAssociation.setPrincipalInfo")
268: * </code> permission to ensure it's ok to access principal information. If
269: * not, a <code>SecurityException</code> will be thrown.
270: * @param principal - the current principal identity.
271: */
272: public static void setPrincipal(Principal principal) {
273: SecurityManager sm = System.getSecurityManager();
274: if (sm != null)
275: sm.checkPermission(setPrincipalInfoPermission);
276:
277: if (trace)
278: log.trace("setPrincipal, p=" + principal + ", server="
279: + server);
280: if (server) {
281: threadPrincipal.set(principal);
282: } else
283: SecurityAssociation.principal = principal;
284: // Integrate with the new SubjectContext
285: SubjectContext sc = threadSubjectStacks.peek();
286: if (sc == null) {
287: // There is no active security context
288: sc = new SubjectContext();
289: threadSubjectStacks.push(sc);
290: } else if ((sc.getFlags() & SubjectContext.PRINCIPAL_WAS_SET) != 0) {
291: // The current security context has its principal set
292: sc = new SubjectContext();
293: threadSubjectStacks.push(sc);
294: }
295: sc.setPrincipal(principal);
296: if (trace)
297: log.trace("setPrincipal, sc=" + sc);
298: }
299:
300: /**
301: * Set the current principal credential information. This can be of any type
302: * including: a String password, a char[] password, an X509 cert, etc.
303: *
304: * If a security manager is present, then this method calls the security
305: * manager's <code>checkPermission</code> method with a <code>
306: * RuntimePermission("org.jboss.security.SecurityAssociation.setPrincipalInfo")
307: * </code> permission to ensure it's ok to access principal information. If
308: * not, a <code>SecurityException</code> will be thrown.
309: * @param credential - the credential that proves the principal identity.
310: */
311: public static void setCredential(Object credential) {
312: SecurityManager sm = System.getSecurityManager();
313: if (sm != null)
314: sm.checkPermission(setPrincipalInfoPermission);
315:
316: if (server)
317: threadCredential.set(credential);
318: else
319: SecurityAssociation.credential = credential;
320: // Integrate with the new SubjectContext
321: SubjectContext sc = threadSubjectStacks.peek();
322: if (sc == null) {
323: // There is no active security context
324: sc = new SubjectContext();
325: threadSubjectStacks.push(sc);
326: } else if ((sc.getFlags() & SubjectContext.CREDENTIAL_WAS_SET) != 0) {
327: // The current security context has its principal set
328: sc = new SubjectContext();
329: threadSubjectStacks.push(sc);
330: }
331: sc.setCredential(credential);
332: if (trace)
333: log.trace("setCredential, sc=" + sc);
334: }
335:
336: /**
337: * Set the current Subject information. If a security manager is present,
338: * then this method calls the security manager's <code>checkPermission</code>
339: * method with a <code> RuntimePermission("org.jboss.security.SecurityAssociation.setPrincipalInfo")
340: * </code> permission to ensure it's ok to access principal information. If
341: * not, a <code>SecurityException</code> will be thrown.
342: * @param subject - the current identity.
343: */
344: public static void setSubject(Subject subject) {
345: SecurityManager sm = System.getSecurityManager();
346: if (sm != null)
347: sm.checkPermission(setPrincipalInfoPermission);
348:
349: if (trace)
350: log
351: .trace("setSubject, s=" + subject + ", server="
352: + server);
353: // Integrate with the new SubjectContext
354: SubjectContext sc = threadSubjectStacks.peek();
355: if (sc == null) {
356: // There is no active security context
357: sc = new SubjectContext();
358: threadSubjectStacks.push(sc);
359: } else if ((sc.getFlags() & SubjectContext.SUBJECT_WAS_SET) != 0) {
360: // The current security context has its subject set
361: sc = new SubjectContext();
362: threadSubjectStacks.push(sc);
363: }
364: sc.setSubject(subject);
365: if (trace)
366: log.trace("setSubject, sc=" + sc);
367: }
368:
369: /**
370: * Get the current thread context info. If a security manager is present,
371: * then this method calls the security manager's <code>checkPermission</code>
372: * method with a <code> RuntimePermission("org.jboss.security.SecurityAssociation.accessContextInfo",
373: * "get") </code> permission to ensure it's ok to access context information.
374: * If not, a <code>SecurityException</code> will be thrown.
375: * @param key - the context key
376: * @return the mapping for the key in the current thread context
377: */
378: public static Object getContextInfo(Object key) {
379: SecurityManager sm = System.getSecurityManager();
380: if (sm != null)
381: sm.checkPermission(getContextInfo);
382:
383: HashMap contextInfo = (HashMap) threadContextMap.get();
384: return contextInfo.get(key);
385: }
386:
387: /**
388: * Set the current thread context info. If a security manager is present,
389: * then this method calls the security manager's <code>checkPermission</code>
390: * method with a <code> RuntimePermission("org.jboss.security.SecurityAssociation.accessContextInfo",
391: * "set") </code> permission to ensure it's ok to access context information.
392: * If not, a <code>SecurityException</code> will be thrown.
393: * @param key - the context key
394: * @param value - the context value to associate under key
395: * @return the previous mapping for the key if one exists
396: */
397: public static Object setContextInfo(Object key, Object value) {
398: SecurityManager sm = System.getSecurityManager();
399: if (sm != null)
400: sm.checkPermission(setContextInfo);
401:
402: HashMap contextInfo = (HashMap) threadContextMap.get();
403: return contextInfo.put(key, value);
404: }
405:
406: /**
407: * Push the current authenticated context. This sets the authenticated subject
408: * along with the principal and proof of identity that was used to validate
409: * the subject. This context is used for authorization checks. Typically
410: * just the subject as seen by getSubject() is input into the authorization.
411: * When run under a security manager this requires the
412: * RuntimePermission("org.jboss.security.SecurityAssociation.setPrincipalInfo")
413: * permission.
414: * @param subject - the authenticated subject
415: * @param principal - the principal that was input into the authentication
416: * @param credential - the credential that was input into the authentication
417: */
418: public static void pushSubjectContext(Subject subject,
419: Principal principal, Object credential) {
420: SecurityManager sm = System.getSecurityManager();
421: if (sm != null)
422: sm.checkPermission(setPrincipalInfoPermission);
423:
424: // Set the legacy single-value access points
425: if (server) {
426: threadPrincipal.set(principal);
427: threadCredential.set(credential);
428: } else {
429: SecurityAssociation.principal = principal;
430: SecurityAssociation.credential = credential;
431: }
432: // Push the subject context
433: SubjectContext sc = new SubjectContext(subject, principal,
434: credential);
435: threadSubjectStacks.push(sc);
436: if (trace)
437: log.trace("pushSubjectContext, subject=" + subject
438: + ", sc=" + sc);
439: }
440:
441: /**
442: * Push a duplicate of the current SubjectContext if one exists.
443: * When run under a security manager this requires the
444: * RuntimePermission("org.jboss.security.SecurityAssociation.setPrincipalInfo")
445: * permission.
446: */
447: public static void dupSubjectContext() {
448: SecurityManager sm = System.getSecurityManager();
449: if (sm != null)
450: sm.checkPermission(setPrincipalInfoPermission);
451:
452: SubjectContext sc = threadSubjectStacks.dup();
453: if (trace)
454: log.trace("dupSubjectContext, sc=" + sc);
455: }
456:
457: /**
458: * Pop the current SubjectContext from the previous pushSubjectContext call
459: * and return the pushed SubjectContext ig there was one.
460: * When run under a security manager this requires the
461: * RuntimePermission("org.jboss.security.SecurityAssociation.setPrincipalInfo")
462: * permission.
463: * @return the SubjectContext pushed previously by a pushSubjectContext call
464: */
465: public static SubjectContext popSubjectContext() {
466: SecurityManager sm = System.getSecurityManager();
467: if (sm != null)
468: sm.checkPermission(setPrincipalInfoPermission);
469:
470: SubjectContext sc = threadSubjectStacks.pop();
471: if (trace) {
472: log.trace("popSubjectContext, sc=" + sc);
473: }
474:
475: Principal principal = null;
476: Object credential = null;
477:
478: SubjectContext top = threadSubjectStacks.peek();
479:
480: if (top != null) {
481: principal = top.getPrincipal();
482: credential = top.getCredential();
483: }
484:
485: if (server) {
486: threadPrincipal.set(principal);
487: threadCredential.set(credential);
488: } else {
489: SecurityAssociation.principal = principal;
490: SecurityAssociation.credential = credential;
491: }
492:
493: return sc;
494: }
495:
496: /**
497: * Look at the current thread of control's authenticated identity on the top
498: * of the stack.
499: * When run under a security manager this requires the
500: * RuntimePermission("org.jboss.security.SecurityAssociation.getPrincipalInfo")
501: * permission.
502: * @return the SubjectContext pushed previously by a pushSubjectContext call
503: */
504: public static SubjectContext peekSubjectContext() {
505: SecurityManager sm = System.getSecurityManager();
506: if (sm != null)
507: sm.checkPermission(getPrincipalInfoPermission);
508:
509: return threadSubjectStacks.peek();
510: }
511:
512: /**
513: * Clear all principal information. If a security manager is present, then
514: * this method calls the security manager's <code>checkPermission</code>
515: * method with a <code> RuntimePermission("org.jboss.security.SecurityAssociation.setPrincipalInfo")
516: * </code> permission to ensure it's ok to access principal information. If
517: * not, a <code>SecurityException</code> will be thrown.
518: */
519: public static void clear() {
520: SecurityManager sm = System.getSecurityManager();
521: if (sm != null)
522: sm.checkPermission(setPrincipalInfoPermission);
523:
524: if (trace)
525: log.trace("clear, server=" + server);
526: if (server == true) {
527: threadPrincipal.set(null);
528: threadCredential.set(null);
529: } else {
530: SecurityAssociation.principal = null;
531: SecurityAssociation.credential = null;
532: }
533: // Remove all subject contexts
534: threadSubjectStacks.clear();
535: }
536:
537: /**
538: * Push the current thread of control's run-as identity.
539: */
540: public static void pushRunAsIdentity(RunAsIdentity runAs) {
541: SecurityManager sm = System.getSecurityManager();
542: if (sm != null)
543: sm.checkPermission(setRunAsIdentity);
544: if (trace)
545: log.trace("pushRunAsIdentity, runAs=" + runAs);
546:
547: threadRunAsStacks.push(runAs);
548: }
549:
550: /**
551: * Pop the current thread of control's run-as identity.
552: */
553: public static RunAsIdentity popRunAsIdentity() {
554: SecurityManager sm = System.getSecurityManager();
555: if (sm != null)
556: sm.checkPermission(setRunAsIdentity);
557: RunAsIdentity runAs = threadRunAsStacks.pop();
558: if (trace)
559: log.trace("popRunAsIdentity, runAs=" + runAs);
560: return runAs;
561: }
562:
563: /**
564: * Look at the current thread of control's run-as identity on the top of the
565: * stack.
566: */
567: public static RunAsIdentity peekRunAsIdentity() {
568: return peekRunAsIdentity(0);
569: }
570:
571: /**
572: * Look at the current thread of control's run-as identity at the indicated
573: * depth. Typically depth is either 0 for the identity the current caller
574: * run-as that will be assumed, or 1 for the active run-as the previous
575: * caller has assumed.
576: * @return RunAsIdentity depth frames up.
577: */
578: public static RunAsIdentity peekRunAsIdentity(int depth) {
579: RunAsIdentity runAs = threadRunAsStacks.peek(depth);
580: return runAs;
581: }
582:
583: /**
584: * Set the server mode of operation. When the server property has been set to
585: * true, the security information is maintained in thread local storage. This
586: * should be called to enable property security semantics in any
587: * multi-threaded environment where more than one thread requires that
588: * security information be restricted to the thread's flow of control.
589: *
590: * If a security manager is present, then this method calls the security
591: * manager's <code>checkPermission</code> method with a <code>
592: * RuntimePermission("org.jboss.security.SecurityAssociation.setServer")
593: * </code> permission to ensure it's ok to access principal information. If
594: * not, a <code>SecurityException</code> will be thrown.
595: */
596: public static void setServer() {
597: SecurityManager sm = System.getSecurityManager();
598: if (sm != null)
599: sm.checkPermission(setServerPermission);
600:
601: server = true;
602: }
603:
604: /**
605: * A subclass of ThreadLocal that implements a value stack using an ArrayList
606: * and implements push, pop and peek stack operations on the thread local
607: * ArrayList.
608: */
609: private static class RunAsThreadLocalStack {
610: ThreadLocal local;
611:
612: RunAsThreadLocalStack(boolean threadLocal) {
613: if (threadLocal == true)
614: local = new ArrayListLocal();
615: else
616: local = new ArrayListInheritableLocal();
617: }
618:
619: int size() {
620: ArrayList stack = (ArrayList) local.get();
621: return stack.size();
622: }
623:
624: void push(RunAsIdentity runAs) {
625: ArrayList stack = (ArrayList) local.get();
626: stack.add(runAs);
627: }
628:
629: RunAsIdentity pop() {
630: ArrayList stack = (ArrayList) local.get();
631: RunAsIdentity runAs = null;
632: int lastIndex = stack.size() - 1;
633: if (lastIndex >= 0)
634: runAs = (RunAsIdentity) stack.remove(lastIndex);
635: return runAs;
636: }
637:
638: /**
639: * Look for the first non-null run-as identity on the stack starting
640: * with the value at depth.
641: * @return The run-as identity if one exists, null otherwise.
642: */
643: RunAsIdentity peek(int depth) {
644: ArrayList stack = (ArrayList) local.get();
645: RunAsIdentity runAs = null;
646: final int stackSize = stack.size();
647: do {
648: int index = stackSize - 1 - depth;
649: if (index >= 0)
650: runAs = (RunAsIdentity) stack.get(index);
651: depth++;
652: } while (runAs == null && depth <= stackSize - 1);
653: return runAs;
654: }
655: }
656:
657: /**
658: * The encapsulation of the authenticated subject
659: */
660: public static class SubjectContext {
661: public static final int SUBJECT_WAS_SET = 1;
662: public static final int PRINCIPAL_WAS_SET = 2;
663: public static final int CREDENTIAL_WAS_SET = 4;
664:
665: private Subject subject;
666: private Principal principal;
667: private Object credential;
668: private int flags;
669:
670: public SubjectContext() {
671: this .flags = 0;
672: }
673:
674: public SubjectContext(Subject s, Principal p, Object cred) {
675: this .subject = s;
676: this .principal = p;
677: this .credential = cred;
678: this .flags = SUBJECT_WAS_SET | PRINCIPAL_WAS_SET
679: | CREDENTIAL_WAS_SET;
680: }
681:
682: public Subject getSubject() {
683: return subject;
684: }
685:
686: public void setSubject(Subject subject) {
687: this .subject = subject;
688: this .flags |= SUBJECT_WAS_SET;
689: }
690:
691: public Principal getPrincipal() {
692: return principal;
693: }
694:
695: public void setPrincipal(Principal principal) {
696: this .principal = principal;
697: this .flags |= PRINCIPAL_WAS_SET;
698: }
699:
700: public Object getCredential() {
701: return credential;
702: }
703:
704: public void setCredential(Object credential) {
705: this .credential = credential;
706: this .flags |= CREDENTIAL_WAS_SET;
707: }
708:
709: public int getFlags() {
710: return this .flags;
711: }
712:
713: public String toString() {
714: StringBuffer tmp = new StringBuffer(super .toString());
715: tmp.append("{principal=");
716: tmp.append(principal);
717: tmp.append(",subject=");
718: if (subject != null)
719: tmp.append(System.identityHashCode(subject));
720: else
721: tmp.append("null");
722: tmp.append("}");
723: return tmp.toString();
724: }
725: }
726:
727: private static class SubjectThreadLocalStack {
728: ThreadLocal local;
729:
730: SubjectThreadLocalStack(boolean threadLocal) {
731: if (threadLocal == true)
732: local = new ArrayListLocal();
733: else
734: local = new ArrayListInheritableLocal();
735: }
736:
737: int size() {
738: ArrayList stack = (ArrayList) local.get();
739: return stack.size();
740: }
741:
742: void push(SubjectContext context) {
743: ArrayList stack = (ArrayList) local.get();
744: stack.add(context);
745: }
746:
747: SubjectContext dup() {
748: ArrayList stack = (ArrayList) local.get();
749: SubjectContext context = null;
750: int lastIndex = stack.size() - 1;
751: if (lastIndex >= 0) {
752: context = (SubjectContext) stack.get(lastIndex);
753: stack.add(context);
754: }
755: return context;
756: }
757:
758: SubjectContext pop() {
759: ArrayList stack = (ArrayList) local.get();
760: SubjectContext context = null;
761: int lastIndex = stack.size() - 1;
762: if (lastIndex >= 0)
763: context = (SubjectContext) stack.remove(lastIndex);
764: return context;
765: }
766:
767: /**
768: * Look for the first non-null run-as identity on the stack starting
769: * with the value at depth.
770: * @return The run-as identity if one exists, null otherwise.
771: */
772: SubjectContext peek() {
773: ArrayList stack = (ArrayList) local.get();
774: SubjectContext context = null;
775: int lastIndex = stack.size() - 1;
776: if (lastIndex >= 0)
777: context = (SubjectContext) stack.get(lastIndex);
778: return context;
779: }
780:
781: /**
782: * Remove all SubjectContext from the current thread stack
783: */
784: void clear() {
785: ArrayList stack = (ArrayList) local.get();
786: stack.clear();
787: }
788: }
789:
790: private static class ArrayListLocal extends ThreadLocal {
791: protected Object initialValue() {
792: return new ArrayList();
793: }
794:
795: }
796:
797: private static class ArrayListInheritableLocal extends
798: InheritableThreadLocal {
799: /**
800: * Override to make a copy of the parent as not doing so results in multiple
801: * threads sharing the unsynchronized list of the parent thread.
802: * @param parentValue - the parent ArrayList
803: * @return a copy of the parent thread list
804: */
805: protected Object childValue(Object parentValue) {
806: ArrayList list = (ArrayList) parentValue;
807: /* It seems there are scenarios where the size can change during the copy so there is
808: a fallback to an empty list here.
809: */
810: ArrayList copy = null;
811: try {
812: copy = new ArrayList(list);
813: } catch (Throwable t) {
814: log.debug("Failed to copy parent list, using new list");
815: copy = new ArrayList();
816: }
817: return copy;
818: }
819:
820: protected Object initialValue() {
821: return new ArrayList();
822: }
823:
824: }
825:
826: private static class HashMapInheritableLocal extends
827: InheritableThreadLocal {
828: /**
829: * Override to make a copy of the parent as not doing so results in multiple
830: * threads sharing the unsynchronized map of the parent thread.
831: * @param parentValue - the parent HashMap
832: * @return a copy of the parent thread map
833: */
834: protected Object childValue(Object parentValue) {
835: HashMap map = (HashMap) parentValue;
836: /* It seems there are scenarios where the size can change during the copy so there is
837: a fallback to an empty map here.
838: */
839: HashMap copy = null;
840: try {
841: copy = new HashMap(map);
842: } catch (Throwable t) {
843: log.debug("Failed to copy parent map, using new map");
844: copy = new HashMap();
845: }
846: return copy;
847: }
848:
849: protected Object initialValue() {
850: return new HashMap();
851: }
852:
853: }
854: }
|