|
@@ -69,7 +69,7 @@
|
|
<sect2 id="security-high-level-design-key-components">
|
|
<sect2 id="security-high-level-design-key-components">
|
|
<title>Key Components</title>
|
|
<title>Key Components</title>
|
|
|
|
|
|
- <para>The Acegi Security System for Spring essentially comprises six
|
|
|
|
|
|
+ <para>The Acegi Security System for Spring essentially comprises seven
|
|
key functional parts:</para>
|
|
key functional parts:</para>
|
|
|
|
|
|
<itemizedlist spacing="compact">
|
|
<itemizedlist spacing="compact">
|
|
@@ -109,6 +109,11 @@
|
|
authentication, authorization, run-as replacement and execution of
|
|
authentication, authorization, run-as replacement and execution of
|
|
a given operation.</para>
|
|
a given operation.</para>
|
|
</listitem>
|
|
</listitem>
|
|
|
|
+
|
|
|
|
+ <listitem>
|
|
|
|
+ <para>An acess control list (ACL) management package, which can be
|
|
|
|
+ used to obtain ACLs for domain object instances.</para>
|
|
|
|
+ </listitem>
|
|
</itemizedlist>
|
|
</itemizedlist>
|
|
|
|
|
|
<para>Secure objects refer to any type of object that can have
|
|
<para>Secure objects refer to any type of object that can have
|
|
@@ -134,8 +139,8 @@
|
|
<literal>FilterInterceptor</literal>) with complete
|
|
<literal>FilterInterceptor</literal>) with complete
|
|
transparency.</para>
|
|
transparency.</para>
|
|
|
|
|
|
- <para>Each of the six key parts is discussed in detail throughout this
|
|
|
|
- document.</para>
|
|
|
|
|
|
+ <para>Each of the seven key parts is discussed in detail throughout
|
|
|
|
+ this document.</para>
|
|
</sect2>
|
|
</sect2>
|
|
|
|
|
|
<sect2 id="security-high-level-design-supported-secure-objects">
|
|
<sect2 id="security-high-level-design-supported-secure-objects">
|
|
@@ -2985,6 +2990,370 @@ $CATALINA_HOME/bin/startup.sh</programlisting></para>
|
|
</sect2>
|
|
</sect2>
|
|
</sect1>
|
|
</sect1>
|
|
|
|
|
|
|
|
+ <sect1 id="acls">
|
|
|
|
+ <title>Instance-Based Access Control List (ACL) Security</title>
|
|
|
|
+
|
|
|
|
+ <sect2 id="acls-overview">
|
|
|
|
+ <title>Overview</title>
|
|
|
|
+
|
|
|
|
+ <para>THIS FEATURE WAS ADDED IN VERSION 0.6. WE WELCOME YOUR COMMENTS
|
|
|
|
+ AND IMPROVEMENTS.</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 Acegi Security System for Spring 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>ContextHolder.getContext()</literal> and casting it to
|
|
|
|
+ <literal>SecureContext</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>
|
|
|
|
+ </sect2>
|
|
|
|
+
|
|
|
|
+ <sect2 id="acls-acl-package">
|
|
|
|
+ <title>The net.sf.acegisecurity.acl Package</title>
|
|
|
|
+
|
|
|
|
+ <para>The <literal>net.sf.acegisecurity.acl</literal> package is very
|
|
|
|
+ simple, comprising only a handful of interfaces and a single class. It
|
|
|
|
+ provides the basic foundation for access control list (ACL) lookups.
|
|
|
|
+ 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>net.sf.acegisecurity.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>
|
|
|
|
+ </sect2>
|
|
|
|
+
|
|
|
|
+ <sect2 id="acls-masking">
|
|
|
|
+ <title>Integer Masked ACLs</title>
|
|
|
|
+
|
|
|
|
+ <para>Acegi Security System for Spring includes a production-quality
|
|
|
|
+ ACL provider implementation. 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>net.sf.acegisecurity.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 Acegi Security System for Spring 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>Acegi Security includes a single <literal>BasicAclDao</literal>
|
|
|
|
+ implementation called <literal>JdbcDaoImpl</literal>. As implied by
|
|
|
|
+ the name, it accesses ACL information from a JDBC database. The
|
|
|
|
+ default database schema and some sample data will aid in understanding
|
|
|
|
+ its function:</para>
|
|
|
|
+
|
|
|
|
+ <para><programlisting>CREATE TABLE acls (
|
|
|
|
+ object_identity VARCHAR_IGNORECASE(250) NOT NULL,
|
|
|
|
+ recipient VARCHAR_IGNORECASE(100) NOT NULL,
|
|
|
|
+ parent_object_identity VARCHAR_IGNORECASE(250),
|
|
|
|
+ mask INTEGER NOT NULL,
|
|
|
|
+ acl_class VARCHAR_IGNORECASE(250) NOT NULL,
|
|
|
|
+ CONSTRAINT pk_acls PRIMARY KEY(object_identity, recipient)
|
|
|
|
+);
|
|
|
|
+
|
|
|
|
+INSERT INTO acls VALUES ('corp.DomainObject:1', 'ROLE_SUPERVISOR', null, 1, 'net.sf.acegisecurity.acl.basic.SimpleAclEntry');
|
|
|
|
+INSERT INTO acls VALUES ('corp.DomainObject:2', 'ROLE_SUPERVISOR', 'corp.DomainObject:1', 0, 'net.sf.acegisecurity.acl.basic.SimpleAclEntry');
|
|
|
|
+INSERT INTO acls VALUES ('corp.DomainObject:2', 'marissa', 'corp.DomainObject:1', 2, 'net.sf.acegisecurity.acl.basic.SimpleAclEntry');
|
|
|
|
+INSERT INTO acls VALUES ('corp.DomainObject:3', 'scott', 'corp.DomainObject:1', 14, 'net.sf.acegisecurity.acl.basic.SimpleAclEntry');
|
|
|
|
+INSERT INTO acls VALUES ('corp.DomainObject:4', 'inheritance_marker_only', 'corp.DomainObject:1', 0, 'net.sf.acegisecurity.acl.basic.SimpleAclEntry');
|
|
|
|
+INSERT INTO acls VALUES ('corp.DomainObject:5', 'inheritance_marker_only', 'corp.DomainObject:3', 0, 'net.sf.acegisecurity.acl.basic.SimpleAclEntry');
|
|
|
|
+INSERT INTO acls VALUES ('corp.DomainObject:6', 'scott', 'corp.DomainObject:3', 1, 'net.sf.acegisecurity.acl.basic.SimpleAclEntry');
|
|
|
|
+INSERT INTO acls VALUES ('corp.DomainObject:7', 'scott', 'some.invalid.parent:1', 2, 'net.sf.acegisecurity.acl.basic.SimpleAclEntry');</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)
|
|
|
|
+--- marissa 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)
|
|
|
|
+--- 7 scott Read (invalid parent ignored)</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>
|
|
|
|
+ </sect2>
|
|
|
|
+
|
|
|
|
+ <sect2 id="acls-conclusion">
|
|
|
|
+ <title>Conclusion</title>
|
|
|
|
+
|
|
|
|
+ <para>Acegi Security's instance-specific ACL packages shield you from
|
|
|
|
+ much of the complexity of developing your own ACL approach. The
|
|
|
|
+ interfaces and classes detailed above provide a scalable, customisable
|
|
|
|
+ ACL solution that is decoupled from your application code. Whilst the
|
|
|
|
+ reference documentation may suggest complexity, the basic
|
|
|
|
+ implementation is able to support most typical applications
|
|
|
|
+ out-of-the-box.</para>
|
|
|
|
+ </sect2>
|
|
|
|
+ </sect1>
|
|
|
|
+
|
|
<sect1 id="security-filters">
|
|
<sect1 id="security-filters">
|
|
<title>Filters</title>
|
|
<title>Filters</title>
|
|
|
|
|
|
@@ -3081,6 +3450,11 @@ $CATALINA_HOME/bin/startup.sh</programlisting></para>
|
|
<para>All of the above filters use
|
|
<para>All of the above filters use
|
|
<literal>FilterToBeanProxy</literal>, which is discussed in the
|
|
<literal>FilterToBeanProxy</literal>, which is discussed in the
|
|
previous section.</para>
|
|
previous section.</para>
|
|
|
|
+
|
|
|
|
+ <para>If you're using SiteMesh, ensure the Acegi Security filters
|
|
|
|
+ execute before the SiteMesh filters are called. This enables the
|
|
|
|
+ <literal>ContextHolder</literal> to be populated in time for use by
|
|
|
|
+ SiteMesh decorators.</para>
|
|
</sect2>
|
|
</sect2>
|
|
</sect1>
|
|
</sect1>
|
|
|
|
|