Named Rules
The concept here is that a policy is just a combination of named rules. For example:
Rule A: user has title="Manager"
Rule B: user has department="12345"
GRANT Role Manager_12345 if A and B
There are a couple of different approaches for how to model this type of scenario in OES.
Model Named Rules as Dynamic Attributes
In this approach, the logic of the specific rules is "hard coded" into the attribute retriever. One nice thing about the AttributeRetrieverV2 interface is that you can get access to attributes via other AttributeRetrievers. Below is psuedocode for Rule A and Rule B
package namedrules;
import com.bea.security.providers.authorization.asi.*;
import com.bea.security.providers.authorization.asi.ARME.evaluator.RequestHandle;
import com.bea.security.providers.authorization.asi.ARME.exceptions.ArmeRuntimeException;
import com.bea.security.providers.authorization.asi.ARME.exceptions.CredvarException;
import com.bea.security.providers.authorization.asi.ARME.exceptions.NotReadyException;
import com.bea.security.quark.exceptions.ParserException;
import com.wles.BadParameterException;
import com.wles.util.AttributeElement;
import java.util.List;
import java.util.Map;
import javax.security.auth.Subject;
import weblogic.security.service.ContextHandler;
import weblogic.security.spi.Resource;
public class NamedRuleAttributeRetriever implements AttributeRetrieverV2 {
public String[] getHandledAttributeNames() {
return new String [] {"RuleA","RuleB"};
}
public Object getAttributeValue(String name, RequestHandle requestHandle,
Subject subject, Map map,
Resource resource,
ContextHandler contextHandler) {
if (name.equals("RuleA")) {
try {
AttributeElement title = requestHandle.getAttribute("title",true);
List titleValues = (List)title.getValueAs(List.class);
return titleValues.contains("Manager");
} catch (Exception e) {
e.printStackTrace();
return Boolean.FALSE;
}
} else if (name.equals("RuleB")) {
try {
AttributeElement dept = requestHandle.getAttribute("department",true);
List deptValues = (List)dept.getValueAs(List.class);
return deptValues.contains("12345");
} catch (Exception e) {
e.printStackTrace();
return Boolean.FALSE;
}
}
}
}
The biggest downside to this approach is that the rules themselves are hard coded inside of the logic of the attribute retriever. If this logic changes, you need to recode the retriever and re-deploy. It would better to have something a little more generic.
Model Named Rules as Resources
The idea is that the actual logic of the rule membership is still modeled in OES policy, instead of in the code. To do this create a separate node in the same SM called "namedrules". Model the rules like this:
GRANT (//priv/RuleA, //app/policy/namedrules, //sgrp/users/allusers) if sys_defined(title) and title="manager"
GRANT (//priv/RuleB, //app/policy/namedrules, //sgrp/users/allusers) if
sys_defined(dept) and dept="12345"
Now, the trick part. How do I get these policies evaluated? You'll need to make a call back into the AuthorizationService. This is pretty powerful, yet some what risky stuff. The issue is that you don't want to wind up with circular dependencies that will cause StackOverflow. The best way to avoid this is to not have named rules referencing named rules. If you need to be more fancy, then the example below could be extended to include a ThreadLocal variable with a counter to make sure that you're not putting more than say 5 requests on the stack. Below is the basics for this type of approach.
package namedrules;
import com.bea.security.AccessResult;
import com.bea.security.AppContext;
import com.bea.security.AppContextElement;
import com.bea.security.AuthenticIdentity;
import com.bea.security.AuthenticationService;
import com.bea.security.AuthorizationService;
import com.bea.security.ParameterException;
import com.bea.security.PolicyDomain;
import com.bea.security.RuntimeAction;
import com.bea.security.RuntimeResource;
import com.bea.security.SecurityRuntime;
import com.bea.security.ServiceNotAvailableException;
import com.bea.security.ServiceType;
import com.bea.security.SimpleContextElement;
import com.bea.security.providers.authorization.asi.ARME.evaluator.RequestHandle;
import com.bea.security.providers.authorization.asi.AttributeRetrieverV2;
import java.util.Map;
import javax.security.auth.Subject;
import weblogic.security.service.ContextElement;
import weblogic.security.service.ContextHandler;
import weblogic.security.spi.Resource;
public class PolicyBasedAttributeRetriever implements AttributeRetrieverV2{
private AuthorizationService atz;
private AuthenticationService auth;
private PolicyDomain pd;
public PolicyBasedAttributeRetriever() {
super();
}
public String[] getHandledAttributeNames() {
return new String[]{};
}
public Object getAttributeValue(String ruleName, RequestHandle requestHandle,
Subject subject, Map map,
Resource resource,
final ContextHandler contextHandler) {
if (atz==null) {
try {
pd = SecurityRuntime.getInstance().getPolicyDomain(System.getProperty("wles.realm"));
this.auth = (AuthenticationService)pd.getService(ServiceType.AUTHENTICATION);
this.atz = (AuthorizationService)pd.getService(ServiceType.AUTHORIZATION);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
try {
AuthenticIdentity ai = this.auth.assertIdentity("WLS.Subject",subject);
RuntimeResource runtimeResource = new RuntimeResource("namedrules","exampleResource");
RuntimeAction runtimeAction = new RuntimeAction(ruleName, "exampleAction");
//This is just a simple wrapper around the ContextHandler
AppContext contextHandlerWrapper = new AppContext() {
public int size() {
return contextHandler.size();
}
public String[] getNames() {
return contextHandler.getNames();
}
public AppContextElement getElement(String name) {
return new SimpleContextElement(name,contextHandler.getValue(name));
}
public AppContextElement[] getElements(String[] strings) {
ContextElement [] ctxElements = contextHandler.getValues(strings);
AppContextElement [] appCtxElements = new AppContextElement[ctxElements.length];
for (int i=0; i<ctxElements.length; i++) {
appCtxElements[i] = new SimpleContextElement(ctxElements[i].getName(),ctxElements[i].getValue());
}
return appCtxElements;
}
};
AccessResult result =
this.atz.isAccessAllowed(ai,runtimeResource,runtimeAction,contextHandlerWrapper);
return Boolean.valueOf(result.isAllowed());
} catch (Exception e) {
return Boolean.FALSE;
}
}
}
A few key things in the example. You'll need to use a lazy initialization of the PolicyDomain. If you try to load it in the constructor of the AttributeRetriever, bad things happen. Also, using the built in WLS.Subject token type for the IdentityAsserter is an easy way to convert from a JAAS Subject to an AuthenticIdentity.
Rule Debug Information
Another scenario is that the caller needs to understand very detailed information about the rules evaluation. Some examples
- Why did the user did not get access?
- What role cause the user to get access?
OES has a number of ways of addressing these types of queries
Add report_as to your policies
The simplest thing to do is to add report_as constraint to a given policy. Note: This only works for authorization policies. An example:
DENY (//priv/read, //app/policy/someresource, //role/authors) if NOT owner = sys_user and report_as("error","You are not the owner of this document")
This works for a few policies, but may not be practical if you have a very large number of policies.
Use DebugInfo
If you are using the Java SM, you can use the DebugInfo to get very detailed information about which roles and rules applied. The instructions for using this Debug API are here. One observation that might make things easier. Through the entitlementsadministration ui you can add notes to policies. Those notes are available through the API. You can even get down to specific attributes and their values.
Custom Evaluation Function
This approach works for SMs other than the Java SM or if you want to report back additional information about policy evaluation and attributes. Take the example where a user is granted access based upon attributes.
GRANT (//priv/read, //app/policy/r1, //sgrp/users/allusers/) if assertAttribute("state","=","CA",true,"You don't live in California") AND assertAttribute("txn_amount","<",500,true,"Transaction is over 500")
The idea with this example is that I want to be able to communicate why the user is not granted access. Below is the psuedocode for the assertAttribute function.
package namedrules;
import com.bea.security.providers.authorization.asi.ARME.evaluator.RequestHandle;
import com.wles.util.AttributeElement;
import java.util.List;
public class AttributeEvalFunction {
public boolean assertAttribute (RequestHandle requestHandle, java.lang.Object[] args, javax.security.auth.Subject subject, java.util.Map roles, weblogic.security.spi.Resource resource, weblogic.security.service.ContextHandler contextHandler) {
try {
String attributeName = (String)args[0];
String operator = (String)args[1];
Boolean assertionResult = (Boolean)args[3];
AttributeElement element = requestHandle.getAttribute(attributeName,true);
boolean result = false;
if (element.getASIType().equals("string")) {
String value = (String)args[2];
if (operator.equals("=")) {
List values = (List)element.getValueAs(List.class);
result = values.contains(value);
}
}
if (assertionResult.booleanValue() == result) {
System.out.println("Assertion Passed: "+attributeName+" "+operator+" "+args[2]+" "+assertionResult.booleanValue());
return true;
} else {
//Return the message
System.out.println("Assertion Failed: "+attributeName+" "+operator+" "+args[2]+" "+assertionResult.booleanValue());
requestHandle.appendReturnData(attributeName,args[4]);
return false;
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
Conclusion
This post outlines a number of ways to use the OES engine to emulate some common capabilities of generic rules engines. Its obviously some bit of work to select the right approach to fit specific requirements. Hopefully what I've show is helpful in bootstrapping some thinking in this area. I look forward to your feedback and some of your own patterns in this area.
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.