package org.jboss.seam.security.permission;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jboss.seam.security.annotations.permission.Permissions;
/**
* Permission actions can either be persisted as a comma-separated list of values, or as a
* bit-masked numerical value where certain bits represent specific actions for that class. This
* is a helper class that handles the conversion automatically and presents a unified API for
* dealing with these persistent actions.
*
* @author Shane Bryzak
*/
public class PermissionMetadata {
private Map<Class<?>, Boolean> usesActionMask = new HashMap<Class<?>, Boolean>();
private Map<Class<?>, Map<String, Long>> classActions = new HashMap<Class<?>, Map<String, Long>>();
private synchronized void initClassActions(Class<?> cls) {
if (!classActions.containsKey(cls)) {
Map<String, Long> actions = new HashMap<String, Long>();
boolean useMask = false;
Permissions p = (Permissions) cls.getAnnotation(Permissions.class);
if (p != null) {
org.jboss.seam.security.annotations.permission.Permission[] permissions = p.value();
if (permissions != null) {
for (org.jboss.seam.security.annotations.permission.Permission permission : permissions) {
actions.put(permission.action(), permission.mask());
if (permission.mask() != 0) {
useMask = true;
}
}
}
}
// Validate that all actions have a proper mask
if (useMask) {
Set<Long> masks = new HashSet<Long>();
for (String action : actions.keySet()) {
Long mask = actions.get(action);
if (masks.contains(mask)) {
throw new IllegalArgumentException("Class " + cls.getName() +
" defines a duplicate mask for permission action [" + action + "]");
}
if (mask == 0) {
throw new IllegalArgumentException("Class " + cls.getName() +
" must define a valid mask value for action [" + action + "]");
}
if ((mask & (mask - 1)) != 0) {
throw new IllegalArgumentException("Class " + cls.getName() +
" must define a mask value that is a power of 2 for action [" + action + "]");
}
masks.add(mask);
}
}
usesActionMask.put(cls, useMask);
classActions.put(cls, actions);
}
}
protected class ActionSet {
private Set<String> members = new HashSet<String>();
private Class<?> targetClass;
public ActionSet(Class<?> targetClass, String members) {
this.targetClass = targetClass;
addMembers(members);
}
public void addMembers(String members) {
if (members == null) return;
if (usesActionMask.get(targetClass)) {
// bit mask-based actions
long vals = Long.valueOf(members);
Map<String, Long> actions = classActions.get(targetClass);
for (String action : actions.keySet()) {
long mask = actions.get(action).longValue();
if ((vals & mask) != 0) {
this.members.add(action);
}
}
} else {
// comma-separated string based actions
String[] actions = members.split(",");
for (String action : actions) {
this.members.add(action);
}
}
}
public boolean contains(String action) {
return members.contains(action);
}
public ActionSet add(String action) {
members.add(action);
return this;
}
public ActionSet remove(String action) {
members.remove(action);
return this;
}
public Set<String> members() {
return members;
}
public boolean isEmpty() {
return members.isEmpty();
}
@Override
public String toString() {
if (usesActionMask.get(targetClass)) {
Map<String, Long> actions = classActions.get(targetClass);
long mask = 0;
for (String member : members) {
mask |= actions.get(member).longValue();
}
return "" + mask;
} else {
StringBuilder sb = new StringBuilder();
for (String member : members) {
if (sb.length() > 0) sb.append(',');
sb.append(member);
}
return sb.toString();
}
}
}
public ActionSet createActionSet(Class<?> targetClass, String members) {
if (!classActions.containsKey(targetClass)) initClassActions(targetClass);
return new ActionSet(targetClass, members);
}
public List<String> listAllowableActions(Class<?> targetClass) {
if (!classActions.containsKey(targetClass)) initClassActions(targetClass);
List<String> actions = new ArrayList<String>();
for (String action : classActions.get(targetClass).keySet()) {
actions.add(action);
}
return actions;
}
}