KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > security > auth > Subject


1 /*
2  * @(#)Subject.java 1.123 04/05/05
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package javax.security.auth;
9
10 import java.util.*;
11 import java.io.*;
12 import java.lang.reflect.*;
13 import java.text.MessageFormat JavaDoc;
14 import java.security.AccessController JavaDoc;
15 import java.security.AccessControlContext JavaDoc;
16 import java.security.DomainCombiner JavaDoc;
17 import java.security.Permission JavaDoc;
18 import java.security.PermissionCollection JavaDoc;
19 import java.security.Principal JavaDoc;
20 import java.security.PrivilegedAction JavaDoc;
21 import java.security.PrivilegedExceptionAction JavaDoc;
22 import java.security.PrivilegedActionException JavaDoc;
23 import java.security.ProtectionDomain JavaDoc;
24 import sun.security.util.ResourcesMgr;
25 import sun.security.util.SecurityConstants;
26
27 /**
28  * <p> A <code>Subject</code> represents a grouping of related information
29  * for a single entity, such as a person.
30  * Such information includes the Subject's identities as well as
31  * its security-related attributes
32  * (passwords and cryptographic keys, for example).
33  *
34  * <p> Subjects may potentially have multiple identities.
35  * Each identity is represented as a <code>Principal</code>
36  * within the <code>Subject</code>. Principals simply bind names to a
37  * <code>Subject</code>. For example, a <code>Subject</code> that happens
38  * to be a person, Alice, might have two Principals:
39  * one which binds "Alice Bar", the name on her driver license,
40  * to the <code>Subject</code>, and another which binds,
41  * "999-99-9999", the number on her student identification card,
42  * to the <code>Subject</code>. Both Principals refer to the same
43  * <code>Subject</code> even though each has a different name.
44  *
45  * <p> A <code>Subject</code> may also own security-related attributes,
46  * which are referred to as credentials.
47  * Sensitive credentials that require special protection, such as
48  * private cryptographic keys, are stored within a private credential
49  * <code>Set</code>. Credentials intended to be shared, such as
50  * public key certificates or Kerberos server tickets are stored
51  * within a public credential <code>Set</code>. Different permissions
52  * are required to access and modify the different credential Sets.
53  *
54  * <p> To retrieve all the Principals associated with a <code>Subject</code>,
55  * invoke the <code>getPrincipals</code> method. To retrieve
56  * all the public or private credentials belonging to a <code>Subject</code>,
57  * invoke the <code>getPublicCredentials</code> method or
58  * <code>getPrivateCredentials</code> method, respectively.
59  * To modify the returned <code>Set</code> of Principals and credentials,
60  * use the methods defined in the <code>Set</code> class.
61  * For example:
62  * <pre>
63  * Subject subject;
64  * Principal principal;
65  * Object credential;
66  *
67  * // add a Principal and credential to the Subject
68  * subject.getPrincipals().add(principal);
69  * subject.getPublicCredentials().add(credential);
70  * </pre>
71  *
72  * <p> This <code>Subject</code> class implements <code>Serializable</code>.
73  * While the Principals associated with the <code>Subject</code> are serialized,
74  * the credentials associated with the <code>Subject</code> are not.
75  * Note that the <code>java.security.Principal</code> class
76  * does not implement <code>Serializable</code>. Therefore all concrete
77  * <code>Principal</code> implementations associated with Subjects
78  * must implement <code>Serializable</code>.
79  *
80  * @version 1.123, 05/05/04
81  * @see java.security.Principal
82  * @see java.security.DomainCombiner
83  */

84 public final class Subject implements java.io.Serializable JavaDoc {
85
86     private static final long serialVersionUID = -8308522755600156056L;
87
88     /**
89      * A <code>Set</code> that provides a view of all of this
90      * Subject's Principals
91      *
92      * <p>
93      *
94      * @serial Each element in this set is a
95      * <code>java.security.Principal</code>.
96      * The set is a <code>Subject.SecureSet</code>.
97      */

98     Set principals;
99
100     /**
101      * Sets that provide a view of all of this
102      * Subject's Credentials
103      */

104     transient Set pubCredentials;
105     transient Set privCredentials;
106
107     /**
108      * Whether this Subject is read-only
109      *
110      * @serial
111      */

112     private volatile boolean readOnly = false;
113
114     private static final int PRINCIPAL_SET = 1;
115     private static final int PUB_CREDENTIAL_SET = 2;
116     private static final int PRIV_CREDENTIAL_SET = 3;
117
118     /**
119      * Create an instance of a <code>Subject</code>
120      * with an empty <code>Set</code> of Principals and empty
121      * Sets of public and private credentials.
122      *
123      * <p> The newly constructed Sets check whether this <code>Subject</code>
124      * has been set read-only before permitting subsequent modifications.
125      * The newly created Sets also prevent illegal modifications
126      * by ensuring that callers have sufficient permissions.
127      *
128      * <p> To modify the Principals Set, the caller must have
129      * <code>AuthPermission("modifyPrincipals")</code>.
130      * To modify the public credential Set, the caller must have
131      * <code>AuthPermission("modifyPublicCredentials")</code>.
132      * To modify the private credential Set, the caller must have
133      * <code>AuthPermission("modifyPrivateCredentials")</code>.
134      */

135     public Subject() {
136
137     this.principals = Collections.synchronizedSet
138                 (new SecureSet(this, PRINCIPAL_SET));
139     this.pubCredentials = Collections.synchronizedSet
140                 (new SecureSet(this, PUB_CREDENTIAL_SET));
141     this.privCredentials = Collections.synchronizedSet
142                 (new SecureSet(this, PRIV_CREDENTIAL_SET));
143     }
144
145     /**
146      * Create an instance of a <code>Subject</code> with
147      * Principals and credentials.
148      *
149      * <p> The Principals and credentials from the specified Sets
150      * are copied into newly constructed Sets.
151      * These newly created Sets check whether this <code>Subject</code>
152      * has been set read-only before permitting subsequent modifications.
153      * The newly created Sets also prevent illegal modifications
154      * by ensuring that callers have sufficient permissions.
155      *
156      * <p> To modify the Principals Set, the caller must have
157      * <code>AuthPermission("modifyPrincipals")</code>.
158      * To modify the public credential Set, the caller must have
159      * <code>AuthPermission("modifyPublicCredentials")</code>.
160      * To modify the private credential Set, the caller must have
161      * <code>AuthPermission("modifyPrivateCredentials")</code>.
162      * <p>
163      *
164      * @param readOnly true if the <code>Subject</code> is to be read-only,
165      * and false otherwise. <p>
166      *
167      * @param principals the <code>Set</code> of Principals
168      * to be associated with this <code>Subject</code>. <p>
169      *
170      * @param pubCredentials the <code>Set</code> of public credentials
171      * to be associated with this <code>Subject</code>. <p>
172      *
173      * @param privCredentials the <code>Set</code> of private credentials
174      * to be associated with this <code>Subject</code>.
175      *
176      * @exception NullPointerException if the specified
177      * <code>principals</code>, <code>pubCredentials</code>,
178      * or <code>privCredentials</code> are <code>null</code>.
179      */

180     public Subject(boolean readOnly, Set<? extends Principal JavaDoc> principals,
181            Set<?> pubCredentials, Set<?> privCredentials)
182     {
183
184     if (principals == null ||
185         pubCredentials == null ||
186         privCredentials == null)
187         throw new NullPointerException JavaDoc
188         (ResourcesMgr.getString("invalid null input(s)"));
189
190     this.principals = Collections.synchronizedSet(new SecureSet
191                 (this, PRINCIPAL_SET, principals));
192     this.pubCredentials = Collections.synchronizedSet(new SecureSet
193                 (this, PUB_CREDENTIAL_SET, pubCredentials));
194     this.privCredentials = Collections.synchronizedSet(new SecureSet
195                 (this, PRIV_CREDENTIAL_SET, privCredentials));
196     this.readOnly = readOnly;
197     }
198
199     /**
200      * Set this <code>Subject</code> to be read-only.
201      *
202      * <p> Modifications (additions and removals) to this Subject's
203      * <code>Principal</code> <code>Set</code> and
204      * credential Sets will be disallowed.
205      * The <code>destroy</code> operation on this Subject's credentials will
206      * still be permitted.
207      *
208      * <p> Subsequent attempts to modify the Subject's <code>Principal</code>
209      * and credential Sets will result in an
210      * <code>IllegalStateException</code> being thrown.
211      * Also, once a <code>Subject</code> is read-only,
212      * it can not be reset to being writable again.
213      *
214      * <p>
215      *
216      * @exception SecurityException if the caller does not have permission
217      * to set this <code>Subject</code> to be read-only.
218      */

219     public void setReadOnly() {
220     java.lang.SecurityManager JavaDoc sm = System.getSecurityManager();
221     if (sm != null) {
222         sm.checkPermission(new AuthPermission JavaDoc("setReadOnly"));
223     }
224
225     this.readOnly = true;
226     }
227
228     /**
229      * Query whether this <code>Subject</code> is read-only.
230      *
231      * <p>
232      *
233      * @return true if this <code>Subject</code> is read-only, false otherwise.
234      */

235     public boolean isReadOnly() {
236     return this.readOnly;
237     }
238
239     /**
240      * Get the <code>Subject</code> associated with the provided
241      * <code>AccessControlContext</code>.
242      *
243      * <p> The <code>AccessControlContext</code> may contain many
244      * Subjects (from nested <code>doAs</code> calls).
245      * In this situation, the most recent <code>Subject</code> associated
246      * with the <code>AccessControlContext</code> is returned.
247      *
248      * <p>
249      *
250      * @param acc the <code>AccessControlContext</code> from which to retrieve
251      * the <code>Subject</code>.
252      *
253      * @return the <code>Subject</code> associated with the provided
254      * <code>AccessControlContext</code>, or <code>null</code>
255      * if no <code>Subject</code> is associated
256      * with the provided <code>AccessControlContext</code>.
257      *
258      * @exception SecurityException if the caller does not have permission
259      * to get the <code>Subject</code>. <p>
260      *
261      * @exception NullPointerException if the provided
262      * <code>AccessControlContext</code> is <code>null</code>.
263      */

264     public static Subject JavaDoc getSubject(final AccessControlContext JavaDoc acc) {
265
266     java.lang.SecurityManager JavaDoc sm = System.getSecurityManager();
267     if (sm != null) {
268         sm.checkPermission(new AuthPermission JavaDoc("getSubject"));
269     }
270
271     if (acc == null) {
272         throw new NullPointerException JavaDoc(ResourcesMgr.getString
273         ("invalid null AccessControlContext provided"));
274     }
275
276     // return the Subject from the DomainCombiner of the provided context
277
return (Subject JavaDoc)AccessController.doPrivileged
278         (new java.security.PrivilegedAction JavaDoc() {
279         public Object JavaDoc run() {
280         DomainCombiner JavaDoc dc = acc.getDomainCombiner();
281         if (!(dc instanceof SubjectDomainCombiner JavaDoc))
282             return null;
283         SubjectDomainCombiner JavaDoc sdc = (SubjectDomainCombiner JavaDoc)dc;
284         return sdc.getSubject();
285         }
286     });
287     }
288
289     /**
290      * Perform work as a particular <code>Subject</code>.
291      *
292      * <p> This method first retrieves the current Thread's
293      * <code>AccessControlContext</code> via
294      * <code>AccessController.getContext</code>,
295      * and then instantiates a new <code>AccessControlContext</code>
296      * using the retrieved context along with a new
297      * <code>SubjectDomainCombiner</code> (constructed using
298      * the provided <code>Subject</code>).
299      * Finally, this method invokes <code>AccessController.doPrivileged</code>,
300      * passing it the provided <code>PrivilegedAction</code>,
301      * as well as the newly constructed <code>AccessControlContext</code>.
302      *
303      * <p>
304      *
305      * @param subject the <code>Subject</code> that the specified
306      * <code>action</code> will run as. This parameter
307      * may be <code>null</code>. <p>
308      *
309      * @param action the code to be run as the specified
310      * <code>Subject</code>. <p>
311      *
312      * @return the <code>Object</code> returned by the PrivilegedAction's
313      * <code>run</code> method.
314      *
315      * @exception NullPointerException if the <code>PrivilegedAction</code>
316      * is <code>null</code>. <p>
317      *
318      * @exception SecurityException if the caller does not have permission
319      * to invoke this method.
320      */

321     public static Object JavaDoc doAs(final Subject JavaDoc subject,
322             final java.security.PrivilegedAction JavaDoc action) {
323
324     java.lang.SecurityManager JavaDoc sm = System.getSecurityManager();
325     if (sm != null) {
326         sm.checkPermission(SecurityConstants.DO_AS_PERMISSION);
327     }
328     if (action == null)
329         throw new NullPointerException JavaDoc
330         (ResourcesMgr.getString("invalid null action provided"));
331
332     // set up the new Subject-based AccessControlContext
333
// for doPrivileged
334
final AccessControlContext JavaDoc currentAcc = AccessController.getContext();
335
336     // call doPrivileged and push this new context on the stack
337
return java.security.AccessController.doPrivileged
338                     (action,
339                     createContext(subject, currentAcc));
340     }
341
342     /**
343      * Perform work as a particular <code>Subject</code>.
344      *
345      * <p> This method first retrieves the current Thread's
346      * <code>AccessControlContext</code> via
347      * <code>AccessController.getContext</code>,
348      * and then instantiates a new <code>AccessControlContext</code>
349      * using the retrieved context along with a new
350      * <code>SubjectDomainCombiner</code> (constructed using
351      * the provided <code>Subject</code>).
352      * Finally, this method invokes <code>AccessController.doPrivileged</code>,
353      * passing it the provided <code>PrivilegedExceptionAction</code>,
354      * as well as the newly constructed <code>AccessControlContext</code>.
355      *
356      * <p>
357      *
358      * @param subject the <code>Subject</code> that the specified
359      * <code>action</code> will run as. This parameter
360      * may be <code>null</code>. <p>
361      *
362      * @param action the code to be run as the specified
363      * <code>Subject</code>. <p>
364      *
365      * @return the <code>Object</code> returned by the
366      * PrivilegedExceptionAction's <code>run</code> method.
367      *
368      * @exception PrivilegedActionException if the
369      * <code>PrivilegedExceptionAction.run</code>
370      * method throws a checked exception. <p>
371      *
372      * @exception NullPointerException if the specified
373      * <code>PrivilegedExceptionAction</code> is
374      * <code>null</code>. <p>
375      *
376      * @exception SecurityException if the caller does not have permission
377      * to invoke this method.
378      */

379     public static Object JavaDoc doAs(final Subject JavaDoc subject,
380             final java.security.PrivilegedExceptionAction JavaDoc action)
381             throws java.security.PrivilegedActionException JavaDoc {
382
383     java.lang.SecurityManager JavaDoc sm = System.getSecurityManager();
384     if (sm != null) {
385         sm.checkPermission(SecurityConstants.DO_AS_PERMISSION);
386     }
387
388     if (action == null)
389         throw new NullPointerException JavaDoc
390         (ResourcesMgr.getString("invalid null action provided"));
391
392     // set up the new Subject-based AccessControlContext for doPrivileged
393
final AccessControlContext JavaDoc currentAcc = AccessController.getContext();
394
395     // call doPrivileged and push this new context on the stack
396
return java.security.AccessController.doPrivileged
397                     (action,
398                     createContext(subject, currentAcc));
399     }
400
401     /**
402      * Perform privileged work as a particular <code>Subject</code>.
403      *
404      * <p> This method behaves exactly as <code>Subject.doAs</code>,
405      * except that instead of retrieving the current Thread's
406      * <code>AccessControlContext</code>, it uses the provided
407      * <code>AccessControlContext</code>. If the provided
408      * <code>AccessControlContext</code> is <code>null</code>,
409      * this method instantiates a new <code>AccessControlContext</code>
410      * with an empty collection of ProtectionDomains.
411      *
412      * <p>
413      *
414      * @param subject the <code>Subject</code> that the specified
415      * <code>action</code> will run as. This parameter
416      * may be <code>null</code>. <p>
417      *
418      * @param action the code to be run as the specified
419      * <code>Subject</code>. <p>
420      *
421      * @param acc the <code>AccessControlContext</code> to be tied to the
422      * specified <i>subject</i> and <i>action</i>. <p>
423      *
424      * @return the <code>Object</code> returned by the PrivilegedAction's
425      * <code>run</code> method.
426      *
427      * @exception NullPointerException if the <code>PrivilegedAction</code>
428      * is <code>null</code>. <p>
429      *
430      * @exception SecurityException if the caller does not have permission
431      * to invoke this method.
432      */

433     public static Object JavaDoc doAsPrivileged(final Subject JavaDoc subject,
434             final java.security.PrivilegedAction JavaDoc action,
435             final java.security.AccessControlContext JavaDoc acc) {
436
437     java.lang.SecurityManager JavaDoc sm = System.getSecurityManager();
438     if (sm != null) {
439         sm.checkPermission(SecurityConstants.DO_AS_PRIVILEGED_PERMISSION);
440     }
441
442     if (action == null)
443         throw new NullPointerException JavaDoc
444         (ResourcesMgr.getString("invalid null action provided"));
445
446     // set up the new Subject-based AccessControlContext
447
// for doPrivileged
448
final AccessControlContext JavaDoc callerAcc =
449         (acc == null ?
450         new AccessControlContext JavaDoc(new ProtectionDomain JavaDoc[0]) :
451         acc);
452
453     // call doPrivileged and push this new context on the stack
454
return java.security.AccessController.doPrivileged
455                     (action,
456                     createContext(subject, callerAcc));
457     }
458
459     /**
460      * Perform privileged work as a particular <code>Subject</code>.
461      *
462      * <p> This method behaves exactly as <code>Subject.doAs</code>,
463      * except that instead of retrieving the current Thread's
464      * <code>AccessControlContext</code>, it uses the provided
465      * <code>AccessControlContext</code>. If the provided
466      * <code>AccessControlContext</code> is <code>null</code>,
467      * this method instantiates a new <code>AccessControlContext</code>
468      * with an empty collection of ProtectionDomains.
469      *
470      * <p>
471      *
472      * @param subject the <code>Subject</code> that the specified
473      * <code>action</code> will run as. This parameter
474      * may be <code>null</code>. <p>
475      *
476      * @param action the code to be run as the specified
477      * <code>Subject</code>. <p>
478      *
479      * @param acc the <code>AccessControlContext</code> to be tied to the
480      * specified <i>subject</i> and <i>action</i>. <p>
481      *
482      * @return the <code>Object</code> returned by the
483      * PrivilegedExceptionAction's <code>run</code> method.
484      *
485      * @exception PrivilegedActionException if the
486      * <code>PrivilegedExceptionAction.run</code>
487      * method throws a checked exception. <p>
488      *
489      * @exception NullPointerException if the specified
490      * <code>PrivilegedExceptionAction</code> is
491      * <code>null</code>. <p>
492      *
493      * @exception SecurityException if the caller does not have permission
494      * to invoke this method.
495      */

496     public static Object JavaDoc doAsPrivileged(final Subject JavaDoc subject,
497             final java.security.PrivilegedExceptionAction JavaDoc action,
498             final java.security.AccessControlContext JavaDoc acc)
499             throws java.security.PrivilegedActionException JavaDoc {
500
501     java.lang.SecurityManager JavaDoc sm = System.getSecurityManager();
502     if (sm != null) {
503         sm.checkPermission(SecurityConstants.DO_AS_PRIVILEGED_PERMISSION);
504     }
505
506     if (action == null)
507         throw new NullPointerException JavaDoc
508         (ResourcesMgr.getString("invalid null action provided"));
509
510     // set up the new Subject-based AccessControlContext for doPrivileged
511
final AccessControlContext JavaDoc callerAcc =
512         (acc == null ?
513         new AccessControlContext JavaDoc(new ProtectionDomain JavaDoc[0]) :
514         acc);
515
516     // call doPrivileged and push this new context on the stack
517
return java.security.AccessController.doPrivileged
518                     (action,
519                     createContext(subject, callerAcc));
520     }
521
522     private static AccessControlContext JavaDoc createContext(final Subject JavaDoc subject,
523                     final AccessControlContext JavaDoc acc) {
524
525
526     return (AccessControlContext JavaDoc)
527         java.security.AccessController.doPrivileged
528         (new java.security.PrivilegedAction JavaDoc() {
529         public Object JavaDoc run() {
530         if (subject == null)
531                 return new AccessControlContext JavaDoc(acc, null);
532         else
533             return new AccessControlContext JavaDoc
534                     (acc,
535                     new SubjectDomainCombiner JavaDoc(subject));
536         }
537     });
538     }
539
540     /**
541      * Return the <code>Set</code> of Principals associated with this
542      * <code>Subject</code>. Each <code>Principal</code> represents
543      * an identity for this <code>Subject</code>.
544      *
545      * <p> The returned <code>Set</code> is backed by this Subject's
546      * internal <code>Principal</code> <code>Set</code>. Any modification
547      * to the returned <code>Set</code> affects the internal
548      * <code>Principal</code> <code>Set</code> as well.
549      *
550      * <p>
551      *
552      * @return The <code>Set</code> of Principals associated with this
553      * <code>Subject</code>.
554      */

555     public Set<Principal JavaDoc> getPrincipals() {
556
557     // always return an empty Set instead of null
558
// so LoginModules can add to the Set if necessary
559
return principals;
560     }
561
562     /**
563      * Return a <code>Set</code> of Principals associated with this
564      * <code>Subject</code> that are instances or subclasses of the specified
565      * <code>Class</code>.
566      *
567      * <p> The returned <code>Set</code> is not backed by this Subject's
568      * internal <code>Principal</code> <code>Set</code>. A new
569      * <code>Set</code> is created and returned for each method invocation.
570      * Modifications to the returned <code>Set</code>
571      * will not affect the internal <code>Principal</code> <code>Set</code>.
572      *
573      * <p>
574      *
575      * @param c the returned <code>Set</code> of Principals will all be
576      * instances of this class.
577      *
578      * @return a <code>Set</code> of Principals that are instances of the
579      * specified <code>Class</code>.
580      *
581      * @exception NullPointerException if the specified <code>Class</code>
582      * is <code>null</code>.
583      */

584     public <T extends Principal JavaDoc> Set<T> getPrincipals(Class JavaDoc<T> c) {
585
586     if (c == null)
587         throw new NullPointerException JavaDoc
588         (ResourcesMgr.getString("invalid null Class provided"));
589
590     // always return an empty Set instead of null
591
// so LoginModules can add to the Set if necessary
592
return new ClassSet(PRINCIPAL_SET, c);
593     }
594
595     /**
596      * Return the <code>Set</code> of public credentials held by this
597      * <code>Subject</code>.
598      *
599      * <p> The returned <code>Set</code> is backed by this Subject's
600      * internal public Credential <code>Set</code>. Any modification
601      * to the returned <code>Set</code> affects the internal public
602      * Credential <code>Set</code> as well.
603      *
604      * <p>
605      *
606      * @return A <code>Set</code> of public credentials held by this
607      * <code>Subject</code>.
608      */

609     public Set<Object JavaDoc> getPublicCredentials() {
610
611     // always return an empty Set instead of null
612
// so LoginModules can add to the Set if necessary
613
return pubCredentials;
614     }
615
616     /**
617      * Return the <code>Set</code> of private credentials held by this
618      * <code>Subject</code>.
619      *
620      * <p> The returned <code>Set</code> is backed by this Subject's
621      * internal private Credential <code>Set</code>. Any modification
622      * to the returned <code>Set</code> affects the internal private
623      * Credential <code>Set</code> as well.
624      *
625      * <p> A caller requires permissions to access the Credentials
626      * in the returned <code>Set</code>, or to modify the
627      * <code>Set</code> itself. A <code>SecurityException</code>
628      * is thrown if the caller does not have the proper permissions.
629      *
630      * <p> While iterating through the <code>Set</code>,
631      * a <code>SecurityException</code> is thrown
632      * if the caller does not have permission to access a
633      * particular Credential. The <code>Iterator</code>
634      * is nevertheless advanced to next element in the <code>Set</code>.
635      *
636      * <p>
637      *
638      * @return A <code>Set</code> of private credentials held by this
639      * <code>Subject</code>.
640      */

641     public Set<Object JavaDoc> getPrivateCredentials() {
642
643     // XXX
644
// we do not need a security check for
645
// AuthPermission(getPrivateCredentials)
646
// because we already restrict access to private credentials
647
// via the PrivateCredentialPermission. all the extra AuthPermission
648
// would do is protect the set operations themselves
649
// (like size()), which don't seem security-sensitive.
650

651     // always return an empty Set instead of null
652
// so LoginModules can add to the Set if necessary
653
return privCredentials;
654     }
655
656     /**
657      * Return a <code>Set</code> of public credentials associated with this
658      * <code>Subject</code> that are instances or subclasses of the specified
659      * <code>Class</code>.
660      *
661      * <p> The returned <code>Set</code> is not backed by this Subject's
662      * internal public Credential <code>Set</code>. A new
663      * <code>Set</code> is created and returned for each method invocation.
664      * Modifications to the returned <code>Set</code>
665      * will not affect the internal public Credential <code>Set</code>.
666      *
667      * <p>
668      *
669      * @param c the returned <code>Set</code> of public credentials will all be
670      * instances of this class.
671      *
672      * @return a <code>Set</code> of public credentials that are instances
673      * of the specified <code>Class</code>.
674      *
675      * @exception NullPointerException if the specified <code>Class</code>
676      * is <code>null</code>.
677      */

678     public <T> Set<T> getPublicCredentials(Class JavaDoc<T> c) {
679
680     if (c == null)
681         throw new NullPointerException JavaDoc
682         (ResourcesMgr.getString("invalid null Class provided"));
683
684     // always return an empty Set instead of null
685
// so LoginModules can add to the Set if necessary
686
return new ClassSet<T>(PUB_CREDENTIAL_SET, c);
687     }
688
689     /**
690      * Return a <code>Set</code> of private credentials associated with this
691      * <code>Subject</code> that are instances or subclasses of the specified
692      * <code>Class</code>.
693      *
694      * <p> The caller must have permission to access all of the
695      * requested Credentials, or a <code>SecurityException</code>
696      * will be thrown.
697      *
698      * <p> The returned <code>Set</code> is not backed by this Subject's
699      * internal private Credential <code>Set</code>. A new
700      * <code>Set</code> is created and returned for each method invocation.
701      * Modifications to the returned <code>Set</code>
702      * will not affect the internal private Credential <code>Set</code>.
703      *
704      * <p>
705      *
706      * @param c the returned <code>Set</code> of private credentials will all be
707      * instances of this class.
708      *
709      * @return a <code>Set</code> of private credentials that are instances
710      * of the specified <code>Class</code>.
711      *
712      * @exception NullPointerException if the specified <code>Class</code>
713      * is <code>null</code>.
714      */

715     public <T> Set<T> getPrivateCredentials(Class JavaDoc<T> c) {
716
717     // XXX
718
// we do not need a security check for
719
// AuthPermission(getPrivateCredentials)
720
// because we already restrict access to private credentials
721
// via the PrivateCredentialPermission. all the extra AuthPermission
722
// would do is protect the set operations themselves
723
// (like size()), which don't seem security-sensitive.
724

725     if (c == null)
726         throw new NullPointerException JavaDoc
727         (ResourcesMgr.getString("invalid null Class provided"));
728
729     // always return an empty Set instead of null
730
// so LoginModules can add to the Set if necessary
731
return new ClassSet<T>(PRIV_CREDENTIAL_SET, c);
732     }
733
734     /**
735      * Compares the specified Object with this <code>Subject</code>
736      * for equality. Returns true if the given object is also a Subject
737      * and the two <code>Subject</code> instances are equivalent.
738      * More formally, two <code>Subject</code> instances are
739      * equal if their <code>Principal</code> and <code>Credential</code>
740      * Sets are equal.
741      *
742      * <p>
743      *
744      * @param o Object to be compared for equality with this
745      * <code>Subject</code>.
746      *
747      * @return true if the specified Object is equal to this
748      * <code>Subject</code>.
749      *
750      * @exception SecurityException if the caller does not have permission
751      * to access the private credentials for this <code>Subject</code>,
752      * or if the caller does not have permission to access the
753      * private credentials for the provided <code>Subject</code>.
754      */

755     public boolean equals(Object JavaDoc o) {
756
757     if (o == null)
758         return false;
759
760     if (this == o)
761         return true;
762
763     if (o instanceof Subject JavaDoc) {
764
765         final Subject JavaDoc that = (Subject JavaDoc)o;
766
767         // check the principal and credential sets
768
Set thatPrincipals;
769         synchronized(that.principals) {
770         // avoid deadlock from dual locks
771
thatPrincipals = new HashSet(that.principals);
772         }
773         if (!principals.equals(thatPrincipals)) {
774         return false;
775         }
776
777         Set thatPubCredentials;
778         synchronized(that.pubCredentials) {
779         // avoid deadlock from dual locks
780
thatPubCredentials = new HashSet(that.pubCredentials);
781         }
782         if (!pubCredentials.equals(thatPubCredentials)) {
783         return false;
784         }
785
786         Set thatPrivCredentials;
787         synchronized(that.privCredentials) {
788         // avoid deadlock from dual locks
789
thatPrivCredentials = new HashSet(that.privCredentials);
790         }
791         if (!privCredentials.equals(thatPrivCredentials)) {
792         return false;
793         }
794         return true;
795     }
796     return false;
797     }
798
799     /**
800      * Return the String representation of this <code>Subject</code>.
801      *
802      * <p>
803      *
804      * @return the String representation of this <code>Subject</code>.
805      */

806     public String JavaDoc toString() {
807     return toString(true);
808     }
809
810     /**
811      * package private convenience method to print out the Subject
812      * without firing off a security check when trying to access
813      * the Private Credentials
814      */

815     String JavaDoc toString(boolean includePrivateCredentials) {
816
817     String JavaDoc s = new String JavaDoc(ResourcesMgr.getString("Subject:\n"));
818     String JavaDoc suffix = new String JavaDoc();
819
820     synchronized(principals) {
821         Iterator pI = principals.iterator();
822         while (pI.hasNext()) {
823         Principal JavaDoc p = (Principal JavaDoc)pI.next();
824         suffix = suffix + ResourcesMgr.getString("\tPrincipal: ") +
825             p.toString() + ResourcesMgr.getString("\n");
826         }
827     }
828
829     synchronized(pubCredentials) {
830         Iterator pI = pubCredentials.iterator();
831         while (pI.hasNext()) {
832         Object JavaDoc o = pI.next();
833         suffix = suffix +
834             ResourcesMgr.getString("\tPublic Credential: ") +
835             o.toString() + ResourcesMgr.getString("\n");
836         }
837     }
838            
839     if (includePrivateCredentials) {
840         synchronized(privCredentials) {
841         Iterator pI = privCredentials.iterator();
842         while (pI.hasNext()) {
843             try {
844             Object JavaDoc o = pI.next();
845             suffix += ResourcesMgr.getString
846                     ("\tPrivate Credential: ") +
847                     o.toString() +
848                     ResourcesMgr.getString("\n");
849             } catch (SecurityException JavaDoc se) {
850             suffix += ResourcesMgr.getString
851                 ("\tPrivate Credential inaccessible\n");
852             break;
853             }
854         }
855         }
856     }
857     return s + suffix;
858     }
859
860     /**
861      * Returns a hashcode for this <code>Subject</code>.
862      *
863      * <p>
864      *
865      * @return a hashcode for this <code>Subject</code>.
866      *
867      * @exception SecurityException if the caller does not have permission
868      * to access this Subject's private credentials.
869      */

870     public int hashCode() {
871
872     /**
873      * The hashcode is derived exclusive or-ing the
874      * hashcodes of this Subject's Principals and credentials.
875      *
876      * If a particular credential was destroyed
877      * (<code>credential.hashCode()</code> throws an
878      * <code>IllegalStateException</code>),
879      * the hashcode for that credential is derived via:
880      * <code>credential.getClass().toString().hashCode()</code>.
881      */

882
883     int hashCode = 0;
884
885     synchronized(principals) {
886         Iterator pIterator = principals.iterator();
887         while (pIterator.hasNext()) {
888         Principal JavaDoc p = (Principal JavaDoc)pIterator.next();
889         hashCode ^= p.hashCode();
890         }
891     }
892
893     synchronized(pubCredentials) {
894         Iterator pubCIterator = pubCredentials.iterator();
895         while (pubCIterator.hasNext()) {
896         hashCode ^= getCredHashCode(pubCIterator.next());
897         }
898     }
899     return hashCode;
900     }
901
902     /**
903      * get a credential's hashcode
904      */

905     private int getCredHashCode(Object JavaDoc o) {
906     try {
907         return o.hashCode();
908     } catch (IllegalStateException JavaDoc ise) {
909         return o.getClass().toString().hashCode();
910     }
911     }
912
913     /**
914      * Writes this object out to a stream (i.e., serializes it).
915      */

916     private void writeObject(java.io.ObjectOutputStream JavaDoc oos)
917         throws java.io.IOException JavaDoc {
918     synchronized(principals) {
919         oos.defaultWriteObject();
920     }
921     }
922
923     /**
924      * Reads this object from a stream (i.e., deserializes it)
925      */

926     private void readObject(java.io.ObjectInputStream JavaDoc s)
927         <