Browse Source

SEC-426: Provide better ACL documentation.

Ben Alex 17 năm trước cách đây
mục cha
commit
ce34ef366d
3 tập tin đã thay đổi với 278 bổ sung634 xóa
  1. 0 479
      src/docbkx/domain-acls-old.xml
  2. 277 152
      src/docbkx/domain-acls.xml
  3. 1 3
      src/docbkx/springsecurity.xml

+ 0 - 479
src/docbkx/domain-acls-old.xml

@@ -1,479 +0,0 @@
-<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="domain-acls-old">
-  <info><title>Domain Object Security (old ACL module)</title></info>
-  
-
-  <section xml:id="domain-acls-overview-old"><info><title>Overview</title></info>
-    
-
-    <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 inheritnum="ignore" continuation="restarts">
-        <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 xml:id="domain-acls-basic-old"><info><title>Basic ACL Package</title></info>
-    
-
-    <para>Please note that our Basic ACL services are currently being
-    refactored. We expect release 1.1.0 will contain this new code.
-    Planned code is already in the Spring Security Subversion sandbox, so
-    please check there if you have a new application requiring ACLs or are
-    in the planning stages. The Basic ACL services will be deprecated from
-    release 1.1.0.</para>
-
-    <para>The <literal>org.springframework.security.acl</literal> package
-    is very simple, comprising only a handful of interfaces and a single
-    class, as shown in <xref linkend="acl-manager"/>. It provides the basic foundation for
-    access control list (ACL) lookups.
-
-    <figure xml:id="acl-manager">
-      <title>Access Control List Manager</title>
-      <mediaobject>
-        <imageobject role="fo">
-          <imagedata align="center" fileref="resources/images/ACLSecurity.gif" format="GIF"/>
-        </imageobject>
-        <imageobject role="html">
-          <imagedata align="center" fileref="images/ACLSecurity.gif" format="GIF"/>
-        </imageobject>
-      </mediaobject>
-    </figure>
-    </para>
-
-    <para>The central interface is <literal>AclManager</literal>, which is
-    defined by two methods:</para>
-
-    <para><programlisting>public AclEntry[] getAcls(java.lang.Object domainInstance);
-public AclEntry[] getAcls(java.lang.Object domainInstance, Authentication authentication);</programlisting></para>
-
-    <para><literal>AclManager</literal> is intended to be used as a
-    collaborator against your business objects, or, more desirably,
-    <literal>AccessDecisionVoter</literal>s. This means you use Spring's
-    normal <literal>ApplicationContext</literal> features to wire up your
-    <literal>AccessDecisionVoter</literal> (or business method) with an
-    <literal>AclManager</literal>. Consideration was given to placing the
-    ACL information in the <literal>ContextHolder</literal>, but it was
-    felt this would be inefficient both in terms of memory usage as well
-    as the time spent loading potentially unused ACL information. The
-    trade-off of needing to wire up a collaborator for those objects
-    requiring ACL information is rather minor, particularly in a
-    Spring-managed application.</para>
-
-    <para>The first method of the <literal>AclManager</literal> will
-    return all ACLs applying to the domain object instance passed to it.
-    The second method does the same, but only returns those ACLs which
-    apply to the passed <literal>Authentication</literal> object.</para>
-
-    <para>The <literal>AclEntry</literal> interface returned by
-    <literal>AclManager</literal> is merely a marker interface. You will
-    need to provide an implementation that reflects that ACL permissions
-    for your application.</para>
-
-    <para>Rounding out the
-    <literal>org.springframework.security.acl</literal> package is an
-    <literal>AclProviderManager</literal> class, with a corresponding
-    <literal>AclProvider</literal> interface.
-    <literal>AclProviderManager</literal> is a concrete implementation of
-    <literal>AclManager</literal>, which iterates through registered
-    <literal>AclProvider</literal>s. The first
-    <literal>AclProvider</literal> that indicates it can authoritatively
-    provide ACL information for the presented domain object instance will
-    be used. This is very similar to the
-    <literal>AuthenticationProvider</literal> interface used for
-    authentication.</para>
-
-    <para>With this background, let's now look at a usable ACL
-    implementation.</para>
-
-    <para>Spring Security includes a production-quality ACL provider
-    implementation, which is shown in <xref linkend="acl-basic-mgr"/>.
-
-    <figure xml:id="acl-basic-mgr">
-      <title>Basic ACL Manager</title>
-      <mediaobject>
-        <imageobject role="fo">
-          <imagedata align="center" fileref="resources/images/BasicAclProvider.gif" format="GIF"/>
-        </imageobject>
-        <imageobject role="html">
-          <imagedata align="center" fileref="images/BasicAclProvider.gif" format="GIF"/>
-        </imageobject>
-      </mediaobject>
-    </figure></para>
-
-    <para>The implementation is based on integer masking, which is
-    commonly used for ACL permissions given its flexibility and speed.
-    Anyone who has used Unix's <literal>chmod</literal> command will know
-    all about this type of permission masking (eg <literal>chmod
-    777</literal>). You'll find the classes and interfaces for the integer
-    masking ACL package under
-    <literal>org.springframework.security.acl.basic</literal>.</para>
-
-    <para>Extending the <literal>AclEntry</literal> interface is a
-    <literal>BasicAclEntry</literal> interface, with the main methods
-    shown below:</para>
-
-    <para><programlisting>public AclObjectIdentity getAclObjectIdentity();
-public AclObjectIdentity getAclObjectParentIdentity();
-public int getMask();
-public java.lang.Object getRecipient();</programlisting></para>
-
-    <para>As shown, each <literal>BasicAclEntry</literal> has four main
-    properties. The <literal>mask</literal> is the integer that represents
-    the permissions granted to the <literal>recipient</literal>. The
-    <literal>aclObjectIdentity</literal> is able to identify the domain
-    object instance for which the ACL applies, and the
-    <literal>aclObjectParentIdentity</literal> optionally specifies the
-    parent of the domain object instance. Multiple
-    <literal>BasicAclEntry</literal>s usually exist against a single
-    domain object instance, and as suggested by the parent identity
-    property, permissions granted higher in the object hierarchy will
-    trickle down and be inherited (unless blocked by integer zero).</para>
-
-    <para><literal>BasicAclEntry</literal> implementations typically
-    provide convenience methods, such as
-    <literal>isReadAllowed()</literal>, to avoid application classes
-    needing to perform bit masking themselves. The
-    <literal>SimpleAclEntry</literal> and
-    <literal>AbstractBasicAclEntry</literal> demonstrate and provide much
-    of this bit masking logic.</para>
-
-    <para>The <literal>AclObjectIdentity</literal> itself is merely a
-    marker interface, so you need to provide implementations for your
-    domain objects. However, the package does include a
-    <literal>NamedEntityObjectIdentity</literal> implementation which will
-    suit many needs. The <literal>NamedEntityObjectIdentity</literal>
-    identifies a given domain object instance by the classname of the
-    instance and the identity of the instance. A
-    <literal>NamedEntityObjectIdentity</literal> can be constructed
-    manually (by calling the constructor and providing the classname and
-    identity <literal>String</literal>s), or by passing in any domain
-    object that contains a <literal>getId()</literal> method.</para>
-
-    <para>The actual <literal>AclProvider</literal> implementation is
-    named <literal>BasicAclProvider</literal>. It has adopted a similar
-    design to that used by the authentication-related
-    <literal>DaoAuthenticationProvder</literal>. Specifically, you define
-    a <literal>BasicAclDao</literal> against the provider, so different
-    ACL repository types can be accessed in a pluggable manner. The
-    <literal>BasicAclProvider</literal> also supports pluggable cache
-    providers (with Spring Security including an implementation that
-    fronts EH-CACHE).</para>
-
-    <para>The <literal>BasicAclDao</literal> interface is very simple to
-    implement:</para>
-
-    <para><programlisting>public BasicAclEntry[] getAcls(AclObjectIdentity aclObjectIdentity);</programlisting></para>
-
-    <para>A <literal>BasicAclDao</literal> implementation needs to
-    understand the presented <literal>AclObjectIdentity</literal> and how
-    it maps to a storage repository, find the relevant records, and create
-    appropriate <literal>BasicAclEntry</literal> objects and return
-    them.</para>
-
-    <para>Spring Security includes a single <literal>BasicAclDao</literal>
-    implementation called <literal>JdbcDaoImpl</literal>. As implied by
-    the name, <literal>JdbcDaoImpl</literal> accesses ACL information from
-    a JDBC database. There is also an extended version of this DAO,
-    <literal>JdbcExtendedDaoImpl</literal>, which provides CRUD operations
-    on the JDBC database, although we won't discuss these features here.
-    The default database schema and some sample data will aid in
-    understanding its function:</para>
-
-    <para><programlisting>CREATE TABLE acl_object_identity (
- id IDENTITY NOT NULL,
- object_identity VARCHAR_IGNORECASE(250) NOT NULL,
- parent_object INTEGER,
- acl_class VARCHAR_IGNORECASE(250) NOT NULL,
- CONSTRAINT unique_object_identity UNIQUE(object_identity),
- FOREIGN KEY (parent_object) REFERENCES acl_object_identity(id)
-);
-
-CREATE TABLE acl_permission (
- id IDENTITY NOT NULL,
- acl_object_identity INTEGER NOT NULL,
- recipient VARCHAR_IGNORECASE(100) NOT NULL,
- mask INTEGER NOT NULL,
- CONSTRAINT unique_recipient UNIQUE(acl_object_identity, recipient),
- FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity(id)
-);
-
-INSERT INTO acl_object_identity VALUES (1, 'corp.DomainObject:1', null,
-        'org.springframework.security.acl.basic.SimpleAclEntry');
-        INSERT INTO acl_object_identity VALUES (2, 'corp.DomainObject:2', 1,
-        'org.springframework.security.acl.basic.SimpleAclEntry');
-        INSERT INTO acl_object_identity VALUES (3, 'corp.DomainObject:3', 1,
-        'org.springframework.security.acl.basic.SimpleAclEntry');
-        INSERT INTO acl_object_identity VALUES (4, 'corp.DomainObject:4', 1,
-        'org.springframework.security.acl.basic.SimpleAclEntry');
-        INSERT INTO acl_object_identity VALUES (5, 'corp.DomainObject:5', 3,
-        'org.springframework.security.acl.basic.SimpleAclEntry');
-        INSERT INTO acl_object_identity VALUES (6, 'corp.DomainObject:6', 3,
-        'org.springframework.security.acl.basic.SimpleAclEntry');
-
-        INSERT INTO acl_permission VALUES (null, 1, 'ROLE_SUPERVISOR', 1);
-INSERT INTO acl_permission VALUES (null, 2, 'ROLE_SUPERVISOR', 0);
-INSERT INTO acl_permission VALUES (null, 2, 'rod', 2);
-INSERT INTO acl_permission VALUES (null, 3, 'scott', 14);
-INSERT INTO acl_permission VALUES (null, 6, 'scott', 1);</programlisting></para>
-
-    <para>As can be seen, database-specific constraints are used
-    extensively to ensure the integrity of the ACL information. If you
-    need to use a different database (Hypersonic SQL statements are shown
-    above), you should try to implement equivalent constraints. The
-    equivalent Oracle configuration is:</para>
-
-    <para><programlisting>CREATE TABLE ACL_OBJECT_IDENTITY (
- ID number(19,0) not null,
- OBJECT_IDENTITY varchar2(255) NOT NULL,
- PARENT_OBJECT number(19,0),
- ACL_CLASS varchar2(255) NOT NULL,
- primary key (ID)
-);
-ALTER TABLE ACL_OBJECT_IDENTITY ADD CONTRAINT FK_PARENT_OBJECT foreign key (ID) references ACL_OBJECT_IDENTITY
-
-CREATE SEQUENCE ACL_OBJECT_IDENTITY_SEQ;
-
-CREATE OR REPLACE TRIGGER ACL_OBJECT_IDENTITY_ID
-BEFORE INSERT ON ACL_OBJECT_IDENTITY
-FOR EACH ROW
-BEGIN
-SELECT ACL_OBJECT_IDENTITY_SEQ.NEXTVAL INTO :new.id FROM dual;
-END;
-
-CREATE TABLE ACL_PERMISSION (
- ID number(19,0) not null,
- ACL_OBJECT_IDENTITY number(19,0) NOT NULL,
- RECIPIENT varchar2(255) NOT NULL,
- MASK number(19,0) NOT NULL,
- primary key (ID)
-);
-
-ALTER TABLE ACL_PERMISSION ADD CONTRAINT UNIQUE_ID_RECIPIENT unique (acl_object_identity, recipient);
-
-CREATE SEQUENCE ACL_PERMISSION_SEQ;
-
-CREATE OR REPLACE TRIGGER ACL_PERMISSION_ID
-BEFORE INSERT ON ACL_PERMISSION
-FOR EACH ROW
-BEGIN
-SELECT ACL_PERMISSION_SEQ.NEXTVAL INTO :new.id FROM dual;
-END;
-
-&lt;bean id="basicAclExtendedDao" class="org.springframework.security.acl.basic.jdbc.JdbcExtendedDaoImpl"&gt;
-&lt;property name="dataSource"&gt;
-    &lt;ref bean="dataSource"/&gt;
-&lt;/property&gt;
-&lt;property name="objectPropertiesQuery" value="${acegi.objectPropertiesQuery}"/&gt;
-&lt;/bean&gt;
-
-&lt;prop key="acegi.objectPropertiesQuery"&gt;SELECT CHILD.ID, CHILD.OBJECT_IDENTITY, CHILD.ACL_CLASS, PARENT.OBJECT_IDENTITY as PARENT_OBJECT_IDENTITY FROM acl_object_identity as CHILD LEFT OUTER JOIN acl_object_identity as PARENT ON CHILD.parent_object=PARENT.id WHERE CHILD.object_identity = ?&lt;/prop&gt; </programlisting></para>
-
-    <para>The <literal>JdbcDaoImpl</literal> will only respond to requests
-    for <literal>NamedEntityObjectIdentity</literal>s. It converts such
-    identities into a single <literal>String</literal>, comprising
-    the<literal> NamedEntityObjectIdentity.getClassname()</literal> +
-    <literal>":"</literal> +
-    <literal>NamedEntityObjectIdentity.getId()</literal>. This yields the
-    type of <literal>object_identity</literal> values shown above. As
-    indicated by the sample data, each database row corresponds to a
-    single <literal>BasicAclEntry</literal>. As stated earlier and
-    demonstrated by <literal>corp.DomainObject:2</literal> in the above
-    sample data, each domain object instance will often have multiple
-    <literal>BasicAclEntry</literal>[]s.</para>
-
-    <para>As <literal>JdbcDaoImpl</literal> is required to return concrete
-    <literal>BasicAclEntry</literal> classes, it needs to know which
-    <literal>BasicAclEntry</literal> implementation it is to create and
-    populate. This is the role of the <literal>acl_class</literal> column.
-    <literal>JdbcDaoImpl</literal> will create the indicated class and set
-    its <literal>mask</literal>, <literal>recipient</literal>,
-    <literal>aclObjectIdentity</literal> and
-    <literal>aclObjectParentIdentity</literal> properties.</para>
-
-    <para>As you can probably tell from the sample data, the
-    <literal>parent_object_identity</literal> value can either be null or
-    in the same format as the <literal>object_identity</literal>. If
-    non-null, <literal>JdbcDaoImpl</literal> will create a
-    <literal>NamedEntityObjectIdentity</literal> to place inside the
-    returned <literal>BasicAclEntry</literal> class.</para>
-
-    <para>Returning to the <literal>BasicAclProvider</literal>, before it
-    can poll the <literal>BasicAclDao</literal> implementation it needs to
-    convert the domain object instance it was passed into an
-    <literal>AclObjectIdentity</literal>.
-    <literal>BasicAclProvider</literal> has a <literal>protected
-    AclObjectIdentity obtainIdentity(Object domainInstance)</literal>
-    method that is responsible for this. As a protected method, it enables
-    subclasses to easily override. The normal implementation checks
-    whether the passed domain object instance implements the
-    <literal>AclObjectIdentityAware</literal> interface, which is merely a
-    getter for an <literal>AclObjectIdentity</literal>. If the domain
-    object does implement this interface, that is the identity returned.
-    If the domain object does not implement this interface, the method
-    will attempt to create an <literal>AclObjectIdentity</literal> by
-    passing the domain object instance to the constructor of a class
-    defined by the
-    <literal>BasicAclProvider.getDefaultAclObjectIdentity()</literal>
-    method. By default the defined class is
-    <literal>NamedEntityObjectIdentity</literal>, which was described in
-    more detail above. Therefore, you will need to either (i) provide a
-    <literal>getId()</literal> method on your domain objects, (ii)
-    implement <literal>AclObjectIdentityAware</literal> on your domain
-    objects, (iii) provide an alternative
-    <literal>AclObjectIdentity</literal> implementation that will accept
-    your domain object in its constructor, or (iv) override the
-    <literal>obtainIdentity(Object)</literal> method.</para>
-
-    <para>Once the <literal>AclObjectIdentity</literal> of the domain
-    object instance is determined, the <literal>BasicAclProvider</literal>
-    will poll the DAO to obtain its <literal>BasicAclEntry</literal>[]s.
-    If any of the entries returned by the DAO indicate there is a parent,
-    that parent will be polled, and the process will repeat until there is
-    no further parent. The permissions assigned to a
-    <literal>recipient</literal> closest to the domain object instance
-    will always take priority and override any inherited permissions. From
-    the sample data above, the following inherited permissions would
-    apply:</para>
-
-    <para><programlisting>--- Mask integer 0  = no permissions
---- Mask integer 1  = administer
---- Mask integer 2  = read
---- Mask integer 6  = read and write permissions
---- Mask integer 14 = read and write and create permissions
-
----------------------------------------------------------------------
---- *** INHERITED RIGHTS FOR DIFFERENT INSTANCES AND RECIPIENTS ***
---- INSTANCE  RECIPIENT         PERMISSION(S) (COMMENT #INSTANCE)
----------------------------------------------------------------------
----    1      ROLE_SUPERVISOR   Administer
----    2      ROLE_SUPERVISOR   None (overrides parent #1)
----           rod               Read
----    3      ROLE_SUPERVISOR   Administer (from parent #1)
----           scott             Read, Write, Create
----    4      ROLE_SUPERVISOR   Administer (from parent #1)
----    5      ROLE_SUPERVISOR   Administer (from parent #3)
----           scott             Read, Write, Create (from parent #3)
----    6      ROLE_SUPERVISOR   Administer (from parent #3)
----           scott             Administer (overrides parent #3)</programlisting></para>
-
-    <para>So the above explains how a domain object instance has its
-    <literal>AclObjectIdentity</literal> discovered, and the
-    <literal>BasicAclDao</literal> will be polled successively until an
-    array of inherited permissions is constructed for the domain object
-    instance. The final step is to determine the
-    <literal>BasicAclEntry</literal>[]s that are actually applicable to a
-    given <literal>Authentication</literal> object.</para>
-
-    <para>As you would recall, the <literal>AclManager</literal> (and all
-    delegates, up to and including <literal>BasicAclProvider</literal>)
-    provides a method which returns only those
-    <literal>BasicAclEntry</literal>[]s applying to a passed
-    <literal>Authentication</literal> object.
-    <literal>BasicAclProvider</literal> delivers this functionality by
-    delegating the filtering operation to an
-    <literal>EffectiveAclsResolver</literal> implementation. The default
-    implementation,
-    <literal>GrantedAuthorityEffectiveAclsResolver</literal>, will iterate
-    through the <literal>BasicAclEntry</literal>[]s and include only those
-    where the <literal>recipient</literal> is equal to either the
-    <literal>Authentication</literal>'s <literal>principal</literal> or
-    any of the <literal>Authentication</literal>'s
-    <literal>GrantedAuthority</literal>[]s. Please refer to the JavaDocs
-    for more information.</para>
-
-    <figure xml:id="acl-instantiation">
-      <title>ACL Instantiation Approach</title>
-    <mediaobject>
-      <imageobject role="fo">
-        <imagedata align="center" fileref="resources/images/Permissions.gif" format="GIF"/>
-      </imageobject>
-      <imageobject role="html">
-        <imagedata align="center" fileref="images/Permissions.gif" format="GIF"/>
-      </imageobject>
-    </mediaobject>
-    </figure>
-
-    <para><xref linkend="acl-instantiation"/> explains the key relationships between objects
-    in the Basic ACL package.</para>
-  </section>
-</chapter>

+ 277 - 152
src/docbkx/domain-acls.xml

@@ -1,179 +1,304 @@
 <chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="domain-acls">
-    
-    <info><title>Domain Object Security</title></info>
-    
-    
-    
-    <section xml:id="domain-acls-overview"><info><title>Overview</title></info>
-        
-        
-        <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 inheritnum="ignore" continuation="restarts">
+    <info>
+        <title>Domain Object Security</title>
+    </info>
+    <section xml:id="domain-acls-overview">
+        <info>
+            <title>Overview</title>
+        </info>
+        <para>PLEASE NOTE: Before release 2.0.0, Spring Security was known as Acegi Security. An ACL
+            module was provided with the old Acegi Security releases under the
+                <literal>org.[acegisecurity/springsecurity].acl</literal> package. This old package
+            is now deprecated and will be removed in a future release of Spring Security. This
+            chapter covers the new ACL module, which is officially recommended from Spring Security
+            2.0.0 and above, and can be found under the
+            <literal>org.springframework.security.acls</literal> package.</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
+                inheritnum="ignore" continuation="restarts">
                 <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>,
+                    <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>
+                    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>
+                    <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>
+                    <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>
+        <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 xml:id="domain-acls-key-concepts"><info><title>Key Concepts</title></info>
-        
-        
-        <para>The org.springframework.security.acls package should be
-            consulted for its major interfaces. The key interfaces are:</para>
-        
+    <section xml:id="domain-acls-key-concepts">
+        <info>
+            <title>Key Concepts</title>
+        </info>
+        <para>Spring Security's ACL services are shipped in the
+            <literal>spring-security-acl-xxx.jar</literal>. You will need to add this JAR to your
+            classpath to use Spring Security's domain object instance security capabilities.</para>
+        <para>Spring Security's domain object instance security capabilities centre on the concept
+            of an access control list (ACL). Every domain object instance in your system has its own
+            ACL, and the ACL records details of who can and can't work with that domain object. With
+            this in mind, Spring Security delivers three main ACL-related capabilities to your application:<itemizedlist>
+                <listitem>
+                    <para>A way of efficiently retrieving ACL entries for all of your domain objects
+                        (and modifying those ACLs)</para>
+                </listitem>
+                <listitem>
+                    <para>A way of ensuring a given principal is permitted to work with your
+                        objects, before methods are called</para>
+                </listitem>
+                <listitem>
+                    <para>A way of ensuring a given principal is permitted to work with your objects
+                        (or something they return), after methods are called</para>
+                </listitem>
+            </itemizedlist></para>
+        <para>As indicated by the first bullet point, one of the main capabilities of the Spring
+            Security ACL module is providing a high-performance way of retrieving ACLs. This ACL
+            repository capability is extremely important, because every domain object instance in
+            your system might have several access control entries, and each ACL might inherit from
+            other ACLs in a tree-like structure (this is supported out-of-the-box by Spring
+            Security, and is very commonly used). Spring Security's ACL capability has been
+            carefully designed to provide high performance retrieval of ACLs, together with
+            pluggable caching, deadlock-minimizing database updates, independence from ORM
+            frameworks (we use JDBC directly), proper encapsulation, and transparent database
+            updating.</para>
+        <para>Given databases are central to the operation of the ACL module, let's explore the four
+            main tables used by default in the implementation. The tables are presented below in
+            order of size in a typical Spring Security ACL deployment, with the table with the most
+            rows listed last:</para>
+        <para>
+            <itemizedlist>
+                <listitem>
+                    <para>ACL_SID allows us to uniquely identify any principal or authority in the
+                        system ("SID" stands for "security identity"). The only columns are the ID,
+                        a textual representation of the SID, and a flag to indicate whether the
+                        textual representation refers to a prncipal name or a
+                            <literal>GrantedAuthority</literal>. Thus, there is a single row for
+                        each unique principal or <literal>GrantedAuthority</literal>. When used in
+                        the context of receiving a permission, a SID is generally called a
+                        "recipient".</para>
+                </listitem>
+                <listitem>
+                    <para>ACL_CLASS allows us to uniquely identify any domain object class in the
+                        system. The only columns are the ID and the Java class name. Thus, there is
+                        a single row for each unique Class we wish to store ACL permissions
+                    for.</para>
+                </listitem>
+                <listitem>
+                    <para>ACL_OBJECT_IDENTITY stores information for each unique domain object
+                        instance in the system. Columns include the ID, a foreign key to the
+                        ACL_CLASS table, a unique identifier so we know which ACL_CLASS instance
+                        we're providing information for, the parent, a foreign key to the ACL_SID
+                        table to represent the owner of the domain object instance, and whether we
+                        allow ACL entries to inherit from any parent ACL. We have a single row for
+                        every domain object instance we're storing ACL permissions for.</para>
+                </listitem>
+                <listitem>
+                    <para>Finally, ACL_ENTRY stores the individual permissions assigned to each
+                        recipient. Columns include a foreign key to the ACL_OBJECT_IDENTITY, the
+                        recipient (ie a foreign key to ACL_SID), whether we'll be auditing or not,
+                        and the integer bit mask that represents the actual permission being granted
+                        or denied. We have a single row for every recipient that receives a
+                        permission to work with a domain object.</para>
+                </listitem>
+            </itemizedlist>
+        </para>
+        <para>As mentioned in the last paragraph, the ACL system uses integer bit masking. Don't
+            worry, you need not be aware of the finer points of bit shifting to use the ACL system,
+            but suffice to say that we have 32 bits we can switch on or off. Each of these bits
+            represents a permission, and by default the permissions are read (bit 0), write (bit 1),
+            create (bit 2), delete (bit 3) and administer (bit 4). It's easy to implement your own
+                <literal>Permission</literal> instance if you wish to use other permissions, and the
+            remainder of the ACL framework will operate without knowledge of your extensions.</para>
+        <para>It is important to understand that the number of domain objects in your system has
+            absolutely no bearing on the fact we've chosen to use integer bit masking. Whilst you
+            have 32 bits available for permissions, you could have billions of domain object
+            instances (which will mean billions of rows in ACL_OBJECT_IDENTITY and quite probably
+            ACL_ENTRY). We make this point because we've found sometimes people mistakenly believe
+            they need a bit for each potential domain object, which is not the case.</para>
+        <para>Now that we've provided a basic overview of what the ACL system does, and what it
+            looks like at a table structure, let's explore the key 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>
+                <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>. The <literal>Acl</literal>
+                    is stored in the ACL_OBJECT_IDENTITY table.</para>
             </listitem>
-            
             <listitem>
-                <para><literal>AccessControlEntry</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>
+                <para><literal>AccessControlEntry</literal>: An <literal>Acl</literal> 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. The ACE is stored in the ACL_ENTRY table.</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>
+                <para><literal>Permission</literal>: A permission represents a particular immutable
+                    bit mask, and offers convenience functions for bit masking and outputting
+                    information. The basic permissions presented above (bits 0 through 4) are
+                    contained in the <literal>BasePermission</literal> class.</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>
+                <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, which is an abbreviation of "security
+                    identity". Common classes include <literal>PrincipalSid</literal> (to represent
+                    the principal inside an <literal>Authentication</literal> object) and
+                        <literal>GrantedAuthoritySid</literal>. The security identity information is
+                    stored in the ACL_SID table.</para>
             </listitem>
-            
             <listitem>
-                <para><literal>ObjectIdentity</literal>: Each domain object is
-                    represented internally within the ACL module by an
-                    <literal>ObjectIdentity</literal>.</para>
+                <para><literal>ObjectIdentity</literal>: Each domain object is represented
+                    internally within the ACL module by an <literal>ObjectIdentity</literal>. The
+                    default implementation is called <literal>ObjectIdentityImpl</literal>.</para>
             </listitem>
-            
             <listitem>
-                <para><literal>AclService</literal>: Retrieves the
-                    <literal>Acl</literal> applicable for a given
-                    <literal>ObjectIdentity</literal>.</para>
+                <para><literal>AclService</literal>: Retrieves the <literal>Acl</literal> applicable
+                    for a given <literal>ObjectIdentity</literal>. In the included implementation
+                        (<literal>JdbcAclService</literal>), retrieval operations are delegated to a
+                        <literal>LookupStrategy</literal>. The <literal>LookupStrategy</literal>
+                    provides a highly optimized strategy for retrieving ACL information, using
+                    batched retrievals <literal>(BasicLookupStrategy</literal>) and supporting
+                    custom implementations that leverage materialized views, hierarchical queries
+                    and similar performance-centric, non-ANSI SQL capabilities.</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>
+                <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>
+        <para>Please note that our out-of-the-box AclService and related database classes all use
+            ANSI SQL. This should therefore work with all major databases. At the time of writing,
+            the system had been successfully tested using Hypersonic SQL, PostgreSQL, Microsoft SQL
+            Server and Oracle.</para>
+        <para>Two samples ship with Spring Security that demonstrate the ACL module. The first is
+            the Contacts Sample, and the other is the Document Management System (DMS) Sample. We
+            suggest taking a look over these for examples.</para>
+    </section>
+    <section xml:id="domain-acls-getting-started">
+        <info>
+            <title>Getting Started</title>
+        </info>
+        <para>To get starting using Spring Security's ACL capability, you will need to store your
+            ACL information somewhere. This necessitates the instantiation of a
+            <literal>DataSource</literal> using Spring. The <literal>DataSource</literal> is then
+            injected into a <literal>JdbcMutableAclService</literal> and
+                <literal>BasicLookupStrategy</literal> instance. The latter provides
+            high-performance ACL retrieval capabilities, and the former provides mutator
+            capabilities. Refer to one of the samples that ship with Spring Security for an example
+            configuration. You'll also need to populate the database with the four ACL-specific
+            tables listed in the last section (refer to the ACL samples for the appropriate SQL
+            statements).</para>
+        <para>Once you've created the required schema and instantiated
+                <literal>JdbcMutableAclService</literal>, you'll next need to ensure your domain
+            model supports interoperability with the Spring Security ACL package. Hopefully
+                <literal>ObjectIdentityImpl</literal> will prove sufficient, as it provides a large
+            number of ways in which it can be used. Most people will have domain objects that
+            contain a <literal>public Serializable getId()</literal> method. If the return type is
+            long, or compatible with long (eg an int), you will find you need not give further
+            consideration to <literal>ObjectIdentity</literal> issues. Many parts of the ACL module
+            rely on long identifiers. If you're not using long (or an int, byte etc), there is a
+            very good chance you'll need to reimplement a number of classes. We do not intend to
+            support non-long identifiers in Spring Security's ACL module, as longs are already
+            compatible with all database sequences, the most common identifier data type, and are of
+            sufficient length to accommodate all common usage scenarios.</para>
+        <para>The following fragment of code shows how to create an <literal>Acl</literal>, or
+            modify an existing
+            <literal>Acl</literal>:<programlisting>// Prepare the information we'd like in our access control entry (ACE)
+ObjectIdentity oi = new ObjectIdentityImpl(Foo.class, new Long(44));
+Sid sid = new PrincipalSid("Samantha");
+Permission p = BasePermission.ADMINISTRATION;
+
+// Create or update the relevant ACL
+MutableAcl acl = null;
+try {
+  acl = (MutableAcl) aclService.readAclById(oi);
+} catch (NotFoundException nfe) {
+  acl = aclService.createAcl(oi);
+}
+
+// Now grant some permissions via an access control entry (ACE)
+acl.insertAce(acl.getEntries().length, p, sid, true);
+aclService.updateAcl(acl);
+</programlisting></para>
+        <para>In the example above, we're retrieving the ACL associated with the "Foo" domain object
+            with identifier number 44. We're then adding an ACE so that a principal named "Samantha"
+            can "administer" the object. The code fragment is relatively self-explanatory, except
+            the insertAce method. The first argument to the insertAce method is determining at what
+            position in the Acl the new entry will be inserted. In the example above, we're just
+            putting the new ACE at the end of the existing ACEs. The final argument is a boolean
+            indicating whether the ACE is granting or denying. Most of the time it will be granting
+            (true), but if it is denying (false), the permissions are effectively being blocked.</para>
+        <para>Spring Security does not provide any special integration to automatically create,
+            update or delete ACLs as part of your DAO or repository operations. Instead, you will
+            need to write code like shown above for your individual domain objects. It's worth
+            considering using AOP on your services layer to automatically integrate the ACL
+            information with your services layer operations. We've found this quite an effective
+            approach in the past.</para>
+        <para>Once you've used the above techniques to store some ACL information in the database,
+            the next step is to actually use the ACL information as part of authorization decision
+            logic. You have a number of choices here. You could write your own
+                <literal>AccessDecisionVoter</literal> or <literal>AfterInvocationProvider</literal>
+            that respectively fires before or after a method invocation. Such classes would use
+                <literal>AclService</literal> to retrieve the relevant ACL and then call
+                <literal>Acl.isGranted(Permission[] permission, Sid[] sids, boolean
+                administrativeMode)</literal> to decide whether permission is granted or denied.
+            Alternately, you could use our <literal>AclEntryVoter</literal>,
+                <literal>AclEntryAfterInvocationProvider</literal> or
+                <literal>AclEntryAfterInvocationCollectionFilteringProvider</literal> classes. All
+            of these classes provide a declarative-based approach to evaluating ACL information at
+            runtime, freeing you from needing to write any code. Please refer to the sample
+            applications to learn how to use these classes.</para>
     </section>
-</chapter>
+</chapter>

+ 1 - 3
src/docbkx/springsecurity.xml

@@ -192,9 +192,7 @@
     <xi:include href="secured-objects.xml"/>
     
     <xi:include href="domain-acls.xml"/>
-    
-    <xi:include href="domain-acls-old.xml"/>
-    
+  
   </part>
 
 </book>