001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package javax.security.auth;
019:
020: import java.io.IOException;
021: import java.io.ObjectInputStream;
022: import java.io.ObjectOutputStream;
023: import java.io.Serializable;
024: import java.security.AccessControlContext;
025: import java.security.AccessController;
026: import java.security.DomainCombiner;
027: import java.security.Permission;
028: import java.security.Principal;
029: import java.security.PrivilegedAction;
030: import java.security.PrivilegedActionException;
031: import java.security.PrivilegedExceptionAction;
032: import java.security.ProtectionDomain;
033: import java.util.AbstractSet;
034: import java.util.Collection;
035: import java.util.Iterator;
036: import java.util.LinkedList;
037: import java.util.Set;
038:
039: import org.apache.harmony.auth.internal.nls.Messages;
040:
041: public final class Subject implements Serializable {
042:
043: private static final long serialVersionUID = -8308522755600156056L;
044:
045: private static final AuthPermission _AS = new AuthPermission("doAs"); //$NON-NLS-1$
046:
047: private static final AuthPermission _AS_PRIVILEGED = new AuthPermission(
048: "doAsPrivileged"); //$NON-NLS-1$
049:
050: private static final AuthPermission _SUBJECT = new AuthPermission(
051: "getSubject"); //$NON-NLS-1$
052:
053: private static final AuthPermission _PRINCIPALS = new AuthPermission(
054: "modifyPrincipals"); //$NON-NLS-1$
055:
056: private static final AuthPermission _PRIVATE_CREDENTIALS = new AuthPermission(
057: "modifyPrivateCredentials"); //$NON-NLS-1$
058:
059: private static final AuthPermission _PUBLIC_CREDENTIALS = new AuthPermission(
060: "modifyPublicCredentials"); //$NON-NLS-1$
061:
062: private static final AuthPermission _READ_ONLY = new AuthPermission(
063: "setReadOnly"); //$NON-NLS-1$
064:
065: private final Set<Principal> principals;
066:
067: private boolean readOnly;
068:
069: // set of private credentials
070: private transient SecureSet<Object> privateCredentials;
071:
072: // set of public credentials
073: private transient SecureSet<Object> publicCredentials;
074:
075: public Subject() {
076: super ();
077: principals = new SecureSet<Principal>(_PRINCIPALS);
078: publicCredentials = new SecureSet<Object>(_PUBLIC_CREDENTIALS);
079: privateCredentials = new SecureSet<Object>(_PRIVATE_CREDENTIALS);
080:
081: readOnly = false;
082: }
083:
084: public Subject(boolean readOnly,
085: Set<? extends Principal> subjPrincipals,
086: Set<?> pubCredentials, Set<?> privCredentials) {
087:
088: if (subjPrincipals == null || pubCredentials == null
089: || privCredentials == null) {
090: throw new NullPointerException();
091: }
092:
093: principals = new SecureSet<Principal>(_PRINCIPALS,
094: subjPrincipals);
095: publicCredentials = new SecureSet<Object>(_PUBLIC_CREDENTIALS,
096: pubCredentials);
097: privateCredentials = new SecureSet<Object>(
098: _PRIVATE_CREDENTIALS, privCredentials);
099:
100: this .readOnly = readOnly;
101: }
102:
103: @SuppressWarnings("unchecked")
104: public static Object doAs(Subject subject, PrivilegedAction action) {
105:
106: checkPermission(_AS);
107:
108: return doAs_PrivilegedAction(subject, action, AccessController
109: .getContext());
110: }
111:
112: @SuppressWarnings("unchecked")
113: public static Object doAsPrivileged(Subject subject,
114: PrivilegedAction action, AccessControlContext context) {
115:
116: checkPermission(_AS_PRIVILEGED);
117:
118: if (context == null) {
119: return doAs_PrivilegedAction(subject, action,
120: new AccessControlContext(new ProtectionDomain[0]));
121: }
122: return doAs_PrivilegedAction(subject, action, context);
123: }
124:
125: // instantiates a new context and passes it to AccessController
126: @SuppressWarnings("unchecked")
127: private static Object doAs_PrivilegedAction(Subject subject,
128: PrivilegedAction action, final AccessControlContext context) {
129:
130: AccessControlContext newContext;
131:
132: final SubjectDomainCombiner combiner;
133: if (subject == null) {
134: // performance optimization
135: // if subject is null there is nothing to combine
136: combiner = null;
137: } else {
138: combiner = new SubjectDomainCombiner(subject);
139: }
140:
141: PrivilegedAction dccAction = new PrivilegedAction() {
142: public Object run() {
143:
144: return new AccessControlContext(context, combiner);
145: }
146: };
147:
148: newContext = (AccessControlContext) AccessController
149: .doPrivileged(dccAction);
150:
151: return AccessController.doPrivileged(action, newContext);
152: }
153:
154: @SuppressWarnings("unchecked")
155: public static Object doAs(Subject subject,
156: PrivilegedExceptionAction action)
157: throws PrivilegedActionException {
158:
159: checkPermission(_AS);
160:
161: return doAs_PrivilegedExceptionAction(subject, action,
162: AccessController.getContext());
163: }
164:
165: @SuppressWarnings("unchecked")
166: public static Object doAsPrivileged(Subject subject,
167: PrivilegedExceptionAction action,
168: AccessControlContext context)
169: throws PrivilegedActionException {
170:
171: checkPermission(_AS_PRIVILEGED);
172:
173: if (context == null) {
174: return doAs_PrivilegedExceptionAction(subject, action,
175: new AccessControlContext(new ProtectionDomain[0]));
176: }
177: return doAs_PrivilegedExceptionAction(subject, action, context);
178: }
179:
180: // instantiates a new context and passes it to AccessController
181: @SuppressWarnings("unchecked")
182: private static Object doAs_PrivilegedExceptionAction(
183: Subject subject, PrivilegedExceptionAction action,
184: final AccessControlContext context)
185: throws PrivilegedActionException {
186:
187: AccessControlContext newContext;
188:
189: final SubjectDomainCombiner combiner;
190: if (subject == null) {
191: // performance optimization
192: // if subject is null there is nothing to combine
193: combiner = null;
194: } else {
195: combiner = new SubjectDomainCombiner(subject);
196: }
197:
198: PrivilegedAction<AccessControlContext> dccAction = new PrivilegedAction<AccessControlContext>() {
199: public AccessControlContext run() {
200: return new AccessControlContext(context, combiner);
201: }
202: };
203:
204: newContext = AccessController.doPrivileged(dccAction);
205:
206: return AccessController.doPrivileged(action, newContext);
207: }
208:
209: @Override
210: public boolean equals(Object obj) {
211:
212: if (this == obj) {
213: return true;
214: }
215:
216: if (obj == null || this .getClass() != obj.getClass()) {
217: return false;
218: }
219:
220: Subject that = (Subject) obj;
221:
222: if (principals.equals(that.principals)
223: && publicCredentials.equals(that.publicCredentials)
224: && privateCredentials.equals(that.privateCredentials)) {
225: return true;
226: }
227: return false;
228: }
229:
230: public Set<Principal> getPrincipals() {
231: return principals;
232: }
233:
234: public <T extends Principal> Set<T> getPrincipals(Class<T> c) {
235: return ((SecureSet<Principal>) principals).get(c);
236: }
237:
238: public Set<Object> getPrivateCredentials() {
239: return privateCredentials;
240: }
241:
242: public <T> Set<T> getPrivateCredentials(Class<T> c) {
243: return privateCredentials.get(c);
244: }
245:
246: public Set<Object> getPublicCredentials() {
247: return publicCredentials;
248: }
249:
250: public <T> Set<T> getPublicCredentials(Class<T> c) {
251: return publicCredentials.get(c);
252: }
253:
254: @Override
255: public int hashCode() {
256: return principals.hashCode() + privateCredentials.hashCode()
257: + publicCredentials.hashCode();
258: }
259:
260: public void setReadOnly() {
261: checkPermission(_READ_ONLY);
262:
263: readOnly = true;
264: }
265:
266: public boolean isReadOnly() {
267: return readOnly;
268: }
269:
270: @Override
271: public String toString() {
272:
273: StringBuffer buf = new StringBuffer("Subject:\n"); //$NON-NLS-1$
274:
275: Iterator<?> it = principals.iterator();
276: while (it.hasNext()) {
277: buf.append("\tPrincipal: "); //$NON-NLS-1$
278: buf.append(it.next());
279: buf.append('\n');
280: }
281:
282: it = publicCredentials.iterator();
283: while (it.hasNext()) {
284: buf.append("\tPublic Credential: "); //$NON-NLS-1$
285: buf.append(it.next());
286: buf.append('\n');
287: }
288:
289: int offset = buf.length() - 1;
290: it = privateCredentials.iterator();
291: try {
292: while (it.hasNext()) {
293: buf.append("\tPrivate Credential: "); //$NON-NLS-1$
294: buf.append(it.next());
295: buf.append('\n');
296: }
297: } catch (SecurityException e) {
298: buf.delete(offset, buf.length());
299: buf
300: .append("\tPrivate Credentials: no accessible information\n"); //$NON-NLS-1$
301: }
302: return buf.toString();
303: }
304:
305: private void readObject(ObjectInputStream in) throws IOException,
306: ClassNotFoundException {
307:
308: in.defaultReadObject();
309:
310: publicCredentials = new SecureSet<Object>(_PUBLIC_CREDENTIALS);
311: privateCredentials = new SecureSet<Object>(_PRIVATE_CREDENTIALS);
312: }
313:
314: private void writeObject(ObjectOutputStream out) throws IOException {
315: out.defaultWriteObject();
316: }
317:
318: public static Subject getSubject(final AccessControlContext context) {
319: checkPermission(_SUBJECT);
320: if (context == null) {
321: throw new NullPointerException(Messages
322: .getString("auth.09")); //$NON-NLS-1$
323: }
324: PrivilegedAction<DomainCombiner> action = new PrivilegedAction<DomainCombiner>() {
325: public DomainCombiner run() {
326: return context.getDomainCombiner();
327: }
328: };
329: DomainCombiner combiner = AccessController.doPrivileged(action);
330:
331: if ((combiner == null)
332: || !(combiner instanceof SubjectDomainCombiner)) {
333: return null;
334: }
335: return ((SubjectDomainCombiner) combiner).getSubject();
336: }
337:
338: // checks passed permission
339: private static void checkPermission(Permission p) {
340: SecurityManager sm = System.getSecurityManager();
341: if (sm != null) {
342: sm.checkPermission(p);
343: }
344: }
345:
346: // FIXME is used only in two places. remove?
347: private void checkState() {
348: if (readOnly) {
349: throw new IllegalStateException(Messages
350: .getString("auth.0A")); //$NON-NLS-1$
351: }
352: }
353:
354: private final class SecureSet<SST> extends AbstractSet<SST>
355: implements Serializable {
356:
357: /**
358: * Compatibility issue: see comments for setType variable
359: */
360: private static final long serialVersionUID = 7911754171111800359L;
361:
362: private LinkedList<SST> elements;
363:
364: /*
365: * Is used to define a set type for serialization.
366: *
367: * A type can be principal, priv. or pub. credential set. The spec.
368: * doesn't clearly says that priv. and pub. credential sets can be
369: * serialized and what classes they are. It is only possible to figure
370: * out from writeObject method comments that priv. credential set is
371: * serializable and it is an instance of SecureSet class. So pub.
372: * credential was implemented by analogy
373: *
374: * Compatibility issue: the class follows its specified serial form.
375: * Also according to the serialization spec. adding new field is a
376: * compatible change. So is ok for principal set (because the default
377: * value for integer is zero). But priv. or pub. credential set it is
378: * not compatible because most probably other implementations resolve
379: * this issue in other way
380: */
381: private int setType;
382:
383: // Defines principal set for serialization.
384: private static final int SET_Principal = 0;
385:
386: // Defines private credential set for serialization.
387: private static final int SET_PrivCred = 1;
388:
389: // Defines public credential set for serialization.
390: private static final int SET_PubCred = 2;
391:
392: // permission required to modify set
393: private transient AuthPermission permission;
394:
395: protected SecureSet(AuthPermission perm) {
396: permission = perm;
397: elements = new LinkedList<SST>();
398: }
399:
400: // creates set from specified collection with specified permission
401: // all collection elements are verified before adding
402: protected SecureSet(AuthPermission perm,
403: Collection<? extends SST> s) {
404: this (perm);
405:
406: // Subject's constructor receives a Set, we can trusts if a set is from bootclasspath,
407: // and not to check whether it contains duplicates or not
408: boolean trust = s.getClass().getClassLoader() == null;
409:
410: Iterator<? extends SST> it = s.iterator();
411: while (it.hasNext()) {
412: SST o = it.next();
413: verifyElement(o);
414: if (trust || !elements.contains(o)) {
415: elements.add(o);
416: }
417: }
418: }
419:
420: // verifies new set element
421: private void verifyElement(Object o) {
422:
423: if (o == null) {
424: throw new NullPointerException();
425: }
426: if (permission == _PRINCIPALS
427: && !(Principal.class.isAssignableFrom(o.getClass()))) {
428: throw new IllegalArgumentException(Messages
429: .getString("auth.0B")); //$NON-NLS-1$
430: }
431: }
432:
433: /*
434: * verifies specified element, checks set state, and security permission
435: * to modify set before adding new element
436: */
437: @Override
438: public boolean add(SST o) {
439:
440: verifyElement(o);
441:
442: checkState();
443: checkPermission(permission);
444:
445: if (!elements.contains(o)) {
446: elements.add(o);
447: return true;
448: }
449: return false;
450: }
451:
452: // returns an instance of SecureIterator
453: @Override
454: public Iterator<SST> iterator() {
455:
456: if (permission == _PRIVATE_CREDENTIALS) {
457: /*
458: * private credential set requires iterator with additional
459: * security check (PrivateCredentialPermission)
460: */
461: return new SecureIterator(elements.iterator()) {
462: /*
463: * checks permission to access next private credential moves
464: * to the next element even SecurityException was thrown
465: */
466: @Override
467: public SST next() {
468: SST obj = iterator.next();
469: checkPermission(new PrivateCredentialPermission(
470: obj.getClass().getName(), principals));
471: return obj;
472: }
473: };
474: }
475: return new SecureIterator(elements.iterator());
476: }
477:
478: @Override
479: public boolean retainAll(Collection<?> c) {
480:
481: if (c == null) {
482: throw new NullPointerException();
483: }
484: return super .retainAll(c);
485: }
486:
487: @Override
488: public int size() {
489: return elements.size();
490: }
491:
492: /**
493: * return set with elements that are instances or subclasses of the
494: * specified class
495: */
496: protected final <E> Set<E> get(final Class<E> c) {
497:
498: if (c == null) {
499: throw new NullPointerException();
500: }
501:
502: AbstractSet<E> s = new AbstractSet<E>() {
503: private LinkedList<E> elements = new LinkedList<E>();
504:
505: @Override
506: public boolean add(E o) {
507:
508: if (!c.isAssignableFrom(o.getClass())) {
509: throw new IllegalArgumentException(Messages
510: .getString("auth.0C", c.getName())); //$NON-NLS-1$
511: }
512:
513: if (elements.contains(o)) {
514: return false;
515: }
516: elements.add(o);
517: return true;
518: }
519:
520: @Override
521: public Iterator<E> iterator() {
522: return elements.iterator();
523: }
524:
525: @Override
526: public boolean retainAll(Collection<?> c) {
527:
528: if (c == null) {
529: throw new NullPointerException();
530: }
531: return super .retainAll(c);
532: }
533:
534: @Override
535: public int size() {
536: return elements.size();
537: }
538: };
539:
540: // FIXME must have permissions for requested priv. credentials
541: for (Iterator<SST> it = iterator(); it.hasNext();) {
542: SST o = it.next();
543: if (c.isAssignableFrom(o.getClass())) {
544: s.add(c.cast(o));
545: }
546: }
547: return s;
548: }
549:
550: private void readObject(ObjectInputStream in)
551: throws IOException, ClassNotFoundException {
552: in.defaultReadObject();
553:
554: switch (setType) {
555: case SET_Principal:
556: permission = _PRINCIPALS;
557: break;
558: case SET_PrivCred:
559: permission = _PRIVATE_CREDENTIALS;
560: break;
561: case SET_PubCred:
562: permission = _PUBLIC_CREDENTIALS;
563: break;
564: default:
565: throw new IllegalArgumentException();
566: }
567:
568: Iterator<SST> it = elements.iterator();
569: while (it.hasNext()) {
570: verifyElement(it.next());
571: }
572: }
573:
574: private void writeObject(ObjectOutputStream out)
575: throws IOException {
576:
577: if (permission == _PRIVATE_CREDENTIALS) {
578: // does security check for each private credential
579: for (Iterator<SST> it = iterator(); it.hasNext();) {
580: it.next();
581: }
582: setType = SET_PrivCred;
583: } else if (permission == _PRINCIPALS) {
584: setType = SET_Principal;
585: } else {
586: setType = SET_PubCred;
587: }
588:
589: out.defaultWriteObject();
590: }
591:
592: /**
593: * Represents iterator for subject's secure set
594: */
595: private class SecureIterator implements Iterator<SST> {
596: protected Iterator<SST> iterator;
597:
598: protected SecureIterator(Iterator<SST> iterator) {
599: this .iterator = iterator;
600: }
601:
602: public boolean hasNext() {
603: return iterator.hasNext();
604: }
605:
606: public SST next() {
607: return iterator.next();
608: }
609:
610: /**
611: * checks set state, and security permission to modify set before
612: * removing current element
613: */
614: public void remove() {
615: checkState();
616: checkPermission(permission);
617: iterator.remove();
618: }
619: }
620: }
621: }
|