ACL Security In Seam, Part 1

Seam has always been about solving the common issues faced by web application developers.  By providing a selection of "Best Practice" solutions to various development challenges in a unified component model, the developer is free to work on the business logic of their application without having to worry as much about the concerns that should be rightly addressed by the framework.  Seam makes it very easy to do things such as generate PDF documents, generate and send e-mails, and internationalise your application.  It also integrates with third party projects such as jBPM and Drools to provide support for long running business processes and business rules.  Support for CAPTCHA, and a wiki-style markup language are also there, as well as a number of ways of doing AJAX.

One of the most important areas of enterprise application development though is security.  Seam has long provided a robust Security API allowing the components and views that a typical application consists of to be secured via user and role security, or rule-based security permissions.  Recently though (as of version 2.1.0.GA) Seam has overhauled its security engine to provide a number of new features offering even more ways to secure your sensitive data.  This article will look at one of these new features, persistent permissions to see how ACL, or "instance" based security can be used to secure your application at the object level.

To get started, let's examine the differences between rule-based and ACL-based security.  Rule-based security is great for applying blanket permissions to a particular class of object.  For example, let's take a look at the following security rule from one of the Seam examples:

  rule DeleteImage
    no-loop
    activation-group "permissions"
  when
    acct: MemberAccount()
    image: MemberImage(mbr : member -> (mbr.memberId.equals(acct.member.memberId)))
    check: PermissionCheck(target == image, action == "delete", granted == false)
  then
    check.grant();
  end

The conditional part of this rule (which allows users to delete images that they've previously uploaded) essentially says, "if you're the owner of this image, then you're allowed to delete it".  In this example, the security permission applies to all images and is dependent on the fact that there is a relational link between the image and its owner.  It is through this relationship that the security rule can determine that the current user is the owner of the image, and in turn grant permission to execute the action.  However, what if there was no relationship between the target of the permission check and the user (who we'll refer to as the principal from here on)?  This is where ACL security comes in.

An ACL (Access Control List) is a list of explicit permissions assigned to a particular object.  Each entry in the list contains a recipient (the principal who is granted the permission) and an action.  If you've ever used a *nix based operating system then you should already be familiar with a particular type of ACL - the file system contains a table of read, write and execute permissions for each file (yes, Windows has similar file security but it's not as obvious).  ACL-based security in Seam works much the same way, except that it is used to secure object instances instead of files.  In a typical application these object instances will generally be entities, however we'll soon see that it is possible to secure any type of object.

 

Before we can start assigning permissions for our objects, we first need to do a little preparation.  Most importantly we need a place to store the actual permissions themselves.  Seam provides a PermissionStore interface that declares the methods required for managing ACL object permissions.  While it is theoretically possible to store permissions in any type of persistent storage (e.g. in files, in LDAP, etc) it generally makes most sense to use a relational database.  To that effect Seam comes with a PermissionStore implementation called JpaPermissionStore, which allows permissions to be stored in a database using JPA.  To use JpaPermissionStore we need to do two things; create an entity bean to hold the permission records, and configure that entity bean in Seam's component.xml.

A special set of annotations is used to configure which properties of the entity represent the aspects of the permission.  The following code shows a bare minimum example (for the sake of brevity the annotations are shown on the fields, and getter/setter methods are omitted):

  @Entity
public class AccountPermission implements Serializable
{
@Id @GeneratedValue public Integer permissionId;
@PermissionUser @PermissionRole public String recipient;
@PermissionTarget public String target;
@PermissionAction public String action;
@PermissionDiscriminator public String discriminator;
}

 

The @PermissionUser and @PermissionRole annotations indicate the property containing the recipient of the permission.  In this example we are using a single table to hold both user and role permissions, so we put both of the annotations on the recipient field - and since we are storing both types of permission in the same table we also need a discriminator (annotated with @PermissionDiscriminator) property so that Seam can tell which entries are for users and which are for roles.  Finally, we need a property to store the permission target (annotated with @PermissionTarget) and a property for the permission action (annotated with @PermissionAction). 

Once we have created our entity, we simply need to configure JpaPermissionStore to use it by adding the following entry to Seam's components.xml configuration file:

 <security:jpa-permission-store user-permission-class="com.acme.AccountPermission"/>

  
Now that we have created and configured our permission store entity, we can start assigning permissions.  Seam's Security API provides a convenient component called PermissionManager, which allows us to easily manage object permissions.  Its methods look very similar to those found in the PermissionStore interface, and in fact they essentially delegate to the underlying PermissionStore however with one small restriction - each permission related operation that is invoked is first checked to ensure that the calling user has the necessary privileges to invoke that operation.  More details about this can be found in the Seam Reference Guide, however suffice it to say that not just any user can manage object permissions, they must first have the required privileges to do so.

 

Let's grant our first permission!  Imagine that your application contains a table of customers (represented by an entity bean called Customer) and you would like to grant update privileges on certain customers to your various salespeople.  Let's say that you want to allow Bob the salesman to manage the details of your best customer, the Jones account (which happens to have a customer ID of 1234).  We grant permission to Bob to manage this customer by invoking the following method:

  PermissionManager.instance().grantPermission(new Permission(entityManager.find(Customer.class, 1234), 
"update", new SimplePrincipal("bob")));

 In doing this, the PermissionManager will delegate the grantPermission() call to the configured JpaPermissionStore (after validating that the current user has the necessary privileges to do so) and a new permission record will be created in the database.  Let's diverge for a moment to see exactly what gets written to the AccountPermission table:

AccountPermission
=================
RECIPIENT  TARGET         ACTION  DISCRIMINATOR
-----------------------------------------------
bob        Customer:1234  update  user

Here we can obviously see from the column values that the recipient is "bob", the action is "update" and the discriminator is "user" (because Bob is a user, not a role).  The contents of the target column are a little more interesting.  The value that is displayed here is known as the object identifier, and is used to uniquely identify a particular instance of an object.  In this case, the target in question is an entity and fortunately for us Seam already knows how to generate unique object identifiers for entities.  If however the target of the permission isn't an entity but an instance of some other type of class then Seam must be told how to generate an object identifier for that class.  This is done by adding the @Identifier annotation to the class in question, and specifying an implementation of IdentifierStrategy like so:

  @Identifier(CustomIdentifierStrategy.class)
public class MyNonEntityClassThatIWantToAssignPermissionsTo {
public String getUniqueProperty() { return foo; }
}

The IdentifierStrategy interface is extremely simple, declaring only two methods.  The canIdentify() method returns true if the IdentifierStrategy implementation is capable of generating an identifier for the specified class, and the getIdentifier() method returns a unique identifier String for the specified object.  For example, the EntityIdentifierStrategy implementation that is included with Seam produces identifiers for entities by concatenating the name of the entity with its ID property.

Here's an example of what an implementation might look like:

  public class CustomIdentifierStrategy implements IdentifierStrategy {
public boolean canIdentify(Class targetClass) {
return targetClass.equals(MyNonEntityClassThatIWantToAssignPermissionsTo.class);
}
public String getIdentifier(Object target) {
return ((MyNonEntityClassThatIWantToAssignPermissionsTo) object).getUniqueProperty();
}
}

It is also worth mentioning that it is possible to assign permissions for literal string constants.  I.e, the permission target isn't always required to be an entity or other object instance.  The PermissionManager will happily allow you to grant permissions against a string literal, which may be useful if you need to maintain a set of arbitrary permissions.  The same goes for classes, for example if you'd like to assign a "create" permission for a certain class then it is possible to do so (obviously it's not possible to assign a "create" permission for an object instance when it doesn't exist yet).

Once all the pieces are in place, you can use the standard mechanisms provided by Seam to secure your objects in the view and within your action components (see the Seam Reference Guide for more info).

In the next article, we'll look in more detail at the way that permission actions can be defined and stored, and also look at how we can create permission management views to more easily manage our object permissions through a nice user interface.

 

转自:http://java.dzone.com/articles/acl-security-in-seam?page=0%2C0

你可能感兴趣的:(object,String,Security,seam,permissions,Annotations)