[jboss-cvs] jboss-seam/doc/reference/en/modules ...
Shane Bryzak
Shane_Bryzak at symantec.com
Mon Jan 29 22:32:46 EST 2007
User: sbryzak2
Date: 07/01/29 22:32:45
Modified: doc/reference/en/modules security.xml
Log:
documented component security
Revision Changes Path
1.9 +232 -73 jboss-seam/doc/reference/en/modules/security.xml
(In the diff below, changes in quantity of whitespace are not shown.)
Index: security.xml
===================================================================
RCS file: /cvsroot/jboss/jboss-seam/doc/reference/en/modules/security.xml,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -b -r1.8 -r1.9
--- security.xml 29 Jan 2007 12:49:09 -0000 1.8
+++ security.xml 30 Jan 2007 03:32:45 -0000 1.9
@@ -38,8 +38,9 @@
</programlisting>
<para>
- The EL expression <literal>#{authenticator.authenticate}</literal> is a method binding indicating the
- <literal>authenticate</literal> method of the <literal>authenticator</literal> component.
+ The EL expression <literal>#{authenticator.authenticate}</literal> is a method binding indicating that
+ the <literal>authenticate</literal> method of the <literal>authenticator</literal> component will be used
+ to authenticate the user.
</para>
</sect2>
@@ -48,8 +49,9 @@
<title>Writing an authentication method</title>
<para>
- The <literal>authMethod</literal> property in <literal>components.xml</literal> specifies which method will be
- used by <literal>SeamLoginModule</literal> to authenticate users. The prototype for this method is expected to be:
+ The <literal>authMethod</literal> property specified for <literal>Identity</literal> in
+ <literal>components.xml</literal> specifies which method will be used by <literal>SeamLoginModule</literal>
+ to authenticate users. The prototype for this method is expected to be:
</para>
<programlisting>
@@ -73,15 +75,15 @@
public boolean authenticate(String username, String password, Set<String> roles) {
try
{
- authenticatedUser = (User) entityManager.createQuery(
+ User user = (User) entityManager.createQuery(
"from User where username = :username and password = :password")
.setParameter("username", username)
.setParameter("password", password)
.getSingleResult();
- if (authenticatedUser.getRoles() != null)
+ if (user.getRoles() != null)
{
- for (UserRole mr : authenticatedUser.getRoles())
+ for (UserRole mr : user.getRoles())
roles.add(mr.getName());
}
@@ -100,14 +102,14 @@
In the above example, both <literal>User</literal> and <literal>UserRole</literal> are application-specific
entity beans. The <literal>roles</literal> parameter is populated with the roles that the user is a member
of, which should be added to the <literal>Set</literal> as literal string values, e.g. "admin", "user".
- If the user record is not found and a <literal>NoResultException</literal> thrown, the authentication method
- returns <literal>false</literal> to indicate the authentication failed.
+ In this case, if the user record is not found and a <literal>NoResultException</literal> thrown, the
+ authentication method returns <literal>false</literal> to indicate the authentication failed.
</para>
</sect2>
<sect2>
- <title>Writing a login page</title>
+ <title>Writing a login form</title>
<para>
The <literal>Identity</literal> component provides both <literal>username</literal> and <literal>password</literal>
@@ -135,6 +137,32 @@
</programlisting>
</sect2>
+ <sect2>
+ <title>Summary</title>
+ <para>
+ So to sum up, here are the steps for configuring authentication:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ Configure an authentication method in <literal>components.xml</literal>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Write an authentication method.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Write a login form so that the user can authenticate.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ </sect2>
+
</sect1>
<sect1>
@@ -143,7 +171,9 @@
<para>
The <literal>Identity</literal> component is the core component of the Security API. It is a session-scoped
component that 1) contains the current state of the user's security, and 2) provides a number of useful
- security-related methods. This section details the most useful features of the <literal>Identity</literal> component.
+ security-related methods. This section details the most useful features of the <literal>Identity</literal>
+ component. Please note that the alias <literal>identity</literal> can be used in EL expressions, including
+ the body of a web page.
</para>
<sect2>
@@ -167,8 +197,14 @@
<para>
The <literal>isLoggedIn()</literal> method returns a boolean value indicating whether the user has been
successfully authenticated or not. It may be useful for controlling the rendering of certain sections of a page,
- depending if the user is logged in or not.
+ depending if the user is logged in or not. For example:
</para>
+
+ <programlisting>
+ <![CDATA[
+ <h:outputLink value="login.seam" rendered="#{not identity.loggedIn}">Log in</h:outputLink>
+ ]]>
+ </programlisting>
</sect2>
<sect2>
@@ -177,21 +213,12 @@
The <literal>getSubject()</literal> method returns the <literal>javax.security.auth.Subject</literal> instance
for the user's session. The subject contains all of an authenticated user's principals. If
you are authenticating with your own <literal>LoginContext</literal>, it should be instantiated with the
- <literal>identity</literal>'s subject.
- </para>
- </sect2>
-
- <sect2>
- <title>isUserInRole()</title>
- <para>
- This method accepts a <literal>java.lang.String</literal> parameter and returns a <literal>boolean</literal>
- indicating whether the user is a member of the specified role. Here's an example:
+ <literal>identity</literal>'s subject, for example:
</para>
<programlisting>
<![CDATA[
- if (!Identity.instance().isUserInRole("admin"))
- throw new AuthorizationException("Insufficient privileges");
+ LoginContext ctx = new LoginContext("custom", Identity.instance().getSubject());
]]>
</programlisting>
</sect2>
@@ -222,8 +249,8 @@
This method attempts to authenticate using the set values of the <literal>username</literal> and
<literal>password</literal> properties. An overloaded version of this method,
<literal>login(LoginContext ctx)</literal> authenticates using an externally provided
- <literal>LoginContext</literal>. This method throws a <literal>LoginException</literal> if authentication
- is not successful.
+ <literal>LoginContext</literal>. This method throws a <literal>LoginException</literal>
+ if authentication is not successful.
</para>
</sect2>
@@ -234,74 +261,206 @@
</para>
</sect2>
+ <sect2>
+ <title>hasRole()</title>
+ <para>
+ Returns <literal>true</literal> if the authenticated user is a member of the specified role.
+ </para>
+
+ <programlisting>
+ <![CDATA[
+ if (!Identity.instance().hasRole("admin"))
+ throw new AuthorizationException("Insufficient privileges");
+ ]]>
+ </programlisting>
+ </sect2>
+
+ <sect2>
+ <title>hasPermission()</title>
+ <para>
+ Returns <literal>true</literal> if the user (either authenticated or not) has the specified permission.
+ This method accepts an object that can be used to determine if the permission should be granted or not.
+ The object is temporarily asserted into the security context while the permission check is performed.
+ If the object is a <literal>java.util.Collection</literal> then each object in the collection is asserted.
+ </para>
+
+ <programlisting>
+ <![CDATA[
+ if (!Identity.instance().hasPermission("customer", "delete", customer))
+ throw new AuthorizationException("Insufficient privileges");
+ ]]>
+ </programlisting>
+ </sect2>
+
+ <sect2>
+ <title>getSecurityContext()</title>
+ <para>
+ Returns the session-scoped <literal>org.drools.WorkingMemory</literal> used to evaluate security rules
+ within the context of the current user. Additional objects may be asserted into the working memory if
+ required by the security rules.
+ </para>
+ </sect2>
+
</sect1>
<sect1>
<title>Authorization</title>
<para>
- The authorization features of the Seam security API make it possible to restrict access to a Seam
- component based on the roles and permissions granted to the authenticated user. Security restrictions
- are defined using EL expressions, and configured by annotating either a Seam component method, or the component
- class itself, with the <literal>@Restrict</literal> annotation.
+ There are a number of authorization features provided by the Seam Security API for securing access to
+ components, component methods, and pages. This section describes each of these.
</para>
<sect2>
- <title>Types of authorization checks</title>
+ <title>Core concepts</title>
+ <para>
+ Each of the authorization mechanisms provided by the Seam Security API are built upon the concept of a user
+ being granted roles and/or permissions. A role is a <emphasis>group</emphasis>, or <emphasis>type</emphasis>,
+ of user that may have been granted certain privileges for performing one or more specific actions within an
+ application. A permission on the other hand is a privilege (sometimes once-off) for performing a single,
+ specific action. It is entirely possible to build an application using nothing but permissions, however
+ roles offer a higher level of convenience when granting privileges to groups of users.
+ </para>
<para>
- The Seam security API provides two types of authorization checks; role checks and permission checks. Role
- checks are simple checks to determine if a user is a member of a specific role. They are equivalent in
- function to the <literal>isUserInRole()</literal> method found within the servlet specification. Role checks
- can be performed by using the <literal>s:hasRole()</literal> EL function. Here's a few examples of role checks.
+ Roles are simple, consisting of only a name such as "admin", "user", "customer", etc. Permissions consist of
+ both a name and an action, and are represented within this documentation in the form <literal>name:action</literal>,
+ for example <literal>customer:delete</literal>, or <literal>customer:insert</literal>.
</para>
+ </sect2>
+
+ <sect2>
+ <title>Securing your components</title>
<para>
- This first example demonstrates how to restrict access to the Seam component <literal>secureAction</literal>
- to all users besides those in the <literal>admin</literal> role.
+ Let's start by examining the simplest form of authorization, component security, starting with the
+ <literal>@Restrict</literal> annotation.
+ </para>
+
+ <sect3>
+ <title>The @Restrict annotation</title>
+
+ <para>
+ Seam components may be secured either at the method or the class level, using the <literal>@Restrict</literal>
+ annotation. If both a method and it's declaring class are annotated with <literal>@Restrict</literal>,
+ the method restriction will take precendence (and the class restriction will not apply). If a method
+ invocation fails a security check, then an exception will be thrown as per the contract for
+ <literal>Identity.checkRestriction()</literal>.
+ </para>
+
+ <para>
+ An empty <literal>@Restrict</literal> implies a permission check of <literal>componentName:methodName</literal>.
+ Take for example the following component method:
</para>
<programlisting>
<![CDATA[
- at Name("secureAction")
- at Restrict("#{s:hasRole('admin')}")
-public class SecureAction {
- // ...
-}
+ @Name("account")
+ public class AccountAction {
+ @Restrict public void delete() {
+ // code
+ }
+ }
]]>
</programlisting>
<para>
- This example demonstrates how to restrict access to a method of a Seam component to users in the
- <literal>superuser</literal> role.
+ In this example, the implied permission required to call the <literal>delete()</literal> method is
+ <literal>account:delete</literal>. The equivalent of this would be to write
+ <literal>@Restrict("#{s:hasPermission('account','delete',null)}")</literal>. Now let's look at
+ another example:
</para>
<programlisting>
<![CDATA[
- @Restrict("#{s:hasRole('superuser')}")
- public void secureMethod {
- // ...
+ @Restrict @Name("account")
+ public class AccountAction {
+ public void insert() {
+ // code
+ }
+ @Restrict("#{s:hasRole('admin')}") public void delete() {
+ // code
+ }
}
]]>
</programlisting>
<para>
- This last example shows how an inline security check can be performed within the body of a method,
- rather than using the <literal>@Restrict</literal> annotation.
+ This time, the component class itself is annotated with <literal>@Restrict</literal>. This means that
+ any methods without an overriding <literal>@Restrict</literal> annotation require an implicit permission check.
+ In the case of this example, the <literal>insert()</literal> method requires a permission of
+ <literal>account:insert</literal>, while the <literal>delete()</literal> method requires that the user is a
+ member of the <literal>admin</literal> role.
+ </para>
+
+ <para>
+ Before we go any further, let's address the <literal>#{s:hasRole()}</literal> expression seen in the above
+ example. Both <literal>s:hasRole</literal> and <literal>s:hasPermission</literal> are EL functions, which
+ delegate to the correspondingly named methods of the <literal>Identity</literal> class. These
+ functions can be used within any EL expression throughout the entirity of the security API.
+ </para>
+
+ <para>
+ Being an EL expression, the value of the <literal>@Restrict</literal> annotation may reference objects that
+ exist within a Seam context. This is extremely useful when performing permission checks for a specific
+ object instance. Look at this example:
+ </para>
+
+ <programlisting>
+ @Name("account")
+ public class AccountAction {
+ @In Account selectedAccount;
+ @Restrict("#{s:hasPermission('account','modify',selectedAccount)}")
+ public void modify() {
+ selectedAccount.modify();
+ }
+ }
+ </programlisting>
+
+ <para>
+ The interesting thing to note from this example is the reference to <literal>selectedAccount</literal>
+ seen within the <literal>hasPermission()</literal> function call. The value of this variable will be
+ looked up from within the Seam context, and passed to the <literal>hasPermission()</literal> method
+ in <literal>Identity</literal>, which in this case can then determine if the user has the required
+ permission for modifying the specified <literal>Account</literal> object.
+ </para>
+ </sect3>
+
+ <sect3>
+ <title>Inline restrictions</title>
+ <para>
+ Sometimes it might be desirable to perform a security check in code, without using the
+ <literal>@Restrict</literal> annotation. In this situation, simply use
+ <literal>Identity.checkRestriction()</literal>, like this:
</para>
<programlisting>
<![CDATA[
- public String changeUserPassword() {
- // code here
- Identity.instance().checkRestriction("#{s:hasRole('superuser')}");
- // code here
+ public void deleteCustomer() {
+ Identity.instance().checkRestriction("#{s:hasPermission('customer','delete',selectedCustomer)}");
}
]]>
</programlisting>
+ <para>
+ If the expression specified doesn't evaluate to <literal>true</literal>, an exception is thrown. It is
+ also possible to call the <literal>hasRole</literal> and <literal>hasPermission</literal> methods
+ directly:
+ </para>
+
+ <programlisting>
+ <![CDATA[
+ if (!Identity.instance().hasRole("admin"))
+ throw new AuthorizationException("Must be admin to perform this action");
+
+ if (!Identity.instance().hasPermission("customer", "create", null))
+ throw new AuthorizationException("You may not create new customers");
+ ]]>
+ </programlisting>
+ </sect3>
</sect2>
+
</sect1>
</chapter>
More information about the jboss-cvs-commits
mailing list