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, final Set principals,
  95:                   final Set pubCred, final Set privCred)
  96:   {
  97:     if (principals == null || pubCred == null || privCred == null)
  98:       {
  99:         throw new NullPointerException();
 100:       }
 101:     this.principals = new SecureSet (this, SecureSet.PRINCIPALS, principals);
 102:     this.pubCred = new SecureSet (this, SecureSet.PUBLIC_CREDENTIALS, pubCred);
 103:     this.privCred = new SecureSet (this, SecureSet.PRIVATE_CREDENTIALS, privCred);
 104:     this.readOnly = readOnly;
 105:   }
 106: 
 107:   // Class methods.
 108:   // -------------------------------------------------------------------------
 109: 
 110:   /**
 111:    * <p>Returns the subject associated with the given {@link
 112:    * AccessControlContext}.</p>
 113:    *
 114:    * <p>All this method does is retrieve the Subject object from the supplied
 115:    * context's {@link DomainCombiner}, if any, and if it is an instance of
 116:    * a {@link SubjectDomainCombiner}.
 117:    *
 118:    * @param context The context to retrieve the subject from.
 119:    * @return The subject assoctiated with the context, or <code>null</code>
 120:    *  if there is none.
 121:    * @throws NullPointerException If <i>subject</i> is null.
 122:    * @throws SecurityException If the caller does not have permission to get
 123:    *  the subject (<code>"getSubject"</code> target of {@link AuthPermission}.
 124:    */
 125:   public static Subject getSubject (final AccessControlContext context)
 126:   {
 127:     final SecurityManager sm = System.getSecurityManager();
 128:     if (sm != null)
 129:       {
 130:         sm.checkPermission (new AuthPermission ("getSubject"));
 131:       }
 132:     DomainCombiner dc = context.getDomainCombiner();
 133:     if (!(dc instanceof SubjectDomainCombiner))
 134:       {
 135:         return null;
 136:       }
 137:     return ((SubjectDomainCombiner) dc).getSubject();
 138:   }
 139: 
 140:   /**
 141:    * <p>Run a method as another subject. This method will obtain the current
 142:    * {@link AccessControlContext} for this thread, then creates another with
 143:    * a {@link SubjectDomainCombiner} with the given subject. The supplied
 144:    * action will then be run with the modified context.</p>
 145:    *
 146:    * @param subject The subject to run as.
 147:    * @param action The action to run.
 148:    * @return The value returned by the privileged action.
 149:    * @throws SecurityException If the caller is not allowed to run under a
 150:    *  different identity (<code>"doAs"</code> target of {@link AuthPermission}.
 151:    */
 152:   public static Object doAs (final Subject subject, final PrivilegedAction action)
 153:   {
 154:     final SecurityManager sm = System.getSecurityManager();
 155:     if (sm != null)
 156:       {
 157:         sm.checkPermission (new AuthPermission ("doAs"));
 158:       }
 159:     AccessControlContext context =
 160:       new AccessControlContext (AccessController.getContext(),
 161:                                 new SubjectDomainCombiner (subject));
 162:     return AccessController.doPrivileged (action, context);
 163:   }
 164: 
 165:   /**
 166:    * <p>Run a method as another subject. This method will obtain the current
 167:    * {@link AccessControlContext} for this thread, then creates another with
 168:    * a {@link SubjectDomainCombiner} with the given subject. The supplied
 169:    * action will then be run with the modified context.</p>
 170:    *
 171:    * @param subject The subject to run as.
 172:    * @param action The action to run.
 173:    * @return The value returned by the privileged action.
 174:    * @throws SecurityException If the caller is not allowed to run under a
 175:    *  different identity (<code>"doAs"</code> target of {@link AuthPermission}.
 176:    * @throws PrivilegedActionException If the action throws an exception.
 177:    */
 178:   public static Object doAs (final Subject subject,
 179:                              final PrivilegedExceptionAction action)
 180:     throws PrivilegedActionException
 181:   {
 182:     final SecurityManager sm = System.getSecurityManager();
 183:     if (sm != null)
 184:       {
 185:         sm.checkPermission (new AuthPermission ("doAs"));
 186:       }
 187:     AccessControlContext context =
 188:       new AccessControlContext (AccessController.getContext(),
 189:                                 new SubjectDomainCombiner(subject));
 190:     return AccessController.doPrivileged (action, context);
 191:   }
 192: 
 193:   /**
 194:    * <p>Run a method as another subject. This method will create a new
 195:    * {@link AccessControlContext} derived from the given one, with a
 196:    * {@link SubjectDomainCombiner} with the given subject. The supplied
 197:    * action will then be run with the modified context.</p>
 198:    *
 199:    * @param subject The subject to run as.
 200:    * @param action The action to run.
 201:    * @param acc The context to use.
 202:    * @return The value returned by the privileged action.
 203:    * @throws SecurityException If the caller is not allowed to run under a
 204:    *  different identity (<code>"doAsPrivileged"</code> target of {@link
 205:    *  AuthPermission}.
 206:    */
 207:   public static Object doAsPrivileged (final Subject subject,
 208:                                        final PrivilegedAction action,
 209:                                        final AccessControlContext acc)
 210:   {
 211:     final SecurityManager sm = System.getSecurityManager();
 212:     if (sm != null)
 213:       {
 214:         sm.checkPermission (new AuthPermission ("doAsPrivileged"));
 215:       }
 216:     AccessControlContext context =
 217:       new AccessControlContext (acc, new SubjectDomainCombiner (subject));
 218:     return AccessController.doPrivileged (action, context);
 219:   }
 220: 
 221:   /**
 222:    * <p>Run a method as another subject. This method will create a new
 223:    * {@link AccessControlContext} derived from the given one, with a
 224:    * {@link SubjectDomainCombiner} with the given subject. The supplied
 225:    * action will then be run with the modified context.</p>
 226:    *
 227:    * @param subject The subject to run as.
 228:    * @param action The action to run.
 229:    * @param acc The context to use.
 230:    * @return The value returned by the privileged action.
 231:    * @throws SecurityException If the caller is not allowed to run under a
 232:    *  different identity (<code>"doAsPrivileged"</code> target of
 233:    *  {@link AuthPermission}.
 234:    * @throws PrivilegedActionException If the action throws an exception.
 235:    */
 236:   public static Object doAsPrivileged (final Subject subject,
 237:                                        final PrivilegedExceptionAction action,
 238:                        AccessControlContext acc)
 239:     throws PrivilegedActionException
 240:   {
 241:     final SecurityManager sm = System.getSecurityManager();
 242:     if (sm != null)
 243:       {
 244:         sm.checkPermission (new AuthPermission ("doAsPrivileged"));
 245:       }
 246:     if (acc == null)
 247:       acc = new AccessControlContext (new java.security.ProtectionDomain[0]);
 248:     AccessControlContext context =
 249:       new AccessControlContext (acc, new SubjectDomainCombiner (subject));
 250:     return AccessController.doPrivileged (action, context);
 251:   }
 252: 
 253:   // Instance methods.
 254:   // -------------------------------------------------------------------------
 255: 
 256:   public boolean equals (Object o)
 257:   {
 258:     if (!(o instanceof Subject))
 259:       {
 260:         return false;
 261:       }
 262:     Subject that = (Subject) o;
 263:     return principals.containsAll (that.getPrincipals()) &&
 264:       pubCred.containsAll (that.getPublicCredentials()) &&
 265:       privCred.containsAll (that.getPrivateCredentials());
 266:   }
 267: 
 268:   public Set getPrincipals()
 269:   {
 270:     return principals;
 271:   }
 272: 
 273:   public Set getPrincipals(Class clazz)
 274:   {
 275:     HashSet result = new HashSet (principals.size());
 276:     for (Iterator it = principals.iterator(); it.hasNext(); )
 277:       {
 278:         Object o = it.next();
 279:         if (o != null && clazz.isAssignableFrom (o.getClass()))
 280:           {
 281:             result.add(o);
 282:           }
 283:       }
 284:     return Collections.unmodifiableSet (result);
 285:   }
 286: 
 287:   public Set getPrivateCredentials()
 288:   {
 289:     return privCred;
 290:   }
 291: 
 292:   public Set getPrivateCredentials (Class clazz)
 293:   {
 294:     HashSet result = new HashSet (privCred.size());
 295:     for (Iterator it = privCred.iterator(); it.hasNext(); )
 296:       {
 297:         Object o = it.next();
 298:         if (o != null && clazz.isAssignableFrom (o.getClass()))
 299:           {
 300:             result.add(o);
 301:           }
 302:       }
 303:     return Collections.unmodifiableSet (result);
 304:   }
 305: 
 306:   public Set getPublicCredentials()
 307:   {
 308:     return pubCred;
 309:   }
 310: 
 311:   public Set getPublicCredentials (Class clazz)
 312:   {
 313:     HashSet result = new HashSet (pubCred.size());
 314:     for (Iterator it = pubCred.iterator(); it.hasNext(); )
 315:       {
 316:         Object o = it.next();
 317:         if (o != null && clazz.isAssignableFrom (o.getClass()))
 318:           {
 319:             result.add(o);
 320:           }
 321:       }
 322:     return Collections.unmodifiableSet (result);
 323:   }
 324: 
 325:   public int hashCode()
 326:   {
 327:     return principals.hashCode() + privCred.hashCode() + pubCred.hashCode();
 328:   }
 329: 
 330:   /**
 331:    * <p>Returns whether or not this subject is read-only.</p>
 332:    *
 333:    * @return True is this subject is read-only.
 334:    */
 335:   public boolean isReadOnly()
 336:   {
 337:     return readOnly;
 338:   }
 339: 
 340:   /**
 341:    * <p>Marks this subject as read-only.</p>
 342:    *
 343:    * @throws SecurityException If the caller does not have permission to
 344:    *  set this subject as read-only (<code>"setReadOnly"</code> target of
 345:    *  {@link AuthPermission}.
 346:    */
 347:   public void setReadOnly()
 348:   {
 349:     final SecurityManager sm = System.getSecurityManager();
 350:     if (sm != null)
 351:       {
 352:         sm.checkPermission (new AuthPermission ("setReadOnly"));
 353:       }
 354:     readOnly = true;
 355:   }
 356: 
 357:   public String toString()
 358:   {
 359:     return Subject.class.getName() + " [ principals=" + principals +
 360:       ", private credentials=" + privCred + ", public credentials=" +
 361:       pubCred + ", read-only=" + readOnly + " ]";
 362:   }
 363: 
 364: // Inner class.
 365:   // -------------------------------------------------------------------------
 366: 
 367:   /**
 368:    * An undocumented inner class that is used for sets in the parent class.
 369:    */
 370:   private static class SecureSet extends AbstractSet implements Serializable
 371:   {
 372:     // Fields.
 373:     // -----------------------------------------------------------------------
 374: 
 375:     private static final long serialVersionUID = 7911754171111800359L;
 376: 
 377:     static final int PRINCIPALS = 0;
 378:     static final int PUBLIC_CREDENTIALS = 1;
 379:     static final int PRIVATE_CREDENTIALS = 2;
 380: 
 381:     private final Subject subject;
 382:     private final LinkedList elements;
 383:     private final transient int type;
 384: 
 385:     // Constructors.
 386:     // -----------------------------------------------------------------------
 387: 
 388:     SecureSet (final Subject subject, final int type, final Collection inElements)
 389:     {
 390:       this (subject, type);
 391:       for (Iterator it = inElements.iterator(); it.hasNext(); )
 392:         {
 393:           Object o = it.next();
 394:           if (type == PRINCIPALS && !(o instanceof Principal))
 395:             {
 396:               throw new IllegalArgumentException(o+" is not a Principal");
 397:             }
 398:           if (!this.elements.contains (o))
 399:             {
 400:               this.elements.add (o);
 401:             }
 402:         }
 403:     }
 404: 
 405:     SecureSet (final Subject subject, final int type)
 406:     {
 407:       this.subject = subject;
 408:       this.type = type;
 409:       this.elements = new LinkedList();
 410:     }
 411: 
 412:     // Instance methods.
 413:     // -----------------------------------------------------------------------
 414: 
 415:     public synchronized int size()
 416:     {
 417:       return elements.size();
 418:     }
 419: 
 420:     public Iterator iterator()
 421:     {
 422:       return elements.iterator();
 423:     }
 424: 
 425:     public synchronized boolean add(Object element)
 426:     {
 427:       if (subject.isReadOnly())
 428:         {
 429:           throw new IllegalStateException ("subject is read-only");
 430:         }
 431:       final SecurityManager sm = System.getSecurityManager();
 432:       switch (type)
 433:         {
 434:         case PRINCIPALS:
 435:           if (sm != null)
 436:             {
 437:               sm.checkPermission (new AuthPermission ("modifyPrincipals"));
 438:             }
 439:           if (!(element instanceof Principal))
 440:             {
 441:               throw new IllegalArgumentException ("element is not a Principal");
 442:             }
 443:           break;
 444: 
 445:         case PUBLIC_CREDENTIALS:
 446:           if (sm != null)
 447:             {
 448:               sm.checkPermission (new AuthPermission ("modifyPublicCredentials"));
 449:             }
 450:           break;
 451: 
 452:         case PRIVATE_CREDENTIALS:
 453:           if (sm != null)
 454:             {
 455:               sm.checkPermission (new AuthPermission ("modifyPrivateCredentials"));
 456:             }
 457:           break;
 458: 
 459:         default:
 460:           throw new Error ("this statement should be unreachable");
 461:         }
 462: 
 463:       if (elements.contains (element))
 464:         {
 465:           return false;
 466:         }
 467: 
 468:       return elements.add (element);
 469:     }
 470: 
 471:     public synchronized boolean remove (final Object element)
 472:     {
 473:       if (subject.isReadOnly())
 474:         {
 475:           throw new IllegalStateException ("subject is read-only");
 476:         }
 477:       final SecurityManager sm = System.getSecurityManager();
 478:       switch (type)
 479:         {
 480:         case PRINCIPALS:
 481:           if (sm != null)
 482:             {
 483:               sm.checkPermission (new AuthPermission ("modifyPrincipals"));
 484:             }
 485:           if (!(element instanceof Principal))
 486:             {
 487:               throw new IllegalArgumentException ("element is not a Principal");
 488:             }
 489:           break;
 490: 
 491:         case PUBLIC_CREDENTIALS:
 492:           if (sm != null)
 493:             {
 494:               sm.checkPermission (new AuthPermission ("modifyPublicCredentials"));
 495:             }
 496:           break;
 497: 
 498:         case PRIVATE_CREDENTIALS:
 499:           if (sm != null)
 500:             {
 501:               sm.checkPermission (new AuthPermission ("modifyPrivateCredentials"));
 502:             }
 503:           break;
 504: 
 505:         default:
 506:           throw new Error("this statement should be unreachable");
 507:         }
 508: 
 509:       return elements.remove(element);
 510:     }
 511: 
 512:     public synchronized boolean contains (final Object element)
 513:     {
 514:       return elements.contains (element);
 515:     }
 516: 
 517:     public boolean removeAll (final Collection c)
 518:     {
 519:       if (subject.isReadOnly())
 520:         {
 521:           throw new IllegalStateException ("subject is read-only");
 522:         }
 523:       return super.removeAll (c);
 524:     }
 525: 
 526:     public boolean retainAll (final Collection c)
 527:     {
 528:       if (subject.isReadOnly())
 529:         {
 530:           throw new IllegalStateException ("subject is read-only");
 531:         }
 532:       return super.retainAll (c);
 533:     }
 534: 
 535:     public void clear()
 536:     {
 537:       if (subject.isReadOnly())
 538:         {
 539:           throw new IllegalStateException ("subject is read-only");
 540:         }
 541:       elements.clear();
 542:     }
 543: 
 544:     private synchronized void writeObject (ObjectOutputStream out)
 545:       throws IOException
 546:     {
 547:       throw new UnsupportedOperationException ("FIXME: determine serialization");
 548:     }
 549: 
 550:     private void readObject (ObjectInputStream in)
 551:       throws ClassNotFoundException, IOException
 552:     {
 553:       throw new UnsupportedOperationException ("FIXME: determine serialization");
 554:     }
 555:   }
 556: }