com.kero.security.core.property.LocalProperty Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kero-security Show documentation
Show all versions of kero-security Show documentation
Kero-Security is a library for statically controlling access to properties of objects / classes.
package com.kero.security.core.property;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.kero.security.core.access.Access;
import com.kero.security.core.config.action.Action;
import com.kero.security.core.config.action.ActionDeny;
import com.kero.security.core.config.action.ActionGrant;
import com.kero.security.core.interceptor.DenyInterceptor;
import com.kero.security.core.property.exceptions.PropertyPrepareException;
import com.kero.security.core.property.exceptions.RoleCollisionException;
import com.kero.security.core.role.Role;
import com.kero.security.core.scheme.AccessScheme;
public class LocalProperty implements Property {
private String name;
private AccessScheme scheme;
private Access defaultAccess = Access.UNKNOWN;
private Set grantRoles = new HashSet<>();
private Set denyRoles = new HashSet<>();
private DenyInterceptor defaultInterceptor;
private List interceptors = new LinkedList<>();
private Map rolesPropagations = new HashMap<>();
public LocalProperty(AccessScheme scheme, String name) {
this.scheme = scheme;
this.name = name;
}
public Access accessible(Collection rolesArg) {
if(rolesArg.isEmpty()) return Access.UNKNOWN;
Set roles = new HashSet<>(rolesArg);
roles.removeAll(this.denyRoles);
if(roles.isEmpty()) return Access.DENY;
if(!Collections.disjoint(roles, this.grantRoles)) {
return Access.GRANT;
}
if(!this.scheme.isInherit()) return Access.UNKNOWN;
return getParent().accessible(roles);
}
public Action prepare(Collection roles) {
Access accessible = accessible(roles);
if(accessible == Access.UNKNOWN) {
accessible = determineDefaultAccess();
}
if(accessible == Access.GRANT) {
return new ActionGrant(this.scheme, propagateRoles(roles));
}
else if(accessible == Access.DENY) {
DenyInterceptor interceptor = determineInterceptor(roles);
if(interceptor != null) {
return interceptor.prepare(roles);
}
else {
return new ActionDeny(this.scheme);
}
}
else {
throw new PropertyPrepareException("Can't prepare property: \""+this.name+"\". Your Kero-Security configuration is bad, if you see this exception.");
}
}
protected Access determineDefaultAccess() {
Access defaultAccess = getDefaultAccess();
if(defaultAccess == Access.UNKNOWN) {
defaultAccess = this.scheme.determineDefaultAccess();
}
return defaultAccess;
}
public DenyInterceptor determineInterceptor(Collection roles) {
int maxOverlap = 0;
int minTrash = Integer.MAX_VALUE;
DenyInterceptor result = null;
List interceptors = getInterceptors();
for(DenyInterceptor interceptor : interceptors) {
Set interceptorRoles = interceptor.getRoles();
int overlap = 0;
int trash = 0;
for(Role interceptorRole : interceptorRoles) {
if(roles.contains(interceptorRole)) {
overlap++;
}
else {
trash++;
}
}
if(overlap > maxOverlap) {
maxOverlap = overlap;
minTrash = trash;
result = interceptor;
}
else if(overlap == maxOverlap && trash < minTrash) {
maxOverlap = overlap;
minTrash = trash;
result = interceptor;
}
}
if(maxOverlap == 0 || result == null) return getDefaultInterceptor();
return result;
}
public Role propagateRole(Role role) {
Set data = new HashSet<>();
data.add(role);
return propagateRoles(data).iterator().next();
}
public Set propagateRoles(Collection rolesArg) {
Set roles = new HashSet<>(rolesArg);
Set result = new HashSet<>();
Set propagated = new HashSet<>();
for(Role fromRole : roles) {
if(hasPropagationFor(fromRole)) {
result.add(this.rolesPropagations.get(fromRole));
propagated.add(fromRole);
}
}
roles.removeAll(propagated);
if(this.scheme.isInherit()) {
result.addAll(this.getParent().propagateRoles(roles));
}
return result;
}
public boolean hasPropagationFor(Role target) {
return rolesPropagations.containsKey(target);
}
public void addRolePropagation(Role from, Role to) {
this.rolesPropagations.put(from, to);
}
public void addInterceptor(DenyInterceptor interceptor) {
this.interceptors.add(interceptor);
}
public List getInterceptors() {
List interceptors = new ArrayList<>(this.interceptors);
if(this.scheme.isInherit()) {
interceptors.addAll(this.getParent().getInterceptors());
}
return interceptors;
}
public void grantRoles(Collection roles) {
for(Role role : roles) {
grantRole(role);
}
}
public void grantRole(Role role) {
if(this.denyRoles.contains(role)) throw new RoleCollisionException("Detected roles collision: "+role);
this.grantRoles.add(role);
}
public void denyRoles(Collection roles) {
for(Role role : roles) {
denyRole(role);
}
}
public void denyRole(Role role) {
if(this.grantRoles.contains(role)) throw new RoleCollisionException("Detected roles collision: "+role);
this.denyRoles.add(role);
}
public Set getGrantRoles() {
return this.grantRoles;
}
public Set getDenyRoles() {
return this.denyRoles;
}
public void setDefaultAccess(Access access) {
this.defaultAccess = access;
}
public boolean hasDefaultAccess() {
return this.defaultAccess != Access.UNKNOWN;
}
public Access getDefaultAccess() {
if(hasDefaultAccess()) return this.defaultAccess;
if(!this.scheme.isInherit()) return Access.UNKNOWN;
return getParent().getDefaultAccess();
}
public String getName() {
return this.name;
}
public void setDefaultInterceptor(DenyInterceptor interceptor) {
this.defaultInterceptor = interceptor;
}
public boolean hasDefaultInterceptor() {
return this.defaultInterceptor != null;
}
public DenyInterceptor getDefaultInterceptor() {
if(hasDefaultInterceptor()) return this.defaultInterceptor;
if(!this.scheme.isInherit()) return null;
return this.getParent().getDefaultInterceptor();
}
public Property getParent() {
return this.scheme.getParentProperty(this.name);
}
}