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