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