Source for javax.security.auth.Subject

   1: /* Subject.java -- a single entity in the system.
   2:    Copyright (C) 2004, 2005 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.security.auth;
  40: 
  41: import java.io.IOException;
  42: import java.io.ObjectInputStream;
  43: import java.io.ObjectOutputStream;
  44: import java.io.Serializable;
  45: 
  46: import java.security.AccessControlContext;
  47: import java.security.AccessController;
  48: import java.security.DomainCombiner;
  49: import java.security.Principal;
  50: import java.security.PrivilegedAction;
  51: import java.security.PrivilegedActionException;
  52: import java.security.PrivilegedExceptionAction;
  53: 
  54: import java.util.AbstractSet;
  55: import java.util.Collection;
  56: import java.util.Collections;
  57: import java.util.HashSet;
  58: import java.util.Iterator;
  59: import java.util.LinkedList;
  60: import java.util.Set;
  61: 
  62: public final class Subject implements Serializable
  63: {
  64:   // Fields.
  65:   // -------------------------------------------------------------------------
  66: 
  67:   private static final long serialVersionUID = -8308522755600156056L;
  68: 
  69:   /**
  70:    * @serial The set of principals. The type of this field is SecureSet, a
  71:    *  private inner class.
  72:    */
  73:   private final Set principals;
  74: 
  75:   /**
  76:    * @serial The read-only flag.
  77:    */
  78:   private boolean readOnly;
  79: 
  80:   private final transient SecureSet pubCred;
  81:   private final transient SecureSet privCred;
  82: 
  83:   // Constructors.
  84:   // -------------------------------------------------------------------------
  85: 
  86:   public Subject()
  87:   {
  88:     principals = new SecureSet (this, SecureSet.PRINCIPALS);
  89:     pubCred = new SecureSet (this, SecureSet.PUBLIC_CREDENTIALS);
  90:     privCred = new SecureSet (this, SecureSet.PRIVATE_CREDENTIALS);
  91:     readOnly = false;
  92:   }
  93: 
  94:   public Subject (final boolean readOnly,
  95:                   final Set<? extends Principal> principals,
  96:                   final Set<?> pubCred, final Set<?> privCred)
  97:   {
  98:     if (principals == null || pubCred == null || privCred == null)
  99:       {
 100:         throw new NullPointerException();
 101:       }
 102:     this.principals = new SecureSet (this, SecureSet.PRINCIPALS, principals);
 103:     this.pubCred = new SecureSet (this, SecureSet.PUBLIC_CREDENTIALS, pubCred);
 104:     this.privCred = new SecureSet (this, SecureSet.PRIVATE_CREDENTIALS, privCred);
 105:     this.readOnly = readOnly;
 106:   }
 107: 
 108:   // Class methods.
 109:   // -------------------------------------------------------------------------
 110: 
 111:   /**
 112:    * <p>Returns the subject associated with the given {@link
 113:    * AccessControlContext}.</p>
 114:    *
 115:    * <p>All this method does is retrieve the Subject object from the supplied
 116:    * context's {@link DomainCombiner}, if any, and if it is an instance of
 117:    * a {@link SubjectDomainCombiner}.
 118:    *
 119:    * @param context The context to retrieve the subject from.
 120:    * @return The subject assoctiated with the context, or <code>null</code>
 121:    *  if there is none.
 122:    * @throws NullPointerException If <i>subject</i> is null.
 123:    * @throws SecurityException If the caller does not have permission to get
 124:    *  the subject (<code>"getSubject"</code> target of {@link AuthPermission}.
 125:    */
 126:   public static Subject getSubject (final AccessControlContext context)
 127:   {
 128:     final SecurityManager sm = System.getSecurityManager();
 129:     if (sm != null)
 130:       {
 131:         sm.checkPermission (new AuthPermission ("getSubject"));
 132:       }
 133:     DomainCombiner dc = context.getDomainCombiner();
 134:     if (!(dc instanceof SubjectDomainCombiner))
 135:       {
 136:         return null;
 137:       }
 138:     return ((SubjectDomainCombiner) dc).getSubject();
 139:   }
 140: 
 141:   /**
 142:    * <p>Run a method as another subject. This method will obtain the current
 143:    * {@link AccessControlContext} for this thread, then creates another with
 144:    * a {@link SubjectDomainCombiner} with the given subject. The supplied
 145:    * action will then be run with the modified context.</p>
 146:    *
 147:    * @param subject The subject to run as.
 148:    * @param action The action to run.
 149:    * @return The value returned by the privileged action.
 150:    * @throws SecurityException If the caller is not allowed to run under a
 151:    *  different identity (<code>"doAs"</code> target of {@link AuthPermission}.
 152:    */
 153:   public static Object doAs (final Subject subject, final PrivilegedAction action)
 154:   {
 155:     final SecurityManager sm = System.getSecurityManager();
 156:     if (sm != null)
 157:       {
 158:         sm.checkPermission (new AuthPermission ("doAs"));
 159:       }
 160:     AccessControlContext context =
 161:       new AccessControlContext (AccessController.getContext(),
 162:                                 new SubjectDomainCombiner (subject));
 163:     return AccessController.doPrivileged (action, context);
 164:   }
 165: 
 166:   /**
 167:    * <p>Run a method as another subject. This method will obtain the current
 168:    * {@link AccessControlContext} for this thread, then creates another with
 169:    * a {@link SubjectDomainCombiner} with the given subject. The supplied
 170:    * action will then be run with the modified context.</p>
 171:    *
 172:    * @param subject The subject to run as.
 173:    * @param action The action to run.
 174:    * @return The value returned by the privileged action.
 175:    * @throws SecurityException If the caller is not allowed to run under a
 176:    *  different identity (<code>"doAs"</code> target of {@link AuthPermission}.
 177:    * @throws PrivilegedActionException If the action throws an exception.
 178:    */
 179:   public static Object doAs (final Subject subject,
 180:                              final PrivilegedExceptionAction action)
 181:     throws PrivilegedActionException
 182:   {
 183:     final SecurityManager sm = System.getSecurityManager();
 184:     if (sm != null)
 185:       {
 186:         sm.checkPermission (new AuthPermission ("doAs"));
 187:       }
 188:     AccessControlContext context =
 189:       new AccessControlContext (AccessController.getContext(),
 190:                                 new SubjectDomainCombiner(subject));
 191:     return AccessController.doPrivileged (action, context);
 192:   }
 193: 
 194:   /**
 195:    * <p>Run a method as another subject. This method will create a new
 196:    * {@link AccessControlContext} derived from the given one, with a
 197:    * {@link SubjectDomainCombiner} with the given subject. The supplied
 198:    * action will then be run with the modified context.</p>
 199:    *
 200:    * @param subject The subject to run as.
 201:    * @param action The action to run.
 202:    * @param acc The context to use.
 203:    * @return The value returned by the privileged action.
 204:    * @throws SecurityException If the caller is not allowed to run under a
 205:    *  different identity (<code>"doAsPrivileged"</code> target of {@link
 206:    *  AuthPermission}.
 207:    */
 208:   public static Object doAsPrivileged (final Subject subject,
 209:                                        final PrivilegedAction action,
 210:                                        final AccessControlContext acc)
 211:   {
 212:     final SecurityManager sm = System.getSecurityManager();
 213:     if (sm != null)
 214:       {
 215:         sm.checkPermission (new AuthPermission ("doAsPrivileged"));
 216:       }
 217:     AccessControlContext context =
 218:       new AccessControlContext (acc, new SubjectDomainCombiner (subject));
 219:     return AccessController.doPrivileged (action, context);
 220:   }
 221: 
 222:   /**
 223:    * <p>Run a method as another subject. This method will create a new
 224:    * {@link AccessControlContext} derived from the given one, with a
 225:    * {@link SubjectDomainCombiner} with the given subject. The supplied
 226:    * action will then be run with the modified context.</p>
 227:    *
 228:    * @param subject The subject to run as.
 229:    * @param action The action to run.
 230:    * @param acc The context to use.
 231:    * @return The value returned by the privileged action.
 232:    * @throws SecurityException If the caller is not allowed to run under a
 233:    *  different identity (<code>"doAsPrivileged"</code> target of
 234:    *  {@link AuthPermission}.
 235:    * @throws PrivilegedActionException If the action throws an exception.
 236:    */
 237:   public static Object doAsPrivileged (final Subject subject,
 238:                                        final PrivilegedExceptionAction action,
 239:                        AccessControlContext acc)
 240:     throws PrivilegedActionException
 241:   {
 242:     final SecurityManager sm = System.getSecurityManager();
 243:     if (sm != null)
 244:       {
 245:         sm.checkPermission (new AuthPermission ("doAsPrivileged"));
 246:       }
 247:     if (acc == null)
 248:       acc = new AccessControlContext (new java.security.ProtectionDomain[0]);
 249:     AccessControlContext context =
 250:       new AccessControlContext (acc, new SubjectDomainCombiner (subject));
 251:     return AccessController.doPrivileged (action, context);
 252:   }
 253: 
 254:   // Instance methods.
 255:   // -------------------------------------------------------------------------
 256: 
 257:   public boolean equals (Object o)
 258:   {
 259:     if (!(o instanceof Subject))
 260:       {
 261:         return false;
 262:       }
 263:     Subject that = (Subject) o;
 264:     return principals.containsAll (that.getPrincipals()) &&
 265:       pubCred.containsAll (that.getPublicCredentials()) &&
 266:       privCred.containsAll (that.getPrivateCredentials());
 267:   }
 268: 
 269:   public Set<Principal> getPrincipals()
 270:   {
 271:     return principals;
 272:   }
 273: 
 274:   public <T extends Principal> Set<T> getPrincipals(Class<T> clazz)
 275:   {
 276:     HashSet result = new HashSet (principals.size());
 277:     for (Iterator it = principals.iterator(); it.hasNext(); )
 278:       {
 279:         Object o = it.next();
 280:         if (o != null && clazz.isAssignableFrom (o.getClass()))
 281:           {
 282:             result.add(o);
 283:           }
 284:       }
 285:     return Collections.unmodifiableSet (result);
 286:   }
 287: 
 288:   public Set<Object> getPrivateCredentials()
 289:   {
 290:     return privCred;
 291:   }
 292: 
 293:   public <T> Set<T> getPrivateCredentials (Class<T> clazz)
 294:   {
 295:     HashSet result = new HashSet (privCred.size());
 296:     for (Iterator it = privCred.iterator(); it.hasNext(); )
 297:       {
 298:         Object o = it.next();
 299:         if (o != null && clazz.isAssignableFrom (o.getClass()))
 300:           {
 301:             result.add(o);
 302:           }
 303:       }
 304:     return Collections.unmodifiableSet (result);
 305:   }
 306: 
 307:   public Set<Object> getPublicCredentials()
 308:   {
 309:     return pubCred;
 310:   }
 311: 
 312:   public <T> Set<T> getPublicCredentials (Class<T> clazz)
 313:   {
 314:     HashSet result = new HashSet (pubCred.size());
 315:     for (Iterator it = pubCred.iterator(); it.hasNext(); )
 316:       {
 317:         Object o = it.next();
 318:         if (o != null && clazz.isAssignableFrom (o.getClass()))
 319:           {
 320:             result.add(o);
 321:           }
 322:       }
 323:     return Collections.unmodifiableSet (result);
 324:   }
 325: 
 326:   public int hashCode()
 327:   {
 328:     return principals.hashCode() + privCred.hashCode() + pubCred.hashCode();
 329:   }
 330: 
 331:   /**
 332:    * <p>Returns whether or not this subject is read-only.</p>
 333:    *
 334:    * @return True is this subject is read-only.
 335:    */
 336:   public boolean isReadOnly()
 337:   {
 338:     return readOnly;
 339:   }
 340: 
 341:   /**
 342:    * <p>Marks this subject as read-only.</p>
 343:    *
 344:    * @throws SecurityException If the caller does not have permission to
 345:    *  set this subject as read-only (<code>"setReadOnly"</code> target of
 346:    *  {@link AuthPermission}.
 347:    */
 348:   public void setReadOnly()
 349:   {
 350:     final SecurityManager sm = System.getSecurityManager();
 351:     if (sm != null)
 352:       {
 353:         sm.checkPermission (new AuthPermission ("setReadOnly"));
 354:       }
 355:     readOnly = true;
 356:   }
 357: 
 358:   public String toString()
 359:   {
 360:     return Subject.class.getName() + " [ principals=" + principals +
 361:       ", private credentials=" + privCred + ", public credentials=" +
 362:       pubCred + ", read-only=" + readOnly + " ]";
 363:   }
 364: 
 365: // Inner class.
 366:   // -------------------------------------------------------------------------
 367: 
 368:   /**
 369:    * An undocumented inner class that is used for sets in the parent class.
 370:    */
 371:   private static class SecureSet extends AbstractSet implements Serializable
 372:   {
 373:     // Fields.
 374:     // -----------------------------------------------------------------------
 375: 
 376:     private static final long serialVersionUID = 7911754171111800359L;
 377: 
 378:     static final int PRINCIPALS = 0;
 379:     static final int PUBLIC_CREDENTIALS = 1;
 380:     static final int PRIVATE_CREDENTIALS = 2;
 381: 
 382:     private final Subject subject;
 383:     private final LinkedList elements;
 384:     private final transient int type;
 385: 
 386:     // Constructors.
 387:     // -----------------------------------------------------------------------
 388: 
 389:     SecureSet (final Subject subject, final int type, final Collection inElements)
 390:     {
 391:       this (subject, type);
 392:       for (Iterator it = inElements.iterator(); it.hasNext(); )
 393:         {
 394:           Object o = it.next();
 395:           if (type == PRINCIPALS && !(o instanceof Principal))
 396:             {
 397:               throw new IllegalArgumentException(o+" is not a Principal");
 398:             }
 399:           if (!this.elements.contains (o))
 400:             {
 401:               this.elements.add (o);
 402:             }
 403:         }
 404:     }
 405: 
 406:     SecureSet (final Subject subject, final int type)
 407:     {
 408:       this.subject = subject;
 409:       this.type = type;
 410:       this.elements = new LinkedList();
 411:     }
 412: 
 413:     // Instance methods.
 414:     // -----------------------------------------------------------------------
 415: 
 416:     public synchronized int size()
 417:     {
 418:       return elements.size();
 419:     }
 420: 
 421:     public Iterator iterator()
 422:     {
 423:       return elements.iterator();
 424:     }
 425: 
 426:     public synchronized boolean add(Object element)
 427:     {
 428:       if (subject.isReadOnly())
 429:         {
 430:           throw new IllegalStateException ("subject is read-only");
 431:         }
 432:       final SecurityManager sm = System.getSecurityManager();
 433:       switch (type)
 434:         {
 435:         case PRINCIPALS:
 436:           if (sm != null)
 437:             {
 438:               sm.checkPermission (new AuthPermission ("modifyPrincipals"));
 439:             }
 440:           if (!(element instanceof Principal))
 441:             {
 442:               throw new IllegalArgumentException ("element is not a Principal");
 443:             }
 444:           break;
 445: 
 446:         case PUBLIC_CREDENTIALS:
 447:           if (sm != null)
 448:             {
 449:               sm.checkPermission (new AuthPermission ("modifyPublicCredentials"));
 450:             }
 451:           break;
 452: 
 453:         case PRIVATE_CREDENTIALS:
 454:           if (sm != null)
 455:             {
 456:               sm.checkPermission (new AuthPermission ("modifyPrivateCredentials"));
 457:             }
 458:           break;
 459: 
 460:         default:
 461:           throw new Error ("this statement should be unreachable");
 462:         }
 463: 
 464:       if (elements.contains (element))
 465:         {
 466:           return false;
 467:         }
 468: 
 469:       return elements.add (element);
 470:     }
 471: 
 472:     public synchronized boolean remove (final Object element)
 473:     {
 474:       if (subject.isReadOnly())
 475:         {
 476:           throw new IllegalStateException ("subject is read-only");
 477:         }
 478:       final SecurityManager sm = System.getSecurityManager();
 479:       switch (type)
 480:         {
 481:         case PRINCIPALS:
 482:           if (sm != null)
 483:             {
 484:               sm.checkPermission (new AuthPermission ("modifyPrincipals"));
 485:             }
 486:           if (!(element instanceof Principal))
 487:             {
 488:               throw new IllegalArgumentException ("element is not a Principal");
 489:             }
 490:           break;
 491: 
 492:         case PUBLIC_CREDENTIALS:
 493:           if (sm != null)
 494:             {
 495:               sm.checkPermission (new AuthPermission ("modifyPublicCredentials"));
 496:             }
 497:           break;
 498: 
 499:         case PRIVATE_CREDENTIALS:
 500:           if (sm != null)
 501:             {
 502:               sm.checkPermission (new AuthPermission ("modifyPrivateCredentials"));
 503:             }
 504:           break;
 505: 
 506:         default:
 507:           throw new Error("this statement should be unreachable");
 508:         }
 509: 
 510:       return elements.remove(element);
 511:     }
 512: 
 513:     public synchronized boolean contains (final Object element)
 514:     {
 515:       return elements.contains (element);
 516:     }
 517: 
 518:     public boolean removeAll (final Collection c)
 519:     {
 520:       if (subject.isReadOnly())
 521:         {
 522:           throw new IllegalStateException ("subject is read-only");
 523:         }
 524:       return super.removeAll (c);
 525:     }
 526: 
 527:     public boolean retainAll (final Collection c)
 528:     {
 529:       if (subject.isReadOnly())
 530:         {
 531:           throw new IllegalStateException ("subject is read-only");
 532:         }
 533:       return super.retainAll (c);
 534:     }
 535: 
 536:     public void clear()
 537:     {
 538:       if (subject.isReadOnly())
 539:         {
 540:           throw new IllegalStateException ("subject is read-only");
 541:         }
 542:       elements.clear();
 543:     }
 544: 
 545:     private synchronized void writeObject (ObjectOutputStream out)
 546:       throws IOException
 547:     {
 548:       throw new UnsupportedOperationException ("FIXME: determine serialization");
 549:     }
 550: 
 551:     private void readObject (ObjectInputStream in)
 552:       throws ClassNotFoundException, IOException
 553:     {
 554:       throw new UnsupportedOperationException ("FIXME: determine serialization");
 555:     }
 556:   }
 557: }