Source for javax.management.MBeanPermission

   1: /* MBeanPermission.java -- Permissions controlling server access.
   2:    Copyright (C) 2006 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: package javax.management;
  39: 
  40: import java.security.Permission;
  41: 
  42: import java.io.IOException;
  43: import java.io.ObjectInputStream;
  44: 
  45: import java.util.HashSet;
  46: import java.util.Iterator;
  47: import java.util.Set;
  48: import java.util.TreeSet;
  49: 
  50: /**
  51:  * <p>
  52:  * Represents the permissions required to perform
  53:  * operations using the {@link MBeanServer}.  As with
  54:  * all {@link java.security.Permission} objects, an
  55:  * instance of this class either represents a permission
  56:  * already held or one that is required to access a
  57:  * particular service.  In the case of {@link MBeanPermission}s,
  58:  * implication checks are made using an instance of this class
  59:  * when a user requests an operation from the server, and a
  60:  * {@link SecurityManager} is in place.
  61:  * </p>
  62:  * <p>
  63:  * An {@link MBeanPermission} consists of four elements,
  64:  * which all have to match for the permission to be implied.
  65:  * These are as follows:
  66:  * </p>
  67:  * <ol>
  68:  * <li><strong>The action</strong>.  For a required permission,
  69:  * this is a single value.  For a permission held by the user,
  70:  * this is a list of comma-separated actions (with spaces allowed),
  71:  * or <code>*</code> (representing all actions).  {@link #getActions()}
  72:  * returns this value.</li>
  73:  * <li><strong>The class name</strong>.  For a required permission,
  74:  * this is the class name of the bean being accessed, if any.  If
  75:  * a bean isn't involved in this action, the value is <code>null</code>.
  76:  * For a permission held by the user, it has one of three values:
  77:  * <ol>
  78:  * <li>The empty string, implying any class.</li>
  79:  * <li><code>*</code>, also implying any class.</li>
  80:  * <li>A class name pattern, which may specify a single class
  81:  * (e.g. <code>java.lang.Object</code>) or a series of classes
  82:  * using the wildcard character <code>*</code> (e.g.
  83:  * <code>javax.swing.*</code>.)</li>
  84:  * </ol></li>
  85:  * <li><strong>The member</strong>.  For a required permission,
  86:  * this is the member of the bean being accessed (an attribute
  87:  * or operation), if any.  If a member of the bean isn't involved
  88:  * in this action, the value is <code>null</code>.
  89:  * For a permission held by the user, it has one of three values:
  90:  * <ol>
  91:  * <li>The empty string, implying any member.</li>
  92:  * <li><code>*</code>, also implying any member.</li>
  93:  * <li>The name of a member.</li>
  94:  * </ol></li>
  95:  * <li>The object name</strong>.  For a required permission,
  96:  * this is the {@link ObjectName} of the bean being accessed, if
  97:  * any.  If a bean isn't involved in this action, the value is
  98:  * <code>null</code>.  The name may not be a pattern.
  99:  * For a permission held by the user, it may be the empty
 100:  * string (allowing everything) or an {@link ObjectName}
 101:  * pattern.
 102:  * </li></ol>
 103:  * {@link #getName()} returns the latter three of these as a
 104:  * single string:
 105:  * </p>
 106:  * <p><code>className#member[objectName]</code></p>
 107:  * <p>
 108:  * where <code>""</code> is disallowed, as, although any of
 109:  * the elements may be omitted, not all of them should be
 110:  * left out simultaneously.  <code>"-"</code> is used to
 111:  * represent <code>null</code>.  When this occurs in a
 112:  * required permission, anything may match it.  When this
 113:  * forms part of a permission held by the user, it only
 114:  * matches another <code>null</code> value. 
 115:  * </p>
 116:  * <p>The list of valid actions is as follows:</p>
 117:  * <ul>
 118:  * <li>addNotificationListener</li>
 119:  * <li>getAttribute</li>
 120:  * <li>getClassLoader</li>
 121:  * <li>getClassLoaderFor</li>
 122:  * <li>getClassLoaderRepository</li>
 123:  * <li>getDomains</li>
 124:  * <li>getMBeanInfo</li>
 125:  * <li>getObjectInstance</li>
 126:  * <li>instantiate</li>
 127:  * <li>invoke</li>
 128:  * <li>isInstanceOf</li>
 129:  * <li>queryMBeans</li>
 130:  * <li>queryNames</li>
 131:  * <li>registerMBean</li>
 132:  * <li>removeNotificationListener</li>
 133:  * <li>setAttribute</li>
 134:  * <li>unregisterMBean</li>
 135:  * </ul>
 136:  *
 137:  * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
 138:  * @since 1.5
 139:  */
 140: public class MBeanPermission
 141:   extends Permission
 142: {
 143: 
 144:   /**
 145:    * Compatible with JDK 1.5
 146:    */
 147:   private static final long serialVersionUID = -2416928705275160661L;
 148: 
 149:   /**
 150:    * The list of actions associated with this permission.
 151:    */
 152:   private String actions;
 153:   
 154:   /**
 155:    * The list of actions as an ordered set.
 156:    */
 157:   private transient Set actionSet;
 158: 
 159:   /**
 160:    * The set of valid actions.
 161:    */
 162:   private static final Set validSet;
 163: 
 164:   /**
 165:    * Initialise the set of valid actions.
 166:    */
 167:   static 
 168:   {
 169:     validSet = new HashSet();
 170:     validSet.add("addNotificationListener");
 171:     validSet.add("getAttribute");
 172:     validSet.add("getClassLoader");
 173:     validSet.add("getClassLoaderFor");
 174:     validSet.add("getClassLoaderRepository");
 175:     validSet.add("getDomains");
 176:     validSet.add("getMBeanInfo");
 177:     validSet.add("getObjectInstance");
 178:     validSet.add("instantiate");
 179:     validSet.add("invoke");
 180:     validSet.add("isInstanceOf");
 181:     validSet.add("queryMBeans");
 182:     validSet.add("queryNames");
 183:     validSet.add("registerMBean");
 184:     validSet.add("removeNotificationListener");
 185:     validSet.add("setAttribute");
 186:     validSet.add("unregisterMBean");
 187:   }
 188: 
 189:   /**
 190:    * Constructs a new {@link MBeanPermission} with the specified name
 191:    * and actions.  The name is of the form <code>className#member[objectName]</code>,
 192:    * where each element is optional, but a completely empty or <code>null</code>
 193:    * name is disallowed.  Actions are specified as a comma-separated list
 194:    * and may also not be empty or <code>null</code>.
 195:    *
 196:    * @param name the name of the permission.
 197:    * @param actions the actions associated with this permission.
 198:    * @throws IllegalArgumentException if the name or actions are invalid.
 199:    */
 200:   public MBeanPermission(String name, String actions)
 201:   {
 202:     super(name);
 203:     if (name == null || name.length() == 0)
 204:       throw new IllegalArgumentException("The supplied name was null or empty.");
 205:     if (actions == null || actions.length() == 0)
 206:       throw new IllegalArgumentException("The supplied action list was null or empty.");
 207:     this.actions = actions;
 208:     updateActionSet();
 209:   }
 210: 
 211:   /**
 212:    * Constructs a new {@link MBeanPermission} with the specified class name,
 213:    * member, object name and actions.  The name of the permission is created
 214:    * using the form <code>className#member[objectName]</code>,
 215:    * where each element is optional, but an empty or <code>null</code>
 216:    * name is disallowed.  Actions are specified as a comma-separated list
 217:    * and may also not be empty or <code>null</code>.
 218:    *
 219:    * @param className the name of the class to which this permission applies,
 220:    *                  or either <code>null</code> or <code>"-"</code> for a
 221:    *                  value which may be implied by any class name, but not
 222:    *                  imply any class name itself.
 223:    * @param member the member of the class to which this permission applies,
 224:    *               or either <code>null</code> or <code>"-"</code> for a
 225:    *               value which may be implied by any member, but not
 226:    *               imply any member itself.
 227:    * @param objectName the {@link ObjectName} to which this permission applies,
 228:    *                  or <code>null</code> for a value which may be implied by
 229:    *                  any object name, but not imply any object name itself.
 230:    * @param actions the actions associated with this permission.
 231:    */
 232:   public MBeanPermission(String className, String member,
 233:              ObjectName name, String actions)
 234:   {
 235:     this((className == null ? "-" : className) + "#" 
 236:      + (member == null ? "-" : member) + "[" 
 237:      + (name == null ? "-" : name.toString()) + "]", actions);
 238:   }
 239: 
 240:   /**
 241:    * Returns true if the given object is also an {@link MBeanPermission}
 242:    * with the same name and actions.
 243:    *
 244:    * @param obj the object to test.
 245:    * @return true if the object is an {@link MBeanPermission} with
 246:    *         the same name and actions.
 247:    */
 248:   public boolean equals(Object obj)
 249:   {
 250:     if (obj instanceof MBeanPermission)
 251:       {
 252:     MBeanPermission p = (MBeanPermission) obj;
 253:     return (p.getName().equals(getName()) &&
 254:         p.getActions().equals(actions));
 255:       }
 256:     return false;
 257:   }
 258: 
 259:   /**
 260:    * Returns the list of actions in alphabetical order.
 261:    *
 262:    * @return the list of actions.
 263:    */
 264:   public String getActions()
 265:   {
 266:     Iterator it = actionSet.iterator();
 267:     StringBuilder builder = new StringBuilder();
 268:     while (it.hasNext())
 269:       {
 270:     builder.append(it.next());
 271:     if (it.hasNext())
 272:       builder.append(",");
 273:       }
 274:     return builder.toString();
 275:   }
 276: 
 277:   /**
 278:    * Returns the hashcode of the permission as the sum
 279:    * of the hashcodes of the name and actions.
 280:    *
 281:    * @return the hashcode of the permission.
 282:    */
 283:   public int hashCode()
 284:   {
 285:     return getName().hashCode() + actions.hashCode();
 286:   }
 287: 
 288:   /**
 289:    * <p>
 290:    * Returns true if this permission implies the supplied permission.
 291:    * This happens if the following holds:
 292:    * </p>
 293:    * <ul>
 294:    * <li>The supplied permission is an {@link MBeanPermission}</li>
 295:    * <li>The supplied permission has either a <code>null</code> classname
 296:    * or its classname matches the classname of this permission.  A
 297:    * classname of <code>"*"</code> for this permission always matches
 298:    * the classname of the supplied permission.  Generally, <code>'*'</code>
 299:    * acts as a wildcard, so <code>".*"</code> matches <code>'.'</code>
 300:    * followed by anything.</li>
 301:    * <li>The supplied permission has either a <code>null</code> member
 302:    * or its member matches the member of this permission.  A member of
 303:    * <code>"*"</code> for this permission always matches the member
 304:    * of the supplied permission.</li>
 305:    * <li>The supplied permission has either a <code>null</code> object name
 306:    * or its object name matches the object name of this permission.  If the
 307:    * object name of this permission is a pattern, {@link ObjectName#apply(ObjectName)}
 308:    * may be used as well.</li>
 309:    * <li>The supplied permission's actions are a subset of the actions
 310:    * of this permission.  If the <code>queryMBeans</code> action is presented,
 311:    * the <code>queryNames</code> action is implied.</li>
 312:    * </ul>
 313:    * 
 314:    * @param p the permission to check that this permission implies.
 315:    * @return true if this permission implies <code>p</code>.
 316:    */
 317:   public boolean implies(Permission p)
 318:   {
 319:     if (p instanceof MBeanPermission)
 320:       {
 321:     MBeanPermission mp = (MBeanPermission) p;
 322:     NameHolder pName = new NameHolder(mp.getName());
 323:     NameHolder name = new NameHolder(getName());
 324:     if (!(name.equals(pName)))
 325:       return false;
 326:     Iterator i = mp.getActionSet().iterator();
 327:     while (i.hasNext())
 328:       {
 329:         String nextAction = (String) i.next();
 330:         boolean found = actions.contains(nextAction);
 331:         if (!found)
 332:           if (nextAction.equals("queryNames"))
 333:         found = actions.contains("queryMBeans");
 334:         if (!found)
 335:           return false;
 336:       }
 337:     return true;
 338:       }
 339:     return false;
 340:   }
 341: 
 342:   /**
 343:    * Small helper class to handle deconstruction of the name.
 344:    *
 345:    * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
 346:    */
 347:   private class NameHolder
 348:   {
 349: 
 350:     /**
 351:      * The class name.
 352:      */
 353:     private String className;
 354: 
 355:     /**
 356:      * The member.
 357:      */
 358:     private String member;
 359: 
 360:     /**
 361:      * The object name.
 362:      */
 363:     private ObjectName objectName;
 364: 
 365:     /**
 366:      * Constructs a broken-down name from a given name.
 367:      *
 368:      * @param name the name to break down.
 369:      */
 370:     public NameHolder(String name)
 371:     {
 372:       String objectName = null;
 373:       int memberIndex = name.indexOf("#");
 374:       int onIndex = name.indexOf("[");
 375:       if (onIndex == -1)
 376:     {
 377:       if (memberIndex == -1)
 378:         className = name;
 379:       else
 380:         {
 381:           className = name.substring(0, memberIndex);
 382:           member = name.substring(memberIndex + 1);
 383:         }
 384:     }
 385:       else
 386:     {
 387:       if (memberIndex == -1)
 388:         {
 389:           className = name.substring(0, onIndex);
 390:           objectName = name.substring(onIndex + 1,
 391:                       name.length() - 1);
 392:         }
 393:       else
 394:         {
 395:           className = name.substring(0, memberIndex);
 396:           member = name.substring(memberIndex + 1, onIndex);
 397:           objectName = name.substring(onIndex + 1,
 398:                       name.length() - 1);
 399:         }
 400:     }      
 401:       if (className.equals("-"))
 402:     className = null;
 403:       if (member.equals("-"))
 404:     member = null;
 405:       if (objectName == null || objectName.equals("-"))
 406:     this.objectName = null;
 407:       else
 408:     try
 409:       {
 410:         this.objectName = new ObjectName(objectName);
 411:       }
 412:     catch (MalformedObjectNameException e)
 413:       {
 414:         throw (Error) 
 415:           (new InternalError("Invalid object name.").initCause(e));
 416:       }
 417:     }
 418: 
 419:     /**
 420:      * <p>
 421:      * Returns true if the supplied object is also a
 422:      * {@link NameHolder} and the following holds:
 423:      * </p>
 424:      * <ul>
 425:      * <li>The supplied classname is <code>null</code> or the two match.  A
 426:      * classname of <code>"*"</code> for this holder always matches
 427:      * the classname of the supplied holder.  Generally, <code>'*'</code>
 428:      * acts as a wildcard, so <code>".*"</code> matches <code>'.'</code>
 429:      * followed by anything.</li>
 430:      * <li>The supplied name holder has either a <code>null</code> member
 431:      * or its member matches the member of this name holder.  A member of
 432:      * <code>"*"</code> for this name holder always matches the member
 433:      * of the supplied name holder.</li>
 434:      * <li>The supplied name holder has either a <code>null</code> object name
 435:      * or its object name matches the object name of this name holder.  If the
 436:      * object name of this name holder is a pattern,
 437:      * {@link ObjectName#apply(ObjectName)} may be used as well.</li>
 438:      * </ul>
 439:      * 
 440:      * @param obj the object to compare with this.
 441:      * @return true if the above holds.
 442:      */
 443:     public boolean equals(Object obj)
 444:     {
 445:       if (obj instanceof NameHolder)
 446:     {
 447:       NameHolder nh = (NameHolder) obj;
 448:       boolean cn = false;
 449:       String ocn = nh.getClassName();
 450:       if (ocn == null || className.equals("*"))
 451:         cn = true;
 452:       else
 453:         {
 454:           int wcIndex = className.indexOf("*");
 455:           if (wcIndex != -1)
 456:         cn = ocn.startsWith(className.substring(0, wcIndex));
 457:           else
 458:         cn = ocn.equals(className);
 459:         }
 460:       boolean m = false;
 461:       String om = nh.getMember();
 462:       if (om == null || member.equals("*")) 
 463:         m = true;
 464:       else
 465:         m = om.equals(member);
 466:       boolean on = false;
 467:       ObjectName oon = nh.getObjectName();
 468:       if (oon == null)
 469:         on = true;
 470:       else if (objectName.isPattern())
 471:         on = objectName.apply(oon);
 472:       else
 473:         on = oon.equals(objectName);
 474:       return (cn && m && on);    
 475:     }
 476:       return false;
 477:     }
 478:     
 479:     /**
 480:      * Returns the class name.
 481:      */
 482:     public String getClassName()
 483:     {
 484:       return className;
 485:     }
 486: 
 487:     /**
 488:      * Returns the member.
 489:      */
 490:     public String getMember()
 491:     {
 492:       return member;
 493:     }
 494: 
 495:     /**
 496:      * Returns the object name.
 497:      */
 498:     public ObjectName getObjectName()
 499:     {
 500:       return objectName;
 501:     }
 502:   }
 503: 
 504:   /**
 505:    * Returns the set of actions.
 506:    *
 507:    * @return the actions as an ordered set.
 508:    */
 509:   Set getActionSet()
 510:   {
 511:     return actionSet;
 512:   }
 513: 
 514:   /**
 515:    * Updates the action set from the current value of
 516:    * the actions string.
 517:    */
 518:   private void updateActionSet()
 519:   {
 520:     String[] actionsArray = actions.split(",");
 521:     actionSet = new TreeSet();
 522:     for (int a = 0; a < actionsArray.length; ++a)
 523:       actionSet.add(actionsArray[a].trim());
 524:   }
 525: 
 526:   /**
 527:    * Reads the object from a stream and ensures the incoming
 528:    * data is valid.
 529:    *
 530:    * @param in the input stream.
 531:    * @throws IOException if an I/O error occurs.
 532:    * @throws ClassNotFoundException if a class used by the object
 533:    *                                can not be found.
 534:    */
 535:   private void readObject(ObjectInputStream in)
 536:     throws IOException, ClassNotFoundException
 537:   {
 538:     in.defaultReadObject();
 539:     updateActionSet();
 540:     checkActions();
 541:   }
 542: 
 543:   /**
 544:    * Checks that the actions used in this permission
 545:    * are from the valid set.
 546:    *
 547:    * @throws IllegalArgumentException if the name or actions are invalid.
 548:    */
 549:   private void checkActions()
 550:   {
 551:     Iterator it = actionSet.iterator();
 552:     while (it.hasNext())
 553:       {
 554:     String action = (String) it.next();
 555:     if (!(validSet.contains(action)))
 556:       throw new IllegalArgumentException("Invalid action " 
 557:                          + action + " found.");
 558:       }
 559:   }
 560: 
 561: }