aboutsummaryrefslogtreecommitdiff
path: root/src/org/apache/harmony/javax/security/auth/Subject.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/apache/harmony/javax/security/auth/Subject.java')
-rw-r--r--src/org/apache/harmony/javax/security/auth/Subject.java782
1 files changed, 782 insertions, 0 deletions
diff --git a/src/org/apache/harmony/javax/security/auth/Subject.java b/src/org/apache/harmony/javax/security/auth/Subject.java
new file mode 100644
index 0000000..142686e
--- /dev/null
+++ b/src/org/apache/harmony/javax/security/auth/Subject.java
@@ -0,0 +1,782 @@
+/*
+ * 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 next() {
+ 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 next() {
+ return iterator.next();
+ }
+
+ /**
+ * checks set state, and security permission to modify set before
+ * removing current element
+ */
+ public void remove() {
+ checkState();
+ checkPermission(permission);
+ iterator.remove();
+ }
+ }
+ }
+}