= Shiro (Authenticator & Authorizor)

:Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you 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. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR  CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
:page-partial:


This guide describes the design and configuration of the Apache Shiro integration with Apache Isis.


== Design

The Shiro integration provides an implementation for both the
xref:refguide:core:index/security/authentication/Authenticator.adoc[Authenticator] and xref:refguide:core:index/security/authorization/Authorizor.adoc[Authorizor] SPIs.
These both delegate to Shiro's `SubjectUtils` class that in turn delegates to the `SecurityManager`.
These are available as thread-locals (set up in a servlet filter):

.High-level design of the Shiro integration
image::shiro-design.drawio.svg[]

Shiro's link:http://shiro.apache.org/static/latest/apidocs/org/apache/shiro/subject/Subject.html[Subject] API defines the notion of a user, and uses the concept of a https://shiro.apache.org/static/1.7.1/apidocs/org/apache/shiro/realm/Realm.html[Realm] as the means to authenticate the ``Subject``s and optionally populate it with permissions.

Shiro ships with a simple text-based realm -- the `IniRealm` -- which reads users (and password), user roles and role permissions from the `shiro.ini` file.
Configuring this realm is described <<shiro-ini-realm,below>>

TIP: The xref:docs:starters:helloworld.adoc[HelloWorld] and xref:docs:starters:simpleapp.adoc[SimpleApp] starter apps are both configured to use this realm.

A more sophisticated option is the LDAP realm.
Shiro has its own implementation which is extended in the xref:security:shiro-realm-ldap:about.adoc[LDAP realm]
This realm also supports permissions.

Permissions are ultimately represented as strings, and their interpretation of them is the responsibility of `AuthorizorShiro`.
An enhanced "wildcard" syntax, described  <<enhanced-wildcard-permission,below>> in more detail, incorporates the notion of read and write permissions, and also of vetoing permissions (as well as the usual allow permissions).


[[configuring-isis-to-use-shiro]]
== Configuring to use Shiro

Apache Isis' security mechanism is configurable, specifying an `Authenticator` and an `Authorizor` (non-public) APIs.
The Shiro security mechanism is an integration with Apache Shiro that implements both interfaces.

[TIP]
====
Both the xref:docs:starters:helloworld.adoc[HelloWorld] and xref:docs:starters:simpleapp.adoc[SimpleApp] starter apps are pre-configured to use Apache Shiro, so much of what follows may well have been set up already.
====

include::docs:mavendeps:partial$setup-and-configure-mavendeps-webapp.adoc[leveloffset=+1]


=== Update AppManifest

In your application's `AppManifest` (top-level Spring `@Configuration` used to bootstrap the app), import the

[source,java]
.AppManifest.java
----
@Configuration
@Import({
        ...
        IsisModuleSecurityShiro.class,
        ...
})
public class AppManifest {
}
----

Make sure that no other `IsisModuleSecurityXxx` module is imported.

=== Configuration Properties

The Shiro integration supports the following config properties:

* xref:refguide:config:sections/isis.security.shiro.adoc#isis.security.shiro.auto-logout-if-already-authenticated[isis.security.shiro.auto-logout-if-already-authenticated]

=== Shiro Realms and shiro.ini

Shiro uses the `shiro.ini` file for configuration, which resides in the default package (in other words, in `src/main/resources` in the webapp module).

Shiro uses the concept of realms to define its own set of authenticated users and their roles, and this is the most important configuration specified in the `shiro.ini` file.
Either one or many realms can be configured.

For example:

[source,ini]
----
securityManager.realms = $realmName
----

where `$realmName` in the above example is a reference to a realm defined elsewhere in `shiro.ini`.
This is an example of Shiro's "poor-man's" dependency injection (link:https://shiro.apache.org/configuration.html[their words]).

It's also possible to configure Shiro to support multiple realms.

[source,ini]
----
securityManager.realms = $realm1,$realm2
----

How to configure the text-based ini realm is explained <<shiro-ini-realm,below>>.
Another option alternative is the xref:security:shiro-realm-ldap:about.adoc[LDAP realm].


As noted above, as well as realms many other aspects of configuration can be specified in this file:

* enable caching for performance; discussed <<caching,below>>
* leverage Apache Isis' <<enhanced-wildcard-permission,enhanced wildcard permissions>>, by specifying the Apache Isis permission resolver; discussed <<permission-resolver-configuration,below>>.


[#shiro-ini-realm]
== Shiro Ini Realm

The Shiro concept of a `Realm` allows different implementations of both the authentication and authorisation mechanism to be plugged in.

The simplest realm to use is Shiro's built-in `IniRealm`, which reads from the (same) `shiro.ini` file.

image::ini/shiro-ini-realm.drawio.svg[]

This is suitable for prototyping, but isn't intended for production use, if only because user/password credentials are stored in plain text.
Nevertheless, it's a good starting point.
The app generated by both the xref:docs:starters:helloworld.adoc[HelloWorld] and xref:docs:starters:simpleapp.adoc[SimpleApp] starter apps are configured to use this realm.



=== Shiro Configuration

To use the built-in `IniRealm`, we add the following to `shiro.ini`:

[source,ini]
----
securityManager.realms = $iniRealm
----

(Unlike other realms) there is no need to "define" `$iniRealm`; it is automatically available to us.

Specifying `$iniRealm` means that the usernames/passwords, roles and permissions are read from the `shiro.ini` file itself.
Specifically:

* the users/passwords and their roles from the `[users]` sections;
* the roles are mapped to permissions in the `[roles]` section.

The format of these is described below.

==== `[users]` section

This section lists users, passwords and their roles.

For example:

[source,ini]
----
sven = pass, admin_role
dick = pass, user_role, analysis_role, self-install_role
bob  = pass, user_role, self-install_role
----

The first value is the password (eg "pass", the remaining values are the role(s).

==== `[roles]` section

This section lists roles and their corresponding permissions.

For example:

[source,ini]
----
user_role = myapp.*,\
            isis.security:*,\
            isis.applib:*
admin_role = *
----

The value is a comma-separated list of permissions for the role.
The format is:

[source,ini]
----
logicalTypeNamespace:logicalTypeSimpleName:memberName:r,w
----

where:

* `logicalTypeNamespace` is the namespace portion of the domain object's logical type name ...

* \... and `logicalTypeSimpleName` is the last portion of the domain object's logical type name.
+
For example, if `@Named("myapp.customer.Customer")`, then the namespace is "myapp.customer" and the simple type name is "Customer".

* `memberName` is the property, collection or action name.
* `r` indicates that the member is visible
* `w` indicates that the member is usable (editable or invokable)

Note that:

* each part of the permission string can be wildcarded using `*`.
* The namespace can also be wildcarded at any level (for example `myapp.*`).
* Missing levels assume wildcards.

Thus:

[source,ini]
----
myapp.customer:Customer:firstName:r,w   # view or edit customer's firstName
myapp.customer:Customer:lastName:r      # view customer's lastName only
myapp.customer:Customer:placeOrder:*    # view and invoke placeOrder action
myapp.customer:Customer:placeOrder      # ditto
myapp.customer:Customer:*:r             # view all customer class members
myapp.customer:*:*:r                    # view-only access for myapp.customer namespace
myapp.customer:*:*:*                    # view/edit for myapp.customer namespace
myapp:*:*                               # view/edit for myapp namespace
myapp:*                                 # ditto
myapp                                   # ditto
*                                       # view/edit access to everything
----

[TIP]
====
The format of the permissions string is configurable in Shiro, and Apache Isis uses this to provide an extended wildcard format, described xref:security:shiro:about.adoc#enhanced-wildcard-permission[here].
====


== Providing permissions to Framework-provided Features

Some features of the framework are exposed as actions that must be provided as permissions.
In particular, permission to the features in `isis.security` must be granted in order that end-users can logout.

The snippet below defines a role for each framework feature:

[source,ini]
.shiro.ini
----
[roles]
default_role   = isis.applib,\
                 isis.security
fixtures_role  = isis.testing.fixtures
features_role  = isis.feat
metamodel_role = isis.metamodel
h2_role        = isis.ext.h2Console
jdo_role       = isis.persistence.jdo
swagger_role   = isis.viewer.restfulobjects
conf_role      = isis.conf
sudo_role      = isis.sudo
----

Notes:

* all users should be granted the `default_role`.
* `conf_role` provides access to the configuration menu (in production mode), which is potentially sensitive
* `sudo_role` provides the ability to impersonate any user, so is _extremely_ sensitive; however it is prototype mode only

Most of the features protected by these roles are only available in prototype mode.
The exceptions are those under `default_role` and `conf_role`.


=== Externalized IniRealm

There's no requirement for all users/roles to be defined in the `shiro.ini` file.
Instead, a realm can be defined that loads its users/roles from some other resource.

For example:

[source,ini]
----
$realm1=org.apache.shiro.realm.text.IniRealm # <1>
realm1.resourcePath=classpath:webapp/realm1.ini # <2>
----
<1> happens to (coincidentally) be the link:http://shiro.apache.org/static/latest/apidocs/org/apache/shiro/realm/text/IniRealm.html[same implementation] as Shiro's built-in $iniRealm
<2> in this case load the users/roles from the `src/main/resources/webapp/realm1.ini` file.

Note that a URL could be provided as the `resourcePath`, so a centralized config file could be used.
Even so, the

[NOTE]
====
If configured this way then the `[users]` and `[roles]` sections of `shiro.ini` become unused.
Instead, the corresponding sections from for `realm1.ini` are used instead.
====




[#enhanced-wildcard-permission]
== Enhanced Wildcard Permission

If using the text-based xref:security:shiro:about.adoc#shiro-ini-realm[IniRealm] or xref:security:shiro-realm-ldap:about.adoc[Apache Isis' LDAP realm], then note that Shiro also allows the string representation of the permissions to be mapped (resolved) to alternative `Permission` instances.
Apache Isis provides its own `IsisPermission` which introduces the concept of a "veto".

A vetoing permission is one that prevents access to a feature, rather than grants it.
This is useful in some situations where most users have access to most features, and only a small number of features are particularly sensitive.
The configuration can therefore be set up to grant fairly broad-brush permissions and then veto permission for the sensitive features for those users that do not have access.

The string representation of the `IsisPermission` uses the following format:

[source,ini]
----
(?<vetoFlag>[!]?)(?:(?<permissionGroup>[^\/]+)[\/])?(?<permission>.+)
----

where:

* the optional `!` prefix indicates this permission is a vetoing permission
* the mandatory `xxx/` prefix is a permission group that scopes any vetoing permissions
* the remainder of the string is the permission (possibly wild-carded, with :rw as optional suffix)

TIP: Use an online regex tester, eg link:https://regex101.com/[] to get an idea of how this works.

For example:

[source,ini]
----
user_role   = !reg/myapp.api,\
              !reg/myapp.webapp.services.admin,\
              reg/*
api_role    = myapp.api
admin_role  = adm/*
----

sets up:

* the `user_role` with access to all permissions except those with a logical type's namespace of `myapp.api` or `myapp.webapp.services.admin`
* the `api_role` with access to all permissions to logical types under the namespace `myapp.api`
* the `admin_role` with access to everything.

The permission group concept is required to scope the applicability of any veto permission.
This is probably best explained by an example.
Suppose that a user has both `admin_role` and `user_role`; we would want the `admin_role` to trump the vetos of the `user_role`, in other words to give the user access to everything.

:asterisk: *
Because of the permission groups, the two `!reg/...` vetos in `user_role` only veto out selected permissions granted by the ``reg/{asterisk}`` permissions, but they do not veto the permissions granted by a different scope, namely `adm/*`.
In this case the prefixes in ``reg/{asterisk}`` and ``adm/{asterisk}`` are required to make the patterns unique.

The net effect is therefore what we would want: that a user with both `admin_role` and `user_role` would have access to everything, irrespective of those two veto permissions of the `user_role`.

[[permission-resolver-configuration]]
=== Configuration

To configure Apache Isis' extended permission support requires that a custom permission resolver is specified in `shiro.ini` file:

[source,ini]
----
permissionResolver = org.apache.isis.security.shiro.authorization.IsisPermissionResolver
myRealm.permissionResolver = $permissionResolver  # <.>
----
<.> `myRealm` is the handle to the configured realm, eg `$iniRealm` or `$isisLdapRealm` etc.



[#caching]
== Caching

To ensure that security operations does not impede performance, Shiro supports caching.
For example, this sets up a simple memory-based cache manager:

[source,ini]
----
memoryCacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager
securityManager.cacheManager = $memoryCacheManager
----

Other implementations can be plugged in; see the Shiro link:http://shiro.apache.org/caching.html[documentation] for further details.

== Further Reading

Shiro provides many other features.
Check out:

* Shiro's documentation page can be found link:http://shiro.apache.org/documentation.html[here].

* community-contributed articles can be found link:http://shiro.apache.org/articles.html[here]. +
+
These include for instance link:http://meri-stuff.blogspot.co.uk/2011/04/apache-shiro-part-2-realms-database-and.html[this interesting article] describing how to perform certificate-based authentication (ie login using Google or Facebook credentials).

