목적 : Access Control 표준이 XACML 이란다.
경과 :
몇가지 xacml 관련 구현체들을 찾아보았는데,
상용이 아닌 것으로 가장 활발한 곳은 jboss xacml 구현체인 picketbox 였다.
Picketbox는 Java Security Framework의 하나인데, Oasis XACML v2.0 에 호환되는 엔진을 제공한다.
절차
1. xacml library download
from http://www.jboss.org/picketbox/downloads
jbossxacml-2.0.8.Final.jar
2. add library
WEB-INF/lib 또는 기타 classpath 상에 해당 jar 파일을 배치한다.
3. register ACL Interceptor to spring mvc context
> context/servlet-context.xml
<mvc:interceptors> <mvc:mapping path="/*"/>
<bean class="com.MyACLInterceptor" >
<property name="config" value="config/policyConfig.xml"/><!-- pdp file classpath -->
</bean>
</mvc:interceptor>
...
</mvc:interceptors>
4. write PDP (Policy Decision Point) configuration file
Caching과 관련해서는 아래 Reference 를 참조
> config/policyConfig.xml
5. policy set 정의<ns:jbosspdp xmlns:ns="urn:jboss:xacml:2.0">
<ns:Policies>
<ns:PolicySet>
<ns:Location>config/mypolicyset.xml</ns:Location>
</ns:PolicySet>
</ns:Policies>
<ns:Locators>
<ns:Locator Name="org.jboss.security.xacml.locators.JBossPolicySetLocator"/>
</ns:Locators>
</ns:jbosspdp>
> config/mypolicyset.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<PolicySet xmlns="urn:oasis:names:tc:xacml:2.0:policy:schema:os" PolicySetId="policyset"
PolicyCombiningAlgId="urn:oasis:names:tc:xacml:1.0:policy-combining-algorithm:first-applicable">
<Target />
<Policy xmlns="urn:oasis:names:tc:xacml:2.0:policy:schema:os"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:oasis:names:tc:xacml:2.0:policy:schema:os
access_control-xacml-2.0-policy-schema-os.xsd"
PolicyId="my-policy"
RuleCombiningAlgId="urn:oasis:names:tc:xacml:1.0:rule-combining-algorithm:first-applicable">
<Description>access control policy</Description>
<Target />
<Rule RuleId="myruleid" Effect="Permit"><!— Permit / Deny -->
<Description>my rule</Description>
<Target >
<Subjects/>
<Resources>
<Resource>
<ResourceMatch MatchId="urn:oasis:names:tc:xacml:1.0:function:string-regexp-match">
<AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">^/( accessuri1|accessuri2)</AttributeValue>
<ResourceAttributeDesignator
AttributeId="urn:oasis:names:tc:xacml:1.0:resource:resource-id" DataType="http://www.w3.org/2001/XMLSchema#string"/>
</ResourceMatch>
</Resource>
</Resources>
</Target>
<Condition>
<Apply FunctionId="urn:oasis:names:tc:xacml:1.0:function:string-is-in">
<AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string">ACCESSIBLE_ROLE_NAME</AttributeValue>
<SubjectAttributeDesignator
DataType="http://www.w3.org/2001/XMLSchema#string" AttributeId="urn:oasis:names:tc:xacml:2.0:subject:role" />
</Apply>
</Condition>
</Rule>
. . .
<Rule RuleId="ImplicitDeny" Effect="Deny" />
</Policy>
</PolicySet>
- policy-combining-algorithm은 경우에 따라 적절하게 사용할 것. ( first-applicable은 첫번째 걸리는 Rule을 따른다는 의미 )
- Rule의 Effect는 Permit 아니면 Deny 를 선택할수 있음
- ResourceMatch 로 대상 Resource 를 정의할 수 있음
- Condition 으로 Rule의 detail을 표현할 수 있음
cf) Policy 작성 BP. ( Ref. 1 - 2.7 )
- policy와 file 이름을 동일하게.
- 룰 조합 규칙은 가능한 심플하게. first-applicable 권고
- Permit, Deny 를 섞기말고 가능한 한쪽으로 모는것이 좋음.
6. Interceptor 작성
> MyACLInterceptor.java
public class MyACLInterceptor extends HandlerInterceptorAdapter {
private String config = "";
public String getConfig(){
return this.config;
}
public void setConfig(String config){
this.config = config;
}
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
if (!hasAccessRole(request)) {
throw new Exception("access not allowed.");
}
return true;
}
private boolean hasAccessRole(HttpServletRequest request) {String resourceId = request.getRequestURI(); // URIList<String> userRoles = getUserRoles(request); // User ROLESACLManager acm = ACLManager.getInstance(this.config);return acm.evaluate(userRoles, resourceId);}List<String> getUserRoles(HttpServletRequest request){return "get user role from session or persistence data".....}}
7. ACLManager 작성
public class ACLManager {
private static ACLManager instance;
private PolicyDecisionPoint pdp = null;
public boolean evaluate(List<String>userRoles, String resourceId) throws Exception{
RequestContext request = createXACMLRequest(userRoles, resourceId);
// try { request.marshall(System.out); } catch (IOException e) { }
ResponseContext response = this.pdp.evaluate(request);
return XACMLConstants.DECISION_PERMIT == response.getDecision();
}
public RequestContext createXACMLRequest(List<String>userRoles, String resourceId) throws Exception{
RequestContext requestCtx = RequestResponseContextFactory.createRequestCtx();
SubjectType subject = new SubjectType();
subject.getAttribute().add(RequestAttributeFactory.createStringAttributeType(XACMLConstants.ATTRIBUTEID_SUBJECT_ID, "myissuer", "mysubject"));
for(String role : userRoles) {
AttributeType attSubjectID = RequestAttributeFactory.createStringAttributeType(XACMLConstants.ATTRIBUTEID_ROLE, "myissuer", role);
subject.getAttribute().add(attSubjectID);
}
ResourceType resourceType = new ResourceType();
resourceType.getAttribute().add(RequestAttributeFactory.createStringAttributeType(XACMLConstants.ATTRIBUTEID_RESOURCE_ID, null, resourceId ));
ActionType actionType = new ActionType();
actionType.getAttribute().add(RequestAttributeFactory.createStringAttributeType("action-id", "myissuer", "read"));
RequestType requestType = new RequestType();
requestType.getSubject().add(subject);
requestType.getResource().add(resourceType);
requestType.setAction(actionType);
requestCtx.setRequest(requestType);
return requestCtx;
}
private void setPDP(String pdpPath) throws Exception {
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(pdpPath);
this.pdp = new JBossPDP(is);
}
private ACLManager(String pdpPath) throws Exception{
setPDP(pdpPath);
}
public static synchronized ACLManager getInstance(String pdpPath) throws Exception{
if (instance == null) {
instance = new ACLManager(pdpPath);
}
return instance;
}
}
[References]
1) XACML Function List : http://fedora-commons.org/download/2.2/userdocs/server/security/XACMLPolicyGuide.htm
XACML caching for Performance : https://community.jboss.org/wiki/XACMLCachingForPerformance