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.plugins;
023:
024: import java.lang.reflect.Method;
025: import java.lang.reflect.UndeclaredThrowableException;
026: import java.security.Principal;
027: import java.security.acl.Group;
028: import java.util.Arrays;
029: import java.util.Enumeration;
030: import java.util.HashSet;
031: import java.util.Iterator;
032: import java.util.Set;
033: import javax.security.auth.Subject;
034: import javax.security.auth.callback.CallbackHandler;
035: import javax.security.auth.login.LoginContext;
036: import javax.security.auth.login.LoginException;
037:
038: import org.jboss.logging.Logger;
039: import org.jboss.security.AnybodyPrincipal;
040: import org.jboss.security.NobodyPrincipal;
041: import org.jboss.security.RealmMapping;
042: import org.jboss.security.SecurityAssociation;
043: import org.jboss.security.SecurityConstants;
044: import org.jboss.security.SubjectSecurityManager;
045: import org.jboss.security.auth.callback.SecurityAssociationHandler;
046: import org.jboss.system.ServiceMBeanSupport;
047: import org.jboss.util.CachePolicy;
048: import org.jboss.util.TimedCachePolicy;
049:
050: /** The JaasSecurityManager is responsible both for authenticating credentials
051: associated with principals and for role mapping. This implementation relies
052: on the JAAS LoginContext/LoginModules associated with the security
053: domain name associated with the class for authentication,
054: and the context JAAS Subject object for role mapping.
055:
056: @see #isValid(Principal, Object, Subject)
057: @see #getPrincipal(Principal)
058: @see #doesUserHaveRole(Principal, Set)
059:
060: @author <a href="on@ibis.odessa.ua">Oleg Nitz</a>
061: @author Scott.Stark@jboss.org
062: @version $Revision: 62863 $
063: */
064: public class JaasSecurityManager extends ServiceMBeanSupport implements
065: SubjectSecurityManager, RealmMapping {
066: /** The authentication cache object.
067: */
068: public static class DomainInfo implements
069: TimedCachePolicy.TimedEntry {
070: private static Logger log = Logger.getLogger(DomainInfo.class);
071: private static boolean trace = log.isTraceEnabled();
072: private LoginContext loginCtx;
073: private Subject subject;
074: private Object credential;
075: private Principal callerPrincipal;
076: private long expirationTime;
077: /** Is there an active authentication in process */
078: private boolean needsDestroy;
079: /** The number of users sharing this DomainInfo */
080: private int activeUsers;
081:
082: /**
083: Create a cache entry with the given lifetime in seconds. Since this comes
084: from the TimedCachePolicy, its expected to be <= Integer.MAX_VALUE.
085:
086: @param lifetime - lifetime in seconds. A lifetime <= 0 means no caching
087: with the exception of -1 which indicates that the cache entry never
088: expires.
089: */
090: public DomainInfo(long lifetime) {
091: expirationTime = lifetime;
092: if (expirationTime != -1)
093: expirationTime *= 1000;
094: }
095:
096: synchronized int acquire() {
097: return activeUsers++;
098: }
099:
100: synchronized int release() {
101: int users = activeUsers--;
102: if (needsDestroy == true && users == 0) {
103: if (trace)
104: log.trace("needsDestroy is true, doing logout");
105: logout();
106: }
107: return users;
108: }
109:
110: synchronized void logout() {
111: if (trace)
112: log.trace("logout, subject=" + subject + ", this="
113: + this );
114: try {
115: if (loginCtx != null)
116: loginCtx.logout();
117: } catch (Throwable e) {
118: if (trace)
119: log.trace("Cache entry logout failed", e);
120: }
121: }
122:
123: public void init(long now) {
124: expirationTime += now;
125: }
126:
127: public boolean isCurrent(long now) {
128: boolean isCurrent = expirationTime == -1;
129: if (isCurrent == false)
130: isCurrent = expirationTime > now;
131: return isCurrent;
132: }
133:
134: public boolean refresh() {
135: return false;
136: }
137:
138: /**
139: * This
140: */
141: public void destroy() {
142: if (trace) {
143: log.trace("destroy, subject=" + subject + ", this="
144: + this + ", activeUsers=" + activeUsers);
145: }
146:
147: synchronized (this ) {
148: if (activeUsers == 0)
149: logout();
150: else {
151: if (trace)
152: log.trace("destroy saw activeUsers="
153: + activeUsers);
154: needsDestroy = true;
155: }
156: }
157: }
158:
159: public Object getValue() {
160: return this ;
161: }
162:
163: public String toString() {
164: StringBuffer tmp = new StringBuffer(super .toString());
165: tmp.append('[');
166: tmp.append(SubjectActions.toString(subject));
167: tmp.append(",credential.class=");
168: if (credential != null) {
169: Class c = credential.getClass();
170: tmp.append(c.getName());
171: tmp.append('@');
172: tmp.append(System.identityHashCode(c));
173: } else {
174: tmp.append("null");
175: }
176: tmp.append(",expirationTime=");
177: tmp.append(expirationTime);
178: tmp.append(']');
179:
180: return tmp.toString();
181: }
182: }
183:
184: /** The name of the domain this instance is securing. It is used as
185: the appName into the SecurityPolicy.
186: */
187: private String securityDomain;
188: /** A cache of DomainInfo objects keyd by Principal. This is now
189: always set externally by our security manager service.
190: */
191: private CachePolicy domainCache;
192: /** The JAAS callback handler to use in defaultLogin */
193: private CallbackHandler handler;
194: /** The setSecurityInfo(Principal, Object) method of the handler obj */
195: private Method setSecurityInfo;
196: /** The flag to indicate that the Subject sets need to be deep copied*/
197: private boolean deepCopySubjectOption = false;
198:
199: /** The log4j category for the security manager domain
200: */
201: protected Logger log;
202: protected boolean trace;
203:
204: /** Creates a default JaasSecurityManager for default securityDomain.
205: */
206: public JaasSecurityManager() {
207: this (SecurityConstants.DEFAULT_APPLICATION_POLICY,
208: new SecurityAssociationHandler());
209: }
210:
211: /** Creates a JaasSecurityManager for with a securityDomain
212: name of that given by the 'securityDomain' argument.
213: @param securityDomain the name of the security domain
214: @param handler the JAAS callback handler instance to use
215: @exception UndeclaredThrowableException thrown if handler does not
216: implement a setSecurityInfo(Princpal, Object) method
217: */
218: public JaasSecurityManager(String securityDomain,
219: CallbackHandler handler) {
220: this .securityDomain = securityDomain;
221: this .handler = handler;
222: String categoryName = getClass().getName() + '.'
223: + securityDomain;
224: this .log = Logger.getLogger(categoryName);
225: this .trace = log.isTraceEnabled();
226:
227: // Get the setSecurityInfo(Principal principal, Object credential) method
228: Class[] sig = { Principal.class, Object.class };
229: try {
230: setSecurityInfo = handler.getClass().getMethod(
231: "setSecurityInfo", sig);
232: } catch (Exception e) {
233: String msg = "Failed to find setSecurityInfo(Princpal, Object) method in handler";
234: throw new UndeclaredThrowableException(e, msg);
235: }
236: log.debug("CallbackHandler: " + handler);
237: }
238:
239: /** The domainCache is typically a shared object that is populated
240: by the login code(LoginModule, etc.) and read by this class in the
241: isValid() method.
242: @see #isValid(Principal, Object, Subject)
243: */
244: public void setCachePolicy(CachePolicy domainCache) {
245: this .domainCache = domainCache;
246: log.debug("CachePolicy set to: " + domainCache);
247: }
248:
249: /**
250: * Flag to specify if deep copy of subject sets needs to be
251: * enabled
252: *
253: * @param flag
254: */
255: public void setDeepCopySubjectOption(Boolean flag) {
256: log.debug("setDeepCopySubjectOption=" + flag);
257: this .deepCopySubjectOption = (flag == Boolean.TRUE);
258: }
259:
260: /** Not really used anymore as the security manager service manages the
261: security domain authentication caches.
262: */
263: public void flushCache() {
264: if (domainCache != null)
265: domainCache.flush();
266: }
267:
268: /** Get the name of the security domain associated with this security mgr.
269: @return Name of the security manager security domain.
270: */
271: public String getSecurityDomain() {
272: return securityDomain;
273: }
274:
275: /** Get the currently authenticated Subject. This is a thread local
276: property shared across all JaasSecurityManager instances.
277: @return The Subject authenticated in the current thread if one
278: exists, null otherwise.
279: */
280: public Subject getActiveSubject() {
281: /* This does not use SubjectActions.getActiveSubject since the caller
282: must have the correct permissions to access the
283: SecurityAssociation.getSubject method.
284: */
285: return SecurityAssociation.getSubject();
286: }
287:
288: /** Validate that the given credential is correct for principal. This
289: returns the value from invoking isValid(principal, credential, null).
290: @param principal - the security domain principal attempting access
291: @param credential - the proof of identity offered by the principal
292: @return true if the principal was authenticated, false otherwise.
293: */
294: public boolean isValid(Principal principal, Object credential) {
295: return isValid(principal, credential, null);
296: }
297:
298: /** Validate that the given credential is correct for principal. This first
299: will check the current CachePolicy object if one exists to see if the
300: user's cached credentials match the given credential. If there is no
301: credential cache or the cache information is invalid or does not match,
302: the user is authenticated against the JAAS login modules configured for
303: the security domain.
304: @param principal - the security domain principal attempting access
305: @param credential the proof of identity offered by the principal
306: @param activeSubject - if not null, a Subject that will be populated with
307: the state of the authenticated Subject.
308: @return true if the principal was authenticated, false otherwise.
309: */
310: public boolean isValid(Principal principal, Object credential,
311: Subject activeSubject) {
312: // Check the cache first
313: DomainInfo cacheInfo = getCacheInfo(principal, true);
314: if (trace)
315: log.trace("Begin isValid, principal:" + principal
316: + ", cache info: " + cacheInfo);
317:
318: boolean isValid = false;
319: if (cacheInfo != null) {
320: isValid = validateCache(cacheInfo, credential,
321: activeSubject);
322: if (cacheInfo != null)
323: cacheInfo.release();
324: }
325: if (isValid == false)
326: isValid = authenticate(principal, credential, activeSubject);
327: if (trace)
328: log.trace("End isValid, " + isValid);
329: return isValid;
330: }
331:
332: /** Map the argument principal from the deployment environment principal
333: to the developer environment. This is called by the EJB context
334: getCallerPrincipal() to return the Principal as described by
335: the EJB developer domain.
336: @return a Principal object that is valid in the deployment environment
337: if one exists. If no Subject exists or the Subject has no principals
338: then the argument principal is returned.
339: */
340: public Principal getPrincipal(Principal principal) {
341: if (domainCache == null)
342: return principal;
343: Principal result = principal;
344: // Get the CallerPrincipal group member
345: synchronized (domainCache) {
346: DomainInfo info = getCacheInfo(principal, false);
347: if (trace)
348: log.trace("getPrincipal, cache info: " + info);
349: if (info != null) {
350: result = info.callerPrincipal;
351: // If the mapping did not have a callerPrincipal just use principal
352: if (result == null)
353: result = principal;
354: info.release();
355: }
356: }
357:
358: return result;
359: }
360:
361: /** Does the current Subject have a role(a Principal) that equates to one
362: of the role names. This method obtains the Group named 'Roles' from
363: the principal set of the currently authenticated Subject as determined
364: by the SecurityAssociation.getSubject() method and then creates a
365: SimplePrincipal for each name in roleNames. If the role is a member of the
366: Roles group, then the user has the role. This requires that the caller
367: establish the correct SecurityAssociation subject prior to calling this
368: method. In the past this was done as a side-effect of an isValid() call,
369: but this is no longer the case.
370:
371: @param principal - ignored. The current authenticated Subject determines
372: the active user and assigned user roles.
373: @param rolePrincipals - a Set of Principals for the roles to check.
374:
375: @see java.security.acl.Group;
376: @see Subject#getPrincipals()
377: */
378: public boolean doesUserHaveRole(Principal principal,
379: Set rolePrincipals) {
380: boolean hasRole = false;
381: // Check that the caller is authenticated to the current thread
382: Subject subject = SubjectActions.getActiveSubject();
383: if (subject != null) {
384: // Check the caller's roles
385: if (trace)
386: log.trace("doesUserHaveRole(Set), subject: " + subject);
387:
388: Group roles = getSubjectRoles(subject);
389: if (trace)
390: log.trace("roles=" + roles);
391: if (roles != null) {
392: Iterator iter = rolePrincipals.iterator();
393: while (hasRole == false && iter.hasNext()) {
394: Principal role = (Principal) iter.next();
395: hasRole = doesRoleGroupHaveRole(role, roles);
396: if (trace)
397: log.trace("hasRole(" + role + ")=" + hasRole);
398: }
399: }
400: if (trace)
401: log.trace("hasRole=" + hasRole);
402: }
403: return hasRole;
404: }
405:
406: /** Does the current Subject have a role(a Principal) that equates to one
407: of the role names.
408:
409: @see #doesUserHaveRole(Principal, Set)
410:
411: @param principal - ignored. The current authenticated Subject determines
412: the active user and assigned user roles.
413: @param role - the application domain role that the principal is to be
414: validated against.
415: @return true if the active principal has the role, false otherwise.
416: */
417: public boolean doesUserHaveRole(Principal principal, Principal role) {
418: boolean hasRole = false;
419: // Check that the caller is authenticated to the current thread
420: Subject subject = SubjectActions.getActiveSubject();
421: if (subject != null) {
422: // Check the caller's roles
423: if (trace)
424: log.trace("doesUserHaveRole(Principal), subject: "
425: + subject);
426:
427: Group roles = getSubjectRoles(subject);
428: if (roles != null) {
429: hasRole = doesRoleGroupHaveRole(role, roles);
430: }
431: }
432: return hasRole;
433: }
434:
435: /** Return the set of domain roles the current active Subject 'Roles' group
436: found in the subject Principals set.
437:
438: @param principal - ignored. The current authenticated Subject determines
439: the active user and assigned user roles.
440: @return The Set<Principal> for the application domain roles that the
441: principal has been assigned.
442: */
443: public Set getUserRoles(Principal principal) {
444: HashSet userRoles = null;
445: // Check that the caller is authenticated to the current thread
446: Subject subject = SubjectActions.getActiveSubject();
447: if (subject != null) {
448: // Copy the caller's roles
449: if (trace)
450: log.trace("getUserRoles, subject: " + subject);
451:
452: Group roles = getSubjectRoles(subject);
453: if (roles != null) {
454: userRoles = new HashSet();
455: Enumeration members = roles.members();
456: while (members.hasMoreElements()) {
457: Principal role = (Principal) members.nextElement();
458: userRoles.add(role);
459: }
460: }
461: }
462: return userRoles;
463: }
464:
465: /** Check that the indicated application domain role is a member of the
466: user's assigned roles. This handles the special AnybodyPrincipal and
467: NobodyPrincipal independent of the Group implementation.
468:
469: @param role , the application domain role required for access
470: @param userRoles , the set of roles assigned to the user
471: @return true if role is in userRoles or an AnybodyPrincipal instance, false
472: if role is a NobodyPrincipal or no a member of userRoles
473: */
474: protected boolean doesRoleGroupHaveRole(Principal role,
475: Group userRoles) {
476: // First check that role is not a NobodyPrincipal
477: if (role instanceof NobodyPrincipal)
478: return false;
479:
480: // Check for inclusion in the user's role set
481: boolean isMember = userRoles.isMember(role);
482: if (isMember == false) { // Check the AnybodyPrincipal special cases
483: isMember = (role instanceof AnybodyPrincipal);
484: }
485:
486: return isMember;
487: }
488:
489: /** Currently this simply calls defaultLogin() to do a JAAS login using the
490: security domain name as the login module configuration name.
491:
492: * @param principal - the user id to authenticate
493: * @param credential - an opaque credential.
494: * @return false on failure, true on success.
495: */
496: private boolean authenticate(Principal principal,
497: Object credential, Subject theSubject) {
498: Subject subject = null;
499: boolean authenticated = false;
500: LoginException authException = null;
501:
502: try {
503: // Validate the principal using the login configuration for this domain
504: LoginContext lc = defaultLogin(principal, credential);
505: subject = lc.getSubject();
506:
507: // Set the current subject if login was successful
508: if (subject != null) {
509: // Copy the current subject into theSubject
510: if (theSubject != null) {
511: SubjectActions.copySubject(subject, theSubject,
512: false, this .deepCopySubjectOption);
513: } else {
514: theSubject = subject;
515: }
516:
517: authenticated = true;
518: // Build the Subject based DomainInfo cache value
519: updateCache(lc, subject, principal, credential);
520: }
521: } catch (LoginException e) {
522: // Don't log anonymous user failures unless trace level logging is on
523: if (principal != null && principal.getName() != null
524: || trace)
525: log.trace("Login failure", e);
526: authException = e;
527: }
528: // Set the security association thread context info exception
529: SubjectActions.setContextInfo("org.jboss.security.exception",
530: authException);
531:
532: return authenticated;
533: }
534:
535: /** Pass the security info to the login modules configured for
536: this security domain using our SecurityAssociationHandler.
537: @return The authenticated Subject if successful.
538: @exception LoginException throw if login fails for any reason.
539: */
540: private LoginContext defaultLogin(Principal principal,
541: Object credential) throws LoginException {
542: /* We use our internal CallbackHandler to provide the security info. A
543: copy must be made to ensure there is a unique handler per active
544: login since there can be multiple active logins.
545: */
546: Object[] securityInfo = { principal, credential };
547: CallbackHandler theHandler = null;
548: try {
549: theHandler = (CallbackHandler) handler.getClass()
550: .newInstance();
551: setSecurityInfo.invoke(theHandler, securityInfo);
552: } catch (Throwable e) {
553: if (trace)
554: log.trace(
555: "Failed to create/setSecurityInfo on handler",
556: e);
557: LoginException le = new LoginException(
558: "Failed to setSecurityInfo on handler");
559: le.initCause(e);
560: throw le;
561: }
562: Subject subject = new Subject();
563: LoginContext lc = null;
564: if (trace)
565: log.trace("defaultLogin, principal=" + principal);
566: lc = SubjectActions.createLoginContext(securityDomain, subject,
567: theHandler);
568: lc.login();
569: if (trace)
570: log.trace("defaultLogin, lc=" + lc + ", subject="
571: + SubjectActions.toString(subject));
572: return lc;
573: }
574:
575: /** Validate the cache credential value against the provided credential
576: */
577: private boolean validateCache(DomainInfo info, Object credential,
578: Subject theSubject) {
579: if (trace) {
580: StringBuffer tmp = new StringBuffer(
581: "Begin validateCache, info=");
582: tmp.append(info.toString());
583: tmp.append(";credential.class=");
584: if (credential != null) {
585: Class c = credential.getClass();
586: tmp.append(c.getName());
587: tmp.append('@');
588: tmp.append(System.identityHashCode(c));
589: } else {
590: tmp.append("null");
591: }
592: log.trace(tmp.toString());
593: }
594:
595: Object subjectCredential = info.credential;
596: boolean isValid = false;
597: // Check for a null credential as can be the case for an anonymous user
598: if (credential == null || subjectCredential == null) {
599: // Both credentials must be null
600: isValid = (credential == null)
601: && (subjectCredential == null);
602: }
603: // See if the credential is assignable to the cache value
604: else if (subjectCredential.getClass().isAssignableFrom(
605: credential.getClass())) {
606: /* Validate the credential by trying Comparable, char[], byte[],
607: Object[], and finally Object.equals()
608: */
609: if (subjectCredential instanceof Comparable) {
610: Comparable c = (Comparable) subjectCredential;
611: isValid = c.compareTo(credential) == 0;
612: } else if (subjectCredential instanceof char[]) {
613: char[] a1 = (char[]) subjectCredential;
614: char[] a2 = (char[]) credential;
615: isValid = Arrays.equals(a1, a2);
616: } else if (subjectCredential instanceof byte[]) {
617: byte[] a1 = (byte[]) subjectCredential;
618: byte[] a2 = (byte[]) credential;
619: isValid = Arrays.equals(a1, a2);
620: } else if (subjectCredential.getClass().isArray()) {
621: Object[] a1 = (Object[]) subjectCredential;
622: Object[] a2 = (Object[]) credential;
623: isValid = Arrays.equals(a1, a2);
624: } else {
625: isValid = subjectCredential.equals(credential);
626: }
627: } else if (subjectCredential instanceof char[]
628: && credential instanceof String) {
629: char[] a1 = (char[]) subjectCredential;
630: char[] a2 = ((String) credential).toCharArray();
631: isValid = Arrays.equals(a1, a2);
632: } else if (subjectCredential instanceof String
633: && credential instanceof char[]) {
634: char[] a1 = ((String) subjectCredential).toCharArray();
635: char[] a2 = (char[]) credential;
636: isValid = Arrays.equals(a1, a2);
637: }
638:
639: // If the credentials match, set the thread's active Subject
640: if (isValid) {
641: // Copy the current subject into theSubject
642: if (theSubject != null) {
643: SubjectActions.copySubject(info.subject, theSubject,
644: false, this .deepCopySubjectOption);
645: }
646: }
647: if (trace)
648: log.trace("End validateCache, isValid=" + isValid);
649:
650: return isValid;
651: }
652:
653: /** An accessor method that synchronizes access on the domainCache
654: to avoid a race condition that can occur when the cache entry expires
655: in the presence of multi-threaded access. The allowRefresh flag should
656: be true for authentication accesses and false for other accesses.
657: Previously the other accesses included authorization and caller principal
658: mapping. Now the only use of the
659:
660: @param principal - the caller identity whose cached credentials are to
661: be accessed.
662: @param allowRefresh - a flag indicating if the cache access should flush
663: any expired entries.
664: */
665: private DomainInfo getCacheInfo(Principal principal,
666: boolean allowRefresh) {
667: if (domainCache == null)
668: return null;
669:
670: DomainInfo cacheInfo = null;
671: synchronized (domainCache) {
672: if (allowRefresh == true)
673: cacheInfo = (DomainInfo) domainCache.get(principal);
674: else
675: cacheInfo = (DomainInfo) domainCache.peek(principal);
676: if (cacheInfo != null)
677: cacheInfo.acquire();
678: }
679: return cacheInfo;
680: }
681:
682: private Subject updateCache(LoginContext lc, Subject subject,
683: Principal principal, Object credential) {
684: // If we don't have a cache there is nothing to update
685: if (domainCache == null)
686: return subject;
687:
688: long lifetime = 0;
689: if (domainCache instanceof TimedCachePolicy) {
690: TimedCachePolicy cache = (TimedCachePolicy) domainCache;
691: lifetime = cache.getDefaultLifetime();
692: }
693: DomainInfo info = new DomainInfo(lifetime);
694: info.loginCtx = lc;
695: info.subject = new Subject();
696: SubjectActions.copySubject(subject, info.subject, true,
697: this .deepCopySubjectOption);
698: info.credential = credential;
699:
700: if (trace) {
701: log.trace("updateCache, inputSubject="
702: + SubjectActions.toString(subject)
703: + ", cacheSubject="
704: + SubjectActions.toString(info.subject));
705: }
706:
707: /* Get the Subject callerPrincipal by looking for a Group called
708: 'CallerPrincipal'
709: */
710: Set subjectGroups = subject.getPrincipals(Group.class);
711: Iterator iter = subjectGroups.iterator();
712: while (iter.hasNext()) {
713: Group grp = (Group) iter.next();
714: String name = grp.getName();
715: if (name.equals("CallerPrincipal")) {
716: Enumeration members = grp.members();
717: if (members.hasMoreElements())
718: info.callerPrincipal = (Principal) members
719: .nextElement();
720: }
721: }
722:
723: /* Handle null principals with no callerPrincipal. This is an indication
724: of an user that has not provided any authentication info, but
725: has been authenticated by the domain login module stack. Here we look
726: for the first non-Group Principal and use that.
727: */
728: if (principal == null && info.callerPrincipal == null) {
729: Set subjectPrincipals = subject
730: .getPrincipals(Principal.class);
731: iter = subjectPrincipals.iterator();
732: while (iter.hasNext()) {
733: Principal p = (Principal) iter.next();
734: if ((p instanceof Group) == false)
735: info.callerPrincipal = p;
736: }
737: }
738:
739: /* If the user already exists another login is active. Currently
740: only one is allowed so remove the old and insert the new. Synchronize
741: on the domainCache to ensure the removal and addition are an atomic
742: operation so that getCacheInfo cannot see stale data.
743: */
744: synchronized (domainCache) {
745: if (domainCache.peek(principal) != null)
746: domainCache.remove(principal);
747: domainCache.insert(principal, info);
748: if (trace)
749: log.trace("Inserted cache info: " + info);
750: }
751: return info.subject;
752: }
753:
754: /**
755: * Get the Subject roles by looking for a Group called 'Roles'
756: * @param theSubject - the Subject to search for roles
757: * @return the Group contain the subject roles if found, null otherwise
758: */
759: private Group getSubjectRoles(Subject theSubject) {
760: Set subjectGroups = theSubject.getPrincipals(Group.class);
761: Iterator iter = subjectGroups.iterator();
762: Group roles = null;
763: while (iter.hasNext()) {
764: Group grp = (Group) iter.next();
765: String name = grp.getName();
766: if (name.equals("Roles"))
767: roles = grp;
768: }
769: return roles;
770: }
771: }
|