You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
372 lines
13 KiB
372 lines
13 KiB
/*
|
|
* Copyright (c) 2001, 2007, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
package javax.management;
|
|
|
|
import java.io.IOException;
|
|
import java.io.ObjectInputStream;
|
|
import java.security.BasicPermission;
|
|
import java.security.Permission;
|
|
import java.security.PermissionCollection;
|
|
import java.util.Collections;
|
|
import java.util.Enumeration;
|
|
import java.util.Set;
|
|
import java.util.StringTokenizer;
|
|
|
|
/** A Permission to perform actions related to MBeanServers.
|
|
The <em>name</em> of the permission specifies the operation requested
|
|
or granted by the permission. For a granted permission, it can be
|
|
<code>*</code> to allow all of the MBeanServer operations specified below.
|
|
Otherwise, for a granted or requested permission, it must be one of the
|
|
following:
|
|
<dl>
|
|
<dt>createMBeanServer</dt>
|
|
<dd>Create a new MBeanServer object using the method
|
|
{@link MBeanServerFactory#createMBeanServer()} or
|
|
{@link MBeanServerFactory#createMBeanServer(java.lang.String)}.
|
|
<dt>findMBeanServer</dt>
|
|
<dd>Find an MBeanServer with a given name, or all MBeanServers in this
|
|
JVM, using the method {@link MBeanServerFactory#findMBeanServer}.
|
|
<dt>newMBeanServer</dt>
|
|
<dd>Create a new MBeanServer object without keeping a reference to it,
|
|
using the method {@link MBeanServerFactory#newMBeanServer()} or
|
|
{@link MBeanServerFactory#newMBeanServer(java.lang.String)}.
|
|
<dt>releaseMBeanServer</dt>
|
|
<dd>Remove the MBeanServerFactory's reference to an MBeanServer,
|
|
using the method {@link MBeanServerFactory#releaseMBeanServer}.
|
|
</dl>
|
|
The <em>name</em> of the permission can also denote a list of one or more
|
|
comma-separated operations. Spaces are allowed at the beginning and
|
|
end of the <em>name</em> and before and after commas.
|
|
<p>
|
|
<code>MBeanServerPermission("createMBeanServer")</code> implies
|
|
<code>MBeanServerPermission("newMBeanServer")</code>.
|
|
*
|
|
* @since 1.5
|
|
*/
|
|
public class MBeanServerPermission extends BasicPermission {
|
|
private static final long serialVersionUID = -5661980843569388590L;
|
|
|
|
private final static int
|
|
CREATE = 0,
|
|
FIND = 1,
|
|
NEW = 2,
|
|
RELEASE = 3,
|
|
N_NAMES = 4;
|
|
|
|
private final static String[] names = {
|
|
"createMBeanServer",
|
|
"findMBeanServer",
|
|
"newMBeanServer",
|
|
"releaseMBeanServer",
|
|
};
|
|
|
|
private final static int
|
|
CREATE_MASK = 1<<CREATE,
|
|
FIND_MASK = 1<<FIND,
|
|
NEW_MASK = 1<<NEW,
|
|
RELEASE_MASK = 1<<RELEASE,
|
|
ALL_MASK = CREATE_MASK|FIND_MASK|NEW_MASK|RELEASE_MASK;
|
|
|
|
/*
|
|
* Map from permission masks to canonical names. This array is
|
|
* filled in on demand.
|
|
*
|
|
* This isn't very scalable. If we have more than five or six
|
|
* permissions, we should consider doing this differently,
|
|
* e.g. with a Map.
|
|
*/
|
|
private final static String[] canonicalNames = new String[1 << N_NAMES];
|
|
|
|
/*
|
|
* The target names mask. This is not private to avoid having to
|
|
* generate accessor methods for accesses from the collection class.
|
|
*
|
|
* This mask includes implied bits. So if it has CREATE_MASK then
|
|
* it necessarily has NEW_MASK too.
|
|
*/
|
|
transient int mask;
|
|
|
|
/** <p>Create a new MBeanServerPermission with the given name.</p>
|
|
<p>This constructor is equivalent to
|
|
<code>MBeanServerPermission(name,null)</code>.</p>
|
|
@param name the name of the granted permission. It must
|
|
respect the constraints spelt out in the description of the
|
|
{@link MBeanServerPermission} class.
|
|
@exception NullPointerException if the name is null.
|
|
@exception IllegalArgumentException if the name is not
|
|
<code>*</code> or one of the allowed names or a comma-separated
|
|
list of the allowed names.
|
|
*/
|
|
public MBeanServerPermission(String name) {
|
|
this(name, null);
|
|
}
|
|
|
|
/** <p>Create a new MBeanServerPermission with the given name.</p>
|
|
@param name the name of the granted permission. It must
|
|
respect the constraints spelt out in the description of the
|
|
{@link MBeanServerPermission} class.
|
|
@param actions the associated actions. This parameter is not
|
|
currently used and must be null or the empty string.
|
|
@exception NullPointerException if the name is null.
|
|
@exception IllegalArgumentException if the name is not
|
|
<code>*</code> or one of the allowed names or a comma-separated
|
|
list of the allowed names, or if <code>actions</code> is a non-null
|
|
non-empty string.
|
|
*
|
|
* @throws NullPointerException if <code>name</code> is <code>null</code>.
|
|
* @throws IllegalArgumentException if <code>name</code> is empty or
|
|
* if arguments are invalid.
|
|
*/
|
|
public MBeanServerPermission(String name, String actions) {
|
|
super(getCanonicalName(parseMask(name)), actions);
|
|
|
|
/* It's annoying to have to parse the name twice, but since
|
|
Permission.getName() is final and since we can't access "this"
|
|
until after the call to the superclass constructor, there
|
|
isn't any very clean way to do this. MBeanServerPermission
|
|
objects aren't constructed very often, luckily. */
|
|
mask = parseMask(name);
|
|
|
|
/* Check that actions is a null empty string */
|
|
if (actions != null && actions.length() > 0)
|
|
throw new IllegalArgumentException("MBeanServerPermission " +
|
|
"actions must be null: " +
|
|
actions);
|
|
}
|
|
|
|
MBeanServerPermission(int mask) {
|
|
super(getCanonicalName(mask));
|
|
this.mask = impliedMask(mask);
|
|
}
|
|
|
|
private void readObject(ObjectInputStream in)
|
|
throws IOException, ClassNotFoundException {
|
|
in.defaultReadObject();
|
|
mask = parseMask(getName());
|
|
}
|
|
|
|
static int simplifyMask(int mask) {
|
|
if ((mask & CREATE_MASK) != 0)
|
|
mask &= ~NEW_MASK;
|
|
return mask;
|
|
}
|
|
|
|
static int impliedMask(int mask) {
|
|
if ((mask & CREATE_MASK) != 0)
|
|
mask |= NEW_MASK;
|
|
return mask;
|
|
}
|
|
|
|
static String getCanonicalName(int mask) {
|
|
if (mask == ALL_MASK)
|
|
return "*";
|
|
|
|
mask = simplifyMask(mask);
|
|
|
|
synchronized (canonicalNames) {
|
|
if (canonicalNames[mask] == null)
|
|
canonicalNames[mask] = makeCanonicalName(mask);
|
|
}
|
|
|
|
return canonicalNames[mask];
|
|
}
|
|
|
|
private static String makeCanonicalName(int mask) {
|
|
final StringBuilder buf = new StringBuilder();
|
|
for (int i = 0; i < N_NAMES; i++) {
|
|
if ((mask & (1<<i)) != 0) {
|
|
if (buf.length() > 0)
|
|
buf.append(',');
|
|
buf.append(names[i]);
|
|
}
|
|
}
|
|
return buf.toString().intern();
|
|
/* intern() avoids duplication when the mask has only
|
|
one bit, so is equivalent to the string constants
|
|
we have for the names[] array. */
|
|
}
|
|
|
|
/* Convert the string into a bitmask, including bits that
|
|
are implied by the permissions in the string. */
|
|
private static int parseMask(String name) {
|
|
/* Check that target name is a non-null non-empty string */
|
|
if (name == null) {
|
|
throw new NullPointerException("MBeanServerPermission: " +
|
|
"target name can't be null");
|
|
}
|
|
|
|
name = name.trim();
|
|
if (name.equals("*"))
|
|
return ALL_MASK;
|
|
|
|
/* If the name is empty, nameIndex will barf. */
|
|
if (name.indexOf(',') < 0)
|
|
return impliedMask(1 << nameIndex(name.trim()));
|
|
|
|
int mask = 0;
|
|
|
|
StringTokenizer tok = new StringTokenizer(name, ",");
|
|
while (tok.hasMoreTokens()) {
|
|
String action = tok.nextToken();
|
|
int i = nameIndex(action.trim());
|
|
mask |= (1 << i);
|
|
}
|
|
|
|
return impliedMask(mask);
|
|
}
|
|
|
|
private static int nameIndex(String name)
|
|
throws IllegalArgumentException {
|
|
for (int i = 0; i < N_NAMES; i++) {
|
|
if (names[i].equals(name))
|
|
return i;
|
|
}
|
|
final String msg =
|
|
"Invalid MBeanServerPermission name: \"" + name + "\"";
|
|
throw new IllegalArgumentException(msg);
|
|
}
|
|
|
|
public int hashCode() {
|
|
return mask;
|
|
}
|
|
|
|
/**
|
|
* <p>Checks if this MBeanServerPermission object "implies" the specified
|
|
* permission.</p>
|
|
*
|
|
* <p>More specifically, this method returns true if:</p>
|
|
*
|
|
* <ul>
|
|
* <li> <i>p</i> is an instance of MBeanServerPermission,</li>
|
|
* <li> <i>p</i>'s target names are a subset of this object's target
|
|
* names</li>
|
|
* </ul>
|
|
*
|
|
* <p>The <code>createMBeanServer</code> permission implies the
|
|
* <code>newMBeanServer</code> permission.</p>
|
|
*
|
|
* @param p the permission to check against.
|
|
* @return true if the specified permission is implied by this object,
|
|
* false if not.
|
|
*/
|
|
public boolean implies(Permission p) {
|
|
if (!(p instanceof MBeanServerPermission))
|
|
return false;
|
|
|
|
MBeanServerPermission that = (MBeanServerPermission) p;
|
|
|
|
return ((this.mask & that.mask) == that.mask);
|
|
}
|
|
|
|
/**
|
|
* Checks two MBeanServerPermission objects for equality. Checks that
|
|
* <i>obj</i> is an MBeanServerPermission, and represents the same
|
|
* list of allowable actions as this object.
|
|
* <P>
|
|
* @param obj the object we are testing for equality with this object.
|
|
* @return true if the objects are equal.
|
|
*/
|
|
public boolean equals(Object obj) {
|
|
if (obj == this)
|
|
return true;
|
|
|
|
if (! (obj instanceof MBeanServerPermission))
|
|
return false;
|
|
|
|
MBeanServerPermission that = (MBeanServerPermission) obj;
|
|
|
|
return (this.mask == that.mask);
|
|
}
|
|
|
|
public PermissionCollection newPermissionCollection() {
|
|
return new MBeanServerPermissionCollection();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Class returned by {@link MBeanServerPermission#newPermissionCollection()}.
|
|
*
|
|
* @serial include
|
|
*/
|
|
|
|
/*
|
|
* Since every collection of MBSP can be represented by a single MBSP,
|
|
* that is what our PermissionCollection does. We need to define a
|
|
* PermissionCollection because the one inherited from BasicPermission
|
|
* doesn't know that createMBeanServer implies newMBeanServer.
|
|
*
|
|
* Though the serial form is defined, the TCK does not check it. We do
|
|
* not require independent implementations to duplicate it. Even though
|
|
* PermissionCollection is Serializable, instances of this class will
|
|
* hardly ever be serialized, and different implementations do not
|
|
* typically exchange serialized permission collections.
|
|
*
|
|
* If we did require that a particular form be respected here, we would
|
|
* logically also have to require it for
|
|
* MBeanPermission.newPermissionCollection, which would preclude an
|
|
* implementation from defining a PermissionCollection there with an
|
|
* optimized "implies" method.
|
|
*/
|
|
class MBeanServerPermissionCollection extends PermissionCollection {
|
|
/** @serial Null if no permissions in collection, otherwise a
|
|
single permission that is the union of all permissions that
|
|
have been added. */
|
|
private MBeanServerPermission collectionPermission;
|
|
|
|
private static final long serialVersionUID = -5661980843569388590L;
|
|
|
|
public synchronized void add(Permission permission) {
|
|
if (!(permission instanceof MBeanServerPermission)) {
|
|
final String msg =
|
|
"Permission not an MBeanServerPermission: " + permission;
|
|
throw new IllegalArgumentException(msg);
|
|
}
|
|
if (isReadOnly())
|
|
throw new SecurityException("Read-only permission collection");
|
|
MBeanServerPermission mbsp = (MBeanServerPermission) permission;
|
|
if (collectionPermission == null)
|
|
collectionPermission = mbsp;
|
|
else if (!collectionPermission.implies(permission)) {
|
|
int newmask = collectionPermission.mask | mbsp.mask;
|
|
collectionPermission = new MBeanServerPermission(newmask);
|
|
}
|
|
}
|
|
|
|
public synchronized boolean implies(Permission permission) {
|
|
return (collectionPermission != null &&
|
|
collectionPermission.implies(permission));
|
|
}
|
|
|
|
public synchronized Enumeration<Permission> elements() {
|
|
Set<Permission> set;
|
|
if (collectionPermission == null)
|
|
set = Collections.emptySet();
|
|
else
|
|
set = Collections.singleton((Permission) collectionPermission);
|
|
return Collections.enumeration(set);
|
|
}
|
|
}
|