[seam-commits] Seam SVN: r8485 - trunk/doc/Seam_Reference_Guide/en-US.
seam-commits at lists.jboss.org
seam-commits at lists.jboss.org
Sun Jul 20 09:53:49 EDT 2008
Author: shane.bryzak at jboss.com
Date: 2008-07-20 09:53:49 -0400 (Sun, 20 Jul 2008)
New Revision: 8485
Modified:
trunk/doc/Seam_Reference_Guide/en-US/Security.xml
Log:
some more additions
Modified: trunk/doc/Seam_Reference_Guide/en-US/Security.xml
===================================================================
--- trunk/doc/Seam_Reference_Guide/en-US/Security.xml 2008-07-19 15:48:26 UTC (rev 8484)
+++ trunk/doc/Seam_Reference_Guide/en-US/Security.xml 2008-07-20 13:53:49 UTC (rev 8485)
@@ -3062,7 +3062,7 @@
</mediaobject>
<para>
- Each of these classes are explained in more detail in the following sections.
+ The relevant classes are explained in more detail in the following sections.
</para>
<sect3>
@@ -3090,7 +3090,9 @@
<para>
It is very simple to implement your own permission resolver. The <literal>PermissionResolver</literal>
- interface defines only two methods that must be implemented:
+ interface defines only two methods that must be implemented, as shown by the following table. By deploying
+ your own <literal>PermissionResolver</literal> implementation in your Seam project, it will be automatically
+ scanned during deployment and registered with the default <literal>ResolverChain</literal>.
</para>
<table>
@@ -3138,7 +3140,7 @@
</para>
</entry>
</row>
-
+
<row>
<entry>
<para>
@@ -3152,7 +3154,7 @@
</entry>
<entry>
<para>
- This method should iterate through the specified set of objects, and remove any that would otherwise
+ This method should remove any objects from the specified set, that would otherwise
return <literal>false</literal> if passed to the <literal>hasPermission()</literal> method with the
same <literal>action</literal> parameter value.
</para>
@@ -3177,7 +3179,9 @@
<para>
The following sequence diagram shows the interaction between the components of the permission framework during a
- permission check (explanation follows):
+ permission check (explanation follows). A permission check can originate from a number of possible sources,
+ for example - the security interceptor, the <literal>s:hasPermission</literal> EL function, or via an API
+ call to <literal>Identity.checkPermission</literal>:
</para>
<mediaobject>
@@ -3193,19 +3197,22 @@
</sect2>
<sect2>
- <title>Rule-based Permissions</title>
+ <title>RuleBasedPermissionResolver</title>
+ <para>
+ One of the built-in permission resolvers provided by Seam, <literal>RuleBasedPermissionResolver</literal>
+ allows permissions to be evaluated based on a set of Drools (JBoss Rules) security rules. A couple of the
+ advantages of using a rule engine are 1) a centralized location for the business logic that is used to
+ evaluate user permissions, and 2) speed - Drools uses very efficient algorithms for evaluating large
+ numbers of complex rules involving multiple conditions.
+ </para>
+
<sect3>
- <title>Configuration</title>
-
- </sect3>
-
- <sect3>
<title>Requirements</title>
<para>
- If using the rule-based permission features provided by Seam Security, the following jar files are required to be
- distributed with your project:
+ If using the rule-based permission features provided by Seam Security, the following jar files are required by Drools
+ to be distributed with your project:
</para>
<itemizedlist>
@@ -3226,208 +3233,220 @@
</listitem>
</itemizedlist>
- </sect3>
+ </sect3>
- </sect2>
+ <sect3>
+ <title>Configuration</title>
+
+ <para>
+ The configuration for <literal>RuleBasedPermissionResolver</literal> requires that a Drools rule base is first
+ configured in <literal>components.xml</literal>. By default, it expects that the rule base is named
+ <literal>securityRules</literal>, as per the following example:
+ </para>
+
+ <programlisting role="XML"><![CDATA[<components xmlns="http://jboss.com/products/seam/components"
+ xmlns:core="http://jboss.com/products/seam/core"
+ xmlns:security="http://jboss.com/products/seam/security"
+ xmlns:drools="http://jboss.com/products/seam/drools"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation=
+ "http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.1.xsd
+ http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.1.xsd
+ http://jboss.com/products/seam/drools http://jboss.com/products/seam/drools-2.1.xsd"
+ http://jboss.com/products/seam/security http://jboss.com/products/seam/security-2.1.xsd">
+
+ <drools:rule-base name="securityRules">
+ <drools:rule-files>
+ <value>/META-INF/security.drl</value>
+ </drools:rule-files>
+ </drools:rule-base>
+
+ </components>]]></programlisting>
+
+ <para>
+ The default rule base name can be overridden by specifying the <literal>security-rules</literal>
+ property for <literal>RuleBasedPermissionResolver</literal>:
+ </para>
+
+ <programlisting><![CDATA[
+ <security:rule-based-permission-resolver security-rules="#{prodSecurityRules}"/>]]></programlisting>
- </sect1>
-
- <sect1>
- <title>Writing Security Rules</title>
-
- <para>
- Up to this point there has been a lot of mention of permissions, but no information about how permissions
- are actually defined or granted. This section completes the picture, by explaining how permission
- checks are processed, and how to implement permission checks for a Seam application.
- </para>
-
- <sect2>
- <title>Permissions Overview</title>
-
- <para>
- So how does the security API know whether a user has the <literal>customer:modify</literal> permission
- for a specific customer? Seam Security provides quite a novel method for determining user permissions,
- based on JBoss Rules. A couple of the advantages of using a rule engine are 1) a centralized location
- for the business logic that is behind each user permission, and 2) speed - JBoss Rules uses very efficient
- algorithms for evaluating large numbers of complex rules involving multiple conditions.
- </para>
-
- </sect2>
-
- <sect2>
- <title>Configuring a rules file</title>
-
- <para>
- Seam Security expects to find a <literal>RuleBase</literal> component called <literal>securityRules</literal>
- which it uses to evaluate permission checks. This is configured in <literal>components.xml</literal> as follows:
- </para>
-
- <programlisting role="XML"><![CDATA[<components xmlns="http://jboss.com/products/seam/components"
- xmlns:core="http://jboss.com/products/seam/core"
- xmlns:security="http://jboss.com/products/seam/security"
- xmlns:drools="http://jboss.com/products/seam/drools"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation=
- "http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.1.xsd
- http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.1.xsd
- http://jboss.com/products/seam/drools http://jboss.com/products/seam/drools-2.1.xsd"
- http://jboss.com/products/seam/security http://jboss.com/products/seam/security-2.1.xsd">
-
- <drools:rule-base name="securityRules">
- <drools:rule-files>
- <value>/META-INF/security.drl</value>
- </drools:rule-files>
- </drools:rule-base>
-
-</components>]]></programlisting>
-
- <para>
- Once the <literal>RuleBase</literal> component is configured, it's time to write the security rules.
- </para>
- </sect2>
-
- <sect2>
- <title>Creating a security rules file</title>
- <para>
- For this step you need to create a file called <literal>security.drl</literal> in the
- <literal>/META-INF</literal> directory of your application's jar file. In actual fact this file can be called
- anything you want, and exist in any location as long as it is configured appropriately in
- <literal>components.xml</literal>.
- </para>
-
- <para>
- So what should the security rules file contain? At this stage it might be a good idea to at least skim
- through the JBoss Rules documentation, however to get started here's an extremely simple example:
- </para>
-
- <programlisting><![CDATA[package MyApplicationPermissions;
-
-import org.jboss.seam.security.PermissionCheck;
-import org.jboss.seam.security.Role;
-
-rule CanUserDeleteCustomers
+ <para>
+ Once the <literal>RuleBase</literal> component is configured, it's time to write the security rules.
+ </para>
+ </sect3>
+
+ <sect3>
+ <title>Writing Security Rules</title>
+
+ <para>
+ The first step to writing security rules is to create a new rule file in the <literal>/META-INF</literal>
+ directory of your application's jar file. Usually this file would be named something like
+ <literal>security.drl</literal>, however you can name it whatever you like as long as it is configured
+ correspondingly in <literal>components.xml</literal>.
+ </para>
+
+ <para>
+ So what should the security rules file contain? At this stage it might be a good idea to at least skim
+ through the Drools documentation, however to get started here's an extremely simple example:
+ </para>
+
+ <programlisting><![CDATA[package MyApplicationPermissions;
+
+ import org.jboss.seam.security.permission.PermissionCheck;
+ import org.jboss.seam.security.Role;
+
+ rule CanUserDeleteCustomers
+ when
+ c: PermissionCheck(target == "customer", action == "delete")
+ Role(name == "admin")
+ then
+ c.grant();
+ end]]></programlisting>
+
+ <para>
+ Let's break this down step by step. The first thing we see is the package declaration. A package in Drools
+ is essentially a collection of rules. The package name can be anything you want - it doesn't relate
+ to anything else outside the scope of the rule base.
+ </para>
+
+ <para>
+ The next thing we can notice is a couple of import statements for the <literal>PermissionCheck</literal>
+ and <literal>Role</literal> classes. These imports inform the rules engine that we'll be referencing
+ these classes within our rules.
+ </para>
+
+ <para>
+ Finally we have the code for the rule. Each rule within a package should be given a unique name (usually
+ describing the purpose of the rule). In this case our rule is called <literal>CanUserDeleteCustomers</literal>
+ and will be used to check whether a user is allowed to delete a customer record.
+ </para>
+
+ <para>
+ Looking at the body of the rule definition we can notice two distinct sections. Rules have what is known
+ as a left hand side (LHS) and a right hand side (RHS). The LHS consists of the conditional part of the
+ rule, i.e. a list of conditions which must be satisfied for the rule to fire. The LHS is represented by
+ the <literal>when</literal> section. The RHS is the consequence, or action section of the rule that will
+ only be fired if all of the conditions in the LHS are met. The RHS is represented by the
+ <literal>then</literal> section. The end of the rule is denoted by the <literal>end</literal> line.
+ </para>
+
+ <para>
+ If we look at the LHS of the rule, we see two conditions listed there. Let's examine the first condition:
+ </para>
+
+ <programlisting><![CDATA[c: PermissionCheck(target == "customer", action == "delete")]]></programlisting>
+
+ <para>
+ In plain english, this condition is stating that there must exist a <literal>PermissionCheck</literal> object
+ with a <literal>target</literal> property equal to "customer", and an <literal>action</literal> property equal
+ to "delete" within the working memory.
+ </para>
+
+ <para>
+ So what is the working memory? Also known as a "stateful session" in Drools terminology, the working memory
+ is a session-scoped object that contains the contextual information that is required by the rules engine to
+ make a decision about a permission check. Each time the <literal>hasPermission()</literal> method is called,
+ a temporary <literal>PermissionCheck</literal> object, or <emphasis>Fact</emphasis>, is inserted into the
+ working memory. This <literal>PermissionCheck</literal> corresponds exactly to the permission that is being
+ checked, so for example if you call <literal>hasPermission("account", "create")</literal> then a
+ <literal>PermissionCheck</literal> object with a <literal>target</literal> equal to "account" and
+ <literal>action</literal> equal to "create" will be inserted into the working memory for the duration of the
+ permission check.
+ </para>
+
+ <para>
+ Besides the <literal>PermissionCheck</literal> facts, there is also a <literal>org.jboss.seam.security.Role</literal>
+ fact for each of the roles that the authenticated user is a member of. These <literal>Role</literal> facts
+ are synchronized with the user's authenticated roles at the beginning of every permission check. As a consequence,
+ any <literal>Role</literal> object that is inserted into the working memory during the course of a permission
+ check will be removed before the next permission check occurs, if the authenticated user is not actually a member of
+ that role. Besides the <literal>PermissionCheck</literal> and <literal>Role</literal> facts, the working
+ memory also contains the <literal>java.security.Principal</literal> object that was created as a result of
+ the authentication process.
+ </para>
+
+ <para>
+ It is also possible to insert additional long-lived facts into the working memory by calling
+ <literal>RuleBasedPermissionResolver.instance().getSecurityContext().insert()</literal>,
+ passing the object as a parameter. The exception to this is <literal>Role</literal> objects, which as
+ already discussed are synchronized at the start of each permission check.
+ </para>
+
+ <para>
+ Getting back to our simple example, we can also notice that the first line of our LHS is prefixed with
+ <literal>c:</literal>. This is a variable binding, and is used to refer back to the object that is
+ matched by the condition (in this case, the <literal>PermissionCheck</literal>). Moving on to the
+ second line of our LHS, we see this:
+ </para>
+
+ <programlisting><![CDATA[Role(name == "admin")]]></programlisting>
+
+ <para>
+ This condition simply states that there must be a <literal>Role</literal> object with a
+ <literal>name</literal> of "admin" within the working memory. As already mentioned, user roles are inserted into
+ the working memory at the beginning of each permission check. So, putting both conditions together, this
+ rule is essentially saying "I will fire if you are checking for the <literal>customer:delete</literal>
+ permission and the user is a member of the <literal>admin</literal> role".
+ </para>
+
+ <para>
+ So what is the consequence of the rule firing? Let's take a look at the RHS of the rule:
+ </para>
+
+ <programlisting><![CDATA[c.grant()]]></programlisting>
+
+ <para>
+ The RHS consists of Java code, and in this case is invoking the <literal>grant()</literal>
+ method of the <literal>c</literal> object, which as already mentioned is a variable binding
+ for the <literal>PermissionCheck</literal> object. Besides the <literal>name</literal> and
+ <literal>action</literal> properties of the <literal>PermissionCheck</literal> object, there
+ is also a <literal>granted</literal> property which is initially set to <literal>false</literal>.
+ Calling <literal>grant()</literal> on a <literal>PermissionCheck</literal> sets the
+ <literal>granted</literal> property to <literal>true</literal>, which means that the permission
+ check was successful, allowing the user to carry out whatever action the permission check was
+ intended for.
+ </para>
+ </sect3>
+
+ <sect3>
+ <title>Non-String permission targets</title>
+
+ <para>
+ So far we have only seen permission checks for String-literal permission targets. It is of course also
+ possible to write security rules for permission targets of more complex types. For example, let's say that you wish
+ to write a security rule to allow your users to create blog comments. The following rule demonstrates
+ how this may be expressed, by requiring the target of the permission check to be an instance of
+ <literal>MemberBlog</literal>, and also requiring that the currently authenticated user is a member of the
+ <literal>user</literal> role:
+ </para>
+
+ <programlisting><![CDATA[rule CanCreateBlogComment
+ no-loop
+ activation-group "permissions"
when
- c: PermissionCheck(name == "customer", action == "delete")
- Role(name == "admin")
+ blog: MemberBlog()
+ check: PermissionCheck(target == blog, action == "create", granted == false)
+ Role(name == "user")
then
- c.grant();
-end;]]></programlisting>
-
- <para>
- Let's break this down. The first thing we see is the package declaration. A package in JBoss Rules
- is essentially a collection of rules. The package name can be anything you want - it doesn't relate
- to anything else outside the scope of the rule base.
- </para>
-
- <para>
- The next thing we can notice is a couple of import statements for the <literal>PermissionCheck</literal>
- and <literal>Role</literal> classes. These imports inform the rules engine that we'll be referencing
- these classes within our rules.
- </para>
-
- <para>
- Finally we have the code for the rule. Each rule within a package should be given a unique name (usually
- describing the purpose of the rule). In this case our rule is called <literal>CanUserDeleteCustomers</literal>
- and will be used to check whether a user is allowed to delete a customer record.
- </para>
-
- <para>
- Looking at the body of the rule definition we can notice two distinct sections. Rules have what is known
- as a left hand side (LHS) and a right hand side (RHS). The LHS consists of the conditional part of the
- rule, i.e. a list of conditions which must be satisfied for the rule to fire. The LHS is represented by
- the <literal>when</literal> section. The RHS is the consequence, or action section of the rule that will
- only be fired if all of the conditions in the LHS are met. The RHS is represented by the
- <literal>then</literal> section. The end of the rule is denoted by the <literal>end;</literal> line.
- </para>
-
- <para>
- If we look at the LHS of the rule, we see two conditions listed there. Let's examine the first condition:
- </para>
-
- <programlisting><![CDATA[c: PermissionCheck(name == "customer", action == "delete")]]></programlisting>
-
- <para>
- In plain english, this condition is stating that there must exist a <literal>PermissionCheck</literal> object
- with a <literal>name</literal> property equal to "customer", and an <literal>action</literal> property equal
- to "delete" within the working memory.
- </para>
-
- <para>
- So what is the working memory? Also known as a "stateful session" in Drools terminology, the working memory
- is a session-scoped object that contains the contextual information that is required by the rules engine to
- make a decision about a permission check. Each time the <literal>hasPermission()</literal> method is called,
- a temporary <literal>PermissionCheck</literal> object, or <emphasis>Fact</emphasis>, is inserted into the
- working memory. This <literal>PermissionCheck</literal> corresponds exactly to the permission that is being
- checked, so for example if you call <literal>hasPermission("account", "create", null)</literal> then a
- <literal>PermissionCheck</literal> object with a <literal>name</literal> equal to "account" and
- <literal>action</literal> equal to "create" will be inserted into the working memory for the duration of the
- permission check.
- </para>
+ check.grant();
+end
+]]></programlisting>
- <para>
- Besides the <literal>PermissionCheck</literal> facts, there is also a <literal>org.jboss.seam.security.Role</literal>
- fact for each of the roles that the authenticated user is a member of. These <literal>Role</literal> facts
- are synchronized with the user's authenticated roles at the beginning of every permission check. As a consequence,
- any <literal>Role</literal> object that is inserted into the working memory during the course of a permission
- check will be removed before the next permission check occurs, if the authenticated user is not a member of
- that role. Besides the <literal>PermissionCheck</literal> and <literal>Role</literal> facts, the working
- memory also contains the <literal>java.security.Principal</literal> object that was created during
- the authentication process.
- </para>
-
- <para>
- It is also possible to insert additional long-lived facts into the working memory by calling
- <literal>((RuleBasedIdentity) RuleBasedIdentity.instance()).getSecurityContext().insert()</literal>,
- passing the object as a parameter. The exception to this is <literal>Role</literal> objects, which as
- already discussed are synchronized at the start of each permission check.
- </para>
+ </sect3>
- <para>
- Getting back to our simple example, we can also notice that the first line of our LHS is prefixed with
- <literal>c:</literal>. This is a variable binding, and is used to refer back to the object that is
- matched by the condition. Moving onto the second line of our LHS, we see this:
- </para>
-
- <programlisting><![CDATA[Role(name == "admin")]]></programlisting>
-
- <para>
- This condition simply states that there must be a <literal>Role</literal> object with a
- <literal>name</literal> of "admin" within the working memory. As mentioned, user roles are inserted into
- the working memory at the beginning of each permission check. So, putting both conditions together, this
- rule is essentially saying "I will fire if you are checking for the <literal>customer:delete</literal>
- permission and the user is a member of the <literal>admin</literal> role".
- </para>
-
- <para>
- So what is the consequence of the rule firing? Let's take a look at the RHS of the rule:
- </para>
-
- <programlisting><![CDATA[c.grant()]]></programlisting>
-
- <para>
- The RHS consists of Java code, and in this case is invoking the <literal>grant()</literal>
- method of the <literal>c</literal> object, which as already mentioned is a variable binding
- for the <literal>PermissionCheck</literal> object. Besides the <literal>name</literal> and
- <literal>action</literal> properties of the <literal>PermissionCheck</literal> object, there
- is also a <literal>granted</literal> property which is initially set to <literal>false</literal>.
- Calling <literal>grant()</literal> on a <literal>PermissionCheck</literal> sets the
- <literal>granted</literal> property to <literal>true</literal>, which means that the permission
- check was successful, allowing the user to carry out whatever action the permission check was
- intended for.
- </para>
-
<sect3>
<title>Wildcard permission checks</title>
<para>
It is possible to implement a wildcard permission check (which allows all actions for a given permission
- name), by omitting the <literal>action</literal> constraint for the <literal>PermissionCheck</literal> in
+ target), by omitting the <literal>action</literal> constraint for the <literal>PermissionCheck</literal> in
your rule, like this:
</para>
<programlisting><![CDATA[rule CanDoAnythingToCustomersIfYouAreAnAdmin
when
- c: PermissionCheck(name == "customer")
+ c: PermissionCheck(target == "customer")
Role(name == "admin")
then
c.grant();
@@ -3437,12 +3456,649 @@
<para>
This rule allows users with the <literal>admin</literal> role to perform <emphasis>any</emphasis> action for
any <literal>customer</literal> permission check.
+ </para>
+
+ </sect3>
+
+ </sect2>
+
+ <sect2>
+ <title>PersistentPermissionResolver</title>
+
+ <para>
+ Another built-in permission resolver provided by Seam, <literal>PersistentPermissionResolver</literal>
+ allows permissions to be loaded from persistent storage, such as a relational database. This permission
+ resolver provides ACL style instance-based security, allowing for specific object permissions to be assigned
+ to individual users and roles. It also allows for persistent, arbitrarily-named permission targets (not
+ necessarily object/class based) to be assigned in the same way.
+ </para>
+
+ <sect3>
+ <title>Configuration</title>
+
+ <para>
+ Before it can be used, <literal>PersistentPermissionResolver</literal> must be configured with a
+ valid <literal>PermissionStore</literal> in <literal>components.xml</literal>. If not configured,
+ it will attempt to use the default permission store, <literal>JpaIdentityStore</literal> (see section
+ further down for details). To use a permission store other than the default, configure the
+ <literal>permission-store</literal> property as follows:
+ </para>
+
+ <programlisting><![CDATA[ <security:persistent-permission-resolver permission-store="#{myCustomPermissionStore}"/>]]></programlisting>
+
+ </sect3>
+
+ <sect3>
+ <title>Permission Stores</title>
+
+ <para>
+ A permission store is required for <literal>PersistentPermissionResolver</literal> to connect to the
+ backend storage where permissions are persisted. Seam provides one <literal>PermissionStore</literal>
+ implementation out of the box, <literal>JpaPermissionStore</literal>, which is used to store
+ permissions inside a relational database. It is possible to write your own permission store
+ by implementing the <literal>PermissionStore</literal> interface, which defines the following
+ methods:
</para>
+
+ <table>
+ <title>PermissionStore interface</title>
+
+ <tgroup cols="2">
+ <colspec colnum="1" colwidth="2*" />
+ <colspec colnum="2" colwidth="3*" />
+ <colspec colnum="3" colwidth="3*" />
+
+ <thead>
+ <row>
+ <entry align="center">
+ <para>Return type</para>
+ </entry>
+ <entry align="center">
+ <para>Method</para>
+ </entry>
+ <entry align="center">
+ <para>Description</para>
+ </entry>
+ </row>
+ </thead>
+
+ <tbody>
+
+ <row>
+ <entry>
+ <para>
+ <literal>List<Permission></literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>listPermissions(Object target)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This method should return a <literal>List</literal> of <literal>Permission</literal> objects
+ representing all the permissions granted for the specified target object.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>List<Permission></literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>listPermissions(Object target, String action)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This method should return a <literal>List</literal> of <literal>Permission</literal> objects
+ representing all the permissions with the specified action, granted for the specified target object.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>List<Permission></literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>listPermissions(Set<Object> targets, String action)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This method should return a <literal>List</literal> of <literal>Permission</literal> objects
+ representing all the permissions with the specified action, granted for the specified set of
+ target objects.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>boolean</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>grantPermission(Permission)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This method should persist the specified <literal>Permission</literal> object to the backend
+ storage, returning true if successful.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>boolean</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>grantPermissions(List<Permission> permissions)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This method should persist all of the <literal>Permission</literal> objects contained in the
+ specified <literal>List</literal>, returning true if successful.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>boolean</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>revokePermission(Permission permission)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This method should remove the specified <literal>Permission</literal> object from persistent storage.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>boolean</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>revokePermissions(List<Permission> permissions)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This method should remove all of the <literal>Permission</literal> objects in the specified list
+ from persistent storage.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>List<String></literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>listAvailableActions(Object target)</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This method should return a list of all the available actions (as Strings) for the class of the
+ specified target object. It is used in conjunction with permission management to build the user
+ interface for granting specific class permissions (see section further down).
+ </para>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
</sect3>
+
+ <sect3>
+ <title>JpaPermissionStore</title>
+
+ <para>
+ This is the default <literal>PermissionStore</literal> implementation (and the only one provided by Seam), which
+ uses a relational database to store permissions. Before it can be used it must be configured with either one or
+ two entity classes for storing user and role permissions. These entity classes must be annotated with a special
+ set of security annotations to configure which properties of the entity correspond to various aspects of the
+ permissions being stored.
+ </para>
+
+ <para>
+ If you wish to use the same entity (i.e. a single database table) to store both user and role permissions, then
+ only the <literal>user-permission-class</literal> property is required to be configured. If you wish to use
+ separate tables for storing user and role permissions, then in addition to the <literal>user-permission-class</literal>
+ property you must also configure the <literal>role-permission-class</literal> property.
+ </para>
+
+ <para>For example, to configure a single entity class to store both user and role permissions:</para>
+
+ <programlisting><![CDATA[ <security:jpa-permission-store user-permission-class="com.acme.model.AccountPermission"/>]]></programlisting>
+
+ <para>To configure separate entity classes for storing user and role permissions:</para>
+
+ <programlisting><![CDATA[ <security:jpa-permission-store user-permission-class="com.acme.model.UserPermission"
+ role-permission-class="com.acme.model.RolePermission"/>]]></programlisting>
+
+ <sect4>
+ <title>Permission annotations</title>
+
+ <para>
+ As mentioned, the entity classes that contain the user and role permissions must be configured with a
+ special set of annotations, contained within the <literal>org.jboss.seam.annotations.security.permission</literal> package.
+ The following table lists each of these annotations along with a description of how they are used:
+ </para>
+
+ <table>
+ <title>Entity Permission annotations</title>
+
+ <tgroup cols="2">
+ <colspec colnum="1" colwidth="2*" />
+ <colspec colnum="2" colwidth="3*" />
+ <colspec colnum="3" colwidth="3*" />
+
+ <thead>
+ <row>
+ <entry align="center">
+ <para>Annotation</para>
+ </entry>
+ <entry align="center">
+ <para>Target</para>
+ </entry>
+ <entry align="center">
+ <para>Description</para>
+ </entry>
+ </row>
+ </thead>
+
+ <tbody>
+
+ <row>
+ <entry>
+ <para>
+ <literal>@PermissionTarget</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>FIELD,METHOD</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This annotation identifies the property of the entity that will contain the permission target. The property
+ should be of type <literal>java.lang.String</literal>.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>@PermissionAction</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>FIELD,METHOD</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This annotation identifies the property of the entity that will contain the permission action. The property
+ should be of type <literal>java.lang.String</literal>.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>@PermissionUser</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>FIELD,METHOD</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This annotation identifies the property of the entity that will contain the recipient user for the permission. It should
+ be of type <literal>java.lang.String</literal> and contain the user's username.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>@PermissionRole</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>FIELD,METHOD</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This annotation identifies the property of the entity that will contain the recipient role for the permission. It should
+ be of type <literal>java.lang.String</literal> and contain the role name.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>@PermissionDiscriminator</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>FIELD,METHOD</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This annotation should be used when the same entity/table is used to store both user and role permissions. It identifies
+ the property of the entity that is used to discriminate between user and role permissions. By default, if the column
+ value contains the string literal <literal>user</literal>, then the record will be treated as a user permission. If it
+ contains the string literal <literal>role</literal>, then it will be treated as a role permission. It is also possible
+ to override these defaults by specifying the <literal>userValue</literal> and <literal>roleValue</literal> properties
+ within the annotation. For example, to use <literal>u</literal> and <literal>r</literal> instead of <literal>user</literal>
+ and <literal>role</literal>, the annotation would be written like this:
+ </para>
+
+ <programlisting><![CDATA[ @PermissionDiscriminator(userValue = "u", roleValue = "r")]]></programlisting>
+ </entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect4>
+
+ <sect4>
+ <title>Example Entity</title>
+
+ <para>
+ Here is an example of an entity class that is used to store both user and role permissions. The following class can be found
+ inside the SeamSpace example:
+ </para>
+
+ <programlisting><![CDATA[
+ at Entity
+public class AccountPermission implements Serializable {
+ private Integer permissionId;
+ private String recipient;
+ private String target;
+ private String action;
+ private String discriminator;
+
+ @Id @GeneratedValue
+ public Integer getPermissionId() {
+ return permissionId;
+ }
+
+ public void setPermissionId(Integer permissionId) {
+ this.permissionId = permissionId;
+ }
+
+ @PermissionUser @PermissionRole
+ public String getRecipient() {
+ return recipient;
+ }
+
+ public void setRecipient(String recipient) {
+ this.recipient = recipient;
+ }
+
+ @PermissionTarget
+ public String getTarget() {
+ return target;
+ }
+
+ public void setTarget(String target) {
+ this.target = target;
+ }
+
+ @PermissionAction
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ @PermissionDiscriminator
+ public String getDiscriminator() {
+ return discriminator;
+ }
+
+ public void setDiscriminator(String discriminator) {
+ this.discriminator = discriminator;
+ }
+}
+ ]]></programlisting>
+
+ <para>
+ As can be seen in the above example, the <literal>getDiscriminator()</literal> method has been annotated
+ with the <literal>@PermissionDiscriminator</literal> annotation, to allow <literal>JpaPermissionStore</literal> to
+ determine which records represent user permissions and which represent role permissions. In addition, it
+ can also be seen that the <literal>getRecipient()</literal> method is annotated with both
+ <literal>@PermissionUser</literal> and <literal>@PermissionRole</literal> annotations. This is perfectly valid,
+ and simply means that the <literal>recipient</literal> property of the entity will either contain the name
+ of the user or the name of the role, depending on the value of the <literal>discriminator</literal> property.
+ </para>
+
+ </sect4>
+
+ <sect4>
+ <title>Class-specific Permission Configuration</title>
+
+ <para>
+ A further set of class-specific annotations can be used to configure a specific set of allowable permissions
+ for a target class. These permissions can be found in the <literal>org.jboss.seam.annotation.security.permission</literal>
+ package:
+ </para>
+
+ <table>
+ <title>Class Permission Annotations</title>
+
+ <tgroup cols="2">
+ <colspec colnum="1" colwidth="2*" />
+ <colspec colnum="2" colwidth="3*" />
+ <colspec colnum="3" colwidth="3*" />
+
+ <thead>
+ <row>
+ <entry align="center">
+ <para>Annotation</para>
+ </entry>
+ <entry align="center">
+ <para>Target</para>
+ </entry>
+ <entry align="center">
+ <para>Description</para>
+ </entry>
+ </row>
+ </thead>
+
+ <tbody>
+
+ <row>
+ <entry>
+ <para>
+ <literal>@Permissions</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>TYPE</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ A container annotation, this annotation may contain an array of <literal>@Permission</literal> annotations.
+ </para>
+ </entry>
+ </row>
+
+ <row>
+ <entry>
+ <para>
+ <literal>@Permission</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ <literal>TYPE</literal>
+ </para>
+ </entry>
+ <entry>
+ <para>
+ This annotation defines a single allowable permission action for the target class. Its <literal>action</literal>
+ property must be specified, and an optional <literal>mask</literal> property may also be specified if permission
+ actions are to be persisted as bitmasked values (see next section).
+ </para>
+ </entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Here's an example of the above annotations in action. The following class can also be found in the SeamSpace example:
+ </para>
+
+ <programlisting><![CDATA[@Permissions({
+ @Permission(action = "view"),
+ @Permission(action = "comment")
+})
+ at Entity
+public class MemberImage implements Serializable {]]></programlisting>
+ <para>
+ This example demonstrates how two allowable permission actions, <literal>view</literal> and <literal>comment</literal>
+ can be declared for the entity class <literal>MemberImage</literal>.
+ </para>
+
+ </sect4>
+
+ <sect4>
+ <title>Permission masks</title>
+
+ <para>
+ By default, multiple permissions for the same target object and recipient will be persisted as a single database record,
+ with the <literal>action</literal> property/column containing a comma-separated list of the granted actions. To reduce
+ the amount of physical storage required to persist a large number of permissions, it is possible to use a bitmasked
+ integer value (instead of a comma-separated list) to store the list of actions.
+ </para>
+
+ <para>
+ For example, if recipient "Bob" is granted both the <literal>view</literal> and <literal>comment</literal> permissions
+ for a particular <literal>MemberImage</literal> instance, then by default the <literal>action</literal> property of the
+ permission entity will contain "<literal>view,comment</literal>", representing the two granted permission actions.
+ Alternatively, if using bitmasked values for the permission actions, as defined like so:
+ </para>
+
+ <programlisting><![CDATA[@Permissions({
+ @Permission(action = "view", mask = 1),
+ @Permission(action = "comment", mask = 2)
+})
+ at Entity
+public class MemberImage implements Serializable {]]></programlisting>
+
+ <para>
+ The <literal>action</literal> property will instead simply contain "3" (with both the 1 bit and 2 bit switched on). Obviously
+ for a large number of allowable actions for any particular target class, the storage required for the permission records
+ is greatly reduced by using bitmasked actions.
+ </para>
+ </sect4>
+
+ <sect4>
+ <title>Identifier Policy</title>
+
+ <para>
+ When storing or looking up permissions, <literal>JpaPermissionStore</literal> must be able to uniquely identify specific
+ object instances to effectively operate on its permissions. To achieve this, an <emphasis>identifier strategy</emphasis>
+ may be assigned to each target class for the generation of unique identifier values. Each identifier strategy
+ implementation knows how to generate unique identifiers for a particular type of class, and it is a simple matter to
+ create new identifier strategies.
+ </para>
+
+ <para>
+ The <literal>IdentifierStrategy</literal> interface is very simple, declaring only two methods:
+ </para>
+
+ <programlisting><![CDATA[public interface IdentifierStrategy {
+ boolean canIdentify(Class targetClass);
+ String getIdentifier(Object target);
+}]]></programlisting>
+
+ <para>
+ The first method, <literal>canIdentify()</literal> simply returns <literal>true</literal> if the identifier strategy
+ is capable of generating a unique identifier for the specified target class. The second method,
+ <literal>getIdentifier()</literal> returns the unique identifier value for the specified target object.
+ </para>
+ </sect4>
+
+ <sect4>
+ <title>Overview</title>
+
+ <para>
+ So how does persistent permission data actually look when stored in the database?
+ </para>
+ </sect4>
+ </sect3>
+
</sect2>
</sect1>
+
+ <sect1>
+ <title>Permission Management</title>
+
+ <para>
+ In much the same way that Seam Security provides an Identity Management API for the management of users and roles,
+ it also provides a Permissions Management API for the management of persistent user permissions.
+ </para>
+
+ </sect1>
<sect1>
<title>SSL Security</title>
More information about the seam-commits
mailing list