Saturday, March 27, 2010

Security Resource, Permission and Administration

First, let's clarify what is a resource in terms of security. According to XACML, a resource is data, service or system component for which access is requested. WebLogic Security defines resource as any software component, such as a server, a service, an application, or an application artifact that can be secured using security roles and policies. There are subtle differences between XACML's definition and WebLogic's definition, e.g. a method to secure on an EJB is considered a resource in WebLogic while for XACML that method is an action on an EJB resource.

While Spring Security supports several resources to protect such as a URL, a method invocation and an (domain) object, it lacks a uniform abstraction for representing the same in its architecture. It also lacks an abstraction to represent a permission on a resource. Let me give examples for what I mean.

Defining and enforcing permissions for URL resources requires configuring FilterSecurityInterceptor with permissions defined in the form of intercept-url elements including URL patterns and allowed roles.

1:    <bean id="filterInvocationInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">  
2:      ...  
3:      <!--property name="securityMetadataSource" ref="cspaceMetadataSource"/-->  
4:      <property name="securityMetadataSource">  
5:        <sec:filter-security-metadata-source>  
6:          <sec:intercept-url pattern="/**" access="ROLE_USERS"/>  
7:        </sec:filter-security-metadata-source>  
8:      </property>  
9:    </bean>  

Defining and enforcing permissions for method resources using MethodSecurityInterceptor requires listing permissions using a set of properties including qualified methods as names and allowed roles as value.

1:  <bean id="bankManagerSecurity"  
2:    class="org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor">  
3:  ...  
4:   <property name="securityMetadataSource">  
5:    <value>  
6:     com.mycompany.BankManager.delete*=ROLE_SUPERVISOR  
7:     com.mycompany.BankManager.getBalance=ROLE_TELLER,ROLE_SUPERVISOR  
8:    </value>  
9:   </property>  
10:  </bean>   

Spring ACL for Domain Object Security has an abstraction for Permission but actually the permission is required to be configured programatically using abstractions such as Permission, ObjectIdentity and Sid (security identity). It offers no management interfaces to configure or administer permissions declaratively.

1:  <security:global-method-security pre-post-annotations="enabled">  
2:    <security:expression-handler ref="expressionHandler"/>  
3:   </security:global-method-security>  
4:   <bean id="expressionHandler"  
5:     class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">  
6:      <property name="permissionEvaluator" ref="myPermissionEvaluator"/>  
7:   </bean>  

The first two examples above show that Spring Security neatly allows to define permissions exactly where those permissions would be enforced. This makes it really easy to configure permissions. However, it also makes it really difficult to administer permissions independently as there are no distinct and unified administrative interfaces to do so. Now, if you had abstractions for Resource and Permission, you would be able to administer these separately and enforce them from FilterInvocation, MethodInvocation and PermissionEvaluator too.

A typical simple non-hierarchical resource would have the following information:
  • Type of the resource (URL, method, object, class, db table, etc.)
  • Id to uniquely identify the resource
  • Pattern (e.g. com.mycompany.BankManager.delete* or /**)
A typical permission might have the following
  • Resource on which access is defined
  • A set of principals (roles and users) it is applicable to
  • An effect (permit or deny)
How this can help in administering and enforcing permissions? I will write about that in my next post...

Saturday, March 20, 2010

Spring ACL for RESTful web services

I had mentioned before that the easiest way to provision permissions for URIs using Spring Security is by adding intercept-urls and corresponding expressions in security metadata for a filter in a web application. It is also possible to define security metadata in file/database for these URIs so that permission could be changed dynamically after the webapp is deployed. Here is a sample using a property file named url.properties (credits: custom security metadata)for more details.

1:  <bean id="filterInvocationInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">  
2:      <property name="authenticationManager" ref="authenticationManager"/>  
3:      <property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/>  
4:      <property name="securityMetadataSource" ref="cspaceMetadataSource"/>  
5:    </bean>  
6:    <bean id="cspaceMetadataSource" class="org.collectionspace.services.authorization.spring.CSpaceSecurityMetadataSource">  
7:      <property name="urlProperties">  
8:        <util:properties location="classpath:urls.properties" />  
9:      </property>  
10:    </bean>  

The url.properties includes a list of permissions. It might look something like following.

1:  /accounts/**=ROLE_ADMINISTRATOR  

And the CSpaceSecurityMetadataResource looks as follows:


1:  import java.util.Collection;  
2:  import java.util.Properties;  
3:  import org.springframework.security.access.ConfigAttribute;  
4:  import org.springframework.security.access.SecurityConfig;  
5:  import org.springframework.security.web.FilterInvocation;  
6:  import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;  
7:  public class CSpaceSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {  
8:    private Properties urlProperties;  
9:    public Collection<ConfigAttribute> getAllConfigAttributes() {  
10:      return null;  
11:    }  
12:    public Collection<ConfigAttribute> getAttributes(Object filter)  
13:        throws IllegalArgumentException {  
14:      FilterInvocation filterInvocation = (FilterInvocation) filter;  
15:      String url = filterInvocation.getRequestUrl();  
16:      String method = filterInvocation.getHttpRequest().getMethod();  
17:      //get the roles for requested page from the property file  
18:      String urlPropsValue = urlProperties.getProperty(url);  
19:      StringBuilder rolesStringBuilder = new StringBuilder();  
20:      //fetch roles from property file that could invoke the given http method on given url
21:      return SecurityConfig.createListFromCommaDelimitedString(rolesStringBuilder.toString());  
22:    }  
23:    public boolean supports(Class<?> arg0) {  
24:      return true;  
25:    }  
26:    public void setUrlProperties(Properties urlProperties) {  
27:      this.urlProperties = urlProperties;  
28:    }  
29:    public Properties getUrlProperties() {  
30:      return urlProperties;  
31:    }  
32:  }  

Limitations :


Http Method

Unlike a web application, for the RESTful web service, it is important to know the http method (e.g. POST is used for creating representation for a resource) in addition to the URI (e.g. /accounts/) from the invocation context. Using this two part information, you should retrieve a list of roles (comma separated) that are allowed to invoke the given HTTP method on the given URI. Note that for this reason the example url.properties file shown earlier is not enough as it does not include any HTTP methods in permission definition.

Sub-resources and permission inheritance

According to the HATEOS principle, it is very common to expect that a representation received from a resource could include additional URIs to retrieve representations of related resources. For example, to retrieve the user profile associated with an account, one could invoke a GET request on URI /accounts/{accountid}/users/{userid}. Security administrator may want to inherit permissions for the users resource from the accounts resource. Using a flat permission model such as shown above in the url.properties, it is not possible to define inheritance.

Therefore, I am exploring if a URI could be considered a domain object and Spring ACL could be used to define permissions. Will keep you posted ...

Tuesday, March 9, 2010

Shutting down log4j

When I integrated Spring Security into CollectionSpace, the dynamic deployment of cspace-services.war stopped working. We use Cargo's JBoss plugin for maven to dynamically deploy war on a running server instance. The redeploy (undeploy + deploy) would fail with an error "Shutting down log4j"! Only restart of the server will allow to deploy the war. We use slf4 with log4j binding.

While the process of coming up to the following list of steps was long, the list itself is short.

1. In our environment, JBoss uses log4j, cspace-services.war uses log4j and Spring used it as well. However, Spring was shutting down log4j logger when our war was undeploying due to the following listener. Remove Log4jConfigListener from web.xml.

1:  <listener>  
2:   <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>  
3:  </listener>  

2. Do not package log4j jar with your web app. JBoss ships with log4j.jar that is available in the system classpath.

3. Have your own log RepositorySelector to separate your log from other applications (here JBoss). This also helps in stopping JBoss's RepositorySelector from jamming application's log entry onto its own. Here is how repository selector looked.

1:  package org.collectionspace.services.common.log;  
2:  import java.util.Hashtable;  
3:  import javax.naming.Context;  
4:  import javax.naming.InitialContext;  
5:  import javax.naming.NamingException;  
6:  import org.apache.log4j.Hierarchy;  
7:  import org.apache.log4j.Level;  
8:  import org.apache.log4j.spi.LoggerRepository;  
9:  import org.apache.log4j.spi.RepositorySelector;  
10:  import org.apache.log4j.spi.RootLogger;  
11:  /**  
12:   * CollectionSpaceLog4jRepositorySelector is a CollectionSpace  
13:   * specific log4j repository selector. See Ceki's solution  
14:   * for more details  
15:   * Courtsey Ceki Gulcu http://articles.qos.ch/sc.html  
16:   */  
17:  /** JNDI based Repository selector */  
18:  public class CollectionSpaceLog4jRepositorySelector implements RepositorySelector {  
19:    // key: name of logging context,  
20:    // value: Hierarchy instance  
21:    private Hashtable ht;  
22:    private Hierarchy defaultHierarchy;  
23:    public CollectionSpaceLog4jRepositorySelector() {  
24:      ht = new Hashtable();  
25:      defaultHierarchy = new Hierarchy(new RootLogger(Level.DEBUG));  
26:    }  
27:    // the returned value is guaranteed to be non-null  
28:    public LoggerRepository getLoggerRepository() {  
29:      String loggingContextName = null;  
30:      try {  
31:        Context ctx = new InitialContext();  
32:        loggingContextName = (String) ctx.lookup("java:comp/env/cspace-logging-context");  
33:      } catch (NamingException ne) {  
34:        // we can't log here  
35:      }  
36:      if (loggingContextName == null) {  
37:        return defaultHierarchy;  
38:      } else {  
39:        Hierarchy h = (Hierarchy) ht.get(loggingContextName);  
40:        if (h == null) {  
41:          h = new Hierarchy(new RootLogger(Level.DEBUG));  
42:          ht.put(loggingContextName, h);  
43:        }  
44:        return h;  
45:      }  
46:    }  
47:    /**  
48:     * The Container should remove the entry when the web-application  
49:     * is removed or restarted.  
50:     * */  
51:    public void remove(ClassLoader cl) {  
52:      ht.remove(cl);  
53:    }  
54:  }  

The JNDI environment entry was registered as follows in web.xml:

1:    <env-entry>  
2:      <description>Sets the logging context for the Tiger web-app</description>  
3:      <env-entry-name>cspace-logging-context</env-entry-name>  
4:      <env-entry-value>CSpaceLoggingContext</env-entry-value>  
5:      <env-entry-type>java.lang.String</env-entry-type>  
6:    </env-entry>  

Here is how we initialized it in our ServletContextLIstener

1:  public class CollectionSpaceServiceContextListener implements ServletContextListener {  
2:    @Override  
3:    public void contextInitialized(ServletContextEvent event) {  
4:  ...  
5:        LogManager.setRepositorySelector(new CollectionSpaceLog4jRepositorySelector(),  
6:            null);  
7:  ...  
8:    }  



4. Remove log4j.properties or log4j.xml from your war. We could still share log4j configuration with JBoss by adding categories into the jboss-log4j.xml. This is nice because you could dynamically change the log level using JMX mbeans provided by JBoss. If you package your own configuration in the war, you will need to redeploy it if you want to change log level or will have to build your own JMX Mbeans to change the level dynamically.

As I said before, the debugging took longer than writing this post. Hopefully, it is useful...

Sunday, March 7, 2010

What I would like to see in Spring Authorization...

I am trying to integrate Spring Security in CollectionSpace web services. I chose Spring Security because it is used quite widely in enterprise applications and has proven its utility and reliability. Also, it is a well thought out architecture that is easy to understand for the application developers. I like most of the features of Spring Security Authorization. This includes role-based access control, possibility for several types of voting in access decision manager and pre/post invocation hooks, etc.

In Spring Security, a resource to protect such as URI is treated differently from other resources (methods, domain objects, ...) both for permission administration as well as enforcement.  Easiest way to provision permissions for URI is by adding intercept-urls and corresponding expressions in security metadata for a filter in web application. This is metadata packaged with the war file.

1:  <bean id="filterSecurityInterceptor"  
2:      class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">  
3:   <property name="authenticationManager" ref="authenticationManager"/>  
4:   <property name="accessDecisionManager" ref="accessDecisionManager"/>  
5:   <property name="securityMetadataSource">  
6:    <security:filter-security-metadata-source>  
7:     <security:intercept-url pattern="/secure/super/**" access="ROLE_WE_DONT_HAVE"/>  
8:     <security:intercept-url pattern="/secure/**" access="ROLE_SUPERVISOR,ROLE_TELLER"/>  
9:    </security:filter-security-metadata-source>  
10:   </property>  
11:  </bean>  

It perhaps is possible to define security metadata in database for these URIs so that permission could be changed dynamically after the webapp is deployed. I am trying to do exactly the same for CollectionSpace. I will explain the why later. However, I am disappointed to see statements such as "The first thing you should ask yourself is if you really need to do this. If an application requires securing, then it also requires that the security be tested thoroughly based on a defined policy." to defend their approach (of embedding permission expressions in the application itself). This seems like a short-sighted argument to me. It is a bad practice to embed permissions in the application package as it requires redeploying the application if permissions change dynamically as they often do, especially if the web application is a multi-tenant software service or exposes web services.

Secondly, while it is good to see permission enforcement for resources such as URI, method and AspectJ JoinPoint "described" as metadata that is not "hard coded" into the application, it is disappointing to see permissions ("securityMetadataSource") associated with these at the same location. I would have loved to see permission provisioning aspect separated out from the permission enforcement points (interceptors). The permissions enforcement points should only know about which resources to protect and not what permissions are associated with those resources. There seems to be a nicer way to delegate the permission evaluation using the PermissionsEvaluator interface but that seems only applicable to method security.

Why I need such flexibility?
  1. CollectionSpace is a multi-tenant application. For the same service URI, there could be different permissions per tenant. 
  2. There are lots of URIs that need to be protected for a web service and for a collection of web services in CollectionSpace. CollectionSpace web services are RESTful web services.
  3. Not all services might be packaged into a single war file. It would be good to centralize permission provisioning and administration out of the security interceptor meta data packaged with the war file.
Keep tuned for the next post describing how we do permission administration using Spring Security in CollectionSpace...