A module for Apache Isis for administering users/roles/permissions and providing Shiro-based authentication and/or authorization.
Java Other



Build Status

This module, intended for use within Apache Isis, provides the ability to manage *user*s, *role*s, and *permission*s. Users have roles, roles have permissions, and permissions are associated with *application feature*s. These features are derived from the Isis metamodel and can be scoped at either a package, class or individual class member. Permissions themselves can either allow or veto the ability to view or change any application feature.

A key design objective of this module has been to limit the amount of permissioning data required. To support this objective:

  • permissions are hierarchical: a class-level permission applies to all class members, while a package-level permission applies to all classes of all subpackages

  • permissions can allow or veto access; thus a role can be granted access to most features, but excluded from selective others

  • permissions are scoped: a member-level permission overrides a class-level permission, a class-level permission overrides a package-level permission; the lower-level package permission overrides a higher-level one (eg com.mycompany.invoicing overrides com.mycompany).

  • if there are conflicting permissions at the same scope, then the allow takes precedence over the veto.

The module also supports multi-tenancy, whereby a user can be prevented from either viewing or modifying objects to which they don’t have access ("don’t belong to them").

  • In the original design of this module, this was based on the concept of a hierarchical tenancy, each of which is identified by an application tenancy path ("atPath") and the interpretation of which was hard-coded into the module. For example, users would typically have the ability to view/edit data in "their" tenancy or sub-tenancies, could view data in parent tenancies (for example, global reference data/standing data), and would have no access to any peer entities.

  • As of v1.13.3, this has been generalized; the interpretation of the "atPath" is entirely application-specific (with legacy support for the original design).

  • As of v1.13.6, this has been generalized further; an ApplicationUser's "atPath" is no longer a reference to a particular ApplicationTenancy entity; it is simply a string. That string could of course still be used to represent the identifier of a single ApplicationTenancy, but it need not: it should be thought of instead as encoding and and all characteristics of the user or domain object which pertain to determining visibility/editability. The ApplicationTenancy entity has been retained only to make it easier to migrate to this more generalized model.

The module also provides an implementation of Apache Shiro's AuthorizingRealm. This allows the users/permissions to be used for Isis' authentication and/or authorization.

Authentication is optional; each user is either local or delegated:

  • users with a delegated account type are authenticated through a (configured) delegated authentication realm (for example LDAP). Any other implementation of Shiro’s AuthenticatingRealm can be used.

  • users with a local account type are authenticated through a PasswordEncryptionService.

The module provides a default implementation based on jBCrypt, but other implementations can be plugged-in if required.

Domain Model


The above diagram was generated by yuml.me.


The following screenshots show an example app’s usage of the module, which includes all the services and entities (users, roles, permissions etc) provided by the module itself. This example app’s domain also has its own very simple ExampleEntity entity and corresponding repository.

For further screenshots, see the screenshot tutorial on the wiki.

Automatically Seeds Roles

When the security module starts up, it will automatically (idempotently) seed a number of roles, corresponding permissions and a default isis-module-security-admin user. This user has access to the security menu:

010 menus

One of the roles granted to the isis-module-security-admin user is the corresponding (similarly named) isis-module-security-admin role. It is this that grants all permissions to all classes in the security module itself:

030 role

The isis-module-security-regular-user role grants selected permissions to viewing/changing members of the ApplicationUser class (so that a user with this role can view/update their own record):

035 role regular user

Add permission at different scopes

Permissions can be created at different scopes or levels (highlighted in the above screenshot).

  • Permissions created at the package level apply to all classes in all packages and subpackages (that is, recursively).

  • Permissions defined at the class level take precedence to those defined at the package level.

For example, a user might have allow/viewing at a parent level, but have this escalated to allow/changing for a particular class. Conversely, the class-level permission might veto access.

Permissions can also be defined the member level: action, property or collection. These override permissions defined at either the class- or package-level.

Permissions can ALLOW or VETO access

Permissions can either grant (allow) access or prevent (veto) access. If a user has permissions that contradict each other (for example, they are a member of "roleA" that allows the permission, but also of "roleB" that vetoes the permission) then by default the allow wins. However, this is strategy is pluggable, and the security module can be configured such that a veto would override an allow if required.

050 permission rule

Permissions can apply to VIEWING or CHANGING the feature

For a property, "changing" means being able to edit it. For a collection, "changing" means being able to add or remove from it. For an action, "changing" means being able to invoke it.

060 permission mode

Note that Isis' Wicket viewer currently does not support the concept of "changing" collections; the work-around is instead create a pair of actions to add/remove instead. This level of control is usually needed anyway.

An allow/changing permission naturally enough implies allow/viewing, while conversely and symmetrically veto/viewing permission implies veto/changing.

Specify package

The list of packages (or classes, or class members) is derived from Isis' own metamodel.

070 permission package from isis metamodel

Application users

Application users can have either a local or a delegated account type.

  • Local users are authenticated and authorized through the module’s Shiro realm implementation. The users are created explicitly by the administrator.

  • Optionally a delegate authentication realm can be configured; if so then delegated users can be created and their credentials will be authenticated by the delegate authentication realm. By default, users are created automatically when that user attempts to log in (though this feature can be disabled, see below) However, for safety their ApplicationUser accounts are created in a disabled state and with no roles, so the administrator is still required to update them.

Once the user is created, then additional information about that user can be captured, including their name and contact details. This information is not otherwise used by the security module, but may be of use to other parts of the application. The users' roles and effective permissions are also shown.

289 user details

A user can maintain their own details, but may not alter other users' details. An administrator can alter all details, as well as reset a users' password.

If a user is disabled, then they may not log in. This is useful for temporarily barring access to users without having to change all their roles, for example if they leave the company or go on maternity leave.

User Sign-up (Self-Registration)

Apache Isis allows users to sign-up (self-register) with an application provided that:

  • the application is correctly configured for the EmailNotificationService, by specifying isis.service.email.sender.address and isis.service.email.sender.password configuration properties; and

  • the application provides an implementation of the UserRegistrationService (more on this below).

The sign-up link is shown on the initial login page:

500 sign in register link

Following the link prompts for an email:

510 sign up

An email is sent to the specified address, with a link to complete the registration:

530 sign up email

Completing registration consists of selecting a username and password:

540 complete registration

The user can then login:

550 logged in

In the screenshot above note that the user has a default set of permissions. These are set up by the UserRegistrationService implementation. The security module provides SecurityModuleAppUserRegistrationServiceAbstract which provides most of the implementation of this service; the demo app’s AppUserRegistrationService service completes the implementation by specifying the role(s) to assign any new users:

public class AppUserRegistrationService extends SecurityModuleAppUserRegistrationServiceAbstract {
    protected ApplicationRole getInitialRole() {
    return findRole(ExampleFixtureScriptsRoleAndPermissions.ROLE_NAME);
    protected Set<ApplicationRole> getAdditionalInitialRoles() {
        return Collections.singleton(findRole(ExampleRegularRoleAndPermissions.ROLE_NAME));
    private ApplicationRole findRole(final String roleName) {
        return applicationRoles.findRoleByName(roleName);
    private ApplicationRoles applicationRoles;

So, for the demo app at least, any new user has access to the "example-fixture-scripts" role (= the Prototyping menu) and to the "example-regular-role" (= the Tenanted Entities and the Non-Tenanties Entities menus).

Speaking of which…

Application Tenancy

Application tenancy is the concept of determining which application users can see/modify domain objects. For this to work, there needs to be a application-specific mechanism for making the decision. This is defined by the (optional) ApplicationTenancyEvaluator SPI service:

public interface ApplicationTenancyEvaluator {
    boolean handles(Class<?> cls);                                          (1)
    String hides(Object domainObject, ApplicationUser applicationUser);     (2)
    String disables(Object domainObject, ApplicationUser applicationUser);  (3)
  1. Whether this evaluator can determine the tenancy of the specified domain entity being interacted with (the "what").

  2. Whether this instance of the domain object can be viewed by the user. Any non-null string is interpreted as meaning that the object should be hidden from view

  3. Whether this instance of the domain object can be modifed by the user; a non-null return value is interpreted as the reason it is read-only.


This demo app demonstrates how this can work for an application where the name of each object is correlated to the roles of the user.

ApplicationTenancy using Paths

The security module also provides its own built-in mechanism for determining application tenancy, through the concept of the application tenancy path ("atPath").


This is this design is designed for hierarchical tenancies, eg where a tenancy corresponds to a country or region and a user cannot reside in multiple regions concurrently. If this does not suit your requirements, then use the more general purpose ApplicationTenancyEvaluator SPI service described above.

Both application users and domain objects can have an "at path", this is expected to identify a single ApplicationTenancy entity. For application user’s this is simply a property of the object, for domain object’s this is performed by implementing the HasAtPath interface:

public interface HasAtPath {
    String getAtPath();

The application can then be configured so that access to domain objects can be restricted based on the respective tenancies of the user accessing the object and of the object itself. The table below summarizes the rules:

object’s tenancy user’s tenancy visible? editable?































































To enable this requires a single configuration property to be set, see below.


You may not wish to have your domain objects implement the WithApplicationTenancy. As all that is required is to determine the application "path" of a domain object, an alternative is to provide an implementation of the ApplicationTenancyPathEvaluator SPI service.

This is defined as:

public interface ApplicationTenancyPathEvaluator {
    boolean handles(Class<?> cls);                                  (1)
    String applicationTenancyPathFor(final Object domainObject);    (2)
  1. indicates if the domain object’s class has multi-tenancy

  2. the method that actually returns the path.

For example, the todoapp provides an implementation for its ToDoItem:

        nature = NatureOfService.DOMAIN
public class ApplicationTenancyPathEvaluatorForToDoApp implements ApplicationTenancyPathEvaluator {
    public boolean handles(final Class<?> cls) {
        return ToDoItem.class == cls;
    public String applicationTenancyPathFor(final Object domainObject) {
        // always safe to do, per the handles(...) method earlier
        final ToDoItem toDoItem = (ToDoItem) domainObject;
        return toDoItem.getAtPath();

The evaluator can also optionally handle and return a path for the security domain module’s own ApplicationUser entity; but if it does not, then the user’s own tenancy (ApplicationUser#getTenancy()) is used instead.

How to run the Demo App

The prerequisite software is:

  • Java JDK 8 (>= 1.9.0) or Java JDK 7 (<= 1.8.0)

    • note that the compile source and target remains at JDK 7

  • maven 3 (3.2.x is recommended).

To build the demo app:

git clone https://github.com/isisaddons/isis-module-security.git
mvn clean install

To run the demo app:

cd webapp
mvn jetty:run

Then log on using user: isis-module-security-admin, password: pass

How to configure/use

You can either use this module "out-of-the-box", or you can fork this repo and extend to your own requirements.



Update your classpath by adding this dependency in your dom project’s pom.xml:


If using the PasswordEncryptionServiceUsingJBcrypt service (discussed below), also add a dependency on the underlying jbcrypt library:


Check for later releases by searching [Maven Central Repo](http://search.maven.org/#search|ga|1|isis-module-security-dom).

Shiro configuration (shiro.ini)

The module includes org.isisaddons.module.security.shiro.IsisModuleSecurityRealm, an implementation of Apache Shiro’s org.apache.shiro.realm.AuthorizingRealm class. This realm is intended to be configured as the single realm for Shiro, but it can optionally have a delegateAuthenticationRealm injected into it.

  • if configured without a delegate realm then IsisModuleSecurityRealm deals only with local users and performs both authentication and authorization for them. Authentication is performed against encrypted password. Users with delegate account type will be unable to log in.

  • if configured with a delegate realm then IsisModuleSecurityRealm deals with both delegated and local users. Authentication of delegated users is performed by the delegate authentication realm, while local users continue to be authenticated in the same way as before, against their encrypted password. Authorization is performed the same way for either account type, by reference to their user roles and those roles' permissions.

For both local and delegated users the realm will prevent a disabled user from logging in.

To configure, update your WEB-INF/shiro.ini’s `[main] section:



securityManager.authenticator.authenticationStrategy = $authenticationStrategy

securityManager.realms = $isisModuleSecurityRealm

If a delegate authentication realm is used, then define it and inject (again, in the [main] section):

someOtherRealm=...                                                          (1)

  1. the someOtherRealm variable defines some other realm to perform authentication.

To disable the automatic creation of delegate users, use:


Isis domain services

If using an AppManifest, then update its getModules() method and also its getAdditionalServices() method:

public List<Class<?>> getModules() {
    return Arrays.asList(
public List<Class<?>> getAdditionalServices() {
    return Arrays.asList(
            org.isisaddons.module.security.dom.password.PasswordEncryptionServiceUsingJBcrypt.class         (1)
           ,org.isisaddons.module.security.dom.permission.PermissionsEvaluationServiceAllowBeatsVeto.class  (2)
  1. is an implementation of the PasswordEncryptionService. This is mandatory; local users (including the default isis-module-security-admin administrator user) must be authenticated using the password service. If required, any other implementation can be supplied.

  2. is an implementation of the PermissionsEvaluationService that determines how to resolve conflicting permissions at the same scope. This service is optional; if not presentthen the module will default to an allow-beats-veto strategy. An alternative implementation of PermissionsEvaluationServiceVetoBeatsAllow is also available for use if required; or any other implementation of this interface can be supplied.

There is further discussion of the PasswordEncryptionService and PermissionsEvaluationService below.

If you aren’t using an AppManifest, instead update your WEB-INF/isis.properties:


isis.services = ...,\

The security module automatically seeds users and roles, using fixture scripts. As of 1.9.0 and later it is no longer necessary to register an implementation of FixtureScripts domain service; the core Apache Isis framework provides a default implementation.

Tenancy checking

To enable tenancy checking (as described above, to restrict a user’s access to tenanted objects), then a configuration property must be added. This can either be specified in the AppManifest or in WEB-INF/isis.properties.

If using an AppManifest, then update its getConfigurationProperties() method:

public Map<String, String> getConfigurationProperties() {
    return ImmutableMap.of(
        "isis.reflector.facets.include", "org.isisaddons.module.security.facets.TenantedAuthorizationFacetFactory");


Alternatively, if using isis.properties, then define:


Font awesome icons

The actions for the security module do not include font-awesome icons by default; you will most likely want to choose your own icons.

The easiest way to do this is using the isis.reflector.facet.cssClassFa.patterns configuration property which uses the name of the action methods to associate an appropriate font-awesome icon.

The action names defined by the domain objects within the security module use the following naming conventions:

  • newXxx - create a new persisted object

  • findXxx - find an existing object

  • updateXxx - update an existing object

  • deleteXxx - delete an existing object

  • addXxx - add an existing object to a collection of another

  • removeXxx - remove an object from a collection

  • allXxx - for prototyping actions

There are also some other miscellaneous action names, eg:

  • lock - lock a user (to prevent that user from logging in)

  • unlock - unlock a user so that they can login

  • resetPassword - allow an administrator to reset the password for a user

  • me - to lookup the ApplicationUser entity for the currently logged-in user

For example, define the following configuration property:


Overriding the schema

By default the module’s entities will be installed in the isissecurity schema. This is hard-coded in their annotations.

This can be overridden by creating a .orm file. For example, to change the ApplicationUser table to reside in the "dbo" schema (to run on SQL Server, say), create an ApplicationUser-sqlserver.orm file. This should reside in the org.isisaddons.module.security.dom.user package:

<?xml version="1.0" encoding="UTF-8" ?>
<orm xmlns="http://xmlns.jcp.org/xml/ns/jdo/orm"

    <package name="org.isisaddons.module.security.dom.user">
        <class name="ApplicationUser"

Then, in persistor.properties, add:


This will cause DataNucleus to also search for ApplicationUser-sqlserver.orm files and use them if found.

Running demo against SQL Server

To run the demo application against SQL Server (as opposed to inmemory):

  • create a new and empty database, eg securitydemo, with corresponding user and password

  • edit the webapp/pom.xml to include the sqljdbc4 driver

  • edit the JDBC properties in persistor.properties file:

  • configure to auto-create schema/tables in empty database, by adding to persistor_datanucleus.properties:


"Out-of-the-box" (-SNAPSHOT)

If you want to use the current -SNAPSHOT, then the steps are the same as above, except:

  • when updating the classpath, specify the appropriate -SNAPSHOT version:

  • add the repository definition to pick up the most recent snapshot (we use the Cloudbees continuous integration service). We suggest defining the repository in a <profile>:


Forking the repo

If instead you want to extend this module’s functionality, then we recommend that you fork this repo. The repo is structured as follows:

  • pom.xml - parent pom

  • app - the module implementation, depends on dom and fixture

  • dom - the module implementation, depends on Isis applib

  • fixture - fixtures, holding a sample domain objects and fixture scripts; depends on dom

  • integtests - integration tests for the module; depends on app

  • webapp - demo webapp (see above screenshots); depends on app

Only the dom project is released to Maven Central Repo. The versions of the other modules are purposely left at 0.0.1-SNAPSHOT because they are not intended to be released.

API and Implementation

The module defines a number of services and default implementations. The behaviour of the module can be adjusted by implementing and registerng alternative implementations.


The PasswordEncryptionService (responsible for authenticating local user accounts) is responsible for performing a one-way encryption of password to encrypted form. This encrypted version is then stored in the ApplicationUser entity’s encryptedPassword property.

The service defines the following API:

public interface PasswordEncryptionService {
    public String encrypt(final String password);
    public boolean matches(final String candidate, final String encrypted);

The PasswordEncryptionServiceUsingJbcrypt provides an implementation of this service based on Blowfish algorithm. It depends in turn on org.mindrot:jbcrypt library; see above for details of updating the classpath to reference this library.


The PermissionsEvaluationService is responsible for determining which of a number of possibly conflicting permissions apply to a target member. It defines the following API:

public interface PermissionsEvaluationService {
    public ApplicationPermissionValueSet.Evaluation evaluate(
                final ApplicationFeatureId targetMemberId,
                final ApplicationPermissionMode mode,
                final Collection<ApplicationPermissionValue> permissionValues);

It is not necessary to register any implementation of this service in isis.properties; by default a strategy of allow-beats-veto is applied. However this strategy can be explicitly specified by registering the (provided) PermissionsEvaluationServiceAllowBeatsVeto implementation, or alternatively it can be reversed by registering PermissionsEvaluationServiceVetoBeatsAllow. Of course some other implementation with a different algorithm may instead be registered.

Default Roles, Permissions and Users

Whenever the application starts the security module checks for (and creates if missing) the following roles, permissions and users:

  • isis-module-security-admin role

    • allow changing of all classes (recursively) under the org.isisaddons.module.security.app package

    • allow changing of all classes (recursively) under the org.isisaddons.module.security.dom package

  • isis-module-security-regular-user role

    • allow changing (ie invocation) of the org.isisaddons.module.security.app.user.MeService#me action

    • allow viewing of the org.isisaddons.module.security.app.dom.ApplicationUser class

    • allow changing of the selected "self-service" actions of the org.isisaddons.module.security.app.dom.ApplicationUser class

  • isis-module-security-fixture role

    • allow changing of org.isisaddons.module.security.fixture package (run example fixtures if prototyping)

  • isis-module-security-admin user

    • granted isis-module-security-admin role

  • isis-applib-fixtureresults role

    • allow changing of org.apache.isis.applib.fixturescripts.FixtureResult class

This work is performed by the SeedSecurityModuleService.

Future Directions/Possible Improvements

Limitations in current implementation:

  • It is not possible to set permissions on the root package. The workaround is to specify for org or com top-level package instead.

Ideas for future features:

  • enhance the auto-creation of delegated user accounts, so that an initial role can be assigned and the user left as enabled

  • users could possibly be extended to include user settings, refactored out from isis-module-settings

  • features could possibly be refactored out/merged with isis-module-devutils.

  • hierarchical roles

Change Log

  • 1.14.0 - released against Isis 1.14.0

  • 1.13.6 - released against Isis 1.13.0, closes #47 (change ApplicationUser#getTenancy() to just `#getAtPath()) and #51 (fixes breakage in 1.13.3 thru 1.13.5 where default ApplicatoinTenancyPathEvaluator not correctly installed)

  • 1.13.5 - released against Isis 1.13.0, closes #30 (clone user) and #49 (disable autocreate of delegate users)

  • 1.13.4 - released against Isis 1.13.0, closes #41 (action semantics) and #42 (bulk deletion of orphaned permissions)

  • 1.13.3 - released against Isis 1.13.0, closes #40 (generalized ApplicationTenancyPathEvaluator SPI)

  • 1.13.2 - released against Isis 1.13.0, closes #39 (rename orphanedPermissions to findOrphanedPermissions)

  • 1.13.1 - released against Isis 1.13.0, closes #38 (list all orphaned permissions)

  • 1.13.0 - released against Isis 1.13.0

  • 1.12.2 - released against Isis 1.12.1; closes #32 (can’t create new user)

  • 1.12.1 - released against Isis 1.12.1; converted to use 1.12.x constructs

  • 1.12.0 - released against Isis 1.12.0

  • 1.11.0 - released against Isis 1.11.0

  • 1.10.0 - released against Isis 1.10.0

  • 1.9.0 - released against Isis 1.9.0; closes #18, #17, #16, #15, #14, #13, #12 (mapping entities to 'isissecurity' schema);

  • 1.8.1 - released against Isis 1.8.0; closes #11.

  • 1.8.0 - released against Isis 1.8.0. ApplicationTenancy extended to support hierarchical tenancies, with path as primary key (nb: breaking change), support to make easier to extend (pluggable factories and events for all actions). MeService on TERTIARY menuBar; #10

  • 1.7.0 - released against Isis 1.7.0

  • 1.6.2 - made more resilient so can be called by an application’s own 'security seed' service

  • 1.6.1 - support for account types and delegated authentication realm

  • 1.6.0 - first release


Copyright 2014-date Dan Haywood

Licensed under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at


Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.


In addition to Apache Isis, this module depends on:

  • org.mindrot:jbcrypt (Apache-like license); only required if the PasswordEncryptionServiceUsingJBcrypt service is configured.

Maven deploy notes

Only the dom module is deployed, and is done so using Sonatype’s OSS support (see user guide).

Release to Sonatype’s Snapshot Repo

To deploy a snapshot, use:

pushd dom
mvn clean deploy

The artifacts should be available in Sonatype’s Snapshot Repo.

Release an Interim Build

If you have commit access to this project (or a fork of your own) then you can create interim releases using the interim-release.sh script.

The idea is that this will - in a new branch - update the dom/pom.xml with a timestamped version (eg It then pushes the branch (and a tag) to the specified remote.

A CI server such as Jenkins can monitor the branches matching the wildcard origin/interim/* and create a build. These artifacts can then be published to a snapshot repository.

For example:

sh interim-release.sh 1.14.0 origin


  • 1.13.6 is the base release

  • origin is the name of the remote to which you have permissions to write to.

Release to Maven Central

The release.sh script automates the release process. It performs the following:

  • performs a sanity check (mvn clean install -o) that everything builds ok

  • bumps the pom.xml to a specified release version, and tag

  • performs a double check (mvn clean install -o) that everything still builds ok

  • releases the code using mvn clean deploy

  • bumps the pom.xml to a specified release version

For example:

sh release.sh 1.14.0 \
              1.15.0-SNAPSHOT \
              dan@haywood-associates.co.uk \
              "this is not really my passphrase"

where * $1 is the release version * $2 is the snapshot version * $3 is the email of the secret key (~/.gnupg/secring.gpg) to use for signing * $4 is the corresponding passphrase for that secret key.

Other ways of specifying the key and passphrase are available, see the `pgp-maven-plugin’s documentation).

If the script completes successfully, then push changes:

git push origin master && git push origin 1.14.0

If the script fails to complete, then identify the cause, perform a git reset --hard to start over and fix the issue before trying again. Note that in the dom’s `pom.xml the nexus-staging-maven-plugin has the autoReleaseAfterClose setting set to true (to automatically stage, close and the release the repo). You may want to set this to false if debugging an issue.

According to Sonatype’s guide, it takes about 10 minutes to sync, but up to 2 hours to update search.