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.annotations.security.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.annotations.security.permission.Permission[] permissions = p.value(); if (permissions != null) { for (org.jboss.seam.annotations.security.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; } }