001 /*
002 * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package javax.management;
027
028 import java.io.IOException;
029 import java.io.ObjectInputStream;
030 import java.security.BasicPermission;
031 import java.security.Permission;
032 import java.security.PermissionCollection;
033 import java.util.Collections;
034 import java.util.Enumeration;
035 import java.util.Set;
036 import java.util.StringTokenizer;
037
038 /** A Permission to perform actions related to MBeanServers.
039 The <em>name</em> of the permission specifies the operation requested
040 or granted by the permission. For a granted permission, it can be
041 <code>*</code> to allow all of the MBeanServer operations specified below.
042 Otherwise, for a granted or requested permission, it must be one of the
043 following:
044 <dl>
045 <dt>createMBeanServer</dt>
046 <dd>Create a new MBeanServer object using the method
047 {@link MBeanServerFactory#createMBeanServer()} or
048 {@link MBeanServerFactory#createMBeanServer(java.lang.String)}.
049 <dt>findMBeanServer</dt>
050 <dd>Find an MBeanServer with a given name, or all MBeanServers in this
051 JVM, using the method {@link MBeanServerFactory#findMBeanServer}.
052 <dt>newMBeanServer</dt>
053 <dd>Create a new MBeanServer object without keeping a reference to it,
054 using the method {@link MBeanServerFactory#newMBeanServer()} or
055 {@link MBeanServerFactory#newMBeanServer(java.lang.String)}.
056 <dt>releaseMBeanServer</dt>
057 <dd>Remove the MBeanServerFactory's reference to an MBeanServer,
058 using the method {@link MBeanServerFactory#releaseMBeanServer}.
059 </dl>
060 The <em>name</em> of the permission can also denote a list of one or more
061 comma-separated operations. Spaces are allowed at the beginning and
062 end of the <em>name</em> and before and after commas.
063 <p>
064 <code>MBeanServerPermission("createMBeanServer")</code> implies
065 <code>MBeanServerPermission("newMBeanServer")</code>.
066 *
067 * @since 1.5
068 */
069 public class MBeanServerPermission extends BasicPermission {
070 private static final long serialVersionUID = -5661980843569388590L;
071
072 private final static int CREATE = 0, FIND = 1, NEW = 2,
073 RELEASE = 3, N_NAMES = 4;
074
075 private final static String[] names = { "createMBeanServer",
076 "findMBeanServer", "newMBeanServer", "releaseMBeanServer", };
077
078 private final static int CREATE_MASK = 1 << CREATE,
079 FIND_MASK = 1 << FIND, NEW_MASK = 1 << NEW,
080 RELEASE_MASK = 1 << RELEASE, ALL_MASK = CREATE_MASK
081 | FIND_MASK | NEW_MASK | RELEASE_MASK;
082
083 /*
084 * Map from permission masks to canonical names. This array is
085 * filled in on demand.
086 *
087 * This isn't very scalable. If we have more than five or six
088 * permissions, we should consider doing this differently,
089 * e.g. with a Map.
090 */
091 private final static String[] canonicalNames = new String[1 << N_NAMES];
092
093 /*
094 * The target names mask. This is not private to avoid having to
095 * generate accessor methods for accesses from the collection class.
096 *
097 * This mask includes implied bits. So if it has CREATE_MASK then
098 * it necessarily has NEW_MASK too.
099 */
100 transient int mask;
101
102 /** <p>Create a new MBeanServerPermission with the given name.</p>
103 <p>This constructor is equivalent to
104 <code>MBeanServerPermission(name,null)</code>.</p>
105 @param name the name of the granted permission. It must
106 respect the constraints spelt out in the description of the
107 {@link MBeanServerPermission} class.
108 @exception NullPointerException if the name is null.
109 @exception IllegalArgumentException if the name is not
110 <code>*</code> or one of the allowed names or a comma-separated
111 list of the allowed names.
112 */
113 public MBeanServerPermission(String name) {
114 this (name, null);
115 }
116
117 /** <p>Create a new MBeanServerPermission with the given name.</p>
118 @param name the name of the granted permission. It must
119 respect the constraints spelt out in the description of the
120 {@link MBeanServerPermission} class.
121 @param actions the associated actions. This parameter is not
122 currently used and must be null or the empty string.
123 @exception NullPointerException if the name is null.
124 @exception IllegalArgumentException if the name is not
125 <code>*</code> or one of the allowed names or a comma-separated
126 list of the allowed names, or if <code>actions</code> is a non-null
127 non-empty string.
128 *
129 * @throws NullPointerException if <code>name</code> is <code>null</code>.
130 * @throws IllegalArgumentException if <code>name</code> is empty or
131 * if arguments are invalid.
132 */
133 public MBeanServerPermission(String name, String actions) {
134 super (getCanonicalName(parseMask(name)), actions);
135
136 /* It's annoying to have to parse the name twice, but since
137 Permission.getName() is final and since we can't access "this"
138 until after the call to the superclass constructor, there
139 isn't any very clean way to do this. MBeanServerPermission
140 objects aren't constructed very often, luckily. */
141 mask = parseMask(name);
142
143 /* Check that actions is a null empty string */
144 if (actions != null && actions.length() > 0)
145 throw new IllegalArgumentException("MBeanServerPermission "
146 + "actions must be null: " + actions);
147 }
148
149 MBeanServerPermission(int mask) {
150 super (getCanonicalName(mask));
151 this .mask = impliedMask(mask);
152 }
153
154 private void readObject(ObjectInputStream in) throws IOException,
155 ClassNotFoundException {
156 in.defaultReadObject();
157 mask = parseMask(getName());
158 }
159
160 static int simplifyMask(int mask) {
161 if ((mask & CREATE_MASK) != 0)
162 mask &= ~NEW_MASK;
163 return mask;
164 }
165
166 static int impliedMask(int mask) {
167 if ((mask & CREATE_MASK) != 0)
168 mask |= NEW_MASK;
169 return mask;
170 }
171
172 static String getCanonicalName(int mask) {
173 if (mask == ALL_MASK)
174 return "*";
175
176 mask = simplifyMask(mask);
177
178 synchronized (canonicalNames) {
179 if (canonicalNames[mask] == null)
180 canonicalNames[mask] = makeCanonicalName(mask);
181 }
182
183 return canonicalNames[mask];
184 }
185
186 private static String makeCanonicalName(int mask) {
187 final StringBuilder buf = new StringBuilder();
188 for (int i = 0; i < N_NAMES; i++) {
189 if ((mask & (1 << i)) != 0) {
190 if (buf.length() > 0)
191 buf.append(',');
192 buf.append(names[i]);
193 }
194 }
195 return buf.toString().intern();
196 /* intern() avoids duplication when the mask has only
197 one bit, so is equivalent to the string constants
198 we have for the names[] array. */
199 }
200
201 /* Convert the string into a bitmask, including bits that
202 are implied by the permissions in the string. */
203 private static int parseMask(String name) {
204 /* Check that target name is a non-null non-empty string */
205 if (name == null) {
206 throw new NullPointerException("MBeanServerPermission: "
207 + "target name can't be null");
208 }
209
210 name = name.trim();
211 if (name.equals("*"))
212 return ALL_MASK;
213
214 /* If the name is empty, nameIndex will barf. */
215 if (name.indexOf(',') < 0)
216 return impliedMask(1 << nameIndex(name.trim()));
217
218 int mask = 0;
219
220 StringTokenizer tok = new StringTokenizer(name, ",");
221 while (tok.hasMoreTokens()) {
222 String action = tok.nextToken();
223 int i = nameIndex(action.trim());
224 mask |= (1 << i);
225 }
226
227 return impliedMask(mask);
228 }
229
230 private static int nameIndex(String name)
231 throws IllegalArgumentException {
232 for (int i = 0; i < N_NAMES; i++) {
233 if (names[i].equals(name))
234 return i;
235 }
236 final String msg = "Invalid MBeanServerPermission name: \""
237 + name + "\"";
238 throw new IllegalArgumentException(msg);
239 }
240
241 public int hashCode() {
242 return mask;
243 }
244
245 /**
246 * <p>Checks if this MBeanServerPermission object "implies" the specified
247 * permission.</p>
248 *
249 * <p>More specifically, this method returns true if:</p>
250 *
251 * <ul>
252 * <li> <i>p</i> is an instance of MBeanServerPermission,</li>
253 * <li> <i>p</i>'s target names are a subset of this object's target
254 * names</li>
255 * </ul>
256 *
257 * <p>The <code>createMBeanServer</code> permission implies the
258 * <code>newMBeanServer</code> permission.</p>
259 *
260 * @param p the permission to check against.
261 * @return true if the specified permission is implied by this object,
262 * false if not.
263 */
264 public boolean implies(Permission p) {
265 if (!(p instanceof MBeanServerPermission))
266 return false;
267
268 MBeanServerPermission that = (MBeanServerPermission) p;
269
270 return ((this .mask & that.mask) == that.mask);
271 }
272
273 /**
274 * Checks two MBeanServerPermission objects for equality. Checks that
275 * <i>obj</i> is an MBeanServerPermission, and represents the same
276 * list of allowable actions as this object.
277 * <P>
278 * @param obj the object we are testing for equality with this object.
279 * @return true if the objects are equal.
280 */
281 public boolean equals(Object obj) {
282 if (obj == this )
283 return true;
284
285 if (!(obj instanceof MBeanServerPermission))
286 return false;
287
288 MBeanServerPermission that = (MBeanServerPermission) obj;
289
290 return (this .mask == that.mask);
291 }
292
293 public PermissionCollection newPermissionCollection() {
294 return new MBeanServerPermissionCollection();
295 }
296 }
297
298 /**
299 * Class returned by {@link MBeanServerPermission#newPermissionCollection()}.
300 *
301 * @serial include
302 */
303
304 /*
305 * Since every collection of MBSP can be represented by a single MBSP,
306 * that is what our PermissionCollection does. We need to define a
307 * PermissionCollection because the one inherited from BasicPermission
308 * doesn't know that createMBeanServer implies newMBeanServer.
309 *
310 * Though the serial form is defined, the TCK does not check it. We do
311 * not require independent implementations to duplicate it. Even though
312 * PermissionCollection is Serializable, instances of this class will
313 * hardly ever be serialized, and different implementations do not
314 * typically exchange serialized permission collections.
315 *
316 * If we did require that a particular form be respected here, we would
317 * logically also have to require it for
318 * MBeanPermission.newPermissionCollection, which would preclude an
319 * implementation from defining a PermissionCollection there with an
320 * optimized "implies" method.
321 */
322 class MBeanServerPermissionCollection extends PermissionCollection {
323 /** @serial Null if no permissions in collection, otherwise a
324 single permission that is the union of all permissions that
325 have been added. */
326 private MBeanServerPermission collectionPermission;
327
328 private static final long serialVersionUID = -5661980843569388590L;
329
330 public synchronized void add(Permission permission) {
331 if (!(permission instanceof MBeanServerPermission)) {
332 final String msg = "Permission not an MBeanServerPermission: "
333 + permission;
334 throw new IllegalArgumentException(msg);
335 }
336 if (isReadOnly())
337 throw new SecurityException(
338 "Read-only permission collection");
339 MBeanServerPermission mbsp = (MBeanServerPermission) permission;
340 if (collectionPermission == null)
341 collectionPermission = mbsp;
342 else if (!collectionPermission.implies(permission)) {
343 int newmask = collectionPermission.mask | mbsp.mask;
344 collectionPermission = new MBeanServerPermission(newmask);
345 }
346 }
347
348 public synchronized boolean implies(Permission permission) {
349 return (collectionPermission != null && collectionPermission
350 .implies(permission));
351 }
352
353 public synchronized Enumeration<Permission> elements() {
354 Set<Permission> set;
355 if (collectionPermission == null)
356 set = Collections.emptySet();
357 else
358 set = Collections
359 .singleton((Permission) collectionPermission);
360 return Collections.enumeration(set);
361 }
362 }
|