org.wildfly.common.context.ContextPermissionCollection 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.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import org.wildfly.common.Assert;
import org.wildfly.common._private.CommonMessages;
/**
* @author David M. Lloyd
*/
final class ContextPermissionCollection extends PermissionCollection {
private static final long serialVersionUID = - 3651721703337368351L;
private volatile State state = emptyState;
private static final AtomicReferenceFieldUpdater stateUpdater = AtomicReferenceFieldUpdater.newUpdater(ContextPermissionCollection.class, State.class, "state");
public void add(final Permission permission) throws SecurityException, IllegalArgumentException {
Assert.checkNotNullParam("permission", permission);
if (permission instanceof ContextPermission) {
add((ContextPermission) permission);
} else {
throw CommonMessages.msg.invalidPermissionType(ContextPermission.class, permission.getClass());
}
}
public void add(final ContextPermission contextPermission) throws SecurityException {
Assert.checkNotNullParam("contextPermission", contextPermission);
if (isReadOnly()) {
throw CommonMessages.msg.readOnlyPermissionCollection();
}
final int actionBits = contextPermission.getActionBits();
if (actionBits == 0) {
// no operation
return;
}
final String name = contextPermission.getName();
State oldState, newState;
do {
oldState = this.state;
final ContextPermission oldGlobalPermission = oldState.globalPermission;
final int globalActions = oldGlobalPermission == null ? 0 : oldGlobalPermission.getActionBits();
if (oldGlobalPermission != null && oldGlobalPermission.implies(contextPermission)) {
// already fully implied by global permission
return;
} else {
final Map oldPermissions = oldState.permissions;
final Map newPermissions;
final ContextPermission newGlobalPermission;
if (name.equals("*")) {
// it's global but with some bits we don't have; calculate the new global actions and subtract from the map
if (oldGlobalPermission == null) {
newGlobalPermission = contextPermission;
} else {
newGlobalPermission = oldGlobalPermission.withActionBits(contextPermission.getActionBits());
}
// now subtract
newPermissions = cloneWithout(oldPermissions, newGlobalPermission);
} else {
newGlobalPermission = oldGlobalPermission;
// it's not global; check & add actions to our map permission
final ContextPermission mapPermission = oldPermissions.get(name);
if (mapPermission == null) {
// no map entry; just create one (but without any global actions we have defined)
if (oldPermissions.isEmpty()) {
// change empty map to singleton map
newPermissions = Collections.singletonMap(name, contextPermission.withoutActionBits(globalActions));
} else {
// make a copy of the map plus the new entry
newPermissions = new HashMap<>(oldPermissions);
newPermissions.put(name, contextPermission.withoutActionBits(globalActions));
}
} else if (((mapPermission.getActionBits() | globalActions) & actionBits) == actionBits) {
// already fully implied by a map entry
return;
} else {
// replace the map entry
if (oldPermissions.size() == 1) {
// it was a singleton map, just replace it
newPermissions = Collections.singletonMap(name, mapPermission.withActionBits(actionBits & ~globalActions));
} else {
// copy the map and replace the entry
newPermissions = new HashMap<>(oldPermissions);
newPermissions.put(name, mapPermission.withActionBits(actionBits & ~globalActions));
}
}
}
newState = new State(newGlobalPermission, newPermissions);
}
} while (! stateUpdater.compareAndSet(this, oldState, newState));
}
private static Map cloneWithout(final Map oldPermissions, final ContextPermission newGlobalPermission) {
final Iterator iterator = oldPermissions.values().iterator();
ContextPermission first;
for (;;) {
if (! iterator.hasNext()) {
return Collections.emptyMap();
}
first = iterator.next();
if (! newGlobalPermission.implies(first)) {
// break into next phase
break;
}
}
final int globalActionBits = newGlobalPermission.getActionBits();
ContextPermission second;
for (;;) {
if (! iterator.hasNext()) {
return Collections.singletonMap(first.getName(), first.withoutActionBits(globalActionBits));
}
second = iterator.next();
if (! newGlobalPermission.implies(second)) {
// break into next phase
break;
}
}
HashMap newMap = new HashMap<>();
newMap.put(first.getName(), first.withoutActionBits(globalActionBits));
newMap.put(second.getName(), second.withoutActionBits(globalActionBits));
ContextPermission subsequent;
while (iterator.hasNext()) {
subsequent = iterator.next();
if (! newGlobalPermission.implies(subsequent)) {
newMap.put(subsequent.getName(), subsequent.withoutActionBits(globalActionBits));
}
}
return newMap;
}
public boolean implies(final Permission permission) {
return permission instanceof ContextPermission && implies((ContextPermission) permission);
}
public boolean implies(final ContextPermission permission) {
if (permission == null) return false;
final State state = this.state;
final ContextPermission globalPermission = state.globalPermission;
final int globalBits;
if (globalPermission != null) {
if (globalPermission.implies(permission)) {
return true;
}
globalBits = globalPermission.getActionBits();
} else {
globalBits = 0;
}
final int bits = permission.getActionBits();
final String name = permission.getName();
if (name.equals("*")) {
return false;
}
final ContextPermission ourPermission = state.permissions.get(name);
if (ourPermission == null) {
return false;
}
final int ourBits = ourPermission.getActionBits() | globalBits;
return (bits & ourBits) == bits;
}
public Enumeration elements() {
final State state = this.state;
final Iterator iterator = state.permissions.values().iterator();
return new Enumeration() {
Permission next = state.globalPermission;
public boolean hasMoreElements() {
if (next != null) {
return true;
}
if (iterator.hasNext()) {
next = iterator.next();
return true;
}
return false;
}
public Permission nextElement() {
if (! hasMoreElements()) throw new NoSuchElementException();
try {
return next;
} finally {
next = null;
}
}
};
}
static class State {
private final ContextPermission globalPermission;
private final Map permissions;
State(final ContextPermission globalPermission, final Map permissions) {
this.globalPermission = globalPermission;
this.permissions = permissions;
}
}
private static final State emptyState = new State(null, Collections.emptyMap());
}