spring-security/src/docbkx/domain-acls.xml

181 lines
9.7 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.docbook.org/xml/4.4/docbookx.dtd">
<chapter id="domain-acls">
<title>Domain Object Security</title>
<section id="domain-acls-overview">
<title>Overview</title>
<para>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
<literal>org.springframework.security.acls</literal> package, with the
old ACL module under
<literal>org.springframework.security.acl</literal>. 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.</para>
<para>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
(<literal>Authentication</literal>), where
(<literal>MethodInvocation</literal>) and what
(<literal>SomeDomainObject</literal>). In other words, authorization
decisions also need to consider the actual domain object instance
subject of a method invocation.</para>
<para>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:<orderedlist>
<listitem>
<para>Write your business methods to enforce the security. You
could consult a collection within the
<literal>Customer</literal> domain object instance to determine
which users have access. By using the
<literal>SecurityContextHolder.getContext().getAuthentication()</literal>,
you'll be able to access the <literal>Authentication</literal>
object.</para>
</listitem>
<listitem>
<para>Write an <literal>AccessDecisionVoter</literal> to enforce
the security from the <literal>GrantedAuthority[]</literal>s
stored in the <literal>Authentication</literal> object. This
would mean your <literal>AuthenticationManager</literal> would
need to populate the <literal>Authentication</literal> with
custom <literal>GrantedAuthority</literal>[]s representing each
of the <literal>Customer</literal> domain object instances the
principal has access to.</para>
</listitem>
<listitem>
<para>Write an <literal>AccessDecisionVoter</literal> to enforce
the security and open the target <literal>Customer</literal>
domain object directly. This would mean your voter needs access
to a DAO that allows it to retrieve the
<literal>Customer</literal> object. It would then access the
<literal>Customer</literal> object's collection of approved
users and make the appropriate decision.</para>
</listitem>
</orderedlist></para>
<para>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
<literal>Customer</literal> authorization logic elsewhere. Obtaining
the <literal>GrantedAuthority[]</literal>s from the
<literal>Authentication</literal> object is also fine, but will not
scale to large numbers of <literal>Customer</literal>s. If a user
might be able to access 5,000 <literal>Customer</literal>s (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 <literal>Authentication</literal> object would be undesirable. The
final method, opening the <literal>Customer</literal> 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
<literal>AccessDecisionVoter</literal> and the eventual business
method itself will perform a call to the DAO responsible for
retrieving the <literal>Customer</literal> 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.</para>
<para>Fortunately, there is another alternative, which we'll talk
about below.</para>
</section>
<section id="domain-acls-key-concepts">
<title>Key Concepts</title>
<para>The org.springframework.security.acls package should be
consulted for its major interfaces. The key interfaces are:</para>
<itemizedlist spacing="compact">
<listitem>
<para><literal>Acl</literal>: Every domain object has one and only
one <literal>Acl</literal> object, which internally holds the
<literal>AccessControlEntry</literal>s as well as knows the owner
of the <literal>Acl</literal>. An Acl does not refer directly to
the domain object, but instead to an
<literal>ObjectIdentity</literal>.</para>
</listitem>
<listitem>
<para><literal><literal>AccessControlEntry</literal></literal>: An
Acl holds multiple <literal>AccessControlEntry</literal>s, which
are often abbreviated as ACEs in the framework. Each ACE refers to
a specific tuple of <literal>Permission</literal>,
<literal>Sid</literal> and <literal>Acl</literal>. An ACE can also
be granting or non-granting and contain audit settings.</para>
</listitem>
<listitem>
<para><literal>Permission</literal>: A permission represents an
immutable particular bit mask, and offers convenience functions
for bit masking and outputting information.</para>
</listitem>
<listitem>
<para><literal>Sid</literal>: The ACL module needs to refer to
principals and <literal>GrantedAuthority[]</literal>s. A level of
indirection is provided by the <literal>Sid</literal> interface.
Common classes include <literal>PrincipalSid</literal> (to
represent the principal inside an
<literal>Authentication</literal> object) and
<literal>GrantedAuthoritySid</literal>.</para>
</listitem>
<listitem>
<para><literal>ObjectIdentity</literal>: Each domain object is
represented internally within the ACL module by an
<literal>ObjectIdentity</literal>.</para>
</listitem>
<listitem>
<para><literal>AclService</literal>: Retrieves the
<literal>Acl</literal> applicable for a given
<literal>ObjectIdentity</literal>.</para>
</listitem>
<listitem>
<para><literal>MutableAclService</literal>: Allows a modified
<literal>Acl</literal> to be presented for persistence. It is not
essential to use this interface if you do not wish.</para>
</listitem>
</itemizedlist>
<para>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.</para>
<para>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.</para>
<para>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.</para>
</section>
</chapter>