Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 3 Next »

Overview

Creating security checks in your application code is labor-intensive and a dangerous practice as you can very easily miss a check and leave a big security hole open. Standard container-managed authentication and authorization was create to address this issue, but as a role and URL-based security, it's typically too constricting for modern web applications. With Tapestry, it's relatively easy to get started with more generic security checks by creating custom filters and dispatchers, but really, every user of Tapestry shouldn't need to create their own security framework. Developing comprehensive, proven bullet-proof security framework is difficult and therefore, Tynamo's tapestry-security module provides tight integration with Apache Shiro, an established, high-performing and easy-to-use security framework, for Tapestry applications.

Using security

There are several aspects to providing security. Technically you can either hide functionality from user's view if he doesn't have permissions to it, visibly disable or lock certain functionality or display errors if user is trying to do something illegal. There are several possible access points for making security checks: for example, you may restrict users' access to certain URLs, secure access to the data itself or make specific checks before executing certain functionality. They all come with their own advantages and disadvantages and tapestry-security module supports all of these security models.

Providing configuration

Shiro's default configuration model uses an INI file (a property file with sections). You can use a standard Shiro INI configuration file with tapestry-security and in addition, you can configure security with code and annotations.

The typical, simple security models are based on role/type permission. This is very simple to do with the provided security annotations.

In the domain-driven development world of Trails, you apply your security restrictions via annotations on your domain objects. Annotations can usually be applied either on classes or properties. There are 6 built-in security annotations in Trails:

  • @ViewRequiresRole
  • @UpdateRequiresRole
  • @RemoveRequiresRole
  • @ViewRequiresAssociation
  • @UpdateRequiresAssociation
  • @RemoveRequiresAssociation

Compared to the previous 1.0 Trails release, the security syntax has changed in order to simplify syntax from a more generic @Security annotation with parameters to more specific annotations as there are only limited number of actions one can take on an entity. Role specific annotation accept either a single string (e.g. @ViewRequiresRole("ROLE_ADMIN") or a list of strings (e.g. @ViewRequiresRole({"ROLE_ADMIN", "ROLE_USER"} ) where any named role grants permission to the particular action. There's no annotation for requiring all of named roles; in such cases its recommended to create a new role for the specific purpose.

The syntax for [action]RequiresAssociation is similar to role specific annotations, but the semantics are very different. These annotations are used for "association/instance" security. Very often, you have use cases where only "owners" or users with a specific relationship to an entity have rights to view or modify it. These annotations accept a single string parameter to name the property to associate the security rule. It means that as an example, if an entity Car has an annotation @ViewRequiresAssociation("owner"), it's expected that the Car entity has an "owner" property that's supposed to implement Acegi UserDetails. We then compare the value of AcegiSecurityContext.getAuthentication() to the "username" property of "owner" (either in TrailsSecurityInterceptor for Update and Remove or in SecurePersistenceImpl by adding a matching Hibernate Criteria for view) to determine whether to grant the permissions to execute the action or not. If you omit any parameters, it's expected the entity itself implements Acegi UserDetails (which makes it easy to secure User entity so that for example a logged-in user can only modify his own profile).

Note that if you don't add any security annotations, Trails allows anybody to operate on your entities. You can combine role and association annotations, in which case role-based permission take precedence. Association annotations can only be applied to classes as they are enforced on the entity level. If you bind an association rule to a property that is a collection (such as @UpdateRequiresAssociation("supervisors")) the frameworks the permission against any object in the collection (the current user have to be one of the objects in the collection). Likewise, if you have an entity "Child" protected by @ViewRequiresAssocation("parent") and you ask for a list of Children, you'll only see your own children in the resulting list.

Authentication of users is based on Acegi. Acegi is much more flexible than Container Managed Authentication (CMA) and underneath, you can use any authentication modules, such as "remember me" or single-signon. You may implement users and roles anyway you like, but it's expected that your "user" type of entity implements acegi UserDetails and that you've implemented Acegi Role either as static objects or as a entity. The security example features sample implementation of User, Role, current user as a Tapestry Application State Object (ASO) and a production-quality remember-me implementation. For more information on Acegi, check the Acegi project website and an introduction article.

About seeding the database

In a typical security scenario, you want only admin type of users to be able to create new users in your database. This imposes the problem that you have to have an admin user available who's allowed to create other users. It's up to you how you want to seed your database, but one option is to use Trails' SpringSeedEntityInitializer that initializes the database with entities you've defined as Spring beans. Take a look at the security example on how to set it up for use.

Configuring security

To configure security, you need to make these main changes:

  • Add an Acegi Filter proxy in your web.xml so Acegi can process and provide security for incoming requests
  • Configure Acegi beans to fit your purposes. It's easiest to keep Acegi configuration in a separate Spring configuration and then load multiple configuration files.
  • Configure Trails Security Service and Security Decorator so Trails so Trails know to use the security annotation to process the UI. * In case of Hibernate, change your Trails persistence implementation to use SecurePersistenceServiceImpl and TrailsSecurityInterceptor.

You may use the trails-secure-archetype to add security to your project. This security archetype is meant to be run on an existing Trails project. It's not ideal as it'll overwrite your pom.xml (to declare dependency to trails-security and web.xml (to set the Acegi filter proxy), as unfortunately the current version of the Maven archetype plugin is a bit too primitive to provide better support for more complex cases like this. As such, trails-secure-archetype is most suitable for adding security into test projects or newly created projects with Trails-archetype (as described in the Quick start).

To use the security archetype, run (on a single line) the following command in the parent directory of your project:

Tip

Icon

Above, use -DremoteRepositories=http://repository.codehaus.org/ if the version of archetype you are trying to use is not yet available in repo1.maven.org (mostly with snapshots)

To modify the sample authentication to fit your use case, it's worth to familiarize yourself with Acegi framework, as it offers heaps of authentication options and security models. The archetype adds two default users (user:user, admin:admin) as a sample using the aforementioned SpringSeedEntityInitializer. The configuration of these beans is in Spring configuration file applicationContext-seedData.xml.

#labels Featured
= Annotation =

For a declaratory security management at our disposal is the following annotations
{{{
/* For methods */
@RequiresPermissions
@RequiresRoles
@RequiresUser
@RequiresGuest
@RequiresAuthentication

/* For classes */
@RequiresPermissionsAll
@RequiresRolesAll
@RequiresUserAll
@RequiresGuestAll
@RequiresAuthenticationAll
}}}

== Secure pages ==

{{{
@RequiresRoleAll("admin")
public class AdminPage {
}
}}}

== Secure actions ==

{{{
public class Index {

@RequiresRule("news:delete")
public void onActionFromDeleteNews(EventContext eventContext)

Unknown macro: { ... }

}
}}}
== Secure Services ==

=== Secure Service Method ===
{{{

public interface AlphaService

Unknown macro: { @RequiresAuthentication void secureMethod(); }

public class AlphaServiceImpl implements AlphaService {

@Override
public void secureMethod()

Unknown macro: { ... }

}

}}}

=== Secure Service Class ===
{{{

@RequiresAuthentication
public interface AlphaService {

void secureMethod1();

void secureMethod2();
}

public class AlphaServiceImpl implements AlphaService {

@Override
public void secureMethod1()

@Override
public void secureMethod2()

Unknown macro: { ... }


}

}}}

= Filters =

The configuration of filters is fully consistent with the documentation JSecurity. By default configuration is configured in the file security.conf section [url]
{{{
[urls]

/authc/signup = anon
/authc/** = authc

/user/signup = anon
/user/** = user

/site/user/** = roles[user]
/site/manager/** = roles[manager]

/news/view/** = perms[news:view]
/news/edit/** = perms[news:edit]
}}}

= Components =
{{{
Authenticated
NotAuthenticated
User
Guest
HasAnyRoles
HasPermission
HasRole
LacksPermission
LacksRole
LoginForm
LoginLink
}}}
== Examples ==
{{{
<t:jsec.user>
Hello, $

Unknown macro: {username}

</t:jsec.user>
}}}

{{{
<t:jsec.guest>
<t:actionlink t:id="createAccount">Create account</t:actionlink>
</t:jsec.guest>
}}}

{{{
<t:jsec.hasRole role="admin">
<t:actionlink t:id="delete">delete user</t:actionlink>
</t:jsec.user>
}}}

= Spring Integration =
Tapestry-JSecurity can be used in spring environment.

  • No labels