Project: android-client_1
/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more 
 *  contributor license agreements.  See the NOTICE file distributed with 
 *  this work for additional information regarding copyright ownership. 
 *  The ASF licenses this file to You under the Apache License, Version 2.0 
 *  (the "License"); you may not use this file except in compliance with 
 *  the License.  You may obtain a copy of the License at 
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0 
 * 
 *  Unless required by applicable law or agreed to in writing, software 
 *  distributed under the License is distributed on an "AS IS" BASIS, 
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 *  See the License for the specific language governing permissions and 
 *  limitations under the License. 
 */
 
package org.apache.harmony.javax.security.auth; 
 
import java.io.IOException; 
import java.io.ObjectInputStream; 
import java.io.ObjectOutputStream; 
import java.io.Serializable; 
import java.security.AccessControlContext; 
import java.security.AccessController; 
import java.security.DomainCombiner; 
import java.security.Permission; 
import java.security.Principal; 
import java.security.PrivilegedAction; 
import java.security.PrivilegedActionException; 
import java.security.PrivilegedExceptionAction; 
import java.security.ProtectionDomain; 
import java.util.AbstractSet; 
import java.util.Collection; 
import java.util.Iterator; 
import java.util.LinkedList; 
import java.util.Set; 
 
 
 
/**
 * The central class of the {@code javax.security.auth} package representing an 
 * authenticated user or entity (both referred to as "subject"). IT defines also 
 * the static methods that allow code to be run, and do modifications according 
 * to the subject's permissions. 
 * <p> 
 * A subject has the following features: 
 * <ul> 
 * <li>A set of {@code Principal} objects specifying the identities bound to a 
 * {@code Subject} that distinguish it.</li> 
 * <li>Credentials (public and private) such as certificates, keys, or 
 * authentication proofs such as tickets</li> 
 * </ul> 
 */
 
public final class Subject implements Serializable { 
 
    private static final long serialVersionUID = -8308522755600156056L
     
    private static final AuthPermission _AS = new AuthPermission("doAs"); //$NON-NLS-1$ 
 
    private static final AuthPermission _AS_PRIVILEGED = new AuthPermission( 
            "doAsPrivileged"); //$NON-NLS-1$ 
 
    private static final AuthPermission _SUBJECT = new AuthPermission( 
            "getSubject"); //$NON-NLS-1$ 
 
    private static final AuthPermission _PRINCIPALS = new AuthPermission( 
            "modifyPrincipals"); //$NON-NLS-1$ 
 
    private static final AuthPermission _PRIVATE_CREDENTIALS = new AuthPermission( 
            "modifyPrivateCredentials"); //$NON-NLS-1$ 
 
    private static final AuthPermission _PUBLIC_CREDENTIALS = new AuthPermission( 
            "modifyPublicCredentials"); //$NON-NLS-1$ 
 
    private static final AuthPermission _READ_ONLY = new AuthPermission( 
            "setReadOnly"); //$NON-NLS-1$ 
 
    private final Set<Principal> principals; 
 
    private boolean readOnly; 
     
    // set of private credentials 
    private transient SecureSet<Object> privateCredentials; 
 
    // set of public credentials 
    private transient SecureSet<Object> publicCredentials; 
     
    /**
     * The default constructor initializing the sets of public and private 
     * credentials and principals with the empty set. 
     */
 
    public Subject() { 
        super(); 
        principals = new SecureSet<Principal>(_PRINCIPALS); 
        publicCredentials = new SecureSet<Object>(_PUBLIC_CREDENTIALS); 
        privateCredentials = new SecureSet<Object>(_PRIVATE_CREDENTIALS); 
 
        readOnly = false
    } 
 
    /**
     * The constructor for the subject, setting its public and private 
     * credentials and principals according to the arguments. 
     * 
     * @param readOnly 
     *            {@code true} if this {@code Subject} is read-only, thus 
     *            preventing any modifications to be done. 
     * @param subjPrincipals 
     *            the set of Principals that are attributed to this {@code 
     *            Subject}. 
     * @param pubCredentials 
     *            the set of public credentials that distinguish this {@code 
     *            Subject}. 
     * @param privCredentials 
     *            the set of private credentials that distinguish this {@code 
     *            Subject}. 
     */
 
    public Subject(boolean readOnly, Set<? extends Principal> subjPrincipals, 
            Set<?> pubCredentials, Set<?> privCredentials) { 
 
        if (subjPrincipals == null || pubCredentials == null || privCredentials == null) { 
            throw new NullPointerException(); 
        } 
 
        principals = new SecureSet<Principal>(_PRINCIPALS, subjPrincipals); 
        publicCredentials = new SecureSet<Object>(_PUBLIC_CREDENTIALS, pubCredentials); 
        privateCredentials = new SecureSet<Object>(_PRIVATE_CREDENTIALS, privCredentials); 
 
        this.readOnly = readOnly; 
    } 
 
    /**
     * Runs the code defined by {@code action} using the permissions granted to 
     * the {@code Subject} itself and to the code as well. 
     * 
     * @param subject 
     *            the distinguished {@code Subject}. 
     * @param action 
     *            the code to be run. 
     * @return the {@code Object} returned when running the {@code action}. 
     */
 
    @SuppressWarnings("unchecked"
    public static Object doAs(Subject subject, PrivilegedAction action) { 
 
        checkPermission(_AS); 
 
        return doAs_PrivilegedAction(subject, action, AccessController.getContext()); 
    } 
 
    /**
     * Run the code defined by {@code action} using the permissions granted to 
     * the {@code Subject} and to the code itself, additionally providing a more 
     * specific context. 
     * 
     * @param subject 
     *            the distinguished {@code Subject}. 
     * @param action 
     *            the code to be run. 
     * @param context 
     *            the specific context in which the {@code action} is invoked. 
     *            if {@code null} a new {@link AccessControlContext} is 
     *            instantiated. 
     * @return the {@code Object} returned when running the {@code action}. 
     */
 
    @SuppressWarnings("unchecked"
    public static Object doAsPrivileged(Subject subject, PrivilegedAction action, 
            AccessControlContext context) { 
 
        checkPermission(_AS_PRIVILEGED); 
 
        if (context == null) { 
            return doAs_PrivilegedAction(subject, action, new AccessControlContext( 
                    new ProtectionDomain[0])); 
        } 
        return doAs_PrivilegedAction(subject, action, context); 
    } 
 
    // instantiates a new context and passes it to AccessController 
    @SuppressWarnings("unchecked"
    private static Object doAs_PrivilegedAction(Subject subject, PrivilegedAction action, 
            final AccessControlContext context) { 
 
        AccessControlContext newContext; 
 
        final SubjectDomainCombiner combiner; 
        if (subject == null) { 
            // performance optimization 
            // if subject is null there is nothing to combine 
            combiner = null
        } else { 
            combiner = new SubjectDomainCombiner(subject); 
        } 
 
        PrivilegedAction dccAction = new PrivilegedAction() { 
            public Object run() { 
 
                return new AccessControlContext(context, combiner); 
            } 
        }; 
 
        newContext = (AccessControlContext) AccessController.doPrivileged(dccAction); 
 
        return AccessController.doPrivileged(action, newContext); 
    } 
 
    /**
     * Runs the code defined by {@code action} using the permissions granted to 
     * the subject and to the code itself. 
     * 
     * @param subject 
     *            the distinguished {@code Subject}. 
     * @param action 
     *            the code to be run. 
     * @return the {@code Object} returned when running the {@code action}. 
     * @throws PrivilegedActionException 
     *             if running the {@code action} throws an exception. 
     */
 
    @SuppressWarnings("unchecked"
    public static Object doAs(Subject subject, PrivilegedExceptionAction action) 
            throws PrivilegedActionException { 
 
        checkPermission(_AS); 
 
        return doAs_PrivilegedExceptionAction(subject, action, AccessController.getContext()); 
    } 
 
    /**
     * Runs the code defined by {@code action} using the permissions granted to 
     * the subject and to the code itself, additionally providing a more 
     * specific context. 
     * 
     * @param subject 
     *            the distinguished {@code Subject}. 
     * @param action 
     *            the code to be run. 
     * @param context 
     *            the specific context in which the {@code action} is invoked. 
     *            if {@code null} a new {@link AccessControlContext} is 
     *            instantiated. 
     * @return the {@code Object} returned when running the {@code action}. 
     * @throws PrivilegedActionException 
     *             if running the {@code action} throws an exception. 
     */
 
    @SuppressWarnings("unchecked"
    public static Object doAsPrivileged(Subject subject, 
            PrivilegedExceptionAction action, AccessControlContext context) 
            throws PrivilegedActionException { 
 
        checkPermission(_AS_PRIVILEGED); 
 
        if (context == null) { 
            return doAs_PrivilegedExceptionAction(subject, action, 
                    new AccessControlContext(new ProtectionDomain[0])); 
        } 
        return doAs_PrivilegedExceptionAction(subject, action, context); 
    } 
 
    // instantiates a new context and passes it to AccessController 
    @SuppressWarnings("unchecked"
    private static Object doAs_PrivilegedExceptionAction(Subject subject, 
            PrivilegedExceptionAction action, final AccessControlContext context) 
            throws PrivilegedActionException { 
 
        AccessControlContext newContext; 
 
        final SubjectDomainCombiner combiner; 
        if (subject == null) { 
            // performance optimization 
            // if subject is null there is nothing to combine 
            combiner = null
        } else { 
            combiner = new SubjectDomainCombiner(subject); 
        } 
 
        PrivilegedAction<AccessControlContext> dccAction = new PrivilegedAction<AccessControlContext>() { 
            public AccessControlContext run() { 
                return new AccessControlContext(context, combiner); 
            } 
        }; 
 
        newContext = AccessController.doPrivileged(dccAction); 
 
        return AccessController.doPrivileged(action, newContext); 
    } 
 
    /**
     * Checks two Subjects for equality. More specifically if the principals, 
     * public and private credentials are equal, equality for two {@code 
     * Subjects} is implied. 
     * 
     * @param obj 
     *            the {@code Object} checked for equality with this {@code 
     *            Subject}. 
     * @return {@code true} if the specified {@code Subject} is equal to this 
     *         one. 
     */
 
    @Override 
    public boolean equals(Object obj) { 
 
        if (this == obj) { 
            return true
        } 
 
        if (obj == null || this.getClass() != obj.getClass()) { 
            return false
        } 
 
        Subject that = (Subject) obj; 
 
        if (principals.equals(that.principals) 
                && publicCredentials.equals(that.publicCredentials) 
                && privateCredentials.equals(that.privateCredentials)) { 
            return true
        } 
        return false
    } 
 
    /**
     * Returns this {@code Subject}'s {@link Principal}. 
     * 
     * @return this {@code Subject}'s {@link Principal}. 
     */
 
    public Set<Principal> getPrincipals() { 
        return principals; 
    } 
 
 
    /**
     * Returns this {@code Subject}'s {@link Principal} which is a subclass of 
     * the {@code Class} provided. 
     * 
     * @param c 
     *            the {@code Class} as a criteria which the {@code Principal} 
     *            returned must satisfy. 
     * @return this {@code Subject}'s {@link Principal}. Modifications to the 
     *         returned set of {@code Principal}s do not affect this {@code 
     *         Subject}'s set. 
     */
 
    public <T extends Principal> Set<T> getPrincipals(Class<T> c) { 
        return ((SecureSet<Principal>) principals).get(c); 
    } 
 
    /**
     * Returns the private credentials associated with this {@code Subject}. 
     * 
     * @return the private credentials associated with this {@code Subject}. 
     */
 
    public Set<Object> getPrivateCredentials() { 
        return privateCredentials; 
    } 
 
    /**
     * Returns this {@code Subject}'s private credentials which are a subclass 
     * of the {@code Class} provided. 
     * 
     * @param c 
     *            the {@code Class} as a criteria which the private credentials 
     *            returned must satisfy. 
     * @return this {@code Subject}'s private credentials. Modifications to the 
     *         returned set of credentials do not affect this {@code Subject}'s 
     *         credentials. 
     */
 
    public <T> Set<T> getPrivateCredentials(Class<T> c) { 
        return privateCredentials.get(c); 
    } 
 
    /**
     * Returns the public credentials associated with this {@code Subject}. 
     * 
     * @return the public credentials associated with this {@code Subject}. 
     */
 
    public Set<Object> getPublicCredentials() { 
        return publicCredentials; 
    } 
 
 
    /**
     * Returns this {@code Subject}'s public credentials which are a subclass of 
     * the {@code Class} provided. 
     * 
     * @param c 
     *            the {@code Class} as a criteria which the public credentials 
     *            returned must satisfy. 
     * @return this {@code Subject}'s public credentials. Modifications to the 
     *         returned set of credentials do not affect this {@code Subject}'s 
     *         credentials. 
     */
 
    public <T> Set<T> getPublicCredentials(Class<T> c) { 
        return publicCredentials.get(c); 
    } 
 
    /**
     * Returns a hash code of this {@code Subject}. 
     * 
     * @return a hash code of this {@code Subject}. 
     */
 
    @Override 
    public int hashCode() { 
        return principals.hashCode() + privateCredentials.hashCode() 
                + publicCredentials.hashCode(); 
    } 
 
    /**
     * Prevents from modifications being done to the credentials and {@link 
     * Principal} sets. After setting it to read-only this {@code Subject} can 
     * not be made writable again. The destroy method on the credentials still 
     * works though. 
     */
 
    public void setReadOnly() { 
        checkPermission(_READ_ONLY); 
 
        readOnly = true
    } 
 
    /**
     * Returns whether this {@code Subject} is read-only or not. 
     * 
     * @return whether this {@code Subject} is read-only or not. 
     */
 
    public boolean isReadOnly() { 
        return readOnly; 
    } 
 
    /**
     * Returns a {@code String} representation of this {@code Subject}. 
     * 
     * @return a {@code String} representation of this {@code Subject}. 
     */
 
    @Override 
    public String toString() { 
 
        StringBuilder buf = new StringBuilder("Subject:\n"); //$NON-NLS-1$ 
 
        Iterator<?> it = principals.iterator(); 
        while (it.hasNext()) { 
            buf.append("\tPrincipal: "); //$NON-NLS-1$ 
            buf.append(it.next()); 
            buf.append('\n'); 
        } 
 
        it = publicCredentials.iterator(); 
        while (it.hasNext()) { 
            buf.append("\tPublic Credential: "); //$NON-NLS-1$ 
            buf.append(it.next()); 
            buf.append('\n'); 
        } 
 
        int offset = buf.length() - 1
        it = privateCredentials.iterator(); 
        try { 
            while (it.hasNext()) { 
                buf.append("\tPrivate Credential: "); //$NON-NLS-1$ 
                buf.append(it.next()); 
                buf.append('\n'); 
            } 
        } catch (SecurityException e) { 
            buf.delete(offset, buf.length()); 
            buf.append("\tPrivate Credentials: no accessible information\n"); //$NON-NLS-1$ 
        } 
        return buf.toString(); 
    } 
 
    private void readObject(ObjectInputStream in) throws IOException, 
            ClassNotFoundException { 
 
        in.defaultReadObject(); 
 
        publicCredentials = new SecureSet<Object>(_PUBLIC_CREDENTIALS); 
        privateCredentials = new SecureSet<Object>(_PRIVATE_CREDENTIALS); 
    } 
 
    private void writeObject(ObjectOutputStream out) throws IOException { 
        out.defaultWriteObject(); 
    } 
 
    /**
     * Returns the {@code Subject} that was last associated with the {@code 
     * context} provided as argument. 
     * 
     * @param context 
     *            the {@code context} that was associated with the 
     *            {@code Subject}. 
     * @return the {@code Subject} that was last associated with the {@code 
     *         context} provided as argument. 
     */
 
    public static Subject getSubject(final AccessControlContext context) { 
        checkPermission(_SUBJECT); 
        if (context == null) { 
            throw new NullPointerException("auth.09"); //$NON-NLS-1$ 
        } 
        PrivilegedAction<DomainCombiner> action = new PrivilegedAction<DomainCombiner>() { 
            public DomainCombiner run() { 
                return context.getDomainCombiner(); 
            } 
        }; 
        DomainCombiner combiner = AccessController.doPrivileged(action); 
 
        if ((combiner == null) || !(combiner instanceof SubjectDomainCombiner)) { 
            return null
        } 
        return ((SubjectDomainCombiner) combiner).getSubject(); 
    } 
 
    // checks passed permission 
    private static void checkPermission(Permission p) { 
        SecurityManager sm = System.getSecurityManager(); 
        if (sm != null) { 
            sm.checkPermission(p); 
        } 
    } 
 
    // FIXME is used only in two places. remove? 
    private void checkState() { 
        if (readOnly) { 
            throw new IllegalStateException("auth.0A"); //$NON-NLS-1$ 
        } 
    } 
 
    private final class SecureSet<SST> extends AbstractSet<SST> implements Serializable { 
 
        /**
         * Compatibility issue: see comments for setType variable 
         */
 
        private static final long serialVersionUID = 7911754171111800359L
 
        private LinkedList<SST> elements; 
 
        /*
         * Is used to define a set type for serialization. 
         *  
         * A type can be principal, priv. or pub. credential set. The spec. 
         * doesn't clearly says that priv. and pub. credential sets can be 
         * serialized and what classes they are. It is only possible to figure 
         * out from writeObject method comments that priv. credential set is 
         * serializable and it is an instance of SecureSet class. So pub. 
         * credential was implemented by analogy 
         *  
         * Compatibility issue: the class follows its specified serial form. 
         * Also according to the serialization spec. adding new field is a 
         * compatible change. So is ok for principal set (because the default 
         * value for integer is zero). But priv. or pub. credential set it is 
         * not compatible because most probably other implementations resolve 
         * this issue in other way 
         */
 
        private int setType; 
 
        // Defines principal set for serialization. 
        private static final int SET_Principal = 0
 
        // Defines private credential set for serialization. 
        private static final int SET_PrivCred = 1
 
        // Defines public credential set for serialization. 
        private static final int SET_PubCred = 2
 
        // permission required to modify set 
        private transient AuthPermission permission; 
 
        protected SecureSet(AuthPermission perm) { 
            permission = perm; 
            elements = new LinkedList<SST>(); 
        } 
 
        // creates set from specified collection with specified permission 
        // all collection elements are verified before adding 
        protected SecureSet(AuthPermission perm, Collection<? extends SST> s) { 
            this(perm); 
 
            // Subject's constructor receives a Set, we can trusts if a set is from bootclasspath, 
            // and not to check whether it contains duplicates or not 
            boolean trust = s.getClass().getClassLoader() == null;  
             
            Iterator<? extends SST> it = s.iterator(); 
            while (it.hasNext()) { 
                SST o = it.next(); 
                verifyElement(o); 
                if (trust || !elements.contains(o)) { 
                    elements.add(o); 
                } 
            } 
        } 
 
        // verifies new set element 
        private void verifyElement(Object o) { 
 
            if (o == null) { 
                throw new NullPointerException(); 
            } 
            if (permission == _PRINCIPALS && !(Principal.class.isAssignableFrom(o.getClass()))) { 
                throw new IllegalArgumentException("auth.0B"); //$NON-NLS-1$ 
            } 
        } 
 
        /*
         * verifies specified element, checks set state, and security permission 
         * to modify set before adding new element 
         */
 
        @Override 
        public boolean add(SST o) { 
 
            verifyElement(o); 
 
            checkState(); 
            checkPermission(permission); 
 
            if (!elements.contains(o)) { 
                elements.add(o); 
                return true
            } 
            return false
        } 
 
        // returns an instance of SecureIterator 
        @Override 
        public Iterator<SST> iterator() { 
 
            if (permission == _PRIVATE_CREDENTIALS) { 
                /*
                 * private credential set requires iterator with additional 
                 * security check (PrivateCredentialPermission) 
                 */
 
                return new SecureIterator(elements.iterator()) { 
                    /*
                     * checks permission to access next private credential moves 
                     * to the next element even SecurityException was thrown 
                     */
 
                    @Override 
                    public SST () { 
                        SST obj = iterator.next(); 
                        checkPermission(new PrivateCredentialPermission(obj 
                                .getClass().getName(), principals)); 
                        return obj; 
                    } 
                }; 
            } 
            return new SecureIterator(elements.iterator()); 
        } 
 
        @Override 
        public boolean retainAll(Collection<?> c) { 
 
            if (c == null) { 
                throw new NullPointerException(); 
            } 
            return super.retainAll(c); 
        } 
 
        @Override 
        public int size() { 
            return elements.size(); 
        } 
 
        /**
         * return set with elements that are instances or subclasses of the 
         * specified class 
         */
 
        protected final <E> Set<E> get(final Class<E> c) { 
 
            if (c == null) { 
                throw new NullPointerException(); 
            } 
 
            AbstractSet<E> s = new AbstractSet<E>() { 
                private LinkedList<E> elements = new LinkedList<E>(); 
 
                @Override 
                public boolean add(E o) { 
 
                    if (!c.isAssignableFrom(o.getClass())) { 
                        throw new IllegalArgumentException( 
                                "auth.0C " + c.getName()); //$NON-NLS-1$ 
                    } 
 
                    if (elements.contains(o)) { 
                        return false
                    } 
                    elements.add(o); 
                    return true
                } 
 
                @Override 
                public Iterator<E> iterator() { 
                    return elements.iterator(); 
                } 
 
                @Override 
                public boolean retainAll(Collection<?> c) { 
 
                    if (c == null) { 
                        throw new NullPointerException(); 
                    } 
                    return super.retainAll(c); 
                } 
 
                @Override 
                public int size() { 
                    return elements.size(); 
                } 
            }; 
 
            // FIXME must have permissions for requested priv. credentials 
            for (Iterator<SST> it = iterator(); it.hasNext();) { 
                SST o = it.next(); 
                if (c.isAssignableFrom(o.getClass())) { 
                    s.add(c.cast(o)); 
                } 
            } 
            return s; 
        } 
 
        private void readObject(ObjectInputStream in) throws IOException, 
                ClassNotFoundException { 
            in.defaultReadObject(); 
 
            switch (setType) { 
            case SET_Principal: 
                permission = _PRINCIPALS; 
                break
            case SET_PrivCred: 
                permission = _PRIVATE_CREDENTIALS; 
                break
            case SET_PubCred: 
                permission = _PUBLIC_CREDENTIALS; 
                break
            default
                throw new IllegalArgumentException(); 
            } 
 
            Iterator<SST> it = elements.iterator(); 
            while (it.hasNext()) { 
                verifyElement(it.next()); 
            } 
        } 
 
        private void writeObject(ObjectOutputStream out) throws IOException { 
 
            if (permission == _PRIVATE_CREDENTIALS) { 
                // does security check for each private credential 
                for (Iterator<SST> it = iterator(); it.hasNext();) { 
                    it.next(); 
                } 
                setType = SET_PrivCred; 
            } else if (permission == _PRINCIPALS) { 
                setType = SET_Principal; 
            } else { 
                setType = SET_PubCred; 
            } 
 
            out.defaultWriteObject(); 
        } 
 
        /**
         * Represents iterator for subject's secure set 
         */
 
        private class SecureIterator implements Iterator<SST> { 
            protected Iterator<SST> iterator; 
 
            protected SecureIterator(Iterator<SST> iterator) { 
                this.iterator = iterator; 
            } 
 
            public boolean hasNext() { 
                return iterator.hasNext(); 
            } 
 
            public SST () { 
                return iterator.next(); 
            } 
 
            /**
             * checks set state, and security permission to modify set before 
             * removing current element 
             */
 
            public void remove() { 
                checkState(); 
                checkPermission(permission); 
                iterator.remove(); 
            } 
        } 
    } 
}