org.wildfly.security.permission.PermissionActions Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
/*
* 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.security.permission;
import static org.wildfly.security.permission.SecurityMessages.permission;
import org.wildfly.common.Assert;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Iterator;
/**
* A helper class for defining permissions which use a finite list of actions. Define custom permissions using
* an {@code enum} of actions, where the string representation (via {@code toString()}) of each enum is one possible
* action name. Typically the {@code enum} should be non-public, and the constant names should be lowercase. If
* an action name contains a character which is not a valid Java identifier, then the {@code toString()} method of
* such constants should be overridden to report the correct string. The actions may be stored on the permission as
* an {@code EnumSet}, an {@code int}, or a {@code long}. The field should be marked {@code transient}, and
* the actions represented by a (possibly synthetic) field of type {@code String} which uses the canonical representation
* of the actions.
*
* @author David M. Lloyd
*
* @deprecated Use one of the abstract permission classes like {@link AbstractActionSetPermission} instead.
*/
@Deprecated
public final class PermissionActions {
private PermissionActions() {
}
static final class TrieNode {
private static final char[] C_EMPTY = new char[0];
private static final TrieNode[] T_EMPTY = new TrieNode[0];
private E result;
private char[] matches = C_EMPTY;
@SuppressWarnings("unchecked")
private TrieNode[] children = T_EMPTY;
void put(String s, int idx, E value) {
if (idx == s.length()) {
result = value;
return;
}
char c = s.charAt(idx);
final int i = Arrays.binarySearch(matches, c);
if (i < 0) {
// copy and add
final int oldLength = matches.length;
final char[] newMatches = Arrays.copyOf(matches, oldLength + 1);
final TrieNode[] newChildren = Arrays.copyOf(children, oldLength + 1);
// i is the negated insertion index
final int insertIndex = -i - 1;
System.arraycopy(newMatches, insertIndex, newMatches, insertIndex + 1, oldLength - insertIndex);
System.arraycopy(newChildren, insertIndex, newChildren, insertIndex + 1, oldLength - insertIndex);
newMatches[insertIndex] = c;
final TrieNode newNode = new TrieNode<>();
newChildren[insertIndex] = newNode;
matches = newMatches;
children = newChildren;
newNode.put(s, idx + 1, value);
} else {
children[i].put(s, idx + 1, value);
}
}
E get(String s, int idx, int end) {
if (idx == end) {
return result;
}
final char c = s.charAt(idx);
final int i = Arrays.binarySearch(matches, c);
if (i < 0) {
return null;
}
return children[i].get(s, idx + 1, end);
}
}
static final class Info {
final TrieNode root;
final E[] constants;
Info(final TrieNode root, final E[] constants) {
this.root = root;
this.constants = constants;
}
}
private static final ClassValue> storedInfo = new ClassValue>() {
protected Info> computeValue(final Class> type) {
return computeReal(type);
}
private Info computeReal(final Class type) {
final TrieNode root = new TrieNode<>();
final E[] enumConstants = type.getEnumConstants();
for (E e : enumConstants) {
root.put(e.toString(), 0, e);
}
return new Info<>(root, type.getEnumConstants());
}
};
interface MatchAction> {
void matched(E item);
void matchedAll(Class type);
}
static class SetMatchAction> implements MatchAction {
private EnumSet set;
SetMatchAction(final EnumSet set) {
this.set = set;
}
public void matched(final E item) {
set.add(item);
}
public void matchedAll(final Class type) {
set = EnumSet.allOf(type);
}
public EnumSet getSet() {
return set;
}
}
static class IntMatchAction> implements MatchAction {
private int result;
IntMatchAction() {
}
public void matched(final E item) {
result |= 1 << item.ordinal();
}
public void matchedAll(final Class type) {
result |= (1 << storedInfo.get(type).constants.length) - 1;
}
public int getResult() {
return result;
}
}
static class LongMatchAction> implements MatchAction {
private long result;
LongMatchAction() {
}
public void matched(final E item) {
result |= 1L << item.ordinal();
}
public void matchedAll(final Class type) {
result |= (1L << storedInfo.get(type).constants.length) - 1;
}
public long getResult() {
return result;
}
}
/**
* Parse an action string using the given action type to an {@code EnumSet}.
*
* @param actionType the action {@code enum} type class
* @param actionString the string to parse
* @param the action {@code enum} type
*
* @return the set of actions from the string
*
* @throws IllegalArgumentException if the string contained an invalid action
*/
public static > EnumSet parseActionStringToSet(Class actionType, String actionString) throws IllegalArgumentException {
Assert.checkNotNullParam("actionType", actionType);
Assert.checkNotNullParam("actionString", actionString);
final SetMatchAction matchAction = new SetMatchAction<>(EnumSet.noneOf(actionType));
doParse(actionType, actionString, matchAction);
return matchAction.getSet();
}
/**
* Parse an action string using the given action type to an {@code int}. The given {@code enum} type must have
* 32 or fewer constant values.
*
* @param actionType the action {@code enum} type class
* @param actionString the string to parse
* @param the action {@code enum} type
*
* @return the set of actions from the string
*
* @throws IllegalArgumentException if the string contained an invalid action
*/
public static > int parseActionStringToInt(Class actionType, String actionString) throws IllegalArgumentException {
Assert.checkNotNullParam("actionType", actionType);
Assert.checkNotNullParam("actionString", actionString);
final IntMatchAction matchAction = new IntMatchAction<>();
doParse(actionType, actionString, matchAction);
return matchAction.getResult();
}
/**
* Parse an action string using the given action type to a {@code long}. The given {@code enum} type must have
* 64 or fewer constant values.
*
* @param actionType the action {@code enum} type class
* @param actionString the string to parse
* @param the action {@code enum} type
*
* @return the set of actions from the string
*
* @throws IllegalArgumentException if the string contained an invalid action
*/
public static > long parseActionStringToLong(Class actionType, String actionString) throws IllegalArgumentException {
Assert.checkNotNullParam("actionType", actionType);
Assert.checkNotNullParam("actionString", actionString);
final LongMatchAction matchAction = new LongMatchAction<>();
doParse(actionType, actionString, matchAction);
return matchAction.getResult();
}
private static > void doParse(final Class actionType, final String actionString, final MatchAction matchAction) {
@SuppressWarnings("unchecked")
final Info info = (Info) storedInfo.get(actionType);
final TrieNode rootNode = info.root;
// begin parse
char c;
final int length = actionString.length();
int i = 0;
L0: for (;;) {
if (i == length) {
// OK
break L0;
}
c = actionString.charAt(i);
if (Character.isWhitespace(c)) {
i ++;
continue L0;
}
if (c == ',') {
// hmm, empty segment; ignore it
i ++;
continue L0;
}
if (c == '*') {
// potential star
matchAction.matchedAll(actionType);
for (;;) {
i ++;
if (i == length) {
// done
break L0;
}
c = actionString.charAt(i);
if (c == ',') {
// pointless, but go on
i ++;
continue L0;
}
if (! Character.isWhitespace(c)) {
throw permission.unexpectedActionCharacter(c, i, actionString);
}
}
// not reachable
}
// else it's a potentially valid character
int start = i;
for (;;) {
i++;
c = i < length ? actionString.charAt(i) : 0;
if (i == length || Character.isWhitespace(c) || c == ',') {
// action string ends here
final E action = rootNode.get(actionString, start, i);
if (action == null) {
throw permission.invalidAction(actionString.substring(start, i), start, actionString);
}
matchAction.matched(action);
if (i == length) {
// done
break L0;
}
while (Character.isWhitespace(c)) {
i++;
if (i == length) {
// done
break L0;
}
c = actionString.charAt(i);
}
if (c != ',') {
throw permission.unexpectedActionCharacter(c, i, actionString);
}
i ++;
continue L0;
}
}
// not reachable
}
}
/**
* Get the canonical action string representation for the given action set.
*
* @param set the action set
* @param the action type
* @return the canonical representation
*/
public static > String getCanonicalActionString(EnumSet set) {
if (set == null || set.isEmpty()) return "";
final StringBuilder b = new StringBuilder();
getCanonicalActionString(set, b);
return b.toString();
}
/**
* Get the canonical action string representation for the given action set, appending it to the given string builder.
*
* @param set the action set
* @param b the string builder
* @param the action type
*/
public static > void getCanonicalActionString(EnumSet set, StringBuilder b) {
if (set == null || set.isEmpty()) return;
final Iterator iterator = set.iterator();
if (iterator.hasNext()) {
E e = iterator.next();
b.append(e.toString());
while (iterator.hasNext()) {
e = iterator.next();
b.append(',');
b.append(e.toString());
}
}
}
/**
* Get the canonical action string representation for the given action set.
*
* @param type the action {@code enum} type class
* @param set the action set
* @param the action type
* @return the canonical representation
*/
public static > String getCanonicalActionString(Class type, int set) {
if (set == 0) return "";
final StringBuilder b = new StringBuilder();
getCanonicalActionString(type, set, b);
return b.toString();
}
/**
* Get the canonical action string representation for the given action set, appending it to the given string builder.
*
* @param type the action {@code enum} type class
* @param set the action set
* @param b the string builder
* @param the action type
*/
public static > void getCanonicalActionString(Class type, int set, StringBuilder b) {
if (set == 0) return;
@SuppressWarnings("unchecked")
final E[] constants = (E[]) storedInfo.get(type).constants;
int bit = Integer.lowestOneBit(set);
E e = constants[Integer.numberOfTrailingZeros(bit)];
b.append(e.toString());
set &= ~bit;
while (set != 0) {
bit = Integer.lowestOneBit(set);
e = constants[Integer.numberOfTrailingZeros(bit)];
b.append(',').append(e.toString());
set &= ~bit;
}
}
/**
* Get the canonical action string representation for the given action set.
*
* @param type the action {@code enum} type class
* @param set the action set
* @param the action type
* @return the canonical representation
*/
public static > String getCanonicalActionString(Class type, long set) {
if (set == 0) return "";
final StringBuilder b = new StringBuilder();
getCanonicalActionString(type, set, b);
return b.toString();
}
/**
* Get the canonical action string representation for the given action set, appending it to the given string builder.
*
* @param type the action {@code enum} type class
* @param set the action set
* @param b the string builder
* @param the action type
*/
public static > void getCanonicalActionString(Class type, long set, StringBuilder b) {
if (set == 0) return;
@SuppressWarnings("unchecked")
final E[] constants = (E[]) storedInfo.get(type).constants;
long bit = Long.lowestOneBit(set);
E e = constants[Long.numberOfTrailingZeros(bit)];
b.append(e.toString());
set &= ~bit;
while (set != 0) {
bit = Long.lowestOneBit(set);
e = constants[Long.numberOfTrailingZeros(bit)];
b.append(',').append(e.toString());
set &= ~bit;
}
}
}