1 /*
2 * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package javax.security.auth;
27
28 import java.io.IOException;
29 import java.io.ObjectInputStream;
30 import java.io.ObjectOutputStream;
31 import java.io.ObjectStreamField;
32 import java.security.*;
33 import java.text.MessageFormat;
34 import java.util.*;
35 import java.util.concurrent.Callable;
36 import java.util.concurrent.CompletionException;
37
38 import sun.security.util.ResourcesMgr;
39
40 /**
41 * <p> A {@code Subject} represents a grouping of related information
42 * for a single entity, such as a person.
43 * Such information includes the Subject's identities as well as
44 * its security-related attributes
45 * (passwords and cryptographic keys, for example).
46 *
47 * <p> Subjects may potentially have multiple identities.
48 * Each identity is represented as a {@code Principal}
49 * within the {@code Subject}. Principals simply bind names to a
50 * {@code Subject}. For example, a {@code Subject} that happens
51 * to be a person, Alice, might have two Principals:
52 * one which binds "Alice Bar", the name on her driver license,
53 * to the {@code Subject}, and another which binds,
54 * "999-99-9999", the number on her student identification card,
55 * to the {@code Subject}. Both Principals refer to the same
56 * {@code Subject} even though each has a different name.
57 *
58 * <p> A {@code Subject} may also own security-related attributes,
59 * which are referred to as credentials.
60 * Sensitive credentials that require special protection, such as
61 * private cryptographic keys, are stored within a private credential
62 * {@code Set}. Credentials intended to be shared, such as
63 * public key certificates or Kerberos server tickets are stored
64 * within a public credential {@code Set}.
65 *
66 * <p> To retrieve all the Principals associated with a {@code Subject},
67 * invoke the {@code getPrincipals} method. To retrieve
68 * all the public or private credentials belonging to a {@code Subject},
69 * invoke the {@code getPublicCredentials} method or
70 * {@code getPrivateCredentials} method, respectively.
71 * To modify the returned {@code Set} of Principals and credentials,
72 * use the methods defined in the {@code Set} class.
73 * For example:
74 * <pre>
75 * Subject subject;
76 * Principal principal;
77 * Object credential;
78 *
79 * // add a Principal and credential to the Subject
80 * subject.getPrincipals().add(principal);
81 * subject.getPublicCredentials().add(credential);
82 * </pre>
83 *
84 * <p> This {@code Subject} class implements {@code Serializable}.
85 * While the Principals associated with the {@code Subject} are serialized,
86 * the credentials associated with the {@code Subject} are not.
87 * Note that the {@code java.security.Principal} class
88 * does not implement {@code Serializable}. Therefore, all concrete
89 * {@code Principal} implementations associated with Subjects
90 * must implement {@code Serializable}.
91 *
92 * <h2>Deprecated Methods and Replacements</h2>
93 *
94 * <p> The following methods in this class for user-based authorization
95 * that are dependent on Security Manager APIs are deprecated for removal:
96 * <ul>
97 * <li>{@link #getSubject(AccessControlContext)}
98 * <li>{@link #doAs(Subject, PrivilegedAction)}
99 * <li>{@link #doAs(Subject, PrivilegedExceptionAction)}
100 * <li>{@link #doAsPrivileged(Subject, PrivilegedAction, AccessControlContext)}
101 * <li>{@link #doAsPrivileged(Subject, PrivilegedExceptionAction, AccessControlContext)}
102 * </ul>
103 * Methods {@link #current()} and {@link #callAs(Subject, Callable)}
104 * are replacements for these methods, where {@code current} is equivalent to
105 * {@code getSubject(AccessController.getContext())} (as originally specified)
106 * and {@code callAs} is similar to {@code doAs} except that the
107 * input type and exceptions thrown are slightly different.
108 *
109 * <p> A {@code doAs} or {@code callAs} call
110 * binds a {@code Subject} object to the period of execution of an action,
111 * and the subject can be retrieved using the {@code current} method inside
112 * the action. This subject can be inherited by child threads if they are
113 * started and terminate within the execution of its parent thread using
114 * structured concurrency.
115 *
116 * @since 1.4
117 * @see java.security.Principal
118 * @see java.security.DomainCombiner
119 */
120 public final class Subject implements java.io.Serializable {
121
122 @java.io.Serial
123 private static final long serialVersionUID = -8308522755600156056L;
124
125 /**
126 * A {@code Set} that provides a view of all of this
127 * Subject's Principals
128 *
129 * @serial Each element in this set is a
130 * {@code java.security.Principal}.
131 * The set is a {@code Subject.SecureSet}.
132 */
133 @SuppressWarnings("serial") // Not statically typed as Serializable
134 Set<Principal> principals;
135
136 /**
137 * Sets that provide a view of all of this
138 * Subject's Credentials
139 */
140 transient Set<Object> pubCredentials;
141 transient Set<Object> privCredentials;
142
143 /**
144 * Whether this Subject is read-only
145 *
146 * @serial
147 */
148 private volatile boolean readOnly;
149
150 private static final int PRINCIPAL_SET = 1;
151 private static final int PUB_CREDENTIAL_SET = 2;
152 private static final int PRIV_CREDENTIAL_SET = 3;
153
154 private static final ProtectionDomain[] NULL_PD_ARRAY
155 = new ProtectionDomain[0];
156
157 /**
158 * Create an instance of a {@code Subject}
159 * with an empty {@code Set} of Principals and empty
160 * Sets of public and private credentials.
161 *
162 * <p> The newly constructed Sets check whether this {@code Subject}
163 * has been set read-only before permitting subsequent modifications.
164 * These Sets also prohibit null elements, and attempts to add, query,
165 * or remove a null element will result in a {@code NullPointerException}.
166 */
167 public Subject() {
168
169 this.principals = Collections.synchronizedSet
170 (new SecureSet<>(this, PRINCIPAL_SET));
171 this.pubCredentials = Collections.synchronizedSet
172 (new SecureSet<>(this, PUB_CREDENTIAL_SET));
173 this.privCredentials = Collections.synchronizedSet
174 (new SecureSet<>(this, PRIV_CREDENTIAL_SET));
175 }
176
177 /**
178 * Create an instance of a {@code Subject} with
179 * Principals and credentials.
180 *
181 * <p> The Principals and credentials from the specified Sets
182 * are copied into newly constructed Sets.
183 * These newly created Sets check whether this {@code Subject}
184 * has been set read-only before permitting subsequent modifications.
185 * These Sets also prohibit null elements, and attempts to add, query,
186 * or remove a null element will result in a {@code NullPointerException}.
187 *
188 * @param readOnly true if the {@code Subject} is to be read-only,
189 * and false otherwise.
190 *
191 * @param principals the {@code Set} of Principals
192 * to be associated with this {@code Subject}.
193 *
194 * @param pubCredentials the {@code Set} of public credentials
195 * to be associated with this {@code Subject}.
196 *
197 * @param privCredentials the {@code Set} of private credentials
198 * to be associated with this {@code Subject}.
199 *
200 * @throws NullPointerException if the specified
201 * {@code principals}, {@code pubCredentials},
202 * or {@code privCredentials} are {@code null},
203 * or a null value exists within any of these three
204 * Sets.
205 */
206 public Subject(boolean readOnly, Set<? extends Principal> principals,
207 Set<?> pubCredentials, Set<?> privCredentials) {
208 LinkedList<Principal> principalList
209 = collectionNullClean(principals);
210 LinkedList<Object> pubCredsList
211 = collectionNullClean(pubCredentials);
212 LinkedList<Object> privCredsList
213 = collectionNullClean(privCredentials);
214
215 this.principals = Collections.synchronizedSet(
216 new SecureSet<>(this, PRINCIPAL_SET, principalList));
217 this.pubCredentials = Collections.synchronizedSet(
218 new SecureSet<>(this, PUB_CREDENTIAL_SET, pubCredsList));
219 this.privCredentials = Collections.synchronizedSet(
220 new SecureSet<>(this, PRIV_CREDENTIAL_SET, privCredsList));
221 this.readOnly = readOnly;
222 }
223
224 /**
225 * Set this {@code Subject} to be read-only.
226 *
227 * <p> Modifications (additions and removals) to this Subject's
228 * {@code Principal} {@code Set} and
229 * credential Sets will be disallowed.
230 * The {@code destroy} operation on this Subject's credentials will
231 * still be permitted.
232 *
233 * <p> Subsequent attempts to modify the Subject's {@code Principal}
234 * and credential Sets will result in an
235 * {@code IllegalStateException} being thrown.
236 * Also, once a {@code Subject} is read-only,
237 * it can not be reset to being writable again.
238 */
239 public void setReadOnly() {
240 this.readOnly = true;
241 }
242
243 /**
244 * Query whether this {@code Subject} is read-only.
245 *
246 * @return true if this {@code Subject} is read-only, false otherwise.
247 */
248 public boolean isReadOnly() {
249 return this.readOnly;
250 }
251
252 /**
253 * Throws {@code UnsupportedOperationException}. A replacement API
254 * named {@link #current()} has been added which can be used to obtain
255 * the current subject.
256 *
257 * @param acc ignored
258 *
259 * @return n/a
260 *
261 * @throws UnsupportedOperationException always
262 *
263 * @deprecated This method used to get the subject associated with the
264 * provided {@link AccessControlContext}, which was only useful in
265 * conjunction with {@linkplain SecurityManager the Security Manager},
266 * which is no longer supported. This method has been changed to
267 * always throw {@code UnsupportedOperationException}. A replacement
268 * API named {@link #current()} has been added which can be used to
269 * obtain the current subject. There is no replacement for the
270 * Security Manager.
271 *
272 * @see #current()
273 */
274 @SuppressWarnings("removal")
275 @Deprecated(since="17", forRemoval=true)
276 public static Subject getSubject(final AccessControlContext acc) {
277 throw new UnsupportedOperationException("getSubject is not supported");
278 }
279
280 private static final ScopedValue<Subject> SCOPED_SUBJECT =
281 ScopedValue.newInstance();
282
283 /**
284 * Returns the current subject.
285 *
286 * <p> The current subject is installed by the {@link #callAs} method.
287 * When {@code callAs(subject, action)} is called, {@code action} is
288 * executed with {@code subject} as its current subject which can be
289 * retrieved by this method. After {@code action} is finished, the current
290 * subject is reset to its previous value. The current
291 * subject is {@code null} before the first call of {@code callAs()}.
292 *
293 * <p> This method returns the
294 * {@code Subject} bound to the period of the execution of the current
295 * thread.
296 *
297 * @return the current subject, or {@code null} if a current subject is
298 * not installed or the current subject is set to {@code null}.
299 * @see #callAs(Subject, Callable)
300 * @since 18
301 */
302 public static Subject current() {
303 return SCOPED_SUBJECT.isBound() ? SCOPED_SUBJECT.get() : null;
304 }
305
306 /**
307 * Executes a {@code Callable} with {@code subject} as the
308 * current subject.
309 *
310 * <p> This method launches {@code action} and binds {@code subject} to the
311 * period of its execution.
312 *
313 * @param subject the {@code Subject} that the specified {@code action}
314 * will run as. This parameter may be {@code null}.
315 * @param action the code to be run with {@code subject} as its current
316 * subject. Must not be {@code null}.
317 * @param <T> the type of value returned by the {@code call} method
318 * of {@code action}
319 * @return the value returned by the {@code call} method of {@code action}
320 * @throws NullPointerException if {@code action} is {@code null}
321 * @throws CompletionException if {@code action.call()} throws an exception.
322 * The cause of the {@code CompletionException} is set to the exception
323 * thrown by {@code action.call()}.
324 * @see #current()
325 * @since 18
326 */
327 public static <T> T callAs(final Subject subject,
328 final Callable<T> action) throws CompletionException {
329 Objects.requireNonNull(action);
330 try {
331 return ScopedValue.where(SCOPED_SUBJECT, subject).call(action::call);
332 } catch (Exception e) {
333 throw new CompletionException(e);
334 }
335 }
336
337 /**
338 * Perform work as a particular {@code Subject}.
339 *
340 * <p> This method launches {@code action} and binds {@code subject} to the
341 * period of its execution.
342 *
343 * @param subject the {@code Subject} that the specified
344 * {@code action} will run as. This parameter
345 * may be {@code null}.
346 *
347 * @param <T> the type of the value returned by the PrivilegedAction's
348 * {@code run} method.
349 *
350 * @param action the code to be run as the specified
351 * {@code Subject}.
352 *
353 * @return the value returned by the PrivilegedAction's
354 * {@code run} method.
355 *
356 * @throws NullPointerException if the {@code PrivilegedAction}
357 * is {@code null}.
358 *
359 * @deprecated This method originally performed the specified
360 * {@code PrivilegedAction} with privileges enabled. Running the
361 * action with privileges enabled was only useful in conjunction
362 * with {@linkplain SecurityManager the Security Manager}, which is
363 * no longer supported. This method has been changed to launch the
364 * action as is and bind the subject to the period of its execution.
365 * A replacement API named {@link #callAs} has been added which can
366 * be used to perform the same work. There is no replacement for the
367 * Security Manager.
368 *
369 * @see #callAs(Subject, Callable)
370 */
371 @Deprecated(since="18", forRemoval=true)
372 public static <T> T doAs(final Subject subject,
373 final java.security.PrivilegedAction<T> action) {
374
375 Objects.requireNonNull(action,
376 ResourcesMgr.getString("invalid.null.action.provided"));
377
378 try {
379 return callAs(subject, action::run);
380 } catch (CompletionException ce) {
381 var cause = ce.getCause();
382 if (cause instanceof RuntimeException re) {
383 throw re;
384 } else if (cause instanceof Error er) {
385 throw er;
386 } else {
387 throw new AssertionError(ce);
388 }
389 }
390 }
391
392 /**
393 * Perform work as a particular {@code Subject}.
394 *
395 * <p> This method launches {@code action} and binds {@code subject} to the
396 * period of its execution.
397 *
398 * @param subject the {@code Subject} that the specified
399 * {@code action} will run as. This parameter
400 * may be {@code null}.
401 *
402 * @param <T> the type of the value returned by the
403 * PrivilegedExceptionAction's {@code run} method.
404 *
405 * @param action the code to be run as the specified
406 * {@code Subject}.
407 *
408 * @return the value returned by the
409 * PrivilegedExceptionAction's {@code run} method.
410 *
411 * @throws PrivilegedActionException if the
412 * {@code PrivilegedExceptionAction.run}
413 * method throws a checked exception.
414 *
415 * @throws NullPointerException if the specified
416 * {@code PrivilegedExceptionAction} is
417 * {@code null}.
418 *
419 * @deprecated This method originally performed the specified
420 * {@code PrivilegedExceptionAction} with privileges enabled.
421 * Running the action with privileges enabled was only useful in
422 * conjunction with {@linkplain SecurityManager the Security Manager},
423 * which is no longer supported. This method has been changed to
424 * launch the action as is and bind the subject to the period of its
425 * execution. A replacement API named {@link #callAs} has been added
426 * which can be used to perform the same work. There is no
427 * replacement for the Security Manager.
428 *
429 * @see #callAs(Subject, Callable)
430 */
431 @Deprecated(since="18", forRemoval=true)
432 public static <T> T doAs(final Subject subject,
433 final java.security.PrivilegedExceptionAction<T> action)
434 throws java.security.PrivilegedActionException {
435
436 Objects.requireNonNull(action,
437 ResourcesMgr.getString("invalid.null.action.provided"));
438
439 try {
440 return callAs(subject, action::run);
441 } catch (CompletionException ce) {
442 var cause = ce.getCause();
443 if (cause instanceof RuntimeException re) {
444 throw re;
445 } else if (cause instanceof Error er) {
446 throw er;
447 } else if (cause instanceof Exception e) {
448 throw new PrivilegedActionException(e);
449 } else {
450 throw new PrivilegedActionException(ce);
451 }
452 }
453 }
454
455 /**
456 * Perform work as a particular {@code Subject}.
457 *
458 * <p> This method launches {@code action} and binds {@code subject} to
459 * the period of its execution.
460 *
461 * @param subject the {@code Subject} that the specified
462 * {@code action} will run as. This parameter
463 * may be {@code null}.
464 *
465 * @param <T> the type of the value returned by the PrivilegedAction's
466 * {@code run} method.
467 *
468 * @param action the code to be run as the specified
469 * {@code Subject}.
470 *
471 * @param acc ignored
472 *
473 * @return the value returned by the PrivilegedAction's
474 * {@code run} method.
475 *
476 * @throws NullPointerException if the {@code PrivilegedAction}
477 * is {@code null}.
478 *
479 * @deprecated This method originally performed the specified
480 * {@code PrivilegedAction} with privileges enabled and restricted
481 * by the specified {@code AccessControlContext}. Running the
482 * action with privileges enabled was only useful in conjunction
483 * with {@linkplain SecurityManager the Security Manager}, which is
484 * no longer supported. This method has been changed to ignore the
485 * {@code AccessControlContext} and launch the action as is and bind
486 * the subject to the period of its execution. A replacement API
487 * named {@link #callAs} has been added which can be used to perform
488 * the same work. There is no replacement for the Security Manager.
489 *
490 * @see #callAs(Subject, Callable)
491 */
492 @SuppressWarnings("removal")
493 @Deprecated(since="17", forRemoval=true)
494 public static <T> T doAsPrivileged(final Subject subject,
495 final java.security.PrivilegedAction<T> action,
496 final java.security.AccessControlContext acc) {
497
498 Objects.requireNonNull(action,
499 ResourcesMgr.getString("invalid.null.action.provided"));
500
501 try {
502 return callAs(subject, action::run);
503 } catch (CompletionException ce) {
504 var cause = ce.getCause();
505 if (cause instanceof RuntimeException re) {
506 throw re;
507 } else if (cause instanceof Error er) {
508 throw er;
509 } else {
510 throw new AssertionError(ce);
511 }
512 }
513 }
514
515 /**
516 * Perform work as a particular {@code Subject}.
517 *
518 * <p> This method launches {@code action} and binds {@code subject} to
519 * the period of its execution.
520 *
521 * @param subject the {@code Subject} that the specified
522 * {@code action} will run as. This parameter
523 * may be {@code null}.
524 *
525 * @param <T> the type of the value returned by the
526 * PrivilegedExceptionAction's {@code run} method.
527 *
528 * @param action the code to be run as the specified
529 * {@code Subject}.
530 *
531 * @param acc ignored
532 *
533 * @return the value returned by the
534 * PrivilegedExceptionAction's {@code run} method.
535 *
536 * @throws PrivilegedActionException if the
537 * {@code PrivilegedExceptionAction.run}
538 * method throws a checked exception.
539 *
540 * @throws NullPointerException if the specified
541 * {@code PrivilegedExceptionAction} is
542 * {@code null}.
543 *
544 * @deprecated This method originally performed the specified
545 * {@code PrivilegedExceptionAction} with privileges enabled and
546 * restricted by the specified {@code AccessControlContext}. Running
547 * the action with privileges enabled was only useful in conjunction
548 * with {@linkplain SecurityManager the Security Manager}, which is
549 * no longer supported. This method has been changed to ignore the
550 * {@code AccessControlContext} and launch the action as is and bind
551 * the subject to the period of its execution. A replacement API
552 * named {@link #callAs} has been added which can be used to perform
553 * the same work. There is no replacement for the Security Manager.
554 *
555 * @see #callAs(Subject, Callable)
556 */
557 @SuppressWarnings("removal")
558 @Deprecated(since="17", forRemoval=true)
559 public static <T> T doAsPrivileged(final Subject subject,
560 final java.security.PrivilegedExceptionAction<T> action,
561 final java.security.AccessControlContext acc)
562 throws java.security.PrivilegedActionException {
563
564 Objects.requireNonNull(action,
565 ResourcesMgr.getString("invalid.null.action.provided"));
566
567 try {
568 return callAs(subject, action::run);
569 } catch (CompletionException ce) {
570 var cause = ce.getCause();
571 if (cause instanceof RuntimeException re) {
572 throw re;
573 } else if (cause instanceof Error er) {
574 throw er;
575 } else if (cause instanceof Exception e) {
576 throw new PrivilegedActionException(e);
577 } else {
578 throw new PrivilegedActionException(ce);
579 }
580 }
581 }
582
583 /**
584 * Return the {@code Set} of Principals associated with this
585 * {@code Subject}. Each {@code Principal} represents
586 * an identity for this {@code Subject}.
587 *
588 * <p> The returned {@code Set} is backed by this Subject's
589 * internal {@code Principal} {@code Set}. Any modification
590 * to the returned {@code Set} affects the internal
591 * {@code Principal} {@code Set} as well.
592 *
593 * @return the {@code Set} of Principals associated with this
594 * {@code Subject}.
595 */
596 public Set<Principal> getPrincipals() {
597
598 // always return an empty Set instead of null
599 // so LoginModules can add to the Set if necessary
600 return principals;
601 }
602
603 /**
604 * Return a {@code Set} of Principals associated with this
605 * {@code Subject} that are instances or subclasses of the specified
606 * {@code Class}.
607 *
608 * <p> The returned {@code Set} is not backed by this Subject's
609 * internal {@code Principal} {@code Set}. A new
610 * {@code Set} is created and returned for each method invocation.
611 * Modifications to the returned {@code Set}
612 * will not affect the internal {@code Principal} {@code Set}.
613 *
614 * @param <T> the type of the class modeled by {@code c}
615 *
616 * @param c the returned {@code Set} of Principals will all be
617 * instances of this class.
618 *
619 * @return a {@code Set} of Principals that are instances of the
620 * specified {@code Class}.
621 *
622 * @throws NullPointerException if the specified {@code Class}
623 * is {@code null}.
624 */
625 public <T extends Principal> Set<T> getPrincipals(Class<T> c) {
626
627 Objects.requireNonNull(c,
628 ResourcesMgr.getString("invalid.null.Class.provided"));
629
630 // always return an empty Set instead of null
631 // so LoginModules can add to the Set if necessary
632 return new ClassSet<>(PRINCIPAL_SET, c);
633 }
634
635 /**
636 * Return the {@code Set} of public credentials held by this
637 * {@code Subject}.
638 *
639 * <p> The returned {@code Set} is backed by this Subject's
640 * internal public Credential {@code Set}. Any modification
641 * to the returned {@code Set} affects the internal public
642 * Credential {@code Set} as well.
643 *
644 * @return a {@code Set} of public credentials held by this
645 * {@code Subject}.
646 */
647 public Set<Object> getPublicCredentials() {
648
649 // always return an empty Set instead of null
650 // so LoginModules can add to the Set if necessary
651 return pubCredentials;
652 }
653
654 /**
655 * Return the {@code Set} of private credentials held by this
656 * {@code Subject}.
657 *
658 * <p> The returned {@code Set} is backed by this Subject's
659 * internal private Credential {@code Set}. Any modification
660 * to the returned {@code Set} affects the internal private
661 * Credential {@code Set} as well.
662 *
663 * @return a {@code Set} of private credentials held by this
664 * {@code Subject}.
665 */
666 public Set<Object> getPrivateCredentials() {
667
668 // always return an empty Set instead of null
669 // so LoginModules can add to the Set if necessary
670 return privCredentials;
671 }
672
673 /**
674 * Return a {@code Set} of public credentials associated with this
675 * {@code Subject} that are instances or subclasses of the specified
676 * {@code Class}.
677 *
678 * <p> The returned {@code Set} is not backed by this Subject's
679 * internal public Credential {@code Set}. A new
680 * {@code Set} is created and returned for each method invocation.
681 * Modifications to the returned {@code Set}
682 * will not affect the internal public Credential {@code Set}.
683 *
684 * @param <T> the type of the class modeled by {@code c}
685 *
686 * @param c the returned {@code Set} of public credentials will all be
687 * instances of this class.
688 *
689 * @return a {@code Set} of public credentials that are instances
690 * of the specified {@code Class}.
691 *
692 * @throws NullPointerException if the specified {@code Class}
693 * is {@code null}.
694 */
695 public <T> Set<T> getPublicCredentials(Class<T> c) {
696
697 Objects.requireNonNull(c,
698 ResourcesMgr.getString("invalid.null.Class.provided"));
699
700 // always return an empty Set instead of null
701 // so LoginModules can add to the Set if necessary
702 return new ClassSet<>(PUB_CREDENTIAL_SET, c);
703 }
704
705 /**
706 * Return a {@code Set} of private credentials associated with this
707 * {@code Subject} that are instances or subclasses of the specified
708 * {@code Class}.
709 *
710 * <p> The returned {@code Set} is not backed by this Subject's
711 * internal private Credential {@code Set}. A new
712 * {@code Set} is created and returned for each method invocation.
713 * Modifications to the returned {@code Set}
714 * will not affect the internal private Credential {@code Set}.
715 *
716 * @param <T> the type of the class modeled by {@code c}
717 *
718 * @param c the returned {@code Set} of private credentials will all be
719 * instances of this class.
720 *
721 * @return a {@code Set} of private credentials that are instances
722 * of the specified {@code Class}.
723 *
724 * @throws NullPointerException if the specified {@code Class}
725 * is {@code null}.
726 */
727 public <T> Set<T> getPrivateCredentials(Class<T> c) {
728
729 Objects.requireNonNull(c,
730 ResourcesMgr.getString("invalid.null.Class.provided"));
731
732 // always return an empty Set instead of null
733 // so LoginModules can add to the Set if necessary
734 return new ClassSet<>(PRIV_CREDENTIAL_SET, c);
735 }
736
737 /**
738 * Compares the specified Object with this {@code Subject}
739 * for equality. Returns true if the given object is also a Subject
740 * and the two {@code Subject} instances are equivalent.
741 * More formally, two {@code Subject} instances are
742 * equal if their {@code Principal} and {@code Credential}
743 * Sets are equal.
744 *
745 * @param o Object to be compared for equality with this
746 * {@code Subject}.
747 *
748 * @return true if the specified Object is equal to this
749 * {@code Subject}.
750 */
751 @Override
752 public boolean equals(Object o) {
753
754 if (this == o) {
755 return true;
756 }
757
758 if (o instanceof final Subject that) {
759
760 // check the principal and credential sets
761 Set<Principal> thatPrincipals;
762 synchronized(that.principals) {
763 // avoid deadlock from dual locks
764 thatPrincipals = new HashSet<>(that.principals);
765 }
766 if (!principals.equals(thatPrincipals)) {
767 return false;
768 }
769
770 Set<Object> thatPubCredentials;
771 synchronized(that.pubCredentials) {
772 // avoid deadlock from dual locks
773 thatPubCredentials = new HashSet<>(that.pubCredentials);
774 }
775 if (!pubCredentials.equals(thatPubCredentials)) {
776 return false;
777 }
778
779 Set<Object> thatPrivCredentials;
780 synchronized(that.privCredentials) {
781 // avoid deadlock from dual locks
782 thatPrivCredentials = new HashSet<>(that.privCredentials);
783 }
784 return privCredentials.equals(thatPrivCredentials);
785 }
786 return false;
787 }
788
789 /**
790 * Return the String representation of this {@code Subject}.
791 *
792 * @return the String representation of this {@code Subject}.
793 */
794 @Override
795 public String toString() {
796
797 String s = ResourcesMgr.getString("Subject.");
798 String suffix = "";
799
800 synchronized(principals) {
801 for (Principal p : principals) {
802 suffix = suffix + ResourcesMgr.getString(".Principal.") +
803 p.toString() + ResourcesMgr.getString("NEWLINE");
804 }
805 }
806
807 synchronized(pubCredentials) {
808 for (Object o : pubCredentials) {
809 suffix = suffix +
810 ResourcesMgr.getString(".Public.Credential.") +
811 o.toString() + ResourcesMgr.getString("NEWLINE");
812 }
813 }
814
815 synchronized(privCredentials) {
816 Iterator<Object> pI = privCredentials.iterator();
817 while (pI.hasNext()) {
818 try {
819 Object o = pI.next();
820 suffix += ResourcesMgr.getString
821 (".Private.Credential.") +
822 o.toString() +
823 ResourcesMgr.getString("NEWLINE");
824 } catch (SecurityException se) {
825 suffix += ResourcesMgr.getString
826 (".Private.Credential.inaccessible.");
827 break;
828 }
829 }
830 }
831 return s + suffix;
832 }
833
834 /**
835 * {@return a hashcode for this {@code Subject}}
836 */
837 @Override
838 public int hashCode() {
839
840 /*
841 * The hashcode is derived exclusive or-ing the
842 * hashcodes of this Subject's Principals and credentials.
843 *
844 * If a particular credential was destroyed
845 * ({@code credential.hashCode()} throws an
846 * {@code IllegalStateException}),
847 * the hashcode for that credential is derived via:
848 * {@code credential.getClass().toString().hashCode()}.
849 */
850
851 int hashCode = 0;
852
853 synchronized(principals) {
854 for (Principal p : principals) {
855 hashCode ^= p.hashCode();
856 }
857 }
858
859 synchronized(pubCredentials) {
860 for (Object pubCredential : pubCredentials) {
861 hashCode ^= getCredHashCode(pubCredential);
862 }
863 }
864 return hashCode;
865 }
866
867 /**
868 * get a credential's hashcode
869 */
870 private int getCredHashCode(Object o) {
871 try {
872 return o.hashCode();
873 } catch (IllegalStateException ise) {
874 return o.getClass().toString().hashCode();
875 }
876 }
877
878 /**
879 * Writes this object out to a stream (i.e., serializes it).
880 *
881 * @param oos the {@code ObjectOutputStream} to which data is written
882 * @throws IOException if an I/O error occurs
883 */
884 @java.io.Serial
885 private void writeObject(java.io.ObjectOutputStream oos)
886 throws java.io.IOException {
887 synchronized(principals) {
888 oos.defaultWriteObject();
889 }
890 }
891
892 /**
893 * Reads this object from a stream (i.e., deserializes it)
894 *
895 * @param s the {@code ObjectInputStream} from which data is read
896 * @throws IOException if an I/O error occurs
897 * @throws ClassNotFoundException if a serialized class cannot be loaded
898 */
899 @SuppressWarnings("unchecked")
900 @java.io.Serial
901 private void readObject(java.io.ObjectInputStream s)
902 throws java.io.IOException, ClassNotFoundException {
903
904 ObjectInputStream.GetField gf = s.readFields();
905
906 readOnly = gf.get("readOnly", false);
907
908 Set<Principal> inputPrincs = (Set<Principal>)gf.get("principals", null);
909
910 Objects.requireNonNull(inputPrincs,
911 ResourcesMgr.getString("invalid.null.input.s."));
912
913 // Rewrap the principals into a SecureSet
914 try {
915 LinkedList<Principal> principalList = collectionNullClean(inputPrincs);
916 principals = Collections.synchronizedSet(new SecureSet<>
917 (this, PRINCIPAL_SET, principalList));
918 } catch (NullPointerException npe) {
919 // Sometimes people deserialize the principals set only.
920 // Subject is not accessible, so just don't fail.
921 principals = Collections.synchronizedSet
922 (new SecureSet<>(this, PRINCIPAL_SET));
923 }
924
925 // The Credential {@code Set} is not serialized, but we do not
926 // want the default deserialization routine to set it to null.
927 this.pubCredentials = Collections.synchronizedSet
928 (new SecureSet<>(this, PUB_CREDENTIAL_SET));
929 this.privCredentials = Collections.synchronizedSet
930 (new SecureSet<>(this, PRIV_CREDENTIAL_SET));
931 }
932
933 /**
934 * Tests for null-clean collections (both non-null reference and
935 * no null elements)
936 *
937 * @param coll A {@code Collection} to be tested for null references
938 *
939 * @throws NullPointerException if the specified collection is either
940 * {@code null} or contains a {@code null} element
941 */
942 private static <E> LinkedList<E> collectionNullClean(
943 Collection<? extends E> coll) {
944
945 Objects.requireNonNull(coll,
946 ResourcesMgr.getString("invalid.null.input.s."));
947
948 LinkedList<E> output = new LinkedList<>();
949 for (E e : coll) {
950 output.add(Objects.requireNonNull(e,
951 ResourcesMgr.getString("invalid.null.input.s.")));
952 }
953 return output;
954 }
955
956 /**
957 * Prevent modifications unless caller has permission.
958 *
959 * @serial include
960 */
961 private static class SecureSet<E>
962 implements Set<E>, java.io.Serializable {
963
964 @java.io.Serial
965 private static final long serialVersionUID = 7911754171111800359L;
966
967 /**
968 * @serialField this$0 Subject The outer Subject instance.
969 * @serialField elements LinkedList The elements in this set.
970 */
971 @java.io.Serial
972 private static final ObjectStreamField[] serialPersistentFields = {
973 new ObjectStreamField("this$0", Subject.class),
974 new ObjectStreamField("elements", LinkedList.class),
975 new ObjectStreamField("which", int.class)
976 };
977
978 Subject subject;
979 LinkedList<E> elements;
980
981 /**
982 * @serial An integer identifying the type of objects contained
983 * in this set. If {@code which == 1},
984 * this is a Principal set and all the elements are
985 * of type {@code java.security.Principal}.
986 * If {@code which == 2}, this is a public credential
987 * set and all the elements are of type {@code Object}.
988 * If {@code which == 3}, this is a private credential
989 * set and all the elements are of type {@code Object}.
990 */
991 private int which;
992
993 SecureSet(Subject subject, int which) {
994 this.subject = subject;
995 this.which = which;
996 this.elements = new LinkedList<>();
997 }
998
999 SecureSet(Subject subject, int which, LinkedList<E> list) {
1000 this.subject = subject;
1001 this.which = which;
1002 this.elements = list;
1003 }
1004
1005 public int size() {
1006 return elements.size();
1007 }
1008
1009 public Iterator<E> iterator() {
1010 final LinkedList<E> list = elements;
1011 return new Iterator<>() {
1012 final ListIterator<E> i = list.listIterator(0);
1013
1014 public boolean hasNext() {
1015 return i.hasNext();
1016 }
1017
1018 public E next() {
1019 return i.next();
1020 }
1021
1022 public void remove() {
1023
1024 if (subject.isReadOnly()) {
1025 throw new IllegalStateException(ResourcesMgr.getString
1026 ("Subject.is.read.only"));
1027 }
1028
1029 i.remove();
1030 }
1031 };
1032 }
1033
1034 public boolean add(E o) {
1035
1036 Objects.requireNonNull(o,
1037 ResourcesMgr.getString("invalid.null.input.s."));
1038
1039 if (subject.isReadOnly()) {
1040 throw new IllegalStateException
1041 (ResourcesMgr.getString("Subject.is.read.only"));
1042 }
1043
1044 switch (which) {
1045 case Subject.PRINCIPAL_SET:
1046 if (!(o instanceof Principal)) {
1047 throw new SecurityException(ResourcesMgr.getString
1048 ("attempting.to.add.an.object.which.is.not.an.instance.of.java.security.Principal.to.a.Subject.s.Principal.Set"));
1049 }
1050 break;
1051 default:
1052 // ok to add Objects of any kind to credential sets
1053 break;
1054 }
1055
1056 // check for duplicates
1057 if (!elements.contains(o))
1058 return elements.add(o);
1059 else {
1060 return false;
1061 }
1062 }
1063
1064 public boolean remove(Object o) {
1065
1066 Objects.requireNonNull(o,
1067 ResourcesMgr.getString("invalid.null.input.s."));
1068
1069 final Iterator<E> e = iterator();
1070 while (e.hasNext()) {
1071 E next = e.next();
1072
1073 if (next.equals(o)) {
1074 e.remove();
1075 return true;
1076 }
1077 }
1078 return false;
1079 }
1080
1081 public boolean contains(Object o) {
1082
1083 Objects.requireNonNull(o,
1084 ResourcesMgr.getString("invalid.null.input.s."));
1085
1086 final Iterator<E> e = iterator();
1087 while (e.hasNext()) {
1088 E next = e.next();
1089
1090 if (next.equals(o)) {
1091 return true;
1092 }
1093 }
1094 return false;
1095 }
1096
1097 public boolean addAll(Collection<? extends E> c) {
1098 boolean result = false;
1099
1100 c = collectionNullClean(c);
1101
1102 for (E item : c) {
1103 result |= this.add(item);
1104 }
1105
1106 return result;
1107 }
1108
1109 public boolean removeAll(Collection<?> c) {
1110 c = collectionNullClean(c);
1111
1112 boolean modified = false;
1113 final Iterator<E> e = iterator();
1114 while (e.hasNext()) {
1115 E next = e.next();
1116
1117 for (Object o : c) {
1118 if (next.equals(o)) {
1119 e.remove();
1120 modified = true;
1121 break;
1122 }
1123 }
1124 }
1125 return modified;
1126 }
1127
1128 public boolean containsAll(Collection<?> c) {
1129 c = collectionNullClean(c);
1130
1131 for (Object item : c) {
1132 if (!this.contains(item)) {
1133 return false;
1134 }
1135 }
1136
1137 return true;
1138 }
1139
1140 public boolean retainAll(Collection<?> c) {
1141 c = collectionNullClean(c);
1142
1143 boolean modified = false;
1144 final Iterator<E> e = iterator();
1145 while (e.hasNext()) {
1146 E next = e.next();
1147
1148 if (c.contains(next) == false) {
1149 e.remove();
1150 modified = true;
1151 }
1152 }
1153
1154 return modified;
1155 }
1156
1157 public void clear() {
1158 final Iterator<E> e = iterator();
1159 while (e.hasNext()) {
1160 E next = e.next();
1161 e.remove();
1162 }
1163 }
1164
1165 public boolean isEmpty() {
1166 return elements.isEmpty();
1167 }
1168
1169 public Object[] toArray() {
1170 return elements.toArray();
1171 }
1172
1173 public <T> T[] toArray(T[] a) {
1174 return elements.toArray(a);
1175 }
1176
1177 @Override
1178 public boolean equals(Object o) {
1179 if (o == this) {
1180 return true;
1181 }
1182
1183 if (!(o instanceof Set)) {
1184 return false;
1185 }
1186
1187 Collection<?> c = (Collection<?>) o;
1188 if (c.size() != size()) {
1189 return false;
1190 }
1191
1192 try {
1193 return containsAll(c);
1194 } catch (ClassCastException | NullPointerException unused) {
1195 return false;
1196 }
1197 }
1198
1199 @Override
1200 public int hashCode() {
1201 int h = 0;
1202 for (E obj : this) {
1203 h += Objects.hashCode(obj);
1204 }
1205 return h;
1206 }
1207
1208 /**
1209 * Writes this object out to a stream (i.e., serializes it).
1210 *
1211 * @param oos the {@code ObjectOutputStream} to which data is written
1212 * @throws IOException if an I/O error occurs
1213 */
1214 @java.io.Serial
1215 private void writeObject(java.io.ObjectOutputStream oos)
1216 throws java.io.IOException {
1217
1218 ObjectOutputStream.PutField fields = oos.putFields();
1219 fields.put("this$0", subject);
1220 fields.put("elements", elements);
1221 fields.put("which", which);
1222 oos.writeFields();
1223 }
1224
1225 /**
1226 * Restores the state of this object from the stream.
1227 *
1228 * @param ois the {@code ObjectInputStream} from which data is read
1229 * @throws IOException if an I/O error occurs
1230 * @throws ClassNotFoundException if a serialized class cannot be loaded
1231 */
1232 @SuppressWarnings("unchecked")
1233 @java.io.Serial
1234 private void readObject(ObjectInputStream ois)
1235 throws IOException, ClassNotFoundException
1236 {
1237 ObjectInputStream.GetField fields = ois.readFields();
1238 subject = (Subject) fields.get("this$0", null);
1239 which = fields.get("which", 0);
1240
1241 LinkedList<E> tmp = (LinkedList<E>) fields.get("elements", null);
1242
1243 elements = Subject.collectionNullClean(tmp);
1244 }
1245
1246 }
1247
1248 /**
1249 * This class implements a {@code Set} which returns only
1250 * members that are an instance of a specified Class.
1251 */
1252 private class ClassSet<T> extends AbstractSet<T> {
1253
1254 private final int which;
1255 private final Class<T> c;
1256 private final Set<T> set;
1257
1258 ClassSet(int which, Class<T> c) {
1259 this.which = which;
1260 this.c = c;
1261 set = new HashSet<>();
1262
1263 switch (which) {
1264 case Subject.PRINCIPAL_SET:
1265 synchronized(principals) { populateSet(); }
1266 break;
1267 case Subject.PUB_CREDENTIAL_SET:
1268 synchronized(pubCredentials) { populateSet(); }
1269 break;
1270 default:
1271 synchronized(privCredentials) { populateSet(); }
1272 break;
1273 }
1274 }
1275
1276 @SuppressWarnings("unchecked")
1277 private void populateSet() {
1278 final Iterator<?> iterator;
1279 switch(which) {
1280 case Subject.PRINCIPAL_SET:
1281 iterator = Subject.this.principals.iterator();
1282 break;
1283 case Subject.PUB_CREDENTIAL_SET:
1284 iterator = Subject.this.pubCredentials.iterator();
1285 break;
1286 default:
1287 iterator = Subject.this.privCredentials.iterator();
1288 break;
1289 }
1290
1291 while (iterator.hasNext()) {
1292 Object next = iterator.next();
1293 if (c.isAssignableFrom(next.getClass())) {
1294 set.add((T)next);
1295 }
1296 }
1297 }
1298
1299 @Override
1300 public int size() {
1301 return set.size();
1302 }
1303
1304 @Override
1305 public Iterator<T> iterator() {
1306 return set.iterator();
1307 }
1308
1309 @Override
1310 public boolean add(T o) {
1311
1312 if (!c.isAssignableFrom(o.getClass())) {
1313 MessageFormat form = new MessageFormat(ResourcesMgr.getString
1314 ("attempting.to.add.an.object.which.is.not.an.instance.of.class"));
1315 Object[] source = {c.toString()};
1316 throw new SecurityException(form.format(source));
1317 }
1318
1319 return set.add(o);
1320 }
1321 }
1322 }
--- EOF ---