Enforcing security by implementing security checks in your application code is labor-intensive and a potentially dangerous practice as you can very easily miss a check and leave a big security hole open. Standard container-managed authentication and authorization was created to address this issue, but it's based purely on roles and URLs, and as such, is typically too constricting for modern web applications. With Tapestry, it's relatively easy to get started with securing your application 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 time consuming. Tynamo's tapestry-security is a comprehensive security module that provides tight integration with Apache Shiro, an established, high-performing and easy-to-use security framework for Tapestry applications.
There are several aspects to providing security. Technically you can either hide functionality from user's view, visibly disable or lock certain functionality or display errors when user tries accessing unpermitted resources or operations. Security can be enforced at different integration points: for example, you may restrict users' access to certain URLs, secure access to the data itself or make specific checks in an operation of the controlling page class before executing certain functionality. Tapestry-security module supports all these different types of authorization mechanisms.
To use the feature, you need to add the following dependency to your pom.xml:
Apache Shiro, the security framework that tapestry-security is based on, is modular and extensible, but to get started, you need to understand just three key Shiro concepts: realms, filters and security configuration. A realm is responsible for authenticating and authorizing users, so you at least need to configure a ready-made realm, or, if you are authenticating users against your own custom database, likely need to implement your own custom realm. Typically, in your AppModule you provide a realm configuration such as:
Obviously, if your Realm needs to use other Tapestry services, your Realm implementation could also be a Tapestry service, in which case you'd just inject it in and contribute it to the WebSecurityManager. See an example of a simple, custom Hibernate-based entity realm (service).
Shiro is based on multiple filter chains which is a natural fit with tapestry's (filter) pipeline pattern. In the typical case, you don't have to implement new filters but merely configure them to process desired urls of your application. Refer to the Shiro configuration for more information, but tapestry-security makes the default Shiro filters available so you can refer to them by name.
Shiro supports url-based permission checking out of the box. Tapestry-security also comes with several security annotations and some security components that you can use in your page classes and templates to secure specific operations or access to the page.
Shiro's default configuration model uses an INI file (a property file with sections). You can use a standard Shiro INI configuration file (see Shiro's documentation for more info) with tapestry-security or you can do it all in code and with annotations.
The simplest and most typical security models are based on role permissions. Here's a simple example of configuring url/role permissions with shiro.ini:
If you want to use a shiro.ini configuration file, place it at the root of your classpath and set SecuritySymbols.SHOULD_LOAD_INI_FROM_CONFIG_PATH to true in your ApplicationDefaults.
You don't actually need an INI file - you can do the same in code, for example:
anon and authc above refer to the Shiro filter names. See all Shiro filters available by default. Note that if you want to declare new filters or configure the built-in ones, you currently have to do it with the ini file. See shiro.ini used for integration testing tynamo-federatedaccounts as an example for declaring and configuring filters.
To declaratively secure your pages, you can use the following annotations:
For example, to restrict access to users with roles "admin" only, you would add a following annotation to a page class:
You can also secure access to services and service operations, for example:
Permissions are another interesting aspect of Shiro. The syntax is purposefully left open so they could be flexibly used for implementing different dynamic permission models. Currently, tapestry-security does a string match to validate the current user has been granted the permission specified with @RequiresPermissions. For example:
In a future versions of tapestry-security, we'll use permissions to specify association/instance (as opposed to role/type) permissions (such as current user can only edit his own profile information). The exact syntax is yet to be defined.
There are often cases where it's not enough to simply secure the urls or the pages. If a user doesn't have a permission to invoke a particular action, it's a good practice to also hide it from his view. Tapestry-security module contains several built-in conditional components to make conditional rendering of your page templates and more fine-grained permission control easier.
The names of following components should give you a pretty good hint of their purpose, can you guess what all of them do?
Some simple examples below:
For more extensive examples, take a look at our full-featured integration test web app. See for example the Index page template and class and the AlphaService. More real world examples showcasing Shiro's capabilities to follow.