SeamFramework.orgCommunity Documentation
Seam Security provides a number of facilities for restricting access to certain parts of your application. As mentioned
previously, the security API is centered around the Identity
bean, which is a session-scoped bean used to represent
the identity of the current user.
To be able to restrict the sensitive parts of your code, you may inject the Identity
bean into your class:
@Inject Identity identity;
Once you have injected the Identity
bean, you may invoke its methods to perform various types of authorization.
The following sections will examine each of these in more detail.
The security model in Seam Security is based upon the PicketLink API. Let's briefly examine a few of the core interfaces provided by PicketLink that are used in Seam.
This is the common base interface for both User
and Group
. The getKey()
method should return a unique identifying value for the identity type.
Represents a group. The getName()
method should return the name of the group, while the
getGroupType()
method should return the group type.
Represents a role, which is a direct one-to-one typed relationship between a User and a Group. The
getRoleType()
method should return the role type. The getUser()
method
should return the User for which the role is assigned, and the getGroup()
method should
return the Group that the user is associated with.
This is the simplest type of authorization, used to define coarse-grained privileges for users assigned to a certain role or belonging to a certain group. Users may belong to zero or more roles and groups, and inversely, roles and groups may contain zero or more members.
The concept of a role in Seam Security is based upon the model defined by PicketLink. I.e, a role is a direct relationship between a user and a group, which consists of three aspects - a member, a role name and a group (see the class diagram above). For example, user Bob (the member) may be an admin (the role name) user in the HEAD OFFICE group.
The Identity
bean provides the following two methods for checking role membership:
boolean hasRole(String role, String group, String groupType); void checkRole(String role, String group, String groupType);
These two methods are similar in function, and both accept the same parameter values. Their behaviour differs
when an authorization check fails. The hasRole()
returns a value of false
when the
current user is not a member of the specified role. The checkRole()
method on the other hand,
will throw an AuthorizationException
. Which of the two methods you use will depend on your
requirements.
The following code listing contains a usage example for the hasRole()
method:
if (identity.hasRole("manager", "Head Office", "OFFICE")) { report.addManagementSummary(); }
Groups can be used to define a collection of users that meet some common criteria. For example, an application might use groups to define users in different geographical locations, their role in the company, their department or division or some other criteria which may be significant from a security point of view. As can be seen in the above class diagram, groups consist of a unique combination of group name and group type. Some examples of group types may be "OFFICE", "DEPARTMENT", "SECURITY_LEVEL", etc. An individual user may belong to many different groups.
The Identity
bean provides the following methods for checking group membership:
boolean inGroup(String name, String groupType); void checkGroup(String group, String groupType);
These methods are similar in behaviour to the role-specific methods above. The inGroup()
method
returns a value of false
when the current user isn't in the specified group, and the
checkGroup()
method will throw an exception.
Seam Security provides a way to secure your bean classes and methods by annotating them with a typesafe security binding. Each security binding must have a matching authorizer method, which is responsible for performing the business logic required to determine whether a user has the necessary privileges to invoke a bean method. Creating and applying a security binding is quite simple, and is described in the following steps.
A typesafe security binding is an annotation, meta-annotated with the SecurityBindingType
annotation:
import org.jboss.seam.security.annotations.SecurityBindingType; @SecurityBindingType @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface Admin { }
The security binding annotation may also define member values, which are taken into account when matching
the annotated bean class or method with an authorizer method. All member values are taken into consideration,
except for those annotated with @Nonbinding
, in much the same way as a qualifier binding type.
import javax.enterprise.util.Nonbinding; import org.jboss.seam.security.annotations.SecurityBindingType; @SecurityBindingType @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface Foo { String bar(); @Nonbinding String other() default ""; }
The next step after creating the security binding type is to create a matching authorizer method. This
method must contain the business logic required to perform the required authorization check, and return
a boolean
value indicating whether the authorization check passed or failed.
An authorizer method must be annotated with the @Secures
annotation, and the security binding
types for which it is performing the authorization check. An authorizer method may declare zero or more
method parameters. Any parameters defined by the authorizer method are treated as injection points, and
are automatically injected by the Seam Security extension. The following example demonstrates an authorizer
method that injects the Identity
bean, which is then used to perform the authorization check.
import org.jboss.seam.security.annotations.Secures; public class Restrictions { public @Secures @Admin boolean isAdmin(Identity identity) { return identity.hasRole("admin", "USERS", "GROUP"); } }
Authorizer methods will generally make use of the security API to perform their security check, however this is not a hard restriction.
Once the security binding annotation and the matching authorizer method have been created, the security binding type may be applied to a bean class or method. If applied at the class level, every method of the bean class will have the security restriction applied. Methods annotated with a security binding type also inherit any security bindings on their declaring class. Both bean classes and methods may be annotated with multiple security bindings.
public @ConversationScoped class UserAction { public @Admin void deleteUser(String userId) { // code } }
If a security check fails when invoking a method annotated with a security binding type, an
AuthorizationException
is thrown. The Seam Catch module can be used to handle
this exception gracefully, for example by redirecting them to an error page or displaying an error
message. Here's an example of an exception handler that creates a JSF error message:
@HandlesExceptions public class ExceptionHandler { @Inject FacesContext facesContext; public void handleAuthorizationException(@Handles CaughtException<AuthorizationException> evt) { facesContext.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "You do not have the necessary permissions to perform that operation", "")); evt.handled(); } }
Seam Security provides one security binding annotation out of the box, @LoggedIn
. This annotation
may be applied to a bean to restrict its methods to only those users that are currently authenticated.
import org.jboss.seam.security.annotations.LoggedIn; public @LoggedIn class CustomerAction { public void createCustomer() { // code } }