org.wildfly.security.permission.PermissionActions Maven / Gradle / Ivy
The newest version!
/*
* 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 org.wildfly.security.permission.AbstractActionSetPermission org.wildfly.security.permission.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;
}
}
}