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 * 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 }