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         throws java.io.IOException JavaDoc, ClassNotFoundException JavaDoc {
928
929     s.defaultReadObject();
930
931     // The Credential <code>Set</code> is not serialized, but we do not
932
// want the default deserialization routine to set it to null.
933
this.pubCredentials = Collections.synchronizedSet
934                 (new SecureSet(this, PUB_CREDENTIAL_SET));
935     this.privCredentials = Collections.synchronizedSet
936                 (new SecureSet(this, PRIV_CREDENTIAL_SET));
937     }
938
939     /**
940      * Prevent modifications unless caller has permission.
941      *
942      * @serial include
943      */

944     private static class SecureSet
945     extends AbstractSet
946     implements java.io.Serializable JavaDoc {
947
948     private static final long serialVersionUID = 7911754171111800359L;
949
950     /**
951      * @serialField this$0 Subject The outer Subject instance.
952      * @serialField elements LinkedList The elements in this set.
953      */

954     private static final ObjectStreamField[] serialPersistentFields = {
955         new ObjectStreamField("this$0", Subject JavaDoc.class),
956         new ObjectStreamField("elements", LinkedList.class),
957         new ObjectStreamField("which", int.class)
958     };
959
960     Subject JavaDoc subject;
961     LinkedList elements;
962
963     /**
964      * @serial An integer identifying the type of objects contained
965      * in this set. If <code>which == 1</code>,
966      * this is a Principal set and all the elements are
967      * of type <code>java.security.Principal</code>.
968      * If <code>which == 2</code>, this is a public credential
969      * set and all the elements are of type <code>Object</code>.
970      * If <code>which == 3</code>, this is a private credential
971      * set and all the elements are of type <code>Object</code>.
972      */

973     private int which;
974
975     SecureSet(Subject JavaDoc subject, int which) {
976         this.subject = subject;
977         this.which = which;
978         this.elements = new LinkedList();
979     }
980
981     SecureSet(Subject JavaDoc subject, int which, Set set) {
982         this.subject = subject;
983         this.which = which;
984         this.elements = new LinkedList(set);
985     }
986
987     public int size() {
988         return elements.size();
989     }
990
991     public Iterator iterator() {
992         final LinkedList list = elements;
993         return new Iterator() {
994         ListIterator i = list.listIterator(0);
995
996         public boolean hasNext() {return i.hasNext();}
997         
998         public Object JavaDoc next() {
999             if (which != Subject.PRIV_CREDENTIAL_SET) {
1000            return i.next();
1001            }
1002
1003            SecurityManager JavaDoc sm = System.getSecurityManager();
1004            if (sm != null) {
1005            try {
1006                sm.checkPermission(new PrivateCredentialPermission JavaDoc
1007                (list.get(i.nextIndex()).getClass().getName(),
1008                subject.getPrincipals()));
1009            } catch (SecurityException JavaDoc se) {
1010                i.next();
1011                throw (se);
1012            }
1013            }
1014            return i.next();
1015        }
1016        
1017        public void remove() {
1018
1019            if (subject.isReadOnly()) {
1020            throw new IllegalStateException JavaDoc(ResourcesMgr.getString
1021                ("Subject is read-only"));
1022            }
1023
1024            java.lang.SecurityManager JavaDoc sm = System.getSecurityManager();
1025            if (sm != null) {
1026            switch (which) {
1027            case Subject.PRINCIPAL_SET:
1028                sm.checkPermission(new AuthPermission JavaDoc
1029                    ("modifyPrincipals"));
1030                break;
1031            case Subject.PUB_CREDENTIAL_SET:
1032                sm.checkPermission(new AuthPermission JavaDoc
1033                    ("modifyPublicCredentials"));
1034                break;
1035            default:
1036                sm.checkPermission(new AuthPermission JavaDoc
1037                    ("modifyPrivateCredentials"));
1038                break;
1039            }
1040            }
1041            i.remove();
1042        }
1043        };
1044    }
1045      
1046    public boolean add(Object JavaDoc o) {
1047    
1048        if (subject.isReadOnly()) {
1049        throw new IllegalStateException JavaDoc
1050            (ResourcesMgr.getString("Subject is read-only"));
1051        }
1052    
1053        java.lang.SecurityManager JavaDoc sm = System.getSecurityManager();
1054        if (sm != null) {
1055        switch (which) {
1056        case Subject.PRINCIPAL_SET:
1057            sm.checkPermission
1058            (new AuthPermission JavaDoc("modifyPrincipals"));
1059            break;
1060        case Subject.PUB_CREDENTIAL_SET:
1061            sm.checkPermission
1062            (new AuthPermission JavaDoc("modifyPublicCredentials"));
1063            break;
1064        default:
1065            sm.checkPermission
1066            (new AuthPermission JavaDoc("modifyPrivateCredentials"));
1067            break;
1068        }
1069        }
1070    
1071        switch (which) {
1072        case Subject.PRINCIPAL_SET:
1073        if (!(o instanceof Principal JavaDoc)) {
1074            throw new SecurityException JavaDoc(ResourcesMgr.getString
1075            ("attempting to add an object which is not an " +
1076            "instance of java.security.Principal to a " +
1077            "Subject's Principal Set"));
1078        }
1079        break;
1080        default:
1081        // ok to add Objects of any kind to credential sets
1082
break;
1083        }
1084    
1085        // check for duplicates
1086
if (!elements.contains(o))
1087        return elements.add(o);
1088        else
1089        return false;
1090    }
1091      
1092    public boolean remove(Object JavaDoc o) {
1093    
1094        final Iterator e = iterator();
1095        while (e.hasNext()) {
1096        Object JavaDoc next;
1097        if (which != Subject.PRIV_CREDENTIAL_SET) {
1098            next = e.next();
1099        } else {
1100            next = (Object JavaDoc)java.security.AccessController.doPrivileged
1101            (new java.security.PrivilegedAction JavaDoc() {
1102            public Object JavaDoc run() {
1103                return e.next();
1104            }
1105            });
1106        }
1107      
1108        if (next == null) {
1109            if (o == null) {
1110            e.remove();
1111            return true;
1112            }
1113        } else if (next.equals(o)) {
1114            e.remove();
1115            return true;
1116        }
1117        }
1118        return false;
1119    }
1120    
1121    public boolean contains(Object JavaDoc o) {
1122        final Iterator e = iterator();
1123        while (e.hasNext()) {
1124        Object JavaDoc next;
1125        if (which != Subject.PRIV_CREDENTIAL_SET) {
1126            next = e.next();
1127        } else {
1128
1129            // For private credentials:
1130
// If the caller does not have read permission for
1131
// for o.getClass(), we throw a SecurityException.
1132
// Otherwise we check the private cred set to see whether
1133
// it contains the Object
1134

1135            SecurityManager JavaDoc sm = System.getSecurityManager();
1136            if (sm != null) {
1137            sm.checkPermission(new PrivateCredentialPermission JavaDoc
1138                        (o.getClass().getName(),
1139                        subject.getPrincipals()));
1140            }
1141            next = (Object JavaDoc)java.security.AccessController.doPrivileged
1142            (new java.security.PrivilegedAction JavaDoc() {
1143            public Object JavaDoc run() {
1144                return e.next();
1145            }
1146            });
1147        }
1148      
1149        if (next == null) {
1150            if (o == null) {
1151            return true;
1152            }
1153        } else if (next.equals(o)) {
1154            return true;
1155        }
1156        }
1157        return false;
1158    }
1159      
1160    public boolean removeAll(Collection JavaDoc c) {
1161    
1162        boolean modified = false;
1163        final Iterator e = iterator();
1164        while (e.hasNext()) {
1165        Object JavaDoc next;
1166        if (which != Subject.PRIV_CREDENTIAL_SET) {
1167            next = e.next();
1168        } else {
1169            next = (Object JavaDoc)java.security.AccessController.doPrivileged
1170            (new java.security.PrivilegedAction JavaDoc() {
1171            public Object JavaDoc run() {
1172                return e.next();
1173            }
1174            });
1175        }
1176
1177        Iterator ce = c.iterator();
1178        while (ce.hasNext()) {
1179            Object JavaDoc o = ce.next();
1180            if (next == null) {
1181            if (o == null) {
1182                e.remove();
1183                modified = true;
1184                break;
1185            }
1186            } else if (next.equals(o)) {
1187            e.remove();
1188            modified = true;
1189            break;
1190            }
1191        }
1192        }
1193        return modified;
1194    }
1195
1196    public boolean retainAll(Collection JavaDoc c) {
1197
1198        boolean modified = false;
1199        boolean retain = false;
1200        final Iterator e = iterator();
1201        while (e.hasNext()) {
1202        retain = false;
1203        Object JavaDoc next;
1204        if (which != Subject.PRIV_CREDENTIAL_SET) {
1205            next = e.next();
1206        } else {
1207            next = (Object JavaDoc)java.security.AccessController.doPrivileged
1208            (new java.security.PrivilegedAction JavaDoc() {
1209            public Object JavaDoc run() {
1210                return e.next();
1211            }
1212            });
1213        }
1214
1215        Iterator ce = c.iterator();
1216        while (ce.hasNext()) {
1217            Object JavaDoc o = ce.next();
1218            if (next == null) {
1219            if (o == null) {
1220                retain = true;
1221                break;
1222            }
1223            } else if (next.equals(o)) {
1224            retain = true;
1225            break;
1226            }
1227        }
1228      
1229        if (!retain) {
1230            e.remove();
1231            retain = false;
1232            modified = true;
1233            }
1234        }
1235        return modified;
1236    }
1237      
1238    public void clear() {
1239        final Iterator e = iterator();
1240        while (e.hasNext()) {
1241        Object JavaDoc next;
1242        if (which != Subject.PRIV_CREDENTIAL_SET) {
1243            next = e.next();
1244        } else {
1245            next = (Object JavaDoc)java.security.AccessController.doPrivileged
1246            (new java.security.PrivilegedAction JavaDoc() {
1247            public Object JavaDoc run() {
1248                return e.next();
1249            }
1250            });
1251        }
1252        e.remove();
1253        }
1254    }
1255
1256    /**
1257     * Writes this object out to a stream (i.e., serializes it).
1258     *
1259     * <p>
1260     *
1261     * @serialData If this is a private credential set,
1262     * a security check is performed to ensure that
1263     * the caller has permission to access each credential
1264     * in the set. If the security check passes,
1265     * the set is serialized.
1266     */

1267    private synchronized void writeObject(java.io.ObjectOutputStream JavaDoc oos)
1268        throws java.io.IOException JavaDoc {
1269    
1270        if (which == Subject.PRIV_CREDENTIAL_SET) {
1271        // check permissions before serializing
1272
Iterator i = iterator();
1273        while (i.hasNext()) {
1274            i.next();
1275        }
1276        }
1277        ObjectOutputStream.PutField fields = oos.putFields();
1278        fields.put("this$0", subject);
1279        fields.put("elements", elements);
1280        fields.put("which", which);
1281        oos.writeFields();
1282    }
1283
1284    private void readObject(ObjectInputStream ois)
1285        throws IOException, ClassNotFoundException JavaDoc
1286    {
1287        ObjectInputStream.GetField fields = ois.readFields();
1288        subject = (Subject JavaDoc) fields.get("this$0", null);
1289        elements = (LinkedList) fields.get("elements", null);
1290        which = fields.get("which", 0);
1291    }
1292    }
1293
1294    /**
1295     * This class implements a <code>Set</code> which returns only
1296     * members that are an instance of a specified Class.
1297     */

1298    private class ClassSet<T> extends AbstractSet<T> {
1299
1300    private int which;
1301    private Class JavaDoc c;
1302    private Set<T> set;
1303
1304    ClassSet(int which, Class JavaDoc c) {
1305        this.which = which;
1306        this.c = c;
1307        set = new HashSet();
1308
1309        switch (which) {
1310        case Subject.PRINCIPAL_SET:
1311        synchronized(principals) { populateSet(); }
1312        break;
1313        case Subject.PUB_CREDENTIAL_SET:
1314        synchronized(pubCredentials) { populateSet(); }
1315        break;
1316        default:
1317        synchronized(privCredentials) { populateSet(); }
1318        break;
1319        }
1320    }
1321
1322    private void populateSet() {
1323        final Iterator iterator;
1324        switch(which) {
1325        case Subject.PRINCIPAL_SET:
1326        iterator = Subject.this.principals.iterator();
1327        break;
1328        case Subject.PUB_CREDENTIAL_SET:
1329        iterator = Subject.this.pubCredentials.iterator();
1330        break;
1331        default:
1332        iterator = Subject.this.privCredentials.iterator();
1333        break;
1334        }
1335
1336        // Check whether the caller has permisson to get
1337
// credentials of Class c
1338

1339        while (iterator.hasNext()) {
1340        Object JavaDoc next;
1341        if (which == Subject.PRIV_CREDENTIAL_SET) {
1342            next = (Object JavaDoc)java.security.AccessController.doPrivileged
1343            (new java.security.PrivilegedAction JavaDoc() {
1344            public Object JavaDoc run() {
1345                    return iterator.next();
1346            }
1347            });
1348        } else {
1349                next = iterator.next();
1350        }
1351        if (c.isAssignableFrom(next.getClass())) {
1352            if (which != Subject.PRIV_CREDENTIAL_SET) {
1353            set.add((T)next);
1354            } else {
1355            // Check permission for private creds
1356
SecurityManager JavaDoc sm = System.getSecurityManager();
1357            if (sm != null) {
1358                sm.checkPermission(new PrivateCredentialPermission JavaDoc
1359                        (next.getClass().getName(),
1360                        Subject.this.getPrincipals()));
1361            }
1362            set.add((T)next);
1363            }
1364        }
1365        }
1366    }
1367
1368    public int size() {
1369        return set.size();
1370    }
1371    
1372    public Iterator<T> iterator() {
1373        return set.iterator();
1374    }
1375    
1376    public boolean add(T o) {
1377        
1378        if (!o.getClass().isAssignableFrom(c)) {
1379        MessageFormat JavaDoc form = new MessageFormat JavaDoc(ResourcesMgr.getString
1380            ("attempting to add an object which is not an " +
1381            "instance of class"));
1382        Object JavaDoc[] source = {c.toString()};
1383        throw new SecurityException JavaDoc(form.format(source));
1384        }
1385        
1386        return set.add(o);
1387    }
1388    }
1389}
1390
Popular Tags