[jboss-cvs] jboss-seam/doc/reference/en/modules ...
Shane Bryzak
Shane_Bryzak at symantec.com
Mon Jan 29 07:49:09 EST 2007
User: sbryzak2
Date: 07/01/29 07:49:09
Modified: doc/reference/en/modules security.xml
Log:
updated security documentation
Revision Changes Path
1.8 +147 -890 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.7
retrieving revision 1.8
diff -u -b -r1.7 -r1.8
--- security.xml 17 Jan 2007 23:56:47 -0000 1.7
+++ security.xml 29 Jan 2007 12:49:09 -0000 1.8
@@ -9,876 +9,231 @@
</para>
<sect1>
- <title>Overview</title>
-
- <para>
- The Seam security API provides the following features for securing a Seam application.
- </para>
-
- <sect2>
- <title>JAAS-based Authentication</title>
-
- <para>
- The authentication component of the security API is based on JAAS, allowing authentication to be
- carried out against one or more configurable login modules. The security API provides a login module
- that delegates authentication to a Seam component, making it easy to authenticate a user using the
- application's domain objects.
- </para>
- </sect2>
-
- <sect2>
- <title>Page Security</title>
-
- <para>
- This feature allows direct access to page resources to be controlled based on the set of roles granted
- to the requesting user. Using a servlet filter, each request to a protected resource is validated to
- ensure that the user has the necessary roles to access the resource.
- </para>
- </sect2>
-
- <sect2>
- <title>EL Integration</title>
+ <title>Authentication</title>
<para>
- The <literal>s:hasRole()</literal> and <literal>s:hasPermission()</literal> EL functions can be used
- within the pages of an application to control which parts of a page are rendered based on the security
- level of the user. On the model side, component classes and/or their methods can also be annotated with
- the very same EL expressions to restrict the execution of methods based on the current security context.
+ The authentication features provided by Seam Security are built upon JAAS (Java Authentication and Authorization Service),
+ and as such provide a robust and highly configurable API for handling user authentication. However, for less complex
+ authentication requirements Seam offers a much more simplified method of authentication that hides the complexity of JAAS.
</para>
- </sect2>
<sect2>
- <title>Rule-based Authorization</title>
-
- <para>
- By integrating with JBoss Rules, the security API is able to make security decisions by evaluating
- a set of user-defined security rules defined at deployment time. This feature allows any security
- checks to carry out a decision-making process which can be as simple or as complex as required, while
- taking into account the state of any objects asserted into the security context for the check.
- </para>
- </sect2>
-
- </sect1>
-
- <sect1>
<title>Configuration</title>
<para>
- The first step in configuring Seam Security is to install the <literal>SeamSecurityManager</literal> and
- <literal>SecurityConfiguration</literal> components, as they are not installed by default. The
- <literal>SeamSecurityManager</literal> component is the core of the Seam Security API, and is
- responsible for loading security rules, and performing role and permission checks.
- <literal>SecurityConfiguration</literal> is used to load role and page security configuration from
- a configuration file (or can be extended to load this configuration from an alternative source, such as
- a database). The following entries in <literal>components.xml</literal> will install these two components:
- </para>
-
- <programlisting>
- <![CDATA[
-<!DOCTYPE components PUBLIC "-//JBoss/Seam Component Configuration DTD 1.1//EN"
- "http://jboss.com/products/seam/components-1.1.dtd">
-
-<components>
- <!-- Install a Security Configuration -->
- <component class="org.jboss.seam.security.config.SecurityConfiguration"/>
-
- <!-- Install the Seam Security Manager -->
- <component class="org.jboss.seam.security.SeamSecurityManager"/>
-
-</components>
- ]]>
- </programlisting>
-
- <sect2>
- <title>security-config.xml</title>
-
- <para>
- The next step is to configure roles. While this step is optional (it is possible for a user to
- belong to a role even if it is not configured here), it is necessary if either 1) explicit permissions
- are required by your application, or 2) roles require membership of other roles. Create a file called
- <literal>security-config.xml</literal>, which goes in the <literal>META-INF</literal> directory of the
- Seam application's jar file. Here's an example <literal>security-config.xml</literal> file with a few
- roles defined.
- </para>
-
- <programlisting>
- <![CDATA[
-<security-config>
-
- <roles>
- <role name="admin">
- <memberships>superuser</memberships>
-
- <permissions>
- <permission name="user" action="create"/>
- <permission name="user" action="modify"/>
- <permission name="user" action="delete"/>
- </permissions>
- </role>
-
- <role name="superuser">
- <memberships>user</memberships>
-
- <permissions>
- <permission name="account" action="create"/>
- <permission name="account" action="delete"/>
- </permissions>
- </role>
-
- <role name="user">
- <permissions>
- <permission name="customer" action="create"/>
- <permission name="customer" action="delete"/>
- </permissions>
- </role>
-
- <role name="guest">
-
- </role>
-
- </roles>
-
-</security-config>
- ]]>
- </programlisting>
-
- <para>
- In the above example, we can see that there are four distinct roles: <literal>admin</literal>,
- <literal>superuser</literal>, <literal>user</literal> and <literal>guest</literal>. Strictly
- speaking, the <literal>guest</literal> entry is redundant as it declares no permissions nor contains
- any memberships of other roles. What is most of interest here are the permissions and role memberships,
- which are explained in further detail as follows.
- </para>
-
- <sect3>
- <title>Explicit Permissions</title>
- <para>
- These are permissions which are explicitly granted to members of a role. Explicit permissions are
- used to address simple security concerns, such as <emphasis>"may this user create a new customer
- record?"</emphasis>, or <emphasis>"may this user view customer details?"</emphasis>. While it is
- possible to perform a more complex contextual-based decision to grant a specific permission or not,
- explicit permissions allow simple decision-less permissions to be easily granted to certain groups
- of users. How these permissions are used will be explained further on.
- </para>
- </sect3>
-
- <sect3>
- <title>Role memberships</title>
-
- <para>
- Role memberships are a simple inheritence feature designed to make it easier to build a security
- model from the ground up. In the above example, it can be seen that the <literal>admin</literal>
- role contains a membership of the <literal>superuser</literal> role. What this means, is that any
- explicit permissions that the <literal>superuser</literal> has, whether they were granted to the
- <literal>superuser</literal> role directly or inherited from further up, are automatically granted
- to the <literal>admin</literal> user also, as a result of its membership.
- </para>
-
- <para>
- The following table illustrates this concept by showing the permissions granted to each role based
- on the above <literal>security-config.xml</literal> example.
- </para>
-
- <table>
- <title>Permissions granted to example roles</title>
-
- <tgroup cols="3">
- <colspec colnum="1" colwidth="1*" />
- <colspec colnum="2" colwidth="1*" />
- <colspec colnum="3" colwidth="1*" />
-
- <thead>
- <row>
- <entry align="center">
- <para>Role</para>
- </entry>
-
- <entry align="center">
- <para>Permission</para>
- </entry>
-
- <entry align="center">
- <para>Source</para>
- </entry>
- </row>
- </thead>
-
- <tbody>
- <row>
- <entry>
- <para>user</para>
- </entry>
- <entry>
- <para>customer:create</para>
- </entry>
- <entry>
- <para>Assigned directly</para>
- </entry>
- </row>
- <row>
- <entry>
- <para>user</para>
- </entry>
- <entry>
- <para>customer:delete</para>
- </entry>
- <entry>
- <para>Assigned directly</para>
- </entry>
- </row>
- <row>
- <entry>
- <para>superuser</para>
- </entry>
- <entry>
- <para>customer:create</para>
- </entry>
- <entry>
- <para>Inherited from user</para>
- </entry>
- </row>
- <row>
- <entry>
- <para>superuser</para>
- </entry>
- <entry>
- <para>customer:delete</para>
- </entry>
- <entry>
- <para>Inherited from user</para>
- </entry>
- </row>
- <row>
- <entry>
- <para>superuser</para>
- </entry>
- <entry>
- <para>account:create</para>
- </entry>
- <entry>
- <para>Assigned directly</para>
- </entry>
- </row>
- <row>
- <entry>
- <para>superuser</para>
- </entry>
- <entry>
- <para>account:delete</para>
- </entry>
- <entry>
- <para>Assigned directly</para>
- </entry>
- </row>
- <row>
- <entry>
- <para>admin</para>
- </entry>
- <entry>
- <para>customer:create</para>
- </entry>
- <entry>
- <para>Inherited from superuser</para>
- </entry>
- </row>
- <row>
- <entry>
- <para>admin</para>
- </entry>
- <entry>
- <para>customer:delete</para>
- </entry>
- <entry>
- <para>Inherited from superuser</para>
- </entry>
- </row>
- <row>
- <entry>
- <para>admin</para>
- </entry>
- <entry>
- <para>account:create</para>
- </entry>
- <entry>
- <para>Inherited from superuser</para>
- </entry>
- </row>
- <row>
- <entry>
- <para>admin</para>
- </entry>
- <entry>
- <para>account:delete</para>
- </entry>
- <entry>
- <para>Inherited from superuser</para>
- </entry>
- </row>
- <row>
- <entry>
- <para>admin</para>
- </entry>
- <entry>
- <para>user:create</para>
- </entry>
- <entry>
- <para>Assigned directly</para>
- </entry>
- </row>
- <row>
- <entry>
- <para>admin</para>
- </entry>
- <entry>
- <para>user:modify</para>
- </entry>
- <entry>
- <para>Assigned directly</para>
- </entry>
- </row>
- <row>
- <entry>
- <para>admin</para>
- </entry>
- <entry>
- <para>user:delete</para>
- </entry>
- <entry>
- <para>Assigned directly</para>
- </entry>
- </row>
- </tbody>
-
- </tgroup>
- </table>
-
- </sect3>
-
- <sect3>
- <title>Page security</title>
-
- <para>
- Page security is used to restrict direct access to web resources based on the user's roles. It
- requires the Seam security filter (a servlet filter - see the next section) to be installed.
- To configure page security, <literal>security-constraint</literal> entries must be created within
- <literal>security-config.xml</literal> to control which web resources are accessible to which roles.
- The restricted web resources are defined using a url-pattern expression. The following example
- demonstrates how all of the <literal>*.seam</literal> resources in the <literal>/secure</literal>
- directory are restricted to users in the <literal>admin</literal> role.
- </para>
-
- <programlisting>
- <![CDATA[
-<security-config>
-
- <security-constraint>
- <web-resource-collection>
- <web-resource-name>Secure Page</web-resource-name>
- <url-pattern>/secure/*</url-pattern>
- </web-resource-collection>
-
- <auth-constraint>
- <role-name>admin</role-name>
- </auth-constraint>
- </security-constraint>
-
-</security-config>
- ]]>
- </programlisting>
-
- <para>
- This configuration may look familiar, because it is the same format defined by the servlet
- specification, and as such follows the same wildcard conventions. I.e. at the end of a pattern,
- <literal>/*</literal> matches any sequence of characters from that point forward, and the pattern
- <literal>*.extension</literal> matches any resources ending with <literal>.extension</literal>. An
- asterisk in any other position is not a wildcard.
- </para>
-
- <para>
- So what happens when a user tries to access a page that they don't have the necessary rights for?
- By default, the security filter redirects these requests to the <literal>/securityError.seam</literal>
- page. This page can be overridden by setting the <literal>securityErrorPage</literal> property
- of the <literal>SecurityConfiguration</literal> component in <literal>components.xml</literal>:
+ The simplified authentication method uses a built-in JAAS login module, <literal>SeamLoginModule</literal>, which
+ delegates authentication to one of your own Seam components. This login module is already configured inside Seam as
+ part of a default application policy and as such does not require any additional configuration files. It allows you to
+ write an authentication method using the entity classes that are provided by your own application. Configuring this
+ simplified form of authentication requires the <literal>Identity</literal> component to be configured in
+ <literal>components.xml</literal>:
</para>
<programlisting>
<![CDATA[
- <component class="org.jboss.seam.security.config.SecurityConfiguration">
- <property name="securityErrorPage">/security/generalSecurityError.seam</property>
+ <component class="org.jboss.seam.security.Identity">
+ <property name="authMethod">#{authenticator.authenticate}</property>
</component>
]]>
</programlisting>
- </sect3>
- </sect2>
-
- <sect2>
- <title>Seam Security Filter</title>
-
<para>
- The Seam security filter is a servlet filter that provides page security features. It is an optional
- component in that if your application does not require page security, then there is no requirement to
- install the security filter. To configure which pages are secure, see the previous section on page
- security. To configure the security filter, add the following entries to <literal>web.xml</literal>:
+ The EL expression <literal>#{authenticator.authenticate}</literal> is a method binding indicating the
+ <literal>authenticate</literal> method of the <literal>authenticator</literal> component.
</para>
- <programlisting>
- <![CDATA[
- <!-- Seam security filter -->
-
- <filter>
- <filter-name>Seam Security Filter</filter-name>
- <filter-class>org.jboss.seam.security.filter.SeamSecurityFilter</filter-class>
- </filter>
-
- <filter-mapping>
- <filter-name>Seam Security Filter</filter-name>
- <url-pattern>*.seam</url-pattern>
- </filter-mapping>
- ]]>
- </programlisting>
</sect2>
<sect2>
- <title>security-rules.drl</title>
+ <title>Writing an authentication method</title>
<para>
- This file also goes into the Seam application's <literal>META-INF</literal> directory. It contains
- all the rules that make up an application's authorization policy, and is described further down in the
- Authorization section.
- </para>
-
- </sect2>
-
- </sect1>
-
- <sect1>
- <title>Authentication</title>
-
- <para>
- It is a relatively straight forward process to set up authentication. The first step is to
- configure the login modules that are to be used within the project by adding one or more
- <literal>application-policy</literal> entries to <literal>security-config.xml</literal>.
+ 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:
</para>
<programlisting>
<![CDATA[
- <application-policy>
- <authentication>
- <login-module code="org.jboss.seam.security.spi.SeamLoginModule" flag="required">
- <module-option name="authMethod">#{login.authenticate}</module-option>
- </login-module>
- </authentication>
- </application-policy>
+ boolean (java.lang.String username, java.lang.String password, java.util.Set roles)
]]>
</programlisting>
<para>
- An <literal>application-policy</literal> without a specified name will be given a default name. It is
- possible to create multiple application policies if required, with each one having its own set of login
- modules.
- </para>
-
- <programlisting>
- <![CDATA[
- <application-policy> <!-- default policy -->
- <authentication>
- <login-module ...
- </authentication>
- </application-policy>
-
- <application-policy name="special">
- <authentication>
- <login-module ...
- </authentication>
- </application-policy>
- ]]>
- </programlisting>
-
- <para>
- The login module configuration should look familiar if you've ever used JAAS before. Each login module
- should have its own <literal>login-module</literal> entry, specifying the fully qualified class name of
- the login module class, plus the flag for the login module. It is also possible to configure additional
- options for each login module, by including <literal>module-option</literal> entries as children of the
- <literal>login-module</literal> entry. Flag values are found in the JSE API documentation for
- <literal>javax.security.auth.login.Configuration</literal>, but are repeated here for convenience:
- </para>
-
- <table>
- <title>JAAS Configuration Flags</title>
-
- <tgroup cols="2">
- <colspec colnum="1" colwidth="1*" />
- <colspec colnum="2" colwidth="1*" />
-
- <thead>
- <row>
- <entry align="center">
- <para>Flag</para>
- </entry>
- <entry align="center">
- <para>Description</para>
- </entry>
- </row>
- </thead>
-
- <tbody>
- <row>
- <entry>
- <para>Required</para>
- </entry>
- <entry>
- <para>
- The LoginModule is required to succeed. If it succeeds or fails, authentication still
- continues to proceed down the LoginModule list.
- </para>
- </entry>
- </row>
- <row>
- <entry>
- <para>Requisite</para>
- </entry>
- <entry>
- <para>
- The LoginModule is required to succeed. If it succeeds, authentication continues down the
- LoginModule list. If it fails, control immediately returns to the application (authentication
- does not proceed down the LoginModule list).
- </para>
- </entry>
- </row>
- <row>
- <entry>
- <para>Sufficient</para>
- </entry>
- <entry>
- <para>
- The LoginModule is not required to succeed. If it does succeed, control immediately
- returns to the application (authentication does not proceed down the LoginModule list).
- If it fails, authentication continues down the LoginModule list.
- </para>
- </entry>
- </row>
- <row>
- <entry>
- <para>Optional</para>
- </entry>
- <entry>
- <para>
- The LoginModule is not required to succeed. If it succeeds or fails, authentication still
- continues to proceed down the LoginModule list.
- </para>
- </entry>
- </row>
- </tbody>
- </tgroup>
- </table>
-
- <para>
-
- </para>
-
- <sect2>
- <title>Using <literal>SeamLoginModule</literal> to authenticate</title>
-
- <para>
- One of the simplest ways to authenticate a user is to delegate the authentication to a Seam component.
- There is a special login module, <literal>SeamLoginModule</literal> that allows this. Configure this
- login module in <literal>security-config.xml</literal>, and in its <literal>authMethod</literal>
- option specify an EL method expression. By default the method that you specify should accept the
- parameters [<literal>java.lang.String, java.lang.String, java.util.Set</literal>], however this can be
- customised (see section further down). The third <literal>java.util.Set</literal> parameter is a set
- to which the authentication method should add any roles that the authenticating user is a member of.
- Here's a complete example:
+ The first and second parameters should hopefully be self-explanatory. The third parameter (<literal>roles</literal>)
+ is a Set object that should be populated with the user's roles only if authentication is successful. The return value should
+ be set to <literal>true</literal> for a successful authentication, or <literal>false</literal> for an unsuccessful
+ authentication. Here's a complete example of an authentication method inside a JavaBean component:
</para>
<programlisting>
<![CDATA[
@Name("authenticator")
-public class AuthenticatorAction
-{
- @In(create=true)
- private EntityManager entityManager;
-
- public boolean authenticate(String username, String password, Set<String> roles)
- {
+public class Authenticator {
+ @In EntityManager entityManager;
+ public boolean authenticate(String username, String password, Set<String> roles) {
try
{
- User user = (User) entityManager.createQuery(
+ authenticatedUser = (User) entityManager.createQuery(
"from User where username = :username and password = :password")
.setParameter("username", username)
.setParameter("password", password)
.getSingleResult();
- for (UserRole r : user.getRoles())
- roles.add(r.getName());
+ if (authenticatedUser.getRoles() != null)
+ {
+ for (UserRole mr : authenticatedUser.getRoles())
+ roles.add(mr.getName());
+ }
return true;
}
catch (NoResultException ex)
{
- log.warn("Invalid username/password");
+ FacesMessages.instance().add("Invalid username/password");
return false;
}
}
-}
]]>
</programlisting>
<para>
- Based on the above example, the EL expression that would need to be specified in the
- <literal>authMethod</literal> option for the login module would be
- <literal>#{authenticator.authenticate}</literal>.
+ 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.
</para>
+
</sect2>
<sect2>
- <title>Logging in the user</title>
+ <title>Writing a login page</title>
<para>
- Now that the login module has been configured, and an authentication method written, all that is left
- is to perform a JAAS login. This is the easiest step, as shown by the following example:
+ The <literal>Identity</literal> component provides both <literal>username</literal> and <literal>password</literal>
+ properties, catering for the most common authentication scenario. These properties can be bound directly to the
+ username and password fields on a login form. Once these properties are set, the <literal>identity.login()</literal>
+ method will authenticate the user using the provided credentials. Here's an example of a simple login form:
</para>
<programlisting>
<![CDATA[
- public void login()
- {
- try
- {
- CallbackHandler cbh = SeamSecurityManager.instance().createCallbackHandler(
- user.getUsername(), user.getPassword());
+ <div>
+ <h:outputLabel for="name" value="Username"/>
+ <h:inputText id="name" value="#{identity.username}"/>
+ </div>
- LoginContext lc = SeamSecurityManager.instance().createLoginContext(null, cbh);
- lc.login();
- }
- catch (LoginException ex)
- {
- FacesMessages.instance().add("Invalid login");
- }
- }
- ]]>
- </programlisting>
-
- <para>
- The first thing that happens in the above code is the creation of a <literal>CallbackHandler</literal>
- which is going to be responsible for providing the user's credentials (their username and password) to
- the login module. The <literal>CallbackHandler</literal> is created by a convenience method in
- <literal>SeamSecurityManager</literal>, and knows how to handle <literal>NameCallback</literal> and
- <literal>PasswordCallback</literal> callback types.
- </para>
-
- <para>
- The next thing that happens is the creation of a <literal>LoginContext</literal>. There is a factory
- method in <literal>SeamSecurityManager</literal> for creating this, as the login context doesn't
- use the standard configuration (it uses an application-specific configuration). If the application
- policy isn't configured with a name, then it will have a default name and the String parameter
- passed to <literal>createLoginContext()</literal> can be null. If your application has multiple
- application policies configured, then you can specify which one to use by providing the policy name
- to <literal>createLoginContext()</literal>:
- </para>
+ <div>
+ <h:outputLabel for="password" value="Password"/>
+ <h:inputSecret id="password" value="#{identity.password}"/>
+ </div>
- <programlisting>
- <![CDATA[
- LoginContext lc = SeamSecurityManager.instance().createLoginContext("special", cbh);
+ <div>
+ <h:commandButton value="Login" action="#{identity.login}"/>
+ </div>
]]>
</programlisting>
+ </sect2>
- <para>
- The final step is the call to <literal>lc.login()</literal>, which calls each of the configured login
- modules in turn, passing in the callback handler to each one and performing an authentication based on
- the configured login module flags.
- </para>
+ </sect1>
+
+ <sect1>
+ <title>Identity</title>
<para>
- Once the user is successfully authenticated, calls to <literal>Identity.loggedIn()</literal> will
- return true for the duration of the session.
+ 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.
</para>
- </sect2>
<sect2>
- <title>Customising the Authentication process</title>
+ <title>Obtaining a reference</title>
<para>
- In some situations it may be necessary to perform authentication that isn't based on a simple
- username/password combination. The good news is that this is not very difficult, however it requires a
- few additional steps. This section will walk through an example where authentication requires a
- Company ID in addition to a username and password.
- </para>
-
- <para>
- The first step is to subclass the <literal>SeamLoginModule</literal> class and override the
- <literal>getLoginParams()</literal> method to return the appropriate parameters for the custom
- authentication method.
+ There are two ways of obtaining a reference to the <literal>Identity</literal> for the current user's session.
+ The first, inline method is to simply call <literal>Identity.instance()</literal>. Alternatively, you may inject
+ the <literal>Identity</literal> directly into your own components:
</para>
<programlisting>
<![CDATA[
-public class CustomLoginModule extends SeamLoginModule
-{
- @Override public Object[] getLoginParams()
- throws Exception
- {
- CompanyCallback cbCompany = new CompanyCallback("Enter company ID");
- NameCallback cbName = new NameCallback("Enter username");
- PasswordCallback cbPassword = new PasswordCallback("Enter password", false);
-
- // Get the username and password from the callback handler
- callbackHandler.handle(new Callback[] { cbCompany, cbName, cbPassword });
- username = cbName.getName();
-
- return new Object[] { cbCompany.getCompanyId(), username,
- new String(cbPassword.getPassword()), roles };
- }
-}
+ @In Identity identity;
]]>
</programlisting>
+ </sect2>
+ <sect2>
+ <title>isLoggedIn()</title>
<para>
- Since <literal>CompanyCallback</literal> is a custom callback it needs to be created also:
+ 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.
</para>
+ </sect2>
- <programlisting>
- <![CDATA[
-public class CompanyCallback implements Callback, Serializable {
- private String prompt;
- private int companyId;
- public CompanyCallback(String prompt) {
- if (prompt == null || "".equals(prompt))
- throw new IllegalArgumentException();
- this.prompt = prompt;
- }
- public String getPrompt() {
- return prompt;
- }
- public int getCompanyId() {
- return companyId;
- }
- public void setCompanyId(int companyId) {
- this.companyId = companyId;
- }
-}
- ]]>
- </programlisting>
-
+ <sect2>
+ <title>getSubject()</title>
<para>
- So far so good, now it's time to configure the custom login module in <literal>security-config.xml</literal>:
+ 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>
- <programlisting>
- <![CDATA[
- <loginmodules>
- <loginmodule class="com.acme.security.CustomLoginModule" flag="required">
- <option name="paramTypes">java.lang.Integer,java.lang.String,java.lang.String,java.util.Set</option>
- <option name="authMethod">#{authenticator.authenticate}</option>
- </loginmodule>
- </loginmodules>
- ]]>
- </programlisting>
-
+ <sect2>
+ <title>isUserInRole()</title>
<para>
- The important thing to note above is that the <literal>paramTypes</literal> option is specified, based
- on the customised parameters that are going to be sent to the authentication method. Now that they
- have been configured, it is time to write the authentication method:
+ 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:
</para>
<programlisting>
<![CDATA[
- at Name("authenticator")
-public class CustomAuthenticator {
- @In(create=true)
- private EntityManager entityManager;
-
- public boolean authenticate(int companyId, String username, String password, Set<String> roles)
- {
- try
- {
- User user = (User) entityManager.createQuery(
- "from User where companyId = :companyId and username = :username and password = :password")
- .setParameter("companyId", companyId)
- .setParameter("username", username)
- .setParameter("password", password)
- .getSingleResult();
-
- for (UserRole r : user.getRoles())
- roles.add(r.getName());
-
- return true;
- }
- catch (NoResultException ex)
- {
- log.warn("Invalid username/password");
- return false;
- }
- }
-}
+ if (!Identity.instance().isUserInRole("admin"))
+ throw new AuthorizationException("Insufficient privileges");
]]>
</programlisting>
+ </sect2>
+ <sect2>
+ <title>checkRestriction()</title>
<para>
- As can be seen in the above example, the customised authentication method contains the additional
- <literal>companyId</literal> parameter. The last thing to do is write a login method:
+ The <literal>checkRestriction()</literal> method accepts a <literal>java.lang.String</literal> parameter
+ specifying a security EL expression to evaluate. If the expression evaluates to <literal>false</literal>,
+ one of two things happen; 1) if the user is not logged in, a <literal>NotLoggedInException</literal> is
+ thrown, or 2) if the user is logged in, an <literal>AuthorizationException</literal> is thrown. EL security
+ functions are discussed in the Authorization section of this chapter.
</para>
<programlisting>
<![CDATA[
- at Name("loginAction")
-public class LoginAction implements LoginLocal {
- @In(create = true) EntityManager entityManager;
- @In(required = false) User user;
-
- public void login()
- {
- try
- {
- CallbackHandler cbh = createCallbackHandler(user.getCompanyId(),
- user.getUsername(), user.getPassword());
-
- LoginContext lc = SeamSecurityManager.instance().createLoginContext(cbh);
- lc.login();
- }
- catch (LoginException ex)
- {
- FacesMessages.instance().add("Invalid login");
- }
- }
-
- private CallbackHandler createCallbackHandler(final int companyId,
- final String username, final String password)
- {
- return new CallbackHandler() {
- public void handle(Callback[] callbacks)
- throws IOException, UnsupportedCallbackException
- {
- for (int i = 0; i < callbacks.length; i++)
- {
- if (callbacks[i] instanceof CompanyCallback)
- ((CompanyCallback) callbacks[i]).setCompanyId(companyId);
- else if (callbacks[i] instanceof NameCallback)
- ((NameCallback) callbacks[i]).setName(username);
- else if (callbacks[i] instanceof PasswordCallback)
- ((PasswordCallback) callbacks[i]).setPassword(password.toCharArray());
- else
- throw new UnsupportedCallbackException(callbacks[i],
- "Unsupported callback");
- }
-
- }
- };
+ public void deleteCustomer() {
+ Identity.instance().checkRestriction("#{s:hasPermission('customer', 'delete', customer)}");
+ //...code
}
]]>
</programlisting>
+ </sect2>
+ <sect2>
+ <title>login()</title>
<para>
- The most significant thing to note in the above code is the <literal>createCallbackHandler()</literal>
- method. It returns a callback handler that knows how to handle a <literal>CompanyCallback</literal>,
- in addition to the standard <literal>NameCallback</literal> and <literal>PasswordCallback</literal>.
+ 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.
</para>
+ </sect2>
+ <sect2>
+ <title>logout()</title>
<para>
- That wraps up the creation of a customised authentication process. Based on the above steps, it should
- be possible to create a customised authentication based on any combination of credentials.
+ This method clears the security state for the currently authenticated user.
</para>
</sect2>
+
</sect1>
<sect1>
@@ -945,104 +300,6 @@
]]>
</programlisting>
-
-
-
- </sect2>
-
- <sect2>
- <title>The Security Context</title>
-
- <para>
- The first time that a permission check is performed after a user is authenticated, a security context is
- created for that user and placed into the user's session context. This security context (which in
- fact is an instance of <literal>org.drools.WorkingMemory</literal>) is populated with all of the
- <literal>Principal</literal>s for the authenticated user, plus all of the explicitly granted
- permissions granted to the user's role/s, as instances of<literal>org.jboss.seam.security.SeamPermission</literal>.
- To make this a little clearer, here's a diagram:
- </para>
-
- <mediaobject>
- <imageobject role="fo">
- <imagedata fileref="images/security-context.png" align="center"/>
- </imageobject>
- <imageobject role="html">
- <imagedata fileref="../shared/images/security-context.png" align="center"/>
- </imageobject>
- </mediaobject>
-
- <para>
- In this diagram, the database contains a user "bob", who is a member of the "user" role. The
- security configuration file, <literal>security-config.xml</literal> defines a role called "user"
- that has the explicitly assigned permissions <literal>customer:create</literal>,
- <literal>account:create</literal> and <literal>invoice:create</literal>. After bob authenticates,
- his Security Context will contain all of the permissions granted to him through his role
- memberships, as well as any <literal>Principal</literal>s created as a result of the authentication
- process. This includes his roles, which exist in a <literal>SimpleGroup</literal> principal with the
- name of "roles".
- </para>
-
- </sect2>
-
- <sect2>
- <title>How do permission checks work?</title>
-
- <para>
-
- </para>
- </sect2>
-
- <sect2>
- <title>Establishing a default security policy</title>
-
- <para>
- As mentioned previously, permissions can either be explicitly granted, or they can be granted as the
- result of a rule-based decision. Strictly speaking though, <emphasis>all</emphasis> permissions must
- be granted via a security rule; this includes explicit permissions also. By having all
- permissioning under the control of the rule engine, it is easier to configure any custom security
- requirements for your application. So what does this mean? For starters, granting explicit permissions
- without specifying a minimal security policy will mean that any security checks for those permissions
- will fail. What does a minimal security policy look like? Here's an example
- <literal>security-rules.drl</literal> file with a default security policy defined:
- </para>
-
- <programlisting>
- <![CDATA[
-package MyProjectPermissions;
-
-import org.jboss.seam.security.Identity;
-import org.jboss.seam.security.rules.PermissionCheck;
-import org.jboss.seam.security.SeamPermission;
-
-rule DefaultPolicy
- salience -10
- activation-group "permissions"
-when
- c: PermissionCheck(granted == false)
- p: SeamPermission()
- eval( p.getName().equals(c.getName()) && p.getAction().equals(c.getAction()))
-then
- c.grant();
-end;
- ]]>
- </programlisting>
-
- <para>
- If you're familiar with JBoss Rules then this example should make at least some sense already. In a
- nutshell, what this rule does is "catch" any permission checks that haven't already been granted by
- any higher priority rules, and grant the permission if a <literal>SeamPermission</literal> instance
- having the same name and action as the <literal>PermissionCheck</literal> exists within the security
- context.
- </para>
-
- <para>
- A couple of notes about this rule; first of all, the <literal>salience -10</literal> line means that
- this rule will have a lower priority than any other rules with a higher salience (the default
- salience if not specified is 0). This assigns rules for any dynamic permissions (that don't have an explicit
- <literal>SeamPermission</literal> in the security context) a higher firing priority.
- Secondly, the <literal>activation-group</literal> value means that once the first rule within the
- activation group fires, no other rules within the same activation group will fire.
- </para>
</sect2>
</sect1>
More information about the jboss-cvs-commits
mailing list