blob: 142686e44717d2bcf172d393dea9b26d90d6420c [file] [log] [blame]
Shuyi Chend7955ce2013-05-22 14:51:55 -07001/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package org.apache.harmony.javax.security.auth;
19
20import java.io.IOException;
21import java.io.ObjectInputStream;
22import java.io.ObjectOutputStream;
23import java.io.Serializable;
24import java.security.AccessControlContext;
25import java.security.AccessController;
26import java.security.DomainCombiner;
27import java.security.Permission;
28import java.security.Principal;
29import java.security.PrivilegedAction;
30import java.security.PrivilegedActionException;
31import java.security.PrivilegedExceptionAction;
32import java.security.ProtectionDomain;
33import java.util.AbstractSet;
34import java.util.Collection;
35import java.util.Iterator;
36import java.util.LinkedList;
37import java.util.Set;
38
39
40
41/**
42 * The central class of the {@code javax.security.auth} package representing an
43 * authenticated user or entity (both referred to as "subject"). IT defines also
44 * the static methods that allow code to be run, and do modifications according
45 * to the subject's permissions.
46 * <p>
47 * A subject has the following features:
48 * <ul>
49 * <li>A set of {@code Principal} objects specifying the identities bound to a
50 * {@code Subject} that distinguish it.</li>
51 * <li>Credentials (public and private) such as certificates, keys, or
52 * authentication proofs such as tickets</li>
53 * </ul>
54 */
55public final class Subject implements Serializable {
56
57 private static final long serialVersionUID = -8308522755600156056L;
58
59 private static final AuthPermission _AS = new AuthPermission("doAs"); //$NON-NLS-1$
60
61 private static final AuthPermission _AS_PRIVILEGED = new AuthPermission(
62 "doAsPrivileged"); //$NON-NLS-1$
63
64 private static final AuthPermission _SUBJECT = new AuthPermission(
65 "getSubject"); //$NON-NLS-1$
66
67 private static final AuthPermission _PRINCIPALS = new AuthPermission(
68 "modifyPrincipals"); //$NON-NLS-1$
69
70 private static final AuthPermission _PRIVATE_CREDENTIALS = new AuthPermission(
71 "modifyPrivateCredentials"); //$NON-NLS-1$
72
73 private static final AuthPermission _PUBLIC_CREDENTIALS = new AuthPermission(
74 "modifyPublicCredentials"); //$NON-NLS-1$
75
76 private static final AuthPermission _READ_ONLY = new AuthPermission(
77 "setReadOnly"); //$NON-NLS-1$
78
79 private final Set<Principal> principals;
80
81 private boolean readOnly;
82
83 // set of private credentials
84 private transient SecureSet<Object> privateCredentials;
85
86 // set of public credentials
87 private transient SecureSet<Object> publicCredentials;
88
89 /**
90 * The default constructor initializing the sets of public and private
91 * credentials and principals with the empty set.
92 */
93 public Subject() {
94 super();
95 principals = new SecureSet<Principal>(_PRINCIPALS);
96 publicCredentials = new SecureSet<Object>(_PUBLIC_CREDENTIALS);
97 privateCredentials = new SecureSet<Object>(_PRIVATE_CREDENTIALS);
98
99 readOnly = false;
100 }
101
102 /**
103 * The constructor for the subject, setting its public and private
104 * credentials and principals according to the arguments.
105 *
106 * @param readOnly
107 * {@code true} if this {@code Subject} is read-only, thus
108 * preventing any modifications to be done.
109 * @param subjPrincipals
110 * the set of Principals that are attributed to this {@code
111 * Subject}.
112 * @param pubCredentials
113 * the set of public credentials that distinguish this {@code
114 * Subject}.
115 * @param privCredentials
116 * the set of private credentials that distinguish this {@code
117 * Subject}.
118 */
119 public Subject(boolean readOnly, Set<? extends Principal> subjPrincipals,
120 Set<?> pubCredentials, Set<?> privCredentials) {
121
122 if (subjPrincipals == null || pubCredentials == null || privCredentials == null) {
123 throw new NullPointerException();
124 }
125
126 principals = new SecureSet<Principal>(_PRINCIPALS, subjPrincipals);
127 publicCredentials = new SecureSet<Object>(_PUBLIC_CREDENTIALS, pubCredentials);
128 privateCredentials = new SecureSet<Object>(_PRIVATE_CREDENTIALS, privCredentials);
129
130 this.readOnly = readOnly;
131 }
132
133 /**
134 * Runs the code defined by {@code action} using the permissions granted to
135 * the {@code Subject} itself and to the code as well.
136 *
137 * @param subject
138 * the distinguished {@code Subject}.
139 * @param action
140 * the code to be run.
141 * @return the {@code Object} returned when running the {@code action}.
142 */
143 @SuppressWarnings("unchecked")
144 public static Object doAs(Subject subject, PrivilegedAction action) {
145
146 checkPermission(_AS);
147
148 return doAs_PrivilegedAction(subject, action, AccessController.getContext());
149 }
150
151 /**
152 * Run the code defined by {@code action} using the permissions granted to
153 * the {@code Subject} and to the code itself, additionally providing a more
154 * specific context.
155 *
156 * @param subject
157 * the distinguished {@code Subject}.
158 * @param action
159 * the code to be run.
160 * @param context
161 * the specific context in which the {@code action} is invoked.
162 * if {@code null} a new {@link AccessControlContext} is
163 * instantiated.
164 * @return the {@code Object} returned when running the {@code action}.
165 */
166 @SuppressWarnings("unchecked")
167 public static Object doAsPrivileged(Subject subject, PrivilegedAction action,
168 AccessControlContext context) {
169
170 checkPermission(_AS_PRIVILEGED);
171
172 if (context == null) {
173 return doAs_PrivilegedAction(subject, action, new AccessControlContext(
174 new ProtectionDomain[0]));
175 }
176 return doAs_PrivilegedAction(subject, action, context);
177 }
178
179 // instantiates a new context and passes it to AccessController
180 @SuppressWarnings("unchecked")
181 private static Object doAs_PrivilegedAction(Subject subject, PrivilegedAction action,
182 final AccessControlContext context) {
183
184 AccessControlContext newContext;
185
186 final SubjectDomainCombiner combiner;
187 if (subject == null) {
188 // performance optimization
189 // if subject is null there is nothing to combine
190 combiner = null;
191 } else {
192 combiner = new SubjectDomainCombiner(subject);
193 }
194
195 PrivilegedAction dccAction = new PrivilegedAction() {
196 public Object run() {
197
198 return new AccessControlContext(context, combiner);
199 }
200 };
201
202 newContext = (AccessControlContext) AccessController.doPrivileged(dccAction);
203
204 return AccessController.doPrivileged(action, newContext);
205 }
206
207 /**
208 * Runs the code defined by {@code action} using the permissions granted to
209 * the subject and to the code itself.
210 *
211 * @param subject
212 * the distinguished {@code Subject}.
213 * @param action
214 * the code to be run.
215 * @return the {@code Object} returned when running the {@code action}.
216 * @throws PrivilegedActionException
217 * if running the {@code action} throws an exception.
218 */
219 @SuppressWarnings("unchecked")
220 public static Object doAs(Subject subject, PrivilegedExceptionAction action)
221 throws PrivilegedActionException {
222
223 checkPermission(_AS);
224
225 return doAs_PrivilegedExceptionAction(subject, action, AccessController.getContext());
226 }
227
228 /**
229 * Runs the code defined by {@code action} using the permissions granted to
230 * the subject and to the code itself, additionally providing a more
231 * specific context.
232 *
233 * @param subject
234 * the distinguished {@code Subject}.
235 * @param action
236 * the code to be run.
237 * @param context
238 * the specific context in which the {@code action} is invoked.
239 * if {@code null} a new {@link AccessControlContext} is
240 * instantiated.
241 * @return the {@code Object} returned when running the {@code action}.
242 * @throws PrivilegedActionException
243 * if running the {@code action} throws an exception.
244 */
245 @SuppressWarnings("unchecked")
246 public static Object doAsPrivileged(Subject subject,
247 PrivilegedExceptionAction action, AccessControlContext context)
248 throws PrivilegedActionException {
249
250 checkPermission(_AS_PRIVILEGED);
251
252 if (context == null) {
253 return doAs_PrivilegedExceptionAction(subject, action,
254 new AccessControlContext(new ProtectionDomain[0]));
255 }
256 return doAs_PrivilegedExceptionAction(subject, action, context);
257 }
258
259 // instantiates a new context and passes it to AccessController
260 @SuppressWarnings("unchecked")
261 private static Object doAs_PrivilegedExceptionAction(Subject subject,
262 PrivilegedExceptionAction action, final AccessControlContext context)
263 throws PrivilegedActionException {
264
265 AccessControlContext newContext;
266
267 final SubjectDomainCombiner combiner;
268 if (subject == null) {
269 // performance optimization
270 // if subject is null there is nothing to combine
271 combiner = null;
272 } else {
273 combiner = new SubjectDomainCombiner(subject);
274 }
275
276 PrivilegedAction<AccessControlContext> dccAction = new PrivilegedAction<AccessControlContext>() {
277 public AccessControlContext run() {
278 return new AccessControlContext(context, combiner);
279 }
280 };
281
282 newContext = AccessController.doPrivileged(dccAction);
283
284 return AccessController.doPrivileged(action, newContext);
285 }
286
287 /**
288 * Checks two Subjects for equality. More specifically if the principals,
289 * public and private credentials are equal, equality for two {@code
290 * Subjects} is implied.
291 *
292 * @param obj
293 * the {@code Object} checked for equality with this {@code
294 * Subject}.
295 * @return {@code true} if the specified {@code Subject} is equal to this
296 * one.
297 */
298 @Override
299 public boolean equals(Object obj) {
300
301 if (this == obj) {
302 return true;
303 }
304
305 if (obj == null || this.getClass() != obj.getClass()) {
306 return false;
307 }
308
309 Subject that = (Subject) obj;
310
311 if (principals.equals(that.principals)
312 && publicCredentials.equals(that.publicCredentials)
313 && privateCredentials.equals(that.privateCredentials)) {
314 return true;
315 }
316 return false;
317 }
318
319 /**
320 * Returns this {@code Subject}'s {@link Principal}.
321 *
322 * @return this {@code Subject}'s {@link Principal}.
323 */
324 public Set<Principal> getPrincipals() {
325 return principals;
326 }
327
328
329 /**
330 * Returns this {@code Subject}'s {@link Principal} which is a subclass of
331 * the {@code Class} provided.
332 *
333 * @param c
334 * the {@code Class} as a criteria which the {@code Principal}
335 * returned must satisfy.
336 * @return this {@code Subject}'s {@link Principal}. Modifications to the
337 * returned set of {@code Principal}s do not affect this {@code
338 * Subject}'s set.
339 */
340 public <T extends Principal> Set<T> getPrincipals(Class<T> c) {
341 return ((SecureSet<Principal>) principals).get(c);
342 }
343
344 /**
345 * Returns the private credentials associated with this {@code Subject}.
346 *
347 * @return the private credentials associated with this {@code Subject}.
348 */
349 public Set<Object> getPrivateCredentials() {
350 return privateCredentials;
351 }
352
353 /**
354 * Returns this {@code Subject}'s private credentials which are a subclass
355 * of the {@code Class} provided.
356 *
357 * @param c
358 * the {@code Class} as a criteria which the private credentials
359 * returned must satisfy.
360 * @return this {@code Subject}'s private credentials. Modifications to the
361 * returned set of credentials do not affect this {@code Subject}'s
362 * credentials.
363 */
364 public <T> Set<T> getPrivateCredentials(Class<T> c) {
365 return privateCredentials.get(c);
366 }
367
368 /**
369 * Returns the public credentials associated with this {@code Subject}.
370 *
371 * @return the public credentials associated with this {@code Subject}.
372 */
373 public Set<Object> getPublicCredentials() {
374 return publicCredentials;
375 }
376
377
378 /**
379 * Returns this {@code Subject}'s public credentials which are a subclass of
380 * the {@code Class} provided.
381 *
382 * @param c
383 * the {@code Class} as a criteria which the public credentials
384 * returned must satisfy.
385 * @return this {@code Subject}'s public credentials. Modifications to the
386 * returned set of credentials do not affect this {@code Subject}'s
387 * credentials.
388 */
389 public <T> Set<T> getPublicCredentials(Class<T> c) {
390 return publicCredentials.get(c);
391 }
392
393 /**
394 * Returns a hash code of this {@code Subject}.
395 *
396 * @return a hash code of this {@code Subject}.
397 */
398 @Override
399 public int hashCode() {
400 return principals.hashCode() + privateCredentials.hashCode()
401 + publicCredentials.hashCode();
402 }
403
404 /**
405 * Prevents from modifications being done to the credentials and {@link
406 * Principal} sets. After setting it to read-only this {@code Subject} can
407 * not be made writable again. The destroy method on the credentials still
408 * works though.
409 */
410 public void setReadOnly() {
411 checkPermission(_READ_ONLY);
412
413 readOnly = true;
414 }
415
416 /**
417 * Returns whether this {@code Subject} is read-only or not.
418 *
419 * @return whether this {@code Subject} is read-only or not.
420 */
421 public boolean isReadOnly() {
422 return readOnly;
423 }
424
425 /**
426 * Returns a {@code String} representation of this {@code Subject}.
427 *
428 * @return a {@code String} representation of this {@code Subject}.
429 */
430 @Override
431 public String toString() {
432
433 StringBuilder buf = new StringBuilder("Subject:\n"); //$NON-NLS-1$
434
435 Iterator<?> it = principals.iterator();
436 while (it.hasNext()) {
437 buf.append("\tPrincipal: "); //$NON-NLS-1$
438 buf.append(it.next());
439 buf.append('\n');
440 }
441
442 it = publicCredentials.iterator();
443 while (it.hasNext()) {
444 buf.append("\tPublic Credential: "); //$NON-NLS-1$
445 buf.append(it.next());
446 buf.append('\n');
447 }
448
449 int offset = buf.length() - 1;
450 it = privateCredentials.iterator();
451 try {
452 while (it.hasNext()) {
453 buf.append("\tPrivate Credential: "); //$NON-NLS-1$
454 buf.append(it.next());
455 buf.append('\n');
456 }
457 } catch (SecurityException e) {
458 buf.delete(offset, buf.length());
459 buf.append("\tPrivate Credentials: no accessible information\n"); //$NON-NLS-1$
460 }
461 return buf.toString();
462 }
463
464 private void readObject(ObjectInputStream in) throws IOException,
465 ClassNotFoundException {
466
467 in.defaultReadObject();
468
469 publicCredentials = new SecureSet<Object>(_PUBLIC_CREDENTIALS);
470 privateCredentials = new SecureSet<Object>(_PRIVATE_CREDENTIALS);
471 }
472
473 private void writeObject(ObjectOutputStream out) throws IOException {
474 out.defaultWriteObject();
475 }
476
477 /**
478 * Returns the {@code Subject} that was last associated with the {@code
479 * context} provided as argument.
480 *
481 * @param context
482 * the {@code context} that was associated with the
483 * {@code Subject}.
484 * @return the {@code Subject} that was last associated with the {@code
485 * context} provided as argument.
486 */
487 public static Subject getSubject(final AccessControlContext context) {
488 checkPermission(_SUBJECT);
489 if (context == null) {
490 throw new NullPointerException("auth.09"); //$NON-NLS-1$
491 }
492 PrivilegedAction<DomainCombiner> action = new PrivilegedAction<DomainCombiner>() {
493 public DomainCombiner run() {
494 return context.getDomainCombiner();
495 }
496 };
497 DomainCombiner combiner = AccessController.doPrivileged(action);
498
499 if ((combiner == null) || !(combiner instanceof SubjectDomainCombiner)) {
500 return null;
501 }
502 return ((SubjectDomainCombiner) combiner).getSubject();
503 }
504
505 // checks passed permission
506 private static void checkPermission(Permission p) {
507 SecurityManager sm = System.getSecurityManager();
508 if (sm != null) {
509 sm.checkPermission(p);
510 }
511 }
512
513 // FIXME is used only in two places. remove?
514 private void checkState() {
515 if (readOnly) {
516 throw new IllegalStateException("auth.0A"); //$NON-NLS-1$
517 }
518 }
519
520 private final class SecureSet<SST> extends AbstractSet<SST> implements Serializable {
521
522 /**
523 * Compatibility issue: see comments for setType variable
524 */
525 private static final long serialVersionUID = 7911754171111800359L;
526
527 private LinkedList<SST> elements;
528
529 /*
530 * Is used to define a set type for serialization.
531 *
532 * A type can be principal, priv. or pub. credential set. The spec.
533 * doesn't clearly says that priv. and pub. credential sets can be
534 * serialized and what classes they are. It is only possible to figure
535 * out from writeObject method comments that priv. credential set is
536 * serializable and it is an instance of SecureSet class. So pub.
537 * credential was implemented by analogy
538 *
539 * Compatibility issue: the class follows its specified serial form.
540 * Also according to the serialization spec. adding new field is a
541 * compatible change. So is ok for principal set (because the default
542 * value for integer is zero). But priv. or pub. credential set it is
543 * not compatible because most probably other implementations resolve
544 * this issue in other way
545 */
546 private int setType;
547
548 // Defines principal set for serialization.
549 private static final int SET_Principal = 0;
550
551 // Defines private credential set for serialization.
552 private static final int SET_PrivCred = 1;
553
554 // Defines public credential set for serialization.
555 private static final int SET_PubCred = 2;
556
557 // permission required to modify set
558 private transient AuthPermission permission;
559
560 protected SecureSet(AuthPermission perm) {
561 permission = perm;
562 elements = new LinkedList<SST>();
563 }
564
565 // creates set from specified collection with specified permission
566 // all collection elements are verified before adding
567 protected SecureSet(AuthPermission perm, Collection<? extends SST> s) {
568 this(perm);
569
570 // Subject's constructor receives a Set, we can trusts if a set is from bootclasspath,
571 // and not to check whether it contains duplicates or not
572 boolean trust = s.getClass().getClassLoader() == null;
573
574 Iterator<? extends SST> it = s.iterator();
575 while (it.hasNext()) {
576 SST o = it.next();
577 verifyElement(o);
578 if (trust || !elements.contains(o)) {
579 elements.add(o);
580 }
581 }
582 }
583
584 // verifies new set element
585 private void verifyElement(Object o) {
586
587 if (o == null) {
588 throw new NullPointerException();
589 }
590 if (permission == _PRINCIPALS && !(Principal.class.isAssignableFrom(o.getClass()))) {
591 throw new IllegalArgumentException("auth.0B"); //$NON-NLS-1$
592 }
593 }
594
595 /*
596 * verifies specified element, checks set state, and security permission
597 * to modify set before adding new element
598 */
599 @Override
600 public boolean add(SST o) {
601
602 verifyElement(o);
603
604 checkState();
605 checkPermission(permission);
606
607 if (!elements.contains(o)) {
608 elements.add(o);
609 return true;
610 }
611 return false;
612 }
613
614 // returns an instance of SecureIterator
615 @Override
616 public Iterator<SST> iterator() {
617
618 if (permission == _PRIVATE_CREDENTIALS) {
619 /*
620 * private credential set requires iterator with additional
621 * security check (PrivateCredentialPermission)
622 */
623 return new SecureIterator(elements.iterator()) {
624 /*
625 * checks permission to access next private credential moves
626 * to the next element even SecurityException was thrown
627 */
628 @Override
629 public SST next() {
630 SST obj = iterator.next();
631 checkPermission(new PrivateCredentialPermission(obj
632 .getClass().getName(), principals));
633 return obj;
634 }
635 };
636 }
637 return new SecureIterator(elements.iterator());
638 }
639
640 @Override
641 public boolean retainAll(Collection<?> c) {
642
643 if (c == null) {
644 throw new NullPointerException();
645 }
646 return super.retainAll(c);
647 }
648
649 @Override
650 public int size() {
651 return elements.size();
652 }
653
654 /**
655 * return set with elements that are instances or subclasses of the
656 * specified class
657 */
658 protected final <E> Set<E> get(final Class<E> c) {
659
660 if (c == null) {
661 throw new NullPointerException();
662 }
663
664 AbstractSet<E> s = new AbstractSet<E>() {
665 private LinkedList<E> elements = new LinkedList<E>();
666
667 @Override
668 public boolean add(E o) {
669
670 if (!c.isAssignableFrom(o.getClass())) {
671 throw new IllegalArgumentException(
672 "auth.0C " + c.getName()); //$NON-NLS-1$
673 }
674
675 if (elements.contains(o)) {
676 return false;
677 }
678 elements.add(o);
679 return true;
680 }
681
682 @Override
683 public Iterator<E> iterator() {
684 return elements.iterator();
685 }
686
687 @Override
688 public boolean retainAll(Collection<?> c) {
689
690 if (c == null) {
691 throw new NullPointerException();
692 }
693 return super.retainAll(c);
694 }
695
696 @Override
697 public int size() {
698 return elements.size();
699 }
700 };
701
702 // FIXME must have permissions for requested priv. credentials
703 for (Iterator<SST> it = iterator(); it.hasNext();) {
704 SST o = it.next();
705 if (c.isAssignableFrom(o.getClass())) {
706 s.add(c.cast(o));
707 }
708 }
709 return s;
710 }
711
712 private void readObject(ObjectInputStream in) throws IOException,
713 ClassNotFoundException {
714 in.defaultReadObject();
715
716 switch (setType) {
717 case SET_Principal:
718 permission = _PRINCIPALS;
719 break;
720 case SET_PrivCred:
721 permission = _PRIVATE_CREDENTIALS;
722 break;
723 case SET_PubCred:
724 permission = _PUBLIC_CREDENTIALS;
725 break;
726 default:
727 throw new IllegalArgumentException();
728 }
729
730 Iterator<SST> it = elements.iterator();
731 while (it.hasNext()) {
732 verifyElement(it.next());
733 }
734 }
735
736 private void writeObject(ObjectOutputStream out) throws IOException {
737
738 if (permission == _PRIVATE_CREDENTIALS) {
739 // does security check for each private credential
740 for (Iterator<SST> it = iterator(); it.hasNext();) {
741 it.next();
742 }
743 setType = SET_PrivCred;
744 } else if (permission == _PRINCIPALS) {
745 setType = SET_Principal;
746 } else {
747 setType = SET_PubCred;
748 }
749
750 out.defaultWriteObject();
751 }
752
753 /**
754 * Represents iterator for subject's secure set
755 */
756 private class SecureIterator implements Iterator<SST> {
757 protected Iterator<SST> iterator;
758
759 protected SecureIterator(Iterator<SST> iterator) {
760 this.iterator = iterator;
761 }
762
763 public boolean hasNext() {
764 return iterator.hasNext();
765 }
766
767 public SST next() {
768 return iterator.next();
769 }
770
771 /**
772 * checks set state, and security permission to modify set before
773 * removing current element
774 */
775 public void remove() {
776 checkState();
777 checkPermission(permission);
778 iterator.remove();
779 }
780 }
781 }
782}