Skip to content

Instantly share code, notes, and snippets.

@MuellerConstantin
Created June 28, 2020 16:15
Show Gist options
  • Save MuellerConstantin/fab24b7c595488fcd0c6c73f668a139a to your computer and use it in GitHub Desktop.
Save MuellerConstantin/fab24b7c595488fcd0c6c73f668a139a to your computer and use it in GitHub Desktop.
JSON and SpEL based "Attribute Based Access Control (ABAC)" for the Spring Framework
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;
import javax.persistence.EntityManager;
import javax.persistence.EntityNotFoundException;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
public class AbacPermissionEvaluator implements PermissionEvaluator {
private EntityManager entityManager;
private PolicyEnforcement policyEnforcement;
public AbacPermissionEvaluator(EntityManager entityManager, PolicyEnforcement policyEnforcement) {
this.entityManager = entityManager;
this.policyEnforcement = policyEnforcement;
}
@Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
Map<String, Object> environment = new HashMap<>();
environment.put("time", LocalDateTime.now());
Object principal = authentication.getPrincipal();
return policyEnforcement.evaluate(principal, targetDomainObject, permission, environment);
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetDomainId, String targetDomainType, Object permission) {
Object targetDomainObject = null;
try {
targetDomainObject = entityManager.find(Class.forName(targetDomainType), targetDomainId);
} catch (ClassNotFoundException exc) {
throw new RuntimeException(exc);
}
if (null == targetDomainObject) {
throw new EntityNotFoundException(String.format("Can't find domain target of type %s with ID %s", targetDomainType, targetDomainId));
}
Map<String, Object> environment = new HashMap<>();
environment.put("time", LocalDateTime.now());
Object principal = authentication.getPrincipal();
return policyEnforcement.evaluate(principal, targetDomainObject, permission, environment);
}
}
import java.util.List;
import java.util.stream.Collectors;
public class DefaultPolicyEnforcement implements PolicyEnforcement {
private PolicyDefinition policyDefinition;
public DefaultPolicyEnforcement(PolicyDefinition policyDefinition) {
this.policyDefinition = policyDefinition;
}
@Override
public boolean evaluate(Object subject, Object resource, Object action, Object environment) {
List<PolicyRule> policyRules = policyDefinition.getRules();
SecurityAbacContext context = new SecurityAbacContext(subject, resource, action, environment);
List<PolicyRule> matchedRules = filterRules(policyRules, context);
return evaluateRules(matchedRules, context);
}
private List<PolicyRule> filterRules(List<PolicyRule> policyRules, SecurityAbacContext context) {
return policyRules.stream()
.filter(policyRule -> policyRule.getTarget().getValue(context, Boolean.class))
.collect(Collectors.toList());
}
private boolean evaluateRules(List<PolicyRule> matchedRules, SecurityAbacContext context) {
return matchedRules.stream()
.anyMatch(policyRule -> policyRule.getCondition().getValue(context, Boolean.class));
}
}
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;
import org.x1c1b.pastefly.security.abac.PolicyDefinition;
import org.x1c1b.pastefly.security.abac.PolicyRule;
import java.io.IOException;
import java.util.List;
public class JsonPolicyDefinition implements PolicyDefinition, InitializingBean {
private final Logger log = LoggerFactory.getLogger(LoggingController.class);
private Resource policyResource;
private ObjectMapper objectMapper;
private PolicyRule[] policyRules;
public JsonPolicyDefinition(Resource policyResource, ObjectMapper objectMapper) {
this.policyResource = policyResource;
this.objectMapper = objectMapper;
}
@Override
public List<PolicyRule> getRules() {
return List.of(policyRules);
}
@Override
public void afterPropertiesSet() {
try {
if (null == policyResource || !policyResource.exists()) {
log.warn("Policy file is missing");
return;
}
log.debug("Loading policy file at: {}", policyResource.getURI());
policyRules = objectMapper.readValue(policyResource.getInputStream(), PolicyRule[].class);
log.info("Policy loaded successfully");
} catch (JsonMappingException exc) {
log.error("An error occurred while parsing the policy file", exc);
} catch (IOException exc) {
log.error("Failed to read policy file", exc);
}
}
}
import java.util.List;
public interface PolicyDefinition {
List<PolicyRule> getRules();
}
public interface PolicyEnforcement {
boolean evaluate(Object subject, Object resource, Object action, Object environment);
}
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import org.springframework.expression.Expression;
import org.x1c1b.pastefly.security.abac.json.SpelDeserializer;
public class PolicyRule {
private String description;
@JsonDeserialize(using = SpelDeserializer.class)
private Expression target;
@JsonDeserialize(using = SpelDeserializer.class)
private Expression condition;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Expression getTarget() {
return target;
}
public void setTarget(Expression target) {
this.target = target;
}
public Expression getCondition() {
return condition;
}
public void setCondition(Expression condition) {
this.condition = condition;
}
}
public class SecurityAbacContext {
private Object subject;
private Object resource;
private Object action;
private Object environment;
public Object getSubject() {
return subject;
}
public void setSubject(Object subject) {
this.subject = subject;
}
public Object getResource() {
return resource;
}
public void setResource(Object resource) {
this.resource = resource;
}
public Object getAction() {
return action;
}
public void setAction(Object action) {
this.action = action;
}
public Object getEnvironment() {
return environment;
}
public void setEnvironment(Object environment) {
this.environment = environment;
}
}
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import java.io.IOException;
public class SpelDeserializer extends StdDeserializer<Expression> {
private ExpressionParser expressionParser;
public SpelDeserializer(ExpressionParser expressionParser) {
super(Expression.class);
this.expressionParser = expressionParser;
}
public SpelDeserializer() {
this(new SpelExpressionParser());
}
@Override
public Expression deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
String expression = jsonParser.getCodec().readValue(jsonParser, String.class);
return expressionParser.parseExpression(expression);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment