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