001: /* JFox, the OpenSource J2EE Application Server
002: *
003: * Copyright (C) 2002 huihoo.com
004: * Distributable under GNU LGPL license
005: * See the GNU Lesser General Public License for more details.
006: */
007:
008: package javax.management;
009:
010: import java.security.Permission;
011: import java.security.BasicPermission;
012: import java.util.Set;
013: import java.util.HashSet;
014:
015: /**
016: * <p>Permission controlling access to MBeanServer operations. If a
017: * security manager has been set using {@link
018: * System#setSecurityManager}, most operations on the MBean Server
019: * require that the caller's permissions imply an MBeanPermission
020: * appropriate for the operation. This is described in detail in the
021: * documentation for the {@link MBeanServer} interface.</p>
022: *
023: * <p>As with other {@link Permission} objects, an MBeanPermission can
024: * represent either a permission that you <em>have</em> or a
025: * permission that you <em>need</em>. When a sensitive operation is
026: * being checked for permission, an MBeanPermission is constructed
027: * representing the permission you need. The operation is only
028: * allowed if the permissions you have {@link #implies imply} the
029: * permission you need.</p>
030: *
031: * <p>An MBeanPermission contains four items of information:</p>
032: *
033: * <ul>
034: *
035: * <li><p>The <em>action</em>. For a permission you need,
036: * this is one of the action in the list <a
037: * href="#action-list">below</a>. For a permission you have, this is
038: * a comma-separated list of those action, or <code>*</code>,
039: * representing all action.</p>
040: *
041: * <p>The action is returned by {@link #getActions()}.</p>
042: *
043: * <li><p>The <em>class name</em>.</p>
044: *
045: * <p>For a permission you need, this is the class name of an MBean
046: * you are accessing, as returned by {@link
047: * MBeanServer#getMBeanInfo(ObjectName)
048: * MBeanServer.getMBeanInfo(name)}.{@link MBeanInfo#getClassName()
049: * getClassName()}. Certain operations do not reference a class name,
050: * in which case the class name is null.</p>
051: *
052: * <p>For a permission you have, this is either empty or a <em>class
053: * name pattern</em>. A class name pattern is a string following the
054: * Java conventions for dot-separated class names. It may end with
055: * "<code>.*</code>" meaning that the permission grants access to any
056: * class that begins with the string preceding "<code>.*</code>". For
057: * instance, "<code>javax.management.*</code>" grants access to
058: * <code>javax.management.MBeanServerDelegate</code> and
059: * <code>javax.management.timer.Timer</code>, among other classes.</p>
060: *
061: * <p>A class name pattern can also be empty or the single character
062: * "<code>*</code>", both of which grant access to any class.</p>
063: *
064: * <li><p>The <em>member</em>.</p>
065: *
066: * <p>For a permission you need, this is the name of the attribute or
067: * operation you are accessing. For operations that do not reference
068: * an attribute or operation, the member is null.</p>
069: *
070: * <p>For a permission you have, this is either the name of an attribute
071: * or operation you can access, or it is empty or the single character
072: * "<code>*</code>", both of which grant access to any member.</p>
073: *
074: * <li><p>The <em>object name</em>.</p>
075: *
076: * <p>For a permission you need, this is the {@link ObjectName} of the
077: * MBean you are accessing. For operations that do not reference a
078: * single MBean, it is null. It is never an object name pattern.</p>
079: *
080: * <p>For a permission you have, this is the {@link ObjectName} of the
081: * MBean or MBeans you can access. It may be an object name pattern
082: * to grant access to all MBeans whose names match the pattern. It
083: * may also be empty, which grants access to all MBeans whatever their
084: * name.</p>
085: *
086: * </ul>
087: *
088: * <p>If you have an MBeanPermission, it allows operations only if all
089: * four of the items match.</p>
090: *
091: * <p>The class name, member, and object name can be written together
092: * as a single string, which is the <em>name</em> of this permission.
093: * The name of the permission is the string returned by {@link
094: * Permission#getNameSpace() getNameSpace()}. The format of the string is:</p>
095: *
096: * <blockquote>
097: * <code>className#member[objectName]</code>
098: * </blockquote>
099: *
100: * <p>The object name is written using the usual syntax for {@link
101: * ObjectName}. It may contain any legal characters, including
102: * <code>]</code>. It is terminated by a <code>]</code> character
103: * that is the last character in the string.</p>
104: *
105: * <p>One or more of the <code>className</code>, <code>member</code>,
106: * or <code>objectName</code> may be omitted. If the
107: * <code>member</code> is omitted, the <code>#</code> may be too (but
108: * does not have to be). If the <code>objectName</code> is omitted,
109: * the <code>[]</code> may be too (but does not have to be). It is
110: * not legal to omit all three items, that is to have a <em>name</em>
111: * that is the empty string.</p>
112: *
113: * <p>One or more of the <code>className</code>, <code>member</code>,
114: * or <code>objectName</code> may be the character "<code>-</code>",
115: * which is equivalent to a null value. A null value is implied by
116: * any value (including another null value) but does not imply any
117: * other value.</p>
118: *
119: * <p><a name="action-list">The possible action are these:</a></p>
120: *
121: * <ul>
122: * <li>addNotificationListener</li>
123: * <li>getAttribute</li>
124: * <li>getClassLoader</li>
125: * <li>getClassLoaderFor</li>
126: * <li>getClassLoaderRepository</li>
127: * <li>getDomains</li>
128: * <li>getMBeanInfo</li>
129: * <li>getObjectInstance</li>
130: * <li>instantiate</li>
131: * <li>invoke</li>
132: * <li>isInstanceOf</li>
133: * <li>queryMBeans</li>
134: * <li>queryNames</li>
135: * <li>registerMBean</li>
136: * <li>removeNotificationListener</li>
137: * <li>setAttribute</li>
138: * <li>unregisterMBean</li>
139: * </ul>
140: *
141: * <p>In a comma-separated list of action, spaces are allowed before
142: * and after each action.</p>
143: *
144: * @since JMX 1.2
145: *
146: * @author <a href="mailto:young_yy@hotmail.com">Young Yang</a>
147: */
148:
149: public class MBeanPermission extends Permission {
150: public static final String AddNotificationListener = "addNotificationListener";
151: public static final String GetAttribute = "getAttribute";
152: public static final String GetClassLoader = "getClassLoader";
153: public static final String GetClassLoaderFor = "getClassLoaderFor";
154: public static final String GetClassLoaderRepository = "getClassLoaderRepository";
155: public static final String GetDomains = "getDomains";
156: public static final String GetMBeanInfo = "getMBeanInfo";
157: public static final String GetObjectInstance = "getObjectInstance";
158: public static final String Instantiate = "instantiate";
159: public static final String Invoke = "invoke";
160: public static final String IsInstanceOf = "isInstanceOf";
161: public static final String QueryMBeans = "queryMBeans";
162: public static final String QueryNames = "queryNames";
163: public static final String RegisterMBean = "registerMBean";
164: public static final String RemoveNotificationListener = "removeNotificationListener";
165: public static final String SetAttribute = "setAttribute";
166: public static final String UnregisterMBean = "unregisterMBean";
167: private static final String knownNames[] = {
168: AddNotificationListener, GetAttribute, GetClassLoader,
169: GetClassLoaderFor, GetClassLoaderRepository, GetDomains,
170: GetMBeanInfo, GetObjectInstance, Instantiate, Invoke,
171: IsInstanceOf, QueryMBeans, QueryNames, RegisterMBean,
172: RemoveNotificationListener, SetAttribute, UnregisterMBean };
173:
174: /**
175: * An ObjectName that matches any other.
176: */
177: private static final ObjectName allObjectNames;
178: static {
179: try {
180: allObjectNames = new ObjectName("*:*");
181: } catch (MalformedObjectNameException e) {
182: throw new IllegalArgumentException("can't happen");
183: }
184: }
185:
186: /**
187: * The action string.
188: */
189: private String action;
190: private Set actions;
191:
192: private transient MBeanMemberPermission memberPerssion;
193:
194: /**
195: * The objectName that must match. If null, is implied by any
196: * objectName but does not imply any non-null objectName.
197: */
198: private transient ObjectName objectName;
199:
200: private transient MBeanClassNamePermission classNamePermission;
201:
202: /**
203: * <p>Create a new MBeanPermission object with the specified target name
204: * and action.</p>
205: *
206: * <p>The target name is of the form
207: * "<code>className#member[objectName]</code>" where each part is
208: * optional. It must not be empty or null.</p>
209: *
210: * <p>The action parameter contains a comma-separated list of the
211: * desired action granted on the target name. It must not be
212: * empty or null.</p>
213: *
214: * @param name the triplet "className#member[objectName]".
215: * @param action the action string.
216: *
217: * @exception IllegalArgumentException if the <code>name</code> or
218: * <code>action</code> is invalid.
219: */
220: public MBeanPermission(String name, String action) {
221: super (name);
222: this .parseName(name);
223: this .action = action;
224: this .actions = this .parseActions(action);
225: }
226:
227: /**
228: * <p>Create a new MBeanPermission object with the specified target name
229: * (class name, member, object name) and action.</p>
230: *
231: * <p>The class name, member and object name parameters define a
232: * target name of the form
233: * "<code>className#member[objectName]</code>" where each part is
234: * optional. This will be the result of {@link #getName()} on the
235: * resultant MBeanPermission.</p>
236: *
237: * <p>The action parameter contains a comma-separated list of the
238: * desired action granted on the target name. It must not be
239: * empty or null.</p>
240: *
241: * @param className the class name to which this permission applies.
242: * May be null or <code>"-"</code>, which represents a class name
243: * that is implied by any class name but does not imply any other
244: * class name.
245: * @param member the member to which this permission applies. May
246: * be null or <code>"-"</code>, which represents a member that is
247: * implied by any member but does not imply any other member.
248: * @param objectName the object name to which this permission
249: * applies. May be null, which represents an object name that is
250: * implied by any object name but does not imply any other object
251: * name.
252: * @param action the action string.
253: */
254: public MBeanPermission(String className, String member,
255: ObjectName objectName, String action) {
256:
257: super (makeName(className, member, objectName));
258: this .setClassName(className);
259: this .setMember(member);
260: this .objectName = objectName;
261: this .action = action;
262: actions = parseActions(action);
263: }
264:
265: public boolean implies(Permission p) {
266: if (!(p instanceof MBeanPermission))
267: return false;
268:
269: MBeanPermission that = (MBeanPermission) p;
270:
271: boolean applied = false;
272: try {
273: applied = objectName.apply(that.objectName);
274: } catch (Exception e) {
275: return false;
276: }
277:
278: return applied
279: && actions.containsAll(that.actions)
280: && classNamePermission
281: .implies(that.classNamePermission)
282: && memberPerssion.implies(that.memberPerssion);
283:
284: }
285:
286: public String getActions() {
287: return action;
288: }
289:
290: /**
291: * Returns the hash code value for this object.
292: *
293: * @return a hash code value for this object.
294: */
295: public int hashCode() {
296: return this .getName().hashCode() + this .getActions().hashCode();
297: }
298:
299: /**
300: * Checks two MBeanPermission objects for equality. Checks
301: * that <i>obj</i> is an MBeanPermission, and has the same
302: * name and actions as this object.
303: * <P>
304: * @param obj the object we are testing for equality with this object.
305: * @return true if obj is an MBeanPermission, and has the
306: * same name and actions as this MBeanPermission object.
307: */
308: public boolean equals(Object obj) {
309: if (obj == null)
310: return false;
311: if (obj == this )
312: return true;
313:
314: if (!(obj instanceof MBeanPermission))
315: return false;
316:
317: MBeanPermission that = (MBeanPermission) obj;
318:
319: return (this .actions == that.actions)
320: && (this .getName().equals(that.getName()));
321: }
322:
323: private static String makeName(String className, String member,
324: ObjectName objectName) {
325: StringBuffer name = new StringBuffer();
326: if (className == null)
327: className = "-";
328: name.append(className);
329: if (member == null)
330: member = "-";
331: name.append("#" + member);
332: if (objectName == null) {
333: name.append("[-]");
334: } else {
335: name.append("[").append(objectName.getCanonicalName())
336: .append("]");
337: }
338:
339: /*
340: In the interests of legibility for Permission.toString(), we
341: transform the empty string into "*".
342: */
343: if (name.length() == 0)
344: return "*";
345: else
346: return name.toString();
347: }
348:
349: private void setClassName(String className) {
350: if (className == null || className.equals("-")) {
351: this .classNamePermission = new MBeanClassNamePermission(
352: null);
353: } else if (className.equals("")) {
354: this .classNamePermission = new MBeanClassNamePermission("*");
355: } else {
356: this .classNamePermission = new MBeanClassNamePermission(
357: className);
358: }
359: }
360:
361: private void setMember(String member) {
362: if (member == null || member.equals("-")) {
363: this .memberPerssion = new MBeanMemberPermission(null);
364: } else if (member.equals("")) {
365: this .memberPerssion = new MBeanMemberPermission("*");
366: } else {
367: this .memberPerssion = new MBeanMemberPermission(member);
368: }
369: }
370:
371: /**
372: * Parse <code>actions</code> parameter.
373: */
374: private Set parseActions(String action) {
375: if (action == null || action.trim().length() == 0)
376: throw new IllegalArgumentException("MBeanPermission: "
377: + "actions can't be null " + " or empty");
378: Set _actions = new HashSet();
379: int start = 0;
380: int offset = action.indexOf(",", start);
381: while (offset > 0) {
382: String act = action.substring(start, offset);
383: if (!validAction(act)) {
384: throw new IllegalArgumentException("Invalid action: "
385: + act);
386: }
387: _actions.add(act);
388: start = ++offset;
389: }
390: String lastAction = action.substring(start);
391: if (!validAction(lastAction)) {
392: throw new IllegalArgumentException("Invalid action: "
393: + lastAction);
394: }
395: _actions.add(lastAction);
396:
397: return _actions;
398: }
399:
400: private static boolean validAction(String action) {
401: for (int i = 0; i < knownNames.length; i++) {
402: if (action.equals(knownNames[i])) {
403: return true;
404: }
405: }
406: return false;
407: }
408:
409: private void setObjectName(String objName) {
410: if (objName.equals("")) {
411: objectName = allObjectNames;
412: } else if (objName.equals("-")) {
413: objectName = null;
414: } else {
415: try {
416: objectName = new ObjectName(objName);
417: } catch (MalformedObjectNameException e) {
418: throw new IllegalArgumentException("MBeanPermission: "
419: + "The target name does "
420: + "not specify a valid " + "ObjectName: "
421: + objName);
422: }
423: }
424: }
425:
426: /**
427: * Parse <code>name</code> parameter.
428: */
429: private void parseName(String name) {
430: if (name.equals(""))
431: throw new IllegalArgumentException("MBeanPermission name "
432: + "cannot be empty");
433: /*
434: The name looks like "class#member[objectname]". We subtract
435: elements from the right as we parse, so after parsing the
436: objectname we have "class#member" and after parsing the
437: member we have "class". Each element is optional.
438: */
439:
440: // Parse ObjectName
441: int openingBracket = name.indexOf("[");
442: if (openingBracket == -1) {
443: // If "[on]" missing then ObjectName("*:*")
444: //
445: objectName = allObjectNames;
446: } else {
447: if (!name.endsWith("]")) {
448: throw new IllegalArgumentException("MBeanPermission: "
449: + "The ObjectName in the "
450: + "target name must be "
451: + "included in square " + "brackets");
452: } else {
453: // Create ObjectName
454: //
455: String on = name.substring(openingBracket + 1, name
456: .length() - 1);
457: this .setObjectName(on);
458: }
459:
460: name = name.substring(0, openingBracket);
461: }
462:
463: // Parse member
464: int poundSign = name.indexOf("#");
465: if (poundSign == -1)
466: this .setMember("*");
467: else {
468: String memberName = name.substring(poundSign + 1);
469: this .setMember(memberName);
470: name = name.substring(0, poundSign);
471: }
472:
473: // Parse className
474: this .setClassName(name);
475: }
476:
477: private class MBeanClassNamePermission extends BasicPermission {
478: public MBeanClassNamePermission(String name) {
479: super (name);
480: }
481: }
482:
483: private class MBeanMemberPermission extends BasicPermission {
484: public MBeanMemberPermission(String name) {
485: super (name);
486: }
487: }
488:
489: public static void main(String[] args) {
490:
491: }
492:
493: }
|