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 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}
783