Domain Object Security
Overview
PLEASE NOTE: Acegi Security 1.0.3 contains a preview of a new
ACL module. The new ACL module is a significant rewrite of the
existing ACL module. The new module can be found under the
org.springframework.security.acls package, with the
old ACL module under
org.springframework.security.acl. We encourage
users to consider testing with the new ACL module and build
applications with it. The old ACL module should be considered
deprecated and may be removed from a future release.
Complex applications often will find the need to define access
permissions not simply at a web request or method invocation level.
Instead, security decisions need to comprise both who
(Authentication), where
(MethodInvocation) and what
(SomeDomainObject). In other words, authorization
decisions also need to consider the actual domain object instance
subject of a method invocation.
Imagine you're designing an application for a pet clinic. There
will be two main groups of users of your Spring-based application:
staff of the pet clinic, as well as the pet clinic's customers. The
staff will have access to all of the data, whilst your customers will
only be able to see their own customer records. To make it a little
more interesting, your customers can allow other users to see their
customer records, such as their "puppy preschool "mentor or president
of their local "Pony Club". Using Spring Security as the foundation,
you have several approaches that can be used:
Write your business methods to enforce the security. You
could consult a collection within the
Customer domain object instance to determine
which users have access. By using the
SecurityContextHolder.getContext().getAuthentication(),
you'll be able to access the Authentication
object.
Write an AccessDecisionVoter to enforce
the security from the GrantedAuthority[]s
stored in the Authentication object. This
would mean your AuthenticationManager would
need to populate the Authentication with
custom GrantedAuthority[]s representing each
of the Customer domain object instances the
principal has access to.
Write an AccessDecisionVoter to enforce
the security and open the target Customer
domain object directly. This would mean your voter needs access
to a DAO that allows it to retrieve the
Customer object. It would then access the
Customer object's collection of approved
users and make the appropriate decision.
Each one of these approaches is perfectly legitimate. However,
the first couples your authorization checking to your business code.
The main problems with this include the enhanced difficulty of unit
testing and the fact it would be more difficult to reuse the
Customer authorization logic elsewhere. Obtaining
the GrantedAuthority[]s from the
Authentication object is also fine, but will not
scale to large numbers of Customers. If a user
might be able to access 5,000 Customers (unlikely
in this case, but imagine if it were a popular vet for a large Pony
Club!) the amount of memory consumed and time required to construct
the Authentication object would be undesirable. The
final method, opening the Customer directly from
external code, is probably the best of the three. It achieves
separation of concerns, and doesn't misuse memory or CPU cycles, but
it is still inefficient in that both the
AccessDecisionVoter and the eventual business
method itself will perform a call to the DAO responsible for
retrieving the Customer object. Two accesses per
method invocation is clearly undesirable. In addition, with every
approach listed you'll need to write your own access control list
(ACL) persistence and business logic from scratch.
Fortunately, there is another alternative, which we'll talk
about below.
Key Concepts
The org.springframework.security.acls package should be
consulted for its major interfaces. The key interfaces are:
Acl: Every domain object has one and only
one Acl object, which internally holds the
AccessControlEntrys as well as knows the owner
of the Acl. An Acl does not refer directly to
the domain object, but instead to an
ObjectIdentity.
AccessControlEntry: An
Acl holds multiple AccessControlEntrys, which
are often abbreviated as ACEs in the framework. Each ACE refers to
a specific tuple of Permission,
Sid and Acl. An ACE can also
be granting or non-granting and contain audit settings.
Permission: A permission represents an
immutable particular bit mask, and offers convenience functions
for bit masking and outputting information.
Sid: The ACL module needs to refer to
principals and GrantedAuthority[]s. A level of
indirection is provided by the Sid interface.
Common classes include PrincipalSid (to
represent the principal inside an
Authentication object) and
GrantedAuthoritySid.
ObjectIdentity: Each domain object is
represented internally within the ACL module by an
ObjectIdentity.
AclService: Retrieves the
Acl applicable for a given
ObjectIdentity.
MutableAclService: Allows a modified
Acl to be presented for persistence. It is not
essential to use this interface if you do not wish.
The ACL module was based on extensive feedback from the user
community following real-world use of the original ACL module. This
feedback resulted in a rearchitecture of the ACL module to offer
significantly enhanced performance (particularly in the area of
database retrieval), significantly better encapsulation, higher
cohesion, and enhanced customisation points.
The Contacts Sample that ships with Acegi Security 1.0.3 offers
a demonstration of the new ACL module. Converting Contacts from using
the old module to the new module was relatively simple, and users of
the old ACL module will likely find their applications can be modified
with relatively little work.
We will document the new ACL module more fully with a subsequent
release. Please note that the new ACL module should be considered a
preview only (ie do not use in production without proper prior
testing), and there is a small chance there may be changes between
1.0.3 and 1.1.0 when it will become final. Nevertheless,
compatibility-affecting changes are considered quite unlikely,
especially given the module is already based on several years of
feedback from users of the original ACL module.