1. /*
  2. * @(#)MBeanServerPermission.java 1.26 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.management;
  8. import java.io.IOException;
  9. import java.io.ObjectInputStream;
  10. import java.security.BasicPermission;
  11. import java.security.Permission;
  12. import java.security.PermissionCollection;
  13. import java.util.Collections;
  14. import java.util.Enumeration;
  15. import java.util.Set;
  16. import java.util.StringTokenizer;
  17. /** A Permission to perform actions related to MBeanServers.
  18. The <em>name</em> of the permission specifies the operation requested
  19. or granted by the permission. For a granted permission, it can be
  20. <code>*</code> to allow all of the MBeanServer operations specified below.
  21. Otherwise, for a granted or requested permission, it must be one of the
  22. following:
  23. <dl>
  24. <dt>createMBeanServer</dt>
  25. <dd>Create a new MBeanServer object using the method
  26. {@link MBeanServerFactory#createMBeanServer()} or
  27. {@link MBeanServerFactory#createMBeanServer(java.lang.String)}.
  28. <dt>findMBeanServer</dt>
  29. <dd>Find an MBeanServer with a given name, or all MBeanServers in this
  30. JVM, using the method {@link MBeanServerFactory#findMBeanServer}.
  31. <dt>newMBeanServer</dt>
  32. <dd>Create a new MBeanServer object without keeping a reference to it,
  33. using the method {@link MBeanServerFactory#newMBeanServer()} or
  34. {@link MBeanServerFactory#newMBeanServer(java.lang.String)}.
  35. <dt>releaseMBeanServer</dt>
  36. <dd>Remove the MBeanServerFactory's reference to an MBeanServer,
  37. using the method {@link MBeanServerFactory#releaseMBeanServer}.
  38. </dl>
  39. The <em>name</em> of the permission can also denote a list of one or more
  40. comma-separated operations. Spaces are allowed at the beginning and
  41. end of the <em>name</em> and before and after commas.
  42. <p>
  43. <code>MBeanServerPermission("createMBeanServer")</code> implies
  44. <code>MBeanServerPermission("newMBeanServer")</code>.
  45. *
  46. * @since 1.5
  47. * @since.unbundled JMX 1.1
  48. */
  49. public class MBeanServerPermission extends BasicPermission {
  50. private static final long serialVersionUID = -5661980843569388590L;
  51. private final static int
  52. CREATE = 0,
  53. FIND = 1,
  54. NEW = 2,
  55. RELEASE = 3,
  56. N_NAMES = 4;
  57. private final static String[] names = {
  58. "createMBeanServer",
  59. "findMBeanServer",
  60. "newMBeanServer",
  61. "releaseMBeanServer",
  62. };
  63. private final static int
  64. CREATE_MASK = 1<<CREATE,
  65. FIND_MASK = 1<<FIND,
  66. NEW_MASK = 1<<NEW,
  67. RELEASE_MASK = 1<<RELEASE,
  68. ALL_MASK = CREATE_MASK|FIND_MASK|NEW_MASK|RELEASE_MASK;
  69. /*
  70. * Map from permission masks to canonical names. This array is
  71. * filled in on demand.
  72. *
  73. * This isn't very scalable. If we have more than five or six
  74. * permissions, we should consider doing this differently,
  75. * e.g. with a Map.
  76. */
  77. private final static String[] canonicalNames = new String[1 << N_NAMES];
  78. /*
  79. * The target names mask. This is not private to avoid having to
  80. * generate accessor methods for accesses from the collection class.
  81. *
  82. * This mask includes implied bits. So if it has CREATE_MASK then
  83. * it necessarily has NEW_MASK too.
  84. */
  85. transient int mask;
  86. /** <p>Create a new MBeanServerPermission with the given name.</p>
  87. <p>This constructor is equivalent to
  88. <code>MBeanServerPermission(name,null)</code>.</p>
  89. @param name the name of the granted permission. It must
  90. respect the constraints spelt out in the description of the
  91. {@link MBeanServerPermission} class.
  92. @exception NullPointerException if the name is null.
  93. @exception IllegalArgumentException if the name is not
  94. <code>*</code> or one of the allowed names or a comma-separated
  95. list of the allowed names.
  96. */
  97. public MBeanServerPermission(String name) {
  98. this(name, null);
  99. }
  100. /** <p>Create a new MBeanServerPermission with the given name.</p>
  101. @param name the name of the granted permission. It must
  102. respect the constraints spelt out in the description of the
  103. {@link MBeanServerPermission} class.
  104. @param actions the associated actions. This parameter is not
  105. currently used and must be null or the empty string.
  106. @exception NullPointerException if the name is null.
  107. @exception IllegalArgumentException if the name is not
  108. <code>*</code> or one of the allowed names or a comma-separated
  109. list of the allowed names, or if <code>actions</code> is a non-null
  110. non-empty string. */
  111. public MBeanServerPermission(String name, String actions) {
  112. super(getCanonicalName(parseMask(name)), actions);
  113. /* It's annoying to have to parse the name twice, but since
  114. Permission.getName() is final and since we can't access "this"
  115. until after the call to the superclass constructor, there
  116. isn't any very clean way to do this. MBeanServerPermission
  117. objects aren't constructed very often, luckily. */
  118. mask = parseMask(name);
  119. /* Check that actions is a null empty string */
  120. if (actions != null && actions.length() > 0)
  121. throw new IllegalArgumentException("MBeanServerPermission " +
  122. "actions must be null: " +
  123. actions);
  124. }
  125. MBeanServerPermission(int mask) {
  126. super(getCanonicalName(mask));
  127. this.mask = impliedMask(mask);
  128. }
  129. private void readObject(ObjectInputStream in)
  130. throws IOException, ClassNotFoundException {
  131. in.defaultReadObject();
  132. mask = parseMask(getName());
  133. }
  134. static int simplifyMask(int mask) {
  135. if ((mask & CREATE_MASK) != 0)
  136. mask &= ~NEW_MASK;
  137. return mask;
  138. }
  139. static int impliedMask(int mask) {
  140. if ((mask & CREATE_MASK) != 0)
  141. mask |= NEW_MASK;
  142. return mask;
  143. }
  144. static String getCanonicalName(int mask) {
  145. if (mask == ALL_MASK)
  146. return "*";
  147. mask = simplifyMask(mask);
  148. synchronized (canonicalNames) {
  149. if (canonicalNames[mask] == null)
  150. canonicalNames[mask] = makeCanonicalName(mask);
  151. }
  152. return canonicalNames[mask];
  153. }
  154. private static String makeCanonicalName(int mask) {
  155. StringBuffer buf = new StringBuffer();
  156. for (int i = 0; i < N_NAMES; i++) {
  157. if ((mask & (1<<i)) != 0) {
  158. if (buf.length() > 0)
  159. buf.append(',');
  160. buf.append(names[i]);
  161. }
  162. }
  163. return buf.toString().intern();
  164. /* intern() avoids duplication when the mask has only
  165. one bit, so is equivalent to the string constants
  166. we have for the names[] array. */
  167. }
  168. /* Convert the string into a bitmask, including bits that
  169. are implied by the permissions in the string. */
  170. private static int parseMask(String name) {
  171. /* Check that target name is a non-null non-empty string */
  172. if (name == null) {
  173. throw new NullPointerException("MBeanServerPermission: " +
  174. "target name can't be null");
  175. }
  176. name = name.trim();
  177. if (name.equals("*"))
  178. return ALL_MASK;
  179. /* If the name is empty, nameIndex will barf. */
  180. if (name.indexOf(',') < 0)
  181. return impliedMask(1 << nameIndex(name.trim()));
  182. int mask = 0;
  183. StringTokenizer tok = new StringTokenizer(name, ",");
  184. while (tok.hasMoreTokens()) {
  185. String action = tok.nextToken();
  186. int i = nameIndex(action.trim());
  187. mask |= (1 << i);
  188. }
  189. return impliedMask(mask);
  190. }
  191. private static int nameIndex(String name)
  192. throws IllegalArgumentException {
  193. for (int i = 0; i < N_NAMES; i++) {
  194. if (names[i].equals(name))
  195. return i;
  196. }
  197. final String msg =
  198. "Invalid MBeanServerPermission name: \"" + name + "\"";
  199. throw new IllegalArgumentException(msg);
  200. }
  201. public int hashCode() {
  202. return mask;
  203. }
  204. /**
  205. * <p>Checks if this MBeanServerPermission object "implies" the specified
  206. * permission.</p>
  207. *
  208. * <p>More specifically, this method returns true if:</p>
  209. *
  210. * <ul>
  211. * <li> <i>p</i> is an instance of MBeanServerPermission,</li>
  212. * <li> <i>p</i>'s target names are a subset of this object's target
  213. * names</li>
  214. * </ul>
  215. *
  216. * <p>The <code>createMBeanServer</code> permission implies the
  217. * <code>newMBeanServer</code> permission.</p>
  218. *
  219. * @param p the permission to check against.
  220. * @return true if the specified permission is implied by this object,
  221. * false if not.
  222. */
  223. public boolean implies(Permission p) {
  224. if (!(p instanceof MBeanServerPermission))
  225. return false;
  226. MBeanServerPermission that = (MBeanServerPermission) p;
  227. return ((this.mask & that.mask) == that.mask);
  228. }
  229. /**
  230. * Checks two MBeanServerPermission objects for equality. Checks that
  231. * <i>obj</i> is an MBeanServerPermission, and represents the same
  232. * list of allowable actions as this object.
  233. * <P>
  234. * @param obj the object we are testing for equality with this object.
  235. * @return true if the objects are equal.
  236. */
  237. public boolean equals(Object obj) {
  238. if (obj == this)
  239. return true;
  240. if (! (obj instanceof MBeanServerPermission))
  241. return false;
  242. MBeanServerPermission that = (MBeanServerPermission) obj;
  243. return (this.mask == that.mask);
  244. }
  245. public PermissionCollection newPermissionCollection() {
  246. return new MBeanServerPermissionCollection();
  247. }
  248. }
  249. /**
  250. * Class returned by {@link MBeanServerPermission#newPermissionCollection()}.
  251. *
  252. * @serial include
  253. */
  254. /*
  255. * Since every collection of MBSP can be represented by a single MBSP,
  256. * that is what our PermissionCollection does. We need to define a
  257. * PermissionCollection because the one inherited from BasicPermission
  258. * doesn't know that createMBeanServer implies newMBeanServer.
  259. *
  260. * Though the serial form is defined, the TCK does not check it. We do
  261. * not require independent implementations to duplicate it. Even though
  262. * PermissionCollection is Serializable, instances of this class will
  263. * hardly ever be serialized, and different implementations do not
  264. * typically exchange serialized permission collections.
  265. *
  266. * If we did require that a particular form be respected here, we would
  267. * logically also have to require it for
  268. * MBeanPermission.newPermissionCollection, which would preclude an
  269. * implementation from defining a PermissionCollection there with an
  270. * optimized "implies" method.
  271. */
  272. class MBeanServerPermissionCollection extends PermissionCollection {
  273. /** @serial Null if no permissions in collection, otherwise a
  274. single permission that is the union of all permissions that
  275. have been added. */
  276. private MBeanServerPermission collectionPermission;
  277. private static final long serialVersionUID = -5661980843569388590L;
  278. public synchronized void add(Permission permission) {
  279. if (!(permission instanceof MBeanServerPermission)) {
  280. final String msg =
  281. "Permission not an MBeanServerPermission: " + permission;
  282. throw new IllegalArgumentException(msg);
  283. }
  284. if (isReadOnly())
  285. throw new SecurityException("Read-only permission collection");
  286. MBeanServerPermission mbsp = (MBeanServerPermission) permission;
  287. if (collectionPermission == null)
  288. collectionPermission = mbsp;
  289. else if (!collectionPermission.implies(permission)) {
  290. int newmask = collectionPermission.mask | mbsp.mask;
  291. collectionPermission = new MBeanServerPermission(newmask);
  292. }
  293. }
  294. public synchronized boolean implies(Permission permission) {
  295. return (collectionPermission != null &&
  296. collectionPermission.implies(permission));
  297. }
  298. public synchronized Enumeration elements() {
  299. Set set;
  300. if (collectionPermission == null)
  301. set = Collections.EMPTY_SET;
  302. else
  303. set = Collections.singleton(collectionPermission);
  304. return Collections.enumeration(set);
  305. }
  306. }