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 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: }