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 gnu.java.lang.CPStringBuilder;
  41: 
  42: import java.security.Permission;
  43: 
  44: import java.io.IOException;
  45: import java.io.ObjectInputStream;
  46: 
  47: import java.util.HashSet;
  48: import java.util.Iterator;
  49: import java.util.Set;
  50: import java.util.TreeSet;
  51: 
  52: /**
  53:  * <p>
  54:  * Represents the permissions required to perform
  55:  * operations using the {@link MBeanServer}.  As with
  56:  * all {@link java.security.Permission} objects, an
  57:  * instance of this class either represents a permission
  58:  * already held or one that is required to access a
  59:  * particular service.  In the case of {@link MBeanPermission}s,
  60:  * implication checks are made using an instance of this class
  61:  * when a user requests an operation from the server, and a
  62:  * {@link SecurityManager} is in place.
  63:  * </p>
  64:  * <p>
  65:  * An {@link MBeanPermission} consists of four elements,
  66:  * which all have to match for the permission to be implied.
  67:  * These are as follows:
  68:  * </p>
  69:  * <ol>
  70:  * <li><strong>The action</strong>.  For a required permission,
  71:  * this is a single value.  For a permission held by the user,
  72:  * this is a list of comma-separated actions (with spaces allowed),
  73:  * or <code>*</code> (representing all actions).  {@link #getActions()}
  74:  * returns this value.</li>
  75:  * <li><strong>The class name</strong>.  For a required permission,
  76:  * this is the class name of the bean being accessed, if any.  If
  77:  * a bean isn't involved in this action, the value is <code>null</code>.
  78:  * For a permission held by the user, it has one of three values:
  79:  * <ol>
  80:  * <li>The empty string, implying any class.</li>
  81:  * <li><code>*</code>, also implying any class.</li>
  82:  * <li>A class name pattern, which may specify a single class
  83:  * (e.g. <code>java.lang.Object</code>) or a series of classes
  84:  * using the wildcard character <code>*</code> (e.g.
  85:  * <code>javax.swing.*</code>.)</li>
  86:  * </ol></li>
  87:  * <li><strong>The member</strong>.  For a required permission,
  88:  * this is the member of the bean being accessed (an attribute
  89:  * or operation), if any.  If a member of the bean isn't involved
  90:  * in this action, the value is <code>null</code>.
  91:  * For a permission held by the user, it has one of three values:
  92:  * <ol>
  93:  * <li>The empty string, implying any member.</li>
  94:  * <li><code>*</code>, also implying any member.</li>
  95:  * <li>The name of a member.</li>
  96:  * </ol></li>
  97:  * <li>The object name</strong>.  For a required permission,
  98:  * this is the {@link ObjectName} of the bean being accessed, if
  99:  * any.  If a bean isn't involved in this action, the value is
 100:  * <code>null</code>.  The name may not be a pattern.
 101:  * For a permission held by the user, it may be the empty
 102:  * string (allowing everything) or an {@link ObjectName}
 103:  * pattern.
 104:  * </li></ol>
 105:  * {@link #getName()} returns the latter three of these as a
 106:  * single string:
 107:  * </p>
 108:  * <p><code>className#member[objectName]</code></p>
 109:  * <p>
 110:  * where <code>""</code> is disallowed, as, although any of
 111:  * the elements may be omitted, not all of them should be
 112:  * left out simultaneously.  <code>"-"</code> is used to
 113:  * represent <code>null</code>.  When this occurs in a
 114:  * required permission, anything may match it.  When this
 115:  * forms part of a permission held by the user, it only
 116:  * matches another <code>null</code> value.
 117:  * </p>
 118:  * <p>The list of valid actions is as follows:</p>
 119:  * <ul>
 120:  * <li>addNotificationListener</li>
 121:  * <li>getAttribute</li>
 122:  * <li>getClassLoader</li>
 123:  * <li>getClassLoaderFor</li>
 124:  * <li>getClassLoaderRepository</li>
 125:  * <li>getDomains</li>
 126:  * <li>getMBeanInfo</li>
 127:  * <li>getObjectInstance</li>
 128:  * <li>instantiate</li>
 129:  * <li>invoke</li>
 130:  * <li>isInstanceOf</li>
 131:  * <li>queryMBeans</li>
 132:  * <li>queryNames</li>
 133:  * <li>registerMBean</li>
 134:  * <li>removeNotificationListener</li>
 135:  * <li>setAttribute</li>
 136:  * <li>unregisterMBean</li>
 137:  * </ul>
 138:  *
 139:  * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
 140:  * @since 1.5
 141:  */
 142: public class MBeanPermission
 143:   extends Permission
 144: {
 145: 
 146:   /**
 147:    * Compatible with JDK 1.5
 148:    */
 149:   private static final long serialVersionUID = -2416928705275160661L;
 150: 
 151:   /**
 152:    * The list of actions associated with this permission.
 153:    */
 154:   private String actions;
 155: 
 156:   /**
 157:    * The list of actions as an ordered set.
 158:    */
 159:   private transient Set<String> actionSet;
 160: 
 161:   /**
 162:    * The set of valid actions.
 163:    */
 164:   private static final Set<String> validSet;
 165: 
 166:   /**
 167:    * Initialise the set of valid actions.
 168:    */
 169:   static
 170:   {
 171:     validSet = new HashSet<String>();
 172:     validSet.add("addNotificationListener");
 173:     validSet.add("getAttribute");
 174:     validSet.add("getClassLoader");
 175:     validSet.add("getClassLoaderFor");
 176:     validSet.add("getClassLoaderRepository");
 177:     validSet.add("getDomains");
 178:     validSet.add("getMBeanInfo");
 179:     validSet.add("getObjectInstance");
 180:     validSet.add("instantiate");
 181:     validSet.add("invoke");
 182:     validSet.add("isInstanceOf");
 183:     validSet.add("queryMBeans");
 184:     validSet.add("queryNames");
 185:     validSet.add("registerMBean");
 186:     validSet.add("removeNotificationListener");
 187:     validSet.add("setAttribute");
 188:     validSet.add("unregisterMBean");
 189:   }
 190: 
 191:   /**
 192:    * Constructs a new {@link MBeanPermission} with the specified name
 193:    * and actions.  The name is of the form <code>className#member[objectName]</code>,
 194:    * where each element is optional, but a completely empty or <code>null</code>
 195:    * name is disallowed.  Actions are specified as a comma-separated list
 196:    * and may also not be empty or <code>null</code>.
 197:    *
 198:    * @param name the name of the permission.
 199:    * @param actions the actions associated with this permission.
 200:    * @throws IllegalArgumentException if the name or actions are invalid.
 201:    */
 202:   public MBeanPermission(String name, String actions)
 203:   {
 204:     super(name);
 205:     if (name == null || name.length() == 0)
 206:       throw new IllegalArgumentException("The supplied name was null or empty.");
 207:     if (actions == null || actions.length() == 0)
 208:       throw new IllegalArgumentException("The supplied action list was null or empty.");
 209:     this.actions = actions;
 210:     updateActionSet();
 211:   }
 212: 
 213:   /**
 214:    * Constructs a new {@link MBeanPermission} with the specified class name,
 215:    * member, object name and actions.  The name of the permission is created
 216:    * using the form <code>className#member[objectName]</code>,
 217:    * where each element is optional, but an empty or <code>null</code>
 218:    * name is disallowed.  Actions are specified as a comma-separated list
 219:    * and may also not be empty or <code>null</code>.
 220:    *
 221:    * @param className the name of the class to which this permission applies,
 222:    *                  or either <code>null</code> or <code>"-"</code> for a
 223:    *                  value which may be implied by any class name, but not
 224:    *                  imply any class name itself.
 225:    * @param member the member of the class to which this permission applies,
 226:    *               or either <code>null</code> or <code>"-"</code> for a
 227:    *               value which may be implied by any member, but not
 228:    *               imply any member itself.
 229:    * @param name the {@link ObjectName} to which this permission applies,
 230:    *                  or <code>null</code> for a value which may be implied by
 231:    *                  any object name, but not imply any object name itself.
 232:    * @param actions the actions associated with this permission.
 233:    */
 234:   public MBeanPermission(String className, String member,
 235:                          ObjectName name, String actions)
 236:   {
 237:     this((className == null ? "-" : className) + "#"
 238:          + (member == null ? "-" : member) + "["
 239:          + (name == null ? "-" : name.toString()) + "]", actions);
 240:   }
 241: 
 242:   /**
 243:    * Returns true if the given object is also an {@link MBeanPermission}
 244:    * with the same name and actions.
 245:    *
 246:    * @param obj the object to test.
 247:    * @return true if the object is an {@link MBeanPermission} with
 248:    *         the same name and actions.
 249:    */
 250:   public boolean equals(Object obj)
 251:   {
 252:     if (obj instanceof MBeanPermission)
 253:       {
 254:         MBeanPermission p = (MBeanPermission) obj;
 255:         return (p.getName().equals(getName()) &&
 256:                 p.getActions().equals(actions));
 257:       }
 258:     return false;
 259:   }
 260: 
 261:   /**
 262:    * Returns the list of actions in alphabetical order.
 263:    *
 264:    * @return the list of actions.
 265:    */
 266:   public String getActions()
 267:   {
 268:     Iterator<String> it = actionSet.iterator();
 269:     CPStringBuilder builder = new CPStringBuilder();
 270:     while (it.hasNext())
 271:       {
 272:         builder.append(it.next());
 273:         if (it.hasNext())
 274:           builder.append(",");
 275:       }
 276:     return builder.toString();
 277:   }
 278: 
 279:   /**
 280:    * Returns the hashcode of the permission as the sum
 281:    * of the hashcodes of the name and actions.
 282:    *
 283:    * @return the hashcode of the permission.
 284:    */
 285:   public int hashCode()
 286:   {
 287:     return getName().hashCode() + actions.hashCode();
 288:   }
 289: 
 290:   /**
 291:    * <p>
 292:    * Returns true if this permission implies the supplied permission.
 293:    * This happens if the following holds:
 294:    * </p>
 295:    * <ul>
 296:    * <li>The supplied permission is an {@link MBeanPermission}</li>
 297:    * <li>The supplied permission has either a <code>null</code> classname
 298:    * or its classname matches the classname of this permission.  A
 299:    * classname of <code>"*"</code> for this permission always matches
 300:    * the classname of the supplied permission.  Generally, <code>'*'</code>
 301:    * acts as a wildcard, so <code>".*"</code> matches <code>'.'</code>
 302:    * followed by anything.</li>
 303:    * <li>The supplied permission has either a <code>null</code> member
 304:    * or its member matches the member of this permission.  A member of
 305:    * <code>"*"</code> for this permission always matches the member
 306:    * of the supplied permission.</li>
 307:    * <li>The supplied permission has either a <code>null</code> object name
 308:    * or its object name matches the object name of this permission.  If the
 309:    * object name of this permission is a pattern, {@link ObjectName#apply(ObjectName)}
 310:    * may be used as well.</li>
 311:    * <li>The supplied permission's actions are a subset of the actions
 312:    * of this permission.  If the <code>queryMBeans</code> action is presented,
 313:    * the <code>queryNames</code> action is implied.</li>
 314:    * </ul>
 315:    *
 316:    * @param p the permission to check that this permission implies.
 317:    * @return true if this permission implies <code>p</code>.
 318:    */
 319:   public boolean implies(Permission p)
 320:   {
 321:     if (p instanceof MBeanPermission)
 322:       {
 323:         MBeanPermission mp = (MBeanPermission) p;
 324:         NameHolder pName = new NameHolder(mp.getName());
 325:         NameHolder name = new NameHolder(getName());
 326:         if (!(name.equals(pName)))
 327:           return false;
 328:         for (String nextAction : mp.actionSet)
 329:           {
 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:    * Updates the action set from the current value of
 506:    * the actions string.
 507:    */
 508:   private void updateActionSet()
 509:   {
 510:     String[] actionsArray = actions.split(",");
 511:     actionSet = new TreeSet<String>();
 512:     for (int a = 0; a < actionsArray.length; ++a)
 513:       actionSet.add(actionsArray[a].trim());
 514:   }
 515: 
 516:   /**
 517:    * Reads the object from a stream and ensures the incoming
 518:    * data is valid.
 519:    *
 520:    * @param in the input stream.
 521:    * @throws IOException if an I/O error occurs.
 522:    * @throws ClassNotFoundException if a class used by the object
 523:    *                                can not be found.
 524:    */
 525:   private void readObject(ObjectInputStream in)
 526:     throws IOException, ClassNotFoundException
 527:   {
 528:     in.defaultReadObject();
 529:     updateActionSet();
 530:     checkActions();
 531:   }
 532: 
 533:   /**
 534:    * Checks that the actions used in this permission
 535:    * are from the valid set.
 536:    *
 537:    * @throws IllegalArgumentException if the name or actions are invalid.
 538:    */
 539:   private void checkActions()
 540:   {
 541:     for (String action : actionSet)
 542:       {
 543:         if (!(validSet.contains(action)))
 544:           throw new IllegalArgumentException("Invalid action "
 545:                                              + action + " found.");
 546:       }
 547:   }
 548: 
 549: }