您的位置:首页 > 其它

ACL Security In Seam, Part 1

2009-01-30 16:56 309 查看
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: