Example Code


We've provided a broad overview of Acegi Security and some of its key capabilities. Let's now look at a sample application that demonstrates the ACL services in detail.

Introducing the Sample

For this sample we are going to assume the role of a developer delivering a domain name system (DNS) administration tool for a major Internet hosting provider. The central function of the new application is to provide the hosting provider's customers with a self-service facility to manage the domain names they have registered. The main customer base comprises Internet entrepreneurs who own multiple domain names, so it is important the new DNS administration tool be able to accommodate each customer having many domain names. It is also a requirement that customers be able to delegate DNS administration responsibility to other people, such as their web designers or branch office IT managers.

It is assumed you have a basic understanding of DNS, in that its main function is to resolve hostnames such as www.springhost.zz into TCP/IP addresses. Each domain name is hierarchical, in that each subdomain falls under a parent domain until eventually reaching a top-level domain (such as an .org or.com or .au). For each domain name, an authoritative DNS server is specified. The role of the authoritative DNS server is to respond to requests for records within the domain.

Returning to our sample, there are a variety of DNS server implementations that could be used to implement the DNS administration tool. In order to promote maximum flexibility and reduce development time, your team has decided to store the DNS data in an RDBMS. With a little searching you discover an open source project called myDns (mydns.bboy.net), which interfaces with Postgres and mySql.

Security Unaware Implementation

After reviewing the myDns schema, for this book we developed a simple core for the new DNS administration tool. The three domain objects and service layer are shown in Figure 10-8. The StartOfAuthority and ResourceRecord objects are both modeled after myDns's soa and rr tables respectively. The Domain object is an addition to allow the hierarchy of the domain to be reflected, as the myDns tables, being focused on a DNS server's requirements, do not provide this support. There is a one-to-one relationship between a given Domain and a StartOfAuthority instance. There is a many- to-one relationship between ResourceRecord and StartOfAuthority. DomainDao provides CRUD operations for the Domain (including its contained StartOfAuthority) and ResourceRecord objects. BasicAclExtendedDao is an Acegi Security class that provides a simple interface to perform CRUD operations for ACL entries.

image from book
Figure 10-8

Also included in the Java package is a DataSourcePopulator class that configures the schema and inserts sample data. The applicationContext-business.xml included with the sample configures the persistence layer and services layer, and adds transaction support.

An in-memory Hypersonic SQL database is also configured in the application context, as it facilitates rapid prototyping and testing. Of course, a production implementation would need to use a database supported by myDns.

The classes, interfaces, and application context just described could be used to provide a basic DNS administration tool in conjunction with myDns.

Security Approach

The interfaces and classes in Figure 10-8 are unaware of security, except as follows:

  • When creating Domains and ResourceRecords, the DomainManager implementation needs to create an ACL entry via the BasicAclExtendedDao.

  • When deleting Domains and ResourceRecords, the DomainManager implementation needs to delete all ACL entries via the BasicAclExtendedDao.

  • To allow an administrator to gain full access to a domain (perhaps the owner of that domain revoked the administrator's existing access), DomainManager provides an obtainAdministrativePermission method that operates via the BasicAclExtendedDao.

Though the application does require a small amount of security awareness, this is only to link between the problem domain itself and the Acegi Security ACL services. The remainder of the security requirements of the application are declaratively configured in the Spring bean container. Configuration attributes will be used to tag how we require ContactManager methods to be secured. At an implementation level, we'll use MethodSecurityInterceptor as the application does not use AspectJ or provide web access that requires URI filtering.

Most of the application will be secured using ACL security. The only method on the DomainManager interface that is not secured via ACL security is the obtainAdministrativePermission method. For this special method we'll use role-based security, meaning the principal will need to hold a particular GrantedAuthority.

Over the following pages we'll be discussing the configuration of a security environment appropriate for integration testing using JUnit. The test cases have been developed to test and demonstrate security integration is functioning correctly for the sample application. Each of the unit tests extends from AbstractDomainManagerTest, which extends AbstractTransactionSpringContextTests because we leverage the latter's automatic database rollback capability.

Authentication

AbstractDomainManagerTest is responsible for providing several methods to assist in handling the ContextHolder during our JUnit tests. They allow our tests to simply call makeActiveUser(String username) for the ContextHolder to be configured with an appropriate Authentication request object.

protected void makeActiveUser(String username) { String password = ""; if ("marissa".equals(username)) {     password = "koala";   } else if ("dianne".equals(username)) {     password = "emu";   } else if ("scott".equals(username)) {     password = "wombat";   } else if ("peter".equals(username)) {     password = "opal";   } Authentication authRequest = new UsernamePasswordAuthenticationToken(                                   username, password);   SecureContextImpl secureContext = new SecureContextImpl();   secureContext.setAuthentication(authRequest);   ContextHolder.setContext(secureContext); }     protected void onTearDownInTransaction() {   ContextHolder.setContext(null); }

Though Acegi Security provides a TestingAuthenticationToken and TestingAuthenticationProvider that allow you to specify any GrantedAuthoritys, principal, and credentials you wish (and they will be automatically accepted), in the DNS administration tool testswe've decided to use the popular DaoAuthenticationProvider, which accesses user information using JdbcDaoImpl. Key authentication-related XML is listed here:

<bean    >   <property name="providers">     <list>       <ref local="daoAuthenticationProvider"/>     </list>   </property> </bean> <bean  >   <property name="dataSource"><ref bean="dataSource"/></property> </bean> <bean    >   <property name="authenticationDao"><ref local="jdbcDaoImpl"/></property> </bean>

Authorization

None of the DomainManager methods allow anonymous access, meaning every one of them needs to have one or more configuration attributes defined against the MethodSecurityInterceptor. The configuration attributes we've used for the DNS administration tool are listed here:

DomainManager.createDomain=ACL_DOMAIN_CREATE DomainManager.createResourceRecord=ACL_RESOURCE_RECORD_CREATE DomainManager.deleteDomain=ACL_DOMAIN_DELETE DomainManager.deleteResourceRecord=ACL_RESOURCE_RECORD_DELETE DomainManager.findAllDomainsLike=ROLE_USER,AFTER_ACL_COLLECTION_READ DomainManager.findAllResourceRecordsInDomain=ROLE_USER,AFTER_ACL_COLLECTION_READ DomainManager.obtainAdministrativePermission=ROLE_SUPERVISOR DomainManager.updateDomain=ACL_DOMAIN_WRITE DomainManager.updateResourceRecord=ACL_RESOURCE_RECORD_WRITE

We suggest a convention when naming configuration attributes to help you understand when they will be used. All configuration attributes that will be processed by an AfterInvocationManager should have a name starting with AFTER_. No configuration attributes that will be processed by an AccessDecisionManager should start with AFTER_.

Based on this convention, every method has a configuration attribute indicating an AccessDecisionManager will process it. This is because we want to ensure every principal either holds the ROLE_USER GrantedAuthority or has a specific ACL for the domain object passed as a method argument. Let's consider several examples.

The deleteDomain method uses the ACL_DOMAIN_DELETE configuration attribute. The Spring bean container contains the following declaration:

<bean  >   <property name="processConfigAttribute"><value>ACL_DOMAIN_DELETE</value></property>   <property name="processDomainObjectClass"><value>com.acegitech.dns.domain.Domain</value></pro perty>   <property name="aclManager"><ref local="aclManager"/></property>   <property name="requirePermission">     <list>       <ref local="net.sf.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION"/>       <ref local="net.sf.acegisecurity.acl.basic.SimpleAclEntry.DELETE"/>     </list>   </property> </bean>

The SimpleAclEntry.ADMINISTRATION and DELETE references actually relate to a FieldRetrieveingFactoryBean that enables the integer values of these static variables to be obtained from the specified class, rather than using integers directly inside the XML.

The aclDomainDeleteVoter will vote only when a secure object invocation presents the ACL_DOMAIN_DELETE attribute. Based on our earlier list configuration attributes, only DomainManager.deleteDomain(Domain) uses this configuration attribute. If voting, the voter will look at the method arguments for the first instance of the processDomainObjectClass, which in this case is the Domain object. The Domain will then be presented to the AclManager defined by the property of the same name, and the BasicAclEntrys that apply to the current principal will be obtained. Finally, aclDomainDeleteVoter will determine if the principal has any of the ACL permissions listed in the requirePermission property. If the principal does not, it will vote to deny access, or vote to grant access otherwise.

An important feature of the BasicAclProvider is its support for hierarchical domain object instances. Consider the following unit test:

public void testDeleteAsPeterWhenPermittedViaDeepInheritance() {   makeActiveUser("peter");   Domain domain = getDomain("compsci.science.zoo-uni.edu.zz.");   domainManager.deleteDomain(domain);       // Ensure it was deleted (transaction will rollback post-test)   assertNull(getDomain("compsci.science.zoo-uni.edu.zz.")); } 

In the preceding example, user peter has been granted delete permission for domain science.zoouni.edu.zz. The fact peter is authorized to delete this subdomain is only because BasicAclProvider delivered the parent object's ACLs.

ACL_DOMAIN_CREATE is an interesting use of the BasicAclEntryVoter class because it demonstrates an additional feature. In this case we do not want to vote on the Domain presented in the method argument, but on whether the principal has create permission for that Domain’s parent Domain. To accommodate this we use the internalMethod property, as shown here:

<bean  >   <property name="processConfigAttribute"><value>ACL_DOMAIN_CREATE</value></property>   <property name="processDomainObjectClass"><value>com.acegitech.dns.domain.Domain</value></pro perty>   <property name="internalMethod"><value>getParent</value></property>   <property name="aclManager"><ref local="aclManager"/></property>   <property name="requirePermission">     <list>       <ref local="net.sf.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION"/>       <ref local="net.sf.acegisecurity.acl.basic.SimpleAclEntry.CREATE"/>     </list>   </property> </bean>

Let's consider role-based access control, as used by the DomainManager.obtainAdministrativePermission method. RoleVoter will process the relevant configuration attribute, ROLE_SUPERVISOR. It will simply compare every GrantedAuthority assigned to the principal with the ROLE_SUPERVISOR String. A single RoleVoter is used for all role voting, irrespective of the attribute name (for example, ROLE_USER, ROLE_FOO):

<bean  />

The preceding techniques are used to secure almost every method of DomainManager. The only exceptions are the two find* methods, which use the configuration attribute AFTER_ACL_COLLECTION_READ. The corresponding declaration for this AfterInvocationProvider is:

<bean  >   <property name="aclManager"><ref local="aclManager"/></property>   <property name="requirePermission">     <list>       <ref local="net.sf.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION"/>       <ref local="net.sf.acegisecurity.acl.basic.SimpleAclEntry.READ"/>     </list>   </property> </bean> 

The afterAclCollectionRead bean will present every element in the Collection to the AclManager, removing that element if the current principal does not hold one of the listed permissions (administration or read). It is used in this case to remove Domains and ResourceRecords the principal is not permitted to access. This is demonstrated by the following JUnit test code:

public void testFindAllDomainsLikeAsDianne() {   makeActiveUser("dianne"); // has ROLE_STAFF   List domains = domainManager.findAllDomainsLike("");   assertNotContainsDomain("scotty.com.zz.", domains);   assertEquals(8, domains.size()); // all domains except scotty.com.zz. }

As indicated by the comments, user dianne has the ROLE_STAFF role and is thus granted administrative access to all domains except scotty.com.zz. The test demonstrates that scotty.com.zz was removed from the Collection.

Each DomainManager method has a comprehensive suite of JUnit that tests operation using different domain object instances and principals. We recommend you have a look at those tests to gain a fuller understanding of how Acegi Security is being used in this sample application. Acegi Security also provides a sample application that can be used to query the domains. The sample application is a client- server tool that demonstrates the ease at which Acegi Security supports web services clients. A simple command that displays all resource records in the DNS system is shown here:

maven -Dusername=marissa -Dpassword=koala        -Dcommand=findRecordsByDomainName -Ddomain=. run run:     [java] Acegi DNS Console Query Tool     [java] Domain: 10   arts.zoo-uni.edu.zz.     [java]   10 www     10.1.43.65     [java] Domain: 11   compsci.science.zoo-uni.edu.zz.     [java]   12 ssl     10.1.12.88     [java]   11 www     10.1.12.87     [java] Domain: 4    jackpot.com.zz.     [java]   4  www     192.168.0.3     [java] Domain: 2    petes.com.zz.     [java]   2  www     192.168.0.1     [java] Domain: 9    science.zoo-uni.edu.zz.     [java]   9  www     10.1.12.4     [java] Domain: 7    tafe.edu.zz.     [java]   7  www     192.168.0.5     [java] Domain: 6    zoo-uni.edu.zz.     [java]   6  www     10.1.0.1     [java] Domain: 8    zoohigh.edu.zz.     [java]   8  www     192.168.0.6     maven -o -Dusername=scott -Dpassword=wombat -Dcommand=findAll run run:     [java] Acegi DNS Console Query Tool     [java] Domain: 3    scotty.com.zz.     [java] Domain: 7    tafe.edu.zz.

Finally, Acegi Security itself includes a Contacts sample application that uses domain object instance security and provides a full web layer and web services. This sample also demonstrates form-based, CAS, and container adapter–managed authentication.



Professional Java Development with the Spring Framework
Professional Java Development with the Spring Framework
ISBN: 0764574833
EAN: 2147483647
Year: 2003
Pages: 188

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net