package org.jboss.seam.security.permission;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.enterprise.context.SessionScoped;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.BeanManager;
import javax.inject.Inject;
import org.drools.ClassObjectFilter;
import org.drools.KnowledgeBase;
import org.drools.runtime.StatefulKnowledgeSession;
import org.drools.runtime.rule.FactHandle;
import org.jboss.seam.security.Identity;
import org.jboss.seam.security.events.PostAuthenticateEvent;
import org.jboss.seam.security.events.PostLoggedOutEvent;
import org.jboss.seam.security.qualifiers.Security;
import org.jboss.solder.core.Requires;
import org.jboss.solder.logging.Logger;
import org.picketlink.idm.api.Group;
import org.picketlink.idm.api.Role;
/**
* A permission resolver that uses a Drools rule base to perform permission checks
*
* @author Shane Bryzak
*/
@Requires("org.drools.KnowledgeBase")
@SessionScoped
public class RuleBasedPermissionResolver implements PermissionResolver, Serializable {
private static final long serialVersionUID = -7572627522601793024L;
private StatefulKnowledgeSession securityContext;
@Inject Logger log;
@Inject SecurityRuleLoader securityRuleLoader;
@Inject BeanManager manager;
@Inject Identity identity;
@Inject
public void init() {
if (getSecurityRules() != null) {
setSecurityContext(getSecurityRules().newStatefulKnowledgeSession());
//getSecurityContext().setGlobalResolver(new SeamGlobalResolver(getSecurityContext().getGlobalResolver()));
}
}
/**
* Performs a permission check for the specified name and action
*
* @param target Object The target of the permission check
* @param action String The action to be performed on the target
* @return boolean True if the user has the specified permission
*/
public boolean hasPermission(Object resource, String permission) {
if (getSecurityRules() == null) return false;
StatefulKnowledgeSession securityContext = getSecurityContext();
if (securityContext == null) return false;
List<FactHandle> handles = new ArrayList<FactHandle>();
PermissionCheck check;
synchronized (securityContext) {
if (!(resource instanceof String) && !(resource instanceof Class<?>)) {
handles.add(securityContext.insert(resource));
} else if (resource instanceof Class<?>) {
// TODO fix
String componentName = null; // manager. Seam.getComponentName((Class) target);
resource = componentName != null ? componentName : ((Class<?>) resource).getName();
}
check = new PermissionCheck(resource, permission);
try {
synchronizeContext();
handles.add(securityContext.insert(check));
securityContext.fireAllRules();
} finally {
for (FactHandle handle : handles) {
securityContext.retract(handle);
}
}
}
return check.isGranted();
}
public void filterSetByAction(Set<Object> targets, String action) {
Iterator<?> iter = targets.iterator();
while (iter.hasNext()) {
Object target = iter.next();
if (hasPermission(target, action)) iter.remove();
}
}
public boolean checkConditionalRole(String roleName, Object target, String action) {
if (getSecurityRules() == null) return false;
StatefulKnowledgeSession securityContext = getSecurityContext();
if (securityContext == null) return false;
RoleCheck roleCheck = new RoleCheck(roleName);
List<FactHandle> handles = new ArrayList<FactHandle>();
PermissionCheck check = new PermissionCheck(target, action);
synchronized (securityContext) {
if (!(target instanceof String) && !(target instanceof Class<?>)) {
handles.add(securityContext.insert(target));
} else if (target instanceof Class<?>) {
// TODO fix
String componentName = null; //Seam.getComponentName((Class) target);
target = componentName != null ? componentName : ((Class<?>) target).getName();
}
try {
handles.add(securityContext.insert(check));
// Check if there are any additional requirements
securityContext.fireAllRules();
/*
if (check.hasRequirements())
{
for (String requirement : check.getRequirements())
{
// TODO fix
Object value = null; // Contexts.lookupInStatefulContexts(requirement);
if (value != null)
{
handles.add (securityContext.insert(value));
}
}
}*/
synchronizeContext();
handles.add(securityContext.insert(roleCheck));
handles.add(securityContext.insert(check));
securityContext.fireAllRules();
} finally {
for (FactHandle handle : handles) {
securityContext.retract(handle);
}
}
}
return roleCheck.isGranted();
}
public void unAuthenticate(@Observes PostLoggedOutEvent event) {
if (getSecurityContext() != null) {
getSecurityContext().dispose();
setSecurityContext(null);
}
init();
}
/**
* Synchronises the state of the security context with that of the subject
*/
private void synchronizeContext() {
if (getSecurityContext() != null) {
getSecurityContext().insert(identity.getUser());
for (Role role : identity.getRoles()) {
Iterator<?> iter = getSecurityContext().getObjects(
new ClassObjectFilter(Role.class)).iterator();
boolean found = false;
while (iter.hasNext()) {
Role r = (Role) iter.next();
if (r.equals(role)) {
found = true;
break;
}
}
if (!found) {
getSecurityContext().insert(role);
}
}
for (Group group : identity.getGroups()) {
Iterator<?> iter = getSecurityContext().getObjects(
new ClassObjectFilter(Group.class)).iterator();
boolean found = false;
while (iter.hasNext()) {
Group g = (Group) iter.next();
if (g.equals(group)) {
found = true;
break;
}
}
if (!found) {
getSecurityContext().insert(group);
}
}
Iterator<?> iter = getSecurityContext().getObjects(
new ClassObjectFilter(Role.class)).iterator();
while (iter.hasNext()) {
Role r = (Role) iter.next();
if (!identity.hasRole(r.getRoleType().getName(),
r.getGroup().getName(), r.getGroup().getGroupType())) {
FactHandle fh = getSecurityContext().getFactHandle(r);
getSecurityContext().retract(fh);
}
}
}
}
public StatefulKnowledgeSession getSecurityContext() {
return securityContext;
}
public void setSecurityContext(StatefulKnowledgeSession securityContext) {
this.securityContext = securityContext;
}
public KnowledgeBase getSecurityRules() {
return securityRuleLoader.getKnowledgeBase();
}
/**
* Post-authentication event observer
*/
public void setUserAccountInSecurityContext(@Observes PostAuthenticateEvent event) {
if (getSecurityContext() != null) {
getSecurityContext().insert(identity.getUser());
}
}
}