org.wildfly.common.context.ContextPermission Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source.
* Copyright 2016 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.wildfly.common.context;
import java.security.Permission;
import java.security.PermissionCollection;
import java.util.function.Supplier;
import org.wildfly.common.Assert;
import org.wildfly.common._private.CommonMessages;
import org.wildfly.common.annotation.NotNull;
/**
* A permission object for operations on {@link ContextManager} instances.
*
* This type of permission requires a {@code name} and an {@code action}. The {@code name} may be the name
* of a context manager, or the special {@code *} name which means the permission applies to all context managers.
*
* The {@code action} may be one or more of the following (each action name being separated by a comma):
*
* - {@code get} - allow {@linkplain ContextManager#get() getting} the current context
* - {@code getPrivilegedSupplier} - allow access to the {@link ContextManager#getPrivilegedSupplier()} method
* - {@code getGlobalDefault} - allow access to the {@linkplain ContextManager#getGlobalDefault() global default context}
* - {@code setGlobalDefault} - allow {@linkplain ContextManager#setGlobalDefault(Contextual) setting the global default instance}
* - {@code setGlobalDefaultSupplier} - allow {@linkplain ContextManager#setGlobalDefaultSupplier(Supplier) setting the global default instance supplier}
* - {@code getThreadDefault} - allow access to the {@linkplain ContextManager#getThreadDefault() per-thread default context}
* - {@code setThreadDefault} - allow {@linkplain ContextManager#setThreadDefault(Contextual) setting the per-thread default instance}
* - {@code setThreadDefaultSupplier} - allow {@linkplain ContextManager#setThreadDefaultSupplier(Supplier) setting the per-thread default instance supplier}
*
* Additionally, the special {@code *} action name is allowed which implies all of the above actions.
*
* The {@link #newPermissionCollection()} method returns an optimized container for context permissions.
*
* @author David M. Lloyd
*/
public final class ContextPermission extends Permission {
private static final long serialVersionUID = 2149744699461086708L;
private static final int ACTION_GET = 0b00000000001;
private static final int ACTION_GET_PRIV_SUP = 0b00000000010;
private static final int ACTION_GET_GLOBAL_DEF = 0b00000000100;
private static final int ACTION_SET_GLOBAL_DEF = 0b00000001000;
private static final int ACTION_SET_GLOBAL_DEF_SUP = 0b00000010000;
private static final int ACTION_GET_THREAD_DEF = 0b00000100000;
private static final int ACTION_SET_THREAD_DEF = 0b00001000000;
private static final int ACTION_SET_THREAD_DEF_SUP = 0b00010000000;
private static final int ACTION_GET_CLASSLOADER_DEF = 0b00100000000;
private static final int ACTION_SET_CLASSLOADER_DEF = 0b01000000000;
private static final int ACTION_SET_CLASSLOADER_DEF_SUP = 0b10000000000;
private static final int ACTION_ALL = 0b11111111111;
static final String STR_GET = "get";
static final String STR_GET_PRIV_SUP = "getPrivilegedSupplier";
static final String STR_GET_GLOBAL_DEF = "getGlobalDefault";
static final String STR_SET_GLOBAL_DEF = "setGlobalDefault";
static final String STR_SET_GLOBAL_DEF_SUP = "setGlobalDefaultSupplier";
static final String STR_GET_THREAD_DEF = "getThreadDefault";
static final String STR_SET_THREAD_DEF = "setThreadDefault";
static final String STR_SET_THREAD_DEF_SUP = "setThreadDefaultSupplier";
static final String STR_GET_CLASSLOADER_DEF = "getClassLoaderDefault";
static final String STR_SET_CLASSLOADER_DEF = "setClassLoaderDefault";
static final String STR_SET_CLASSLOADER_DEF_SUP = "setClassLoaderDefaultSupplier";
private final transient int actionBits;
private transient String actionString;
/**
* Constructs a permission with the specified name.
*
* @param name name of the Permission object being created (must not be {@code null})
* @param actions the actions string (must not be {@code null})
*/
public ContextPermission(final String name, final String actions) {
super(name);
Assert.checkNotNullParam("name", name);
Assert.checkNotNullParam("actions", actions);
actionBits = parseActions(actions);
}
ContextPermission(final String name, final int actionBits) {
super(name);
Assert.checkNotNullParam("name", name);
this.actionBits = actionBits & ACTION_ALL;
}
private static int parseActions(final String actions) throws IllegalArgumentException {
int bits = 0;
int start = 0;
int idx = actions.indexOf(',');
if (idx == -1) {
return parseAction(actions);
} else do {
bits |= parseAction(actions.substring(start, idx));
start = idx + 1;
idx = actions.indexOf(',', start);
} while (idx != -1);
bits |= parseAction(actions.substring(start));
return bits;
}
private static int parseAction(final String action) {
switch (action.trim()) {
case STR_GET: return ACTION_GET;
case STR_GET_PRIV_SUP: return ACTION_GET_PRIV_SUP;
case STR_GET_GLOBAL_DEF: return ACTION_GET_GLOBAL_DEF;
case STR_SET_GLOBAL_DEF: return ACTION_SET_GLOBAL_DEF;
case STR_SET_GLOBAL_DEF_SUP: return ACTION_SET_GLOBAL_DEF_SUP;
case STR_GET_THREAD_DEF: return ACTION_GET_THREAD_DEF;
case STR_SET_THREAD_DEF: return ACTION_SET_THREAD_DEF;
case STR_SET_THREAD_DEF_SUP: return ACTION_SET_THREAD_DEF_SUP;
case STR_GET_CLASSLOADER_DEF: return ACTION_GET_CLASSLOADER_DEF;
case STR_SET_CLASSLOADER_DEF: return ACTION_SET_CLASSLOADER_DEF;
case STR_SET_CLASSLOADER_DEF_SUP: return ACTION_SET_CLASSLOADER_DEF_SUP;
case "*": return ACTION_ALL;
case "": return 0;
default: {
throw CommonMessages.msg.invalidPermissionAction(action);
}
}
}
/**
* Determine if the given permission is implied by this permission.
*
* @param permission the other permission
* @return {@code true} if the other permission is not {@code null} and is a context permission which is implied by
* this permission instance; {@code false} otherwise
*/
public boolean implies(final Permission permission) {
return permission instanceof ContextPermission && implies((ContextPermission) permission);
}
/**
* Determine if the given permission is implied by this permission.
*
* @param permission the other permission
* @return {@code true} if the other permission is not {@code null} and is a context permission which is implied by
* this permission instance; {@code false} otherwise
*/
public boolean implies(final ContextPermission permission) {
return this == permission || permission != null && isSet(this.actionBits, permission.actionBits) && impliesName(permission.getName());
}
private boolean impliesName(String otherName) {
final String myName = getName();
return myName.equals("*") || myName.equals(otherName);
}
static boolean isSet(int mask, int test) {
return (mask & test) == test;
}
/**
* Determine if this permission is equal to the given object.
*
* @param obj the other object
* @return {@code true} if the object is a context permission that is exactly equal to this one; {@code false} otherwise
*/
public boolean equals(final Object obj) {
return obj instanceof ContextPermission && equals((ContextPermission) obj);
}
/**
* Determine if this permission is equal to the given permission.
*
* @param permission the other permission
* @return {@code true} if the permission is a context permission that is exactly equal to this one; {@code false} otherwise
*/
public boolean equals(final ContextPermission permission) {
return this == permission || permission != null && actionBits == permission.actionBits && getName().equals(permission.getName());
}
/**
* Get the hash code of this permission.
*
* @return the hash code of this permission
*/
public int hashCode() {
return getName().hashCode() * 17 + actionBits;
}
/**
* Get the actions string. This string will be returned in a canonical format.
*
* @return the actions string
*/
public String getActions() {
String actionString = this.actionString;
if (actionString == null) {
final int actionBits = this.actionBits;
if (isSet(actionBits, ACTION_ALL)) {
return this.actionString = "*";
} else if (actionBits == 0) {
return this.actionString = "";
}
final StringBuilder b = new StringBuilder();
if (isSet(actionBits, ACTION_GET)) b.append(STR_GET).append(',');
if (isSet(actionBits, ACTION_GET_PRIV_SUP)) b.append(STR_GET_PRIV_SUP).append(',');
if (isSet(actionBits, ACTION_GET_GLOBAL_DEF)) b.append(STR_GET_GLOBAL_DEF).append(',');
if (isSet(actionBits, ACTION_SET_GLOBAL_DEF)) b.append(STR_SET_GLOBAL_DEF).append(',');
if (isSet(actionBits, ACTION_SET_GLOBAL_DEF_SUP)) b.append(STR_SET_GLOBAL_DEF_SUP).append(',');
if (isSet(actionBits, ACTION_GET_THREAD_DEF)) b.append(STR_GET_THREAD_DEF).append(',');
if (isSet(actionBits, ACTION_SET_THREAD_DEF)) b.append(STR_SET_THREAD_DEF).append(',');
if (isSet(actionBits, ACTION_SET_THREAD_DEF_SUP)) b.append(STR_SET_THREAD_DEF_SUP).append(',');
if (isSet(actionBits, ACTION_GET_CLASSLOADER_DEF)) b.append(STR_GET_CLASSLOADER_DEF).append(',');
if (isSet(actionBits, ACTION_SET_CLASSLOADER_DEF)) b.append(STR_SET_CLASSLOADER_DEF).append(',');
if (isSet(actionBits, ACTION_SET_CLASSLOADER_DEF_SUP)) b.append(STR_SET_CLASSLOADER_DEF_SUP).append(',');
assert b.length() > 0;
b.setLength(b.length() - 1);
return this.actionString = b.toString();
}
return actionString;
}
/**
* Create a copy of this permission with the additional given actions.
*
* @param actions the additional actions (must not be {@code null})
* @return the new permission (not {@code null})
*/
@NotNull
public ContextPermission withActions(String actions) {
return withActionBits(parseActions(actions));
}
ContextPermission withActionBits(int actionBits) {
if (isSet(this.actionBits, actionBits)) {
return this;
} else {
return new ContextPermission(getName(), this.actionBits | actionBits);
}
}
/**
* Create a copy of this permission without any of the given actions.
*
* @param actions the actions to subtract (must not be {@code null})
* @return the new permission (not {@code null})
*/
@NotNull
public ContextPermission withoutActions(String actions) {
return withoutActionBits(parseActions(actions));
}
ContextPermission withoutActionBits(final int actionBits) {
if ((actionBits & this.actionBits) == 0) {
return this;
} else {
return new ContextPermission(getName(), this.actionBits & ~actionBits);
}
}
/**
* Get a new permission collection instance which can hold this type of permissions.
*
* @return a new permission collection instance (not {@code null})
*/
public PermissionCollection newPermissionCollection() {
return new ContextPermissionCollection();
}
int getActionBits() {
return actionBits;
}
}