GNU Classpath (0.95) | |
Frames | No Frames |
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: }
GNU Classpath (0.95) |