001: /*
002: * Copyright (C) The MX4J Contributors.
003: * All rights reserved.
004: *
005: * This software is distributed under the terms of the MX4J License version 1.0.
006: * See the terms of the MX4J License in the documentation provided with this software.
007: */
008:
009: package javax.management;
010:
011: import java.io.IOException;
012: import java.io.ObjectInputStream;
013: import java.security.Permission;
014: import java.util.ArrayList;
015: import java.util.Collections;
016: import java.util.StringTokenizer;
017:
018: import mx4j.util.Utils;
019:
020: /**
021: * Permission that control access to MBeanServer methods. <br>
022: * The MBeanPermission contains a target name and a comma separated list of target actions.
023: * The target name is composed by:
024: * <ul>
025: * <li> the class name of the MBean, as returned by
026: * {@link javax.management.MBeanInfo#getClassName MBeanInfo.getClassName()} </li>
027: * <li> the pound character '#' </li>
028: * <li> the attribute name or the operation name </li>
029: * <li> the object name of the MBean inclosed in squared brackets </li>
030: * </ul>
031: * When used in the target name, the wildcard '*' may be used to specify packages, classes or methods as a whole. <br>
032: * When used in the actions, the wildcard '*' indicates all actions. <br>
033: * An example of policy file is the following:
034: * <pre>
035: * grant codebase my-jmx-application.jar
036: * {
037: * permission javax.management.MBeanPermission "mx4j.tools.naming.NamingService", "instantiate, registerMBean, unregisterMBean";
038: * permission javax.management.MBeanPermission "mx4j.tools.naming.NamingService#start", "invoke";
039: * permission javax.management.MBeanPermission "mx4j.tools.naming.NamingService#stop", "invoke";
040: * }
041: * </pre>
042: *
043: * @version $Revision: 1.11 $
044: */
045: public class MBeanPermission extends Permission {
046: private static final long serialVersionUID = 0xde755825e2a117abL;
047:
048: private static final String WILDCARD = "*";
049: private static final String NILCARD = "-";
050:
051: /**
052: * @serial The permission actions
053: */
054: private String actions;
055:
056: private transient int hash;
057: private transient String className;
058: private transient String memberName;
059: private transient ObjectName objectName;
060: private transient ArrayList actionsList;
061:
062: /**
063: * Creates a new MBeanPermission
064: *
065: * @param name The target name
066: * @param actions The comma separated list of actions
067: */
068: public MBeanPermission(String name, String actions) {
069: super (name);
070: this .actions = actions;
071:
072: parse(name, actions);
073: }
074:
075: /**
076: * Creates a new MBeanPermission. If the parts composing the target name are all specified as null,
077: * the wildcard target name '*' is assumed.
078: *
079: * @param className The className part of the target name, may be null
080: * @param memberName The memberName part of the target name, may be null
081: * @param objectName The ObjectName part of the target name, may be null
082: * @param actions The comma separated list of actions
083: */
084: public MBeanPermission(String className, String memberName,
085: ObjectName objectName, String actions) {
086: this (createTargetName(className, memberName, objectName),
087: actions);
088: }
089:
090: private static String createTargetName(String className,
091: String memberName, ObjectName objectName) {
092: StringBuffer target = new StringBuffer();
093:
094: target.append(className == null ? "-" : className);
095: target.append('#');
096: target.append(memberName == null ? "-" : memberName);
097: target.append('[');
098: target.append(objectName == null ? "-" : objectName
099: .getCanonicalName());
100: target.append(']');
101:
102: return target.toString();
103: }
104:
105: private void parse(String name, String actions) {
106: className = parseClassName(name);
107: memberName = parseMemberName(name);
108: objectName = parseObjectName(name);
109: actionsList = parseActions(actions);
110: }
111:
112: public int hashCode() {
113: if (hash == 0)
114: hash = computeHash();
115: return hash;
116: }
117:
118: public boolean equals(Object obj) {
119: if (obj == null)
120: return false;
121: if (obj == this )
122: return true;
123: if (getClass() != obj.getClass())
124: return false;
125:
126: // Must have the same target name, and the same action list, after parsing
127: MBeanPermission other = (MBeanPermission) obj;
128:
129: // The parsed members can be null (means they're the nilcard)
130: if (!areEqual(getClassName(), other.getClassName()))
131: return false;
132: if (!areEqual(getMemberName(), other.getMemberName()))
133: return false;
134: if (!areEqual(getObjectName(), other.getObjectName()))
135: return false;
136: return getActionsList().equals(other.getActionsList());
137: }
138:
139: private boolean areEqual(Object obj1, Object obj2) {
140: if (obj1 == null)
141: return obj2 == null;
142: return obj1.equals(obj2);
143: }
144:
145: public String getActions() {
146: return actions;
147: }
148:
149: private String getClassName() {
150: return className;
151: }
152:
153: private String getMemberName() {
154: return memberName;
155: }
156:
157: private ObjectName getObjectName() {
158: return objectName;
159: }
160:
161: private ArrayList getActionsList() {
162: return actionsList;
163: }
164:
165: public boolean implies(Permission p) {
166: if (p == null)
167: return false;
168: if (getClass() != p.getClass())
169: return false;
170:
171: MBeanPermission permission = (MBeanPermission) p;
172: if (!impliesClassName(permission))
173: return false;
174: if (!impliesMemberName(permission))
175: return false;
176: if (!impliesObjectName(permission))
177: return false;
178: if (!impliesActions(permission))
179: return false;
180: return true;
181: }
182:
183: private boolean impliesClassName(MBeanPermission p) {
184: return impliesTarget(getClassName(), p.getClassName());
185: }
186:
187: private boolean impliesMemberName(MBeanPermission p) {
188: return impliesTarget(getMemberName(), p.getMemberName());
189: }
190:
191: private boolean impliesTarget(String this Target, String otherTarget) {
192: // Handle nilcards
193: if (this Target == null)
194: return otherTarget == null;
195: if (otherTarget == null)
196: return true;
197:
198: // Easy and fast check
199: if (this Target.equals(otherTarget))
200: return true;
201:
202: // Now see the wildcard. While thisTarget can be wildcarded in several ways,
203: // otherTarget is created only with the full wildcard, or with no wildcard
204: boolean otherWildcard = otherTarget.indexOf(WILDCARD) >= 0;
205:
206: boolean this Wildcard = this Target.indexOf(WILDCARD) >= 0;
207:
208: if (this Wildcard) {
209: if (otherWildcard) {
210: // otherTarget can only be '*'
211: return this Target.equals(WILDCARD);
212: } else {
213: // We support all types of wildcarding with '*'
214: return Utils.wildcardMatch(this Target, otherTarget);
215: }
216: } else {
217: if (otherWildcard) {
218: return false;
219: } else {
220: return this Target.equals(otherTarget);
221: }
222: }
223: }
224:
225: private boolean impliesObjectName(MBeanPermission p) {
226: ObjectName name1 = getObjectName();
227: ObjectName name2 = p.getObjectName();
228:
229: // Handle nilcards
230: if (name1 == null)
231: return name2 == null;
232: if (name2 == null)
233: return true;
234:
235: return name1.implies(name2);
236: }
237:
238: private boolean impliesActions(MBeanPermission p) {
239: ArrayList this Actions = getActionsList();
240: boolean this Wild = this Actions.contains(WILDCARD);
241:
242: ArrayList otherActions = p.getActionsList();
243: boolean otherWild = otherActions.contains(WILDCARD);
244:
245: // Access is granted for all actions
246: if (this Wild)
247: return true;
248: // Access was not granted for all actions, but is requested for all actions
249: if (otherWild)
250: return false;
251:
252: if (this Actions.containsAll(otherActions))
253: return true;
254:
255: // Special check: queryMBeans implies queryNames
256: if (otherActions.contains("queryNames")
257: && this Actions.contains("queryMBeans")) {
258: // Umpf, this is ugly, but not immediate: must care multithreading
259: for (int i = 0; i < otherActions.size(); ++i) {
260: Object perm = otherActions.get(i);
261: if ("queryNames".equals(perm))
262: continue;
263: if (!this Actions.contains(perm))
264: return false;
265: }
266: return true;
267: }
268:
269: return false;
270: }
271:
272: private String parseClassName(String name) {
273: if (name == null)
274: throw new IllegalArgumentException(
275: "Target name cannot be null");
276:
277: String target = name.trim();
278:
279: if (target.length() == 0)
280: throw new IllegalArgumentException(
281: "Target name cannot be empty");
282:
283: // Try to find the ObjectName beginning
284: int square = target.indexOf('[');
285: if (square >= 0) {
286: // There is an ObjectName, take only the classname and member
287: target = target.substring(0, square).trim();
288: }
289:
290: // There is only the ObjectName, means classname == wildcard
291: if (target.length() == 0)
292: return WILDCARD;
293:
294: // Try to find the member beginning
295: int pound = target.indexOf('#');
296: if (pound >= 0) {
297: // There is a member, take only the classname
298: target = target.substring(0, pound).trim();
299: }
300:
301: // There is only the member, means classname == wildcard
302: if (target.length() == 0)
303: return WILDCARD;
304:
305: // Check for the nilcard
306: if (target.equals(NILCARD))
307: return null;
308:
309: return target;
310: }
311:
312: private String parseMemberName(String name) {
313: // Checks already done in parseClassName
314:
315: String target = name.trim();
316:
317: // Try to find the ObjectName beginning
318: int square = target.indexOf('[');
319: if (square >= 0) {
320: // There is an ObjectName, take only the classname and member
321: target = target.substring(0, square).trim();
322: }
323:
324: // There is only the ObjectName, means member == wildcard
325: if (target.length() == 0)
326: return WILDCARD;
327:
328: // Try to find the member beginning
329: int pound = target.indexOf('#');
330: if (pound >= 0) {
331: // There is a member, take only it
332: target = target.substring(pound + 1).trim();
333: } else {
334: // No member, means member == wildcard
335: target = WILDCARD;
336: }
337:
338: // Check for the nilcard
339: if (target.equals(NILCARD))
340: return null;
341:
342: return target;
343: }
344:
345: private ObjectName parseObjectName(String name) {
346: // Checks already done in parseClassName
347:
348: String target = name.trim();
349: String inside = "*:*";
350:
351: // Find beginning of ObjectName
352: int open = target.indexOf('[');
353: if (open >= 0) {
354: int close = target.indexOf(']');
355: if (close < 0)
356: throw new IllegalArgumentException(
357: "Missing closing ObjectName bracket");
358:
359: // Find the ObjectName string inside the brackets
360: inside = target.substring(open + 1, close).trim();
361: if (inside.length() == 0) {
362: inside = "*:*";
363: } else if (inside.equals(NILCARD)) {
364: return null;
365: }
366: }
367:
368: // Create the ObjectName
369: try {
370: ObjectName objectName = new ObjectName(inside);
371: return objectName;
372: } catch (MalformedObjectNameException x) {
373: throw new IllegalArgumentException("Invalid ObjectName: "
374: + inside);
375: }
376: }
377:
378: private ArrayList parseActions(String actions) {
379: if (actions == null)
380: throw new IllegalArgumentException(
381: "Actions list cannot be null");
382:
383: actions = actions.trim();
384:
385: if (actions.length() == 0)
386: throw new IllegalArgumentException(
387: "Actions list cannot be empty");
388:
389: // Split the comma separated list of actions
390: ArrayList list = new ArrayList();
391: StringTokenizer tokenizer = new StringTokenizer(actions, ",");
392: while (tokenizer.hasMoreTokens()) {
393: String token = tokenizer.nextToken().trim();
394: if (token.length() == 0)
395: continue;
396: if (token.equals(WILDCARD)) {
397: list.clear();
398: list.add(WILDCARD);
399: return list;
400: } else {
401: list.add(token);
402: }
403: }
404:
405: if (list.size() < 1)
406: throw new IllegalArgumentException("No actions specified");
407:
408: // It is very important for hashCode() and equals() that the list is sorted
409: Collections.sort(list);
410:
411: return list;
412: }
413:
414: private int computeHash() {
415: String cls = getClassName();
416: int hash = cls == null ? NILCARD.hashCode() : cls.hashCode();
417: String member = getMemberName();
418: hash ^= member == null ? NILCARD.hashCode() : member.hashCode();
419: ObjectName name = getObjectName();
420: hash ^= name == null ? NILCARD.hashCode() : name.hashCode();
421: hash ^= getActionsList().hashCode();
422: return hash;
423: }
424:
425: private void readObject(ObjectInputStream stream)
426: throws IOException, ClassNotFoundException {
427: stream.defaultReadObject();
428: parse(getName(), getActions());
429: }
430: }
|