blob: 31b8bd1d064fd0a3b03c4bc21cfbfe63f08d5ecb [file] [log] [blame]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Shiro Security :: Apache Isis</title>
<link rel="canonical" href="https://isis.apache.org/security/2.0.0-M3/shiro/about.html">
<meta name="generator" content="Antora 2.2.0">
<link rel="stylesheet" href="../../../_/css/site.css">
<link rel="stylesheet" href="../../../_/css/site-custom.css">
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,700,700i|Raleway:300,400,500,700,800|Montserrat:300,400,700" rel="stylesheet">
<link rel="home" href="https://isis.apache.org" title="Apache Isis">
<link rel="next" href="../keycloak/about.html" title="Keycloak Implementation">
<link rel="prev" href="../bypass/about.html" title="Bypass Implementation">
</head>
<body class="article">
<header class="header">
<nav class="navbar">
<div class="navbar-brand">
<a class="navbar-item" href="https://isis.apache.org">
<span class="icon">
<img src="../../../_/img/isis-logo-48x48.png"></img>
</span>
<span>Apache Isis</span>
</a>
<button class="navbar-burger" data-target="topbar-nav">
<span></span>
<span></span>
<span></span>
</button>
</div>
<div id="topbar-nav" class="navbar-menu">
<a class="navbar-end">
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="#">Quick Start</a>
<div class="navbar-dropdown">
<span class="navbar-item navbar-heading">Starter Apps</span>
<a class="navbar-item" href="../../../docs/latest/starters/helloworld.html">Hello World</a>
<a class="navbar-item" href="../../../docs/latest/starters/simpleapp.html">Simple App</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Demos &amp; Tutorials</span>
<a class="navbar-item" href="../../../docs/latest/demo/about.html">Demo App</a>
<a class="navbar-item" href="https://danhaywood.gitlab.io/isis-petclinic-tutorial-docs/petclinic/1.16.2/intro.html">Petclinic (tutorial)</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Resources</span>
<a class="navbar-item" href="../../../docs/latest/resources/cheatsheet.html">Cheatsheet</a>
<a class="navbar-item" href="../../../docs/latest/resources/icons.html">Icons</a>
</div>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="#">Guides</a>
<div class="navbar-dropdown">
<span class="navbar-item navbar-heading">Development</span>
<a class="navbar-item" href="../../../setupguide/latest/about.html">Setup Guide</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Core</span>
<a class="navbar-item" href="../../../userguide/latest/about.html">User Guide</a>
<a class="navbar-item" href="../../../refguide/latest/about.html">Reference Guide</a>
<a class="navbar-item" href="../../../testing/latest/about.html">Testing Guide</a>
</div>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="#">Libraries</a>
<div class="navbar-dropdown">
<span class="navbar-item navbar-heading">For Use in Apps</span>
<a class="navbar-item" href="../../../subdomains/latest/about.html">Subdomain Libraries</a>
<a class="navbar-item" href="../../../valuetypes/latest/about.html">Value Types</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Integrate between Apps</span>
<a class="navbar-item" href="../../../mappings/latest/about.html">Bounded Context Mapping Libraries</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Other</span>
<a class="navbar-item" href="../../../incubator/latest/about.html">Incubator</a>
<a class="navbar-item" href="../../../legacy/latest/about.html">Legacy</a>
</div>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="#">Components</a>
<div class="navbar-dropdown">
<span class="navbar-item navbar-heading">Viewers</span>
<a class="navbar-item" href="../../../vw/latest/about.html">Wicket UI</a>
<a class="navbar-item" href="../../../vro/latest/about.html">Restful Objects (REST)</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Security</span>
<a class="navbar-item" href="../../../security/latest/about.html">Security Guide</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Persistence</span>
<a class="navbar-item" href="../../../pjdo/latest/about.html">DataNucleus (JDO)</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Extensions</span>
<a class="navbar-item" href="../../../extensions/latest/about.html">Extensions Catalog</a>
</div>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="#">Support</a>
<div class="navbar-dropdown">
<span class="navbar-item navbar-heading">Contact</span>
<a class="navbar-item" href="../../../docs/latest/support/slack-channel.html">Slack</a>
<a class="navbar-item" href="../../../docs/latest/support/mailing-list.html">Mailing Lists</a>
<a class="navbar-item" href="https://issues.apache.org/jira/browse/ISIS">JIRA</a>
<a class="navbar-item" href="https://stackoverflow.com/questions/tagged/isis">Stack Overflow</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Releases</span>
<a class="navbar-item" href="../../../docs/latest/downloads/how-to.html">Downloads</a>
<a class="navbar-item" href="../../../relnotes/latest/about.html">Release Notes</a>
<a class="navbar-item" href="../../../docs/latest/archive/1-x.html">Archive (1.x)</a>
<hr class="navbar-divider"/>
<span class="navbar-item navbar-heading">Framework</span>
<a class="navbar-item" href="../../../conguide/latest/about.html">Contributors' Guide</a>
<a class="navbar-item" href="../../../comguide/latest/about.html">Committers' Guide</a>
<a class="navbar-item" href="../../../core/latest/about.html">Core Design</a>
</div>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link" href="#">ASF</a>
<div class="navbar-dropdown">
<a class="navbar-item" href="http://www.apache.org/">Apache Homepage</a>
<a class="navbar-item" href="https://www.apache.org/events/current-event">Events</a>
<a class="navbar-item" href="https://www.apache.org/licenses/">Licenses</a>
<a class="navbar-item" href="https://www.apache.org/security/">Security</a>
<a class="navbar-item" href="https://www.apache.org/foundation/sponsorship.html">Sponsorship</a>
<a class="navbar-item" href="https://www.apache.org/foundation/thanks.html">Thanks</a>
<hr class="navbar-divider"/>
<a class="navbar-item" href="https://whimsy.apache.org/board/minutes/Isis.html">PMC board minutes</a>
</div>
</div>
<a class="navbar-item" href="../../../docs/latest/about.html">
<span class="icon">
<img src="../../../_/img/home.png"></img>
</span>
</a>
</div>
</div>
</nav>
</header>
<div class="body ">
<div class="nav-container" data-component="security" data-version="2.0.0-M3">
<aside class="nav">
<div class="panels">
<div class="nav-panel-pagination">
<a class="page-previous" rel="prev" href="../bypass/about.html" title="Bypass Implementation"><span></span></a>
<a class="page-next" rel="next"
href="../keycloak/about.html" title="Keycloak Implementation"><span></span></a>
<!--
page.parent doesn't seem to be set...
<a class="page-parent disabled" rel="prev" href="" title="Bypass Implementation"><span></span></a>
-->
</div>
<div class="nav-panel-menu is-active" data-panel="menu">
<nav class="nav-menu">
<h3 class="title"><a href="../about.html">Security Guide</a></h3>
<ul class="nav-list">
<li class="nav-item" data-depth="0">
<ul class="nav-list">
<li class="nav-item" data-depth="1">
<a class="nav-link" href="../domain-services-api-for-applications.html">Domain Services API</a>
</li>
</ul>
</li>
<li class="nav-item" data-depth="0">
<ul class="nav-list">
<li class="nav-item" data-depth="1">
<a class="nav-link" href="../core/about.html">API</a>
</li>
<li class="nav-item" data-depth="1">
<a class="nav-link" href="../bypass/about.html">Bypass Implementation</a>
</li>
<li class="nav-item is-current-page" data-depth="1">
<a class="nav-link" href="about.html">Shiro Implementation</a>
</li>
<li class="nav-item" data-depth="1">
<a class="nav-link" href="../keycloak/about.html">Keycloak Implementation</a>
</li>
<li class="nav-item" data-depth="1">
<a class="nav-link" href="../usage-by-isis-viewers.html">Usage by Isis Viewers</a>
</li>
<li class="nav-item" data-depth="1">
<button class="nav-item-toggle"></button>
<span class="nav-text">Extensions</span>
<ul class="nav-list">
<li class="nav-item" data-depth="2">
<a class="nav-link" href="../audit-trail/about.html">Audit Trail</a>
</li>
<li class="nav-item" data-depth="2">
<a class="nav-link" href="../shiro-realm-ldap/about.html">LDAP Realm for Shiro</a>
</li>
<li class="nav-item" data-depth="2">
<button class="nav-item-toggle"></button>
<a class="nav-link" href="../about.html">Secman</a>
<ul class="nav-list">
<li class="nav-item" data-depth="3">
<a class="nav-link" href="#security:ROOT:api.adoc">API</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="#security:ROOT:model.adoc">Model</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="#security:ROOT:jbcrypt-encryption.adoc">JBCrypt Encryption</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="#security:ROOT:jdo-persistence.adoc">JDO Persistence</a>
</li>
<li class="nav-item" data-depth="3">
<a class="nav-link" href="#security:ROOT:shiro-realm.adoc">Realm (for Shiro)</a>
</li>
</ul>
</li>
<li class="nav-item" data-depth="2">
<a class="nav-link" href="../session-log/about.html">Session Log</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</nav>
</div>
<div class="nav-panel-explore" data-panel="explore">
<div class="context">
<span class="title">Security Guide</span>
<span class="version">2.0.0-M3</span>
</div>
<ul class="components">
<li class="component">
<span class="title"> </span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../docs/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">BC Mappings Catalog</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../mappings/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Committers' Guide</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../comguide/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Contributors' Guide</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../conguide/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Design Docs</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../core/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Extensions Catalog</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../extensions/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Incubator Catalog</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../incubator/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">JDO/DataNucleus</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../pjdo/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Legacy Catalog</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../legacy/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Reference Guide</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../refguide/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Release Notes</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../relnotes/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Restful Objects Viewer</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../vro/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component is-current">
<span class="title">Security Guide</span>
<ul class="versions">
<li class="version is-current is-latest">
<a href="../about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Setup Guide</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../setupguide/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Subdomains Catalog</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../subdomains/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Testing Guide</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../testing/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">User Guide</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../userguide/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Value Types Catalog</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../valuetypes/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
<li class="component">
<span class="title">Wicket Viewer</span>
<ul class="versions">
<li class="version is-latest">
<a href="../../../vw/2.0.0-M3/about.html">2.0.0-M3</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
</aside>
</div>
<main role="main">
<div class="toolbar" role="navigation">
<button class="nav-toggle"></button>
<a href="../../../docs/2.0.0-M3/about.html" class="home-link"></a>
<nav class="breadcrumbs" aria-label="breadcrumbs">
<ul>
<li><a href="../about.html">Security Guide</a></li>
<li><a href="about.html">Shiro Implementation</a></li>
</ul>
</nav>
<div class="edit-this-page"><a href="https://github.com/apache/isis/edit/2.0.0-M3/security/shiro/src/main/adoc/modules/shiro/pages/about.adoc">Edit</a></div>
</div>
<article class="doc">
<a name="section-top"></a>
<h1 class="page">Shiro Security</h1>
<div id="preamble">
<div class="sectionbody">
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
TODO: this content has not yet been reviewed/updated for v2.0
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>This guide describes the configuration of the Shiro implementation of Apache Isis' <code>Authenticator and `Authorizor</code> APIs.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="configuring-isis-to-use-shiro"><a class="anchor" href="#configuring-isis-to-use-shiro"></a>Configuring to use Shiro</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Apache Isis' security mechanism is configurable, specifying an <code>Authenticator</code> and an <code>Authorizor</code> (non-public) APIs.
The Shiro security mechanism is an integration wih Apache Shiro that implements both interfaces.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>Both the <a href="../../../docs/2.0.0-M3/starters/helloworld.html" class="page">HelloWorld</a> and <a href="../../../docs/2.0.0-M3/starters/simpleapp.html" class="page">SimpleApp</a> starter apps are pre-configured to use Apache Shiro, so much of what follows may well have been set up already.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="sect2">
<h3 id="telling-apache-isis-to-use-shiro"><a class="anchor" href="#telling-apache-isis-to-use-shiro"></a>Telling Apache Isis to use Shiro</h3>
<div class="paragraph">
<p>To tell Apache Isis to use Shiro, include <code>IsisModuleSecurityShiro</code> module in the top-level "app manifest".</p>
</div>
<div class="paragraph">
<p>For example, the <a href="../../../docs/2.0.0-M3/starters/simpleapp.html" class="page">SimpleApp</a> starter app bootstraps using:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Configuration
@Import({
// ...
IsisModuleSecurityShiro.class, <i class="conum" data-value="1"></i><b>(1)</b>
// ...
})
// ...
public class AppManifest {
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>configures Shiro.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>This installs the appropriate implementation (the <code>ShiroAuthenticatorOrAuthorizor</code> class) that use Shiro&#8217;s APIs to perform authentication and authorization:</p>
</div>
<div class="imageblock">
<div class="content">
<a class="image" href="_images/configuration/configure-isis-to-use-shiro.png"><img src="_images/configuration/configure-isis-to-use-shiro.png" alt="configure isis to use shiro" width="600px"></a>
</div>
</div>
<div class="paragraph">
<p>The figure above doesn&#8217;t tell the whole story; we haven&#8217;t yet seen how Shiro itself is configured to use realms.
The <code>ShiroAuthenticatorOrAuthorizor</code> is in essence the glue between the Apache Isis runtime and Shiro.</p>
</div>
</div>
<div class="sect2">
<h3 id="configuring-shiro-authenticator"><a class="anchor" href="#configuring-shiro-authenticator"></a>Configuring Shiro Authenticator</h3>
<div class="paragraph">
<p>The <code>ShiroAuthenticatorOrAuthorizor</code> class itself supports a single optional property.
This can be configured in <code>authentication_shiro.properties</code> file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ini hljs" data-lang="ini">isis.security.shiro.autoLogoutIfAlreadyAuthenticated=false</code></pre>
</div>
</div>
<div class="paragraph">
<p>This configuration property only comes into effect for the <a href="../../../vro/2.0.0-M3/about.html" class="page">Restful Objects viewer</a>; if set then the Shiro subject - if found to be still authenticated - will be logged out anyway and then re-authenticated.</p>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
<div class="paragraph">
<p>There should generally be no need to change this property from its default (<code>false</code>).
Setting it to <code>true</code> may cause a race condition resulting in exceptions being logged.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="bootstrapping-shiro"><a class="anchor" href="#bootstrapping-shiro"></a>Bootstrapping Shiro</h3>
<div class="paragraph">
<p>The Shiro environment (in essence, thread-locals holding the security credentials) needs to be bootstrapped using the following settings in the <code>WEB-INF/web.xml</code> file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;listener&gt;
&lt;listener-class&gt;org.apache.shiro.web.env.EnvironmentLoaderListener&lt;/listener-class&gt;
&lt;/listener&gt;
&lt;filter&gt;
&lt;filter-name&gt;ShiroFilter&lt;/filter-name&gt;
&lt;filter-class&gt;org.apache.shiro.web.servlet.ShiroFilter&lt;/filter-class&gt;
&lt;/filter&gt;
&lt;filter-mapping&gt;
&lt;filter-name&gt;ShiroFilter&lt;/filter-name&gt;
&lt;url-pattern&gt;/*&lt;/url-pattern&gt;
&lt;/filter-mapping&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>Based on this Shiro will then read <code>WEB-INF/shiro.ini</code> file to configure its Realm definitions for authentication and authorization.</p>
</div>
</div>
<div class="sect2">
<h3 id="web-infshiro-ini"><a class="anchor" href="#web-infshiro-ini"></a><code>WEB-INF/shiro.ini</code></h3>
<div class="paragraph">
<p>The <code>shiro.ini</code> file is used to specify the realm(s) that Shiro will delegate to:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ini hljs" data-lang="ini">securityManager.realms = $realmName</code></pre>
</div>
</div>
<div class="paragraph">
<p>Shiro&#8217;s ini file supports a "poor-man&#8217;s" dependency injection (<a href="https://shiro.apache.org/configuration.html">their words</a>), and so <code>$realmName</code> in the above example is a reference to a realm defined elsewhere in <code>shiro.ini</code>.
The subsequent sections describe the specifics for thevarious realm implementations available to you.</p>
</div>
<div class="paragraph">
<p>It&#8217;s also possible to configure Shiro to support multiple realms.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ini hljs" data-lang="ini">securityManager.realms = $realm1,$realm2</code></pre>
</div>
</div>
<div class="paragraph">
<p>You can learn more about Shiro realms in the <a href="http://shiro.apache.org/realm.html">Shiro documentation</a>.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="shiro-ini-realm"><a class="anchor" href="#shiro-ini-realm"></a>Shiro Ini Realm</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The Shiro concept of a <code>Realm</code> allows different implementations of both the authentication and authorisation mechanism to be plugged in.</p>
</div>
<div class="paragraph">
<p>Probably the simplest realm to use is Shiro&#8217;s built-in <code>IniRealm</code>, which reads from the (same) <code>WEB-INF/shiro.ini</code> file.</p>
</div>
<div class="paragraph">
<p>This is suitable for prototyping, but isn&#8217;t intended for production use, if only because user/password credentials are stored in plain text.
Nevertheless, it&#8217;s a good starting point.
The app generated by both the <a href="../../../docs/2.0.0-M3/starters/helloworld.html" class="page">HelloWorld</a> and <a href="../../../docs/2.0.0-M3/starters/simpleapp.html" class="page">SimpleApp</a> starter apps are configured to use this realm.</p>
</div>
<div class="paragraph">
<p>The diagram below shows the Isis and components involved:</p>
</div>
<div class="imageblock">
<div class="content">
<a class="image" href="_images/configuration/configuring-shiro/ini/configure-shiro-to-use-ini-realm.PNG"><img src="_images/configuration/configuring-shiro/ini/configure-shiro-to-use-ini-realm.PNG" alt="configure shiro to use ini realm" width="600px"></a>
</div>
</div>
<div class="paragraph">
<p>The realm is responsible for validating the user credentials, and then creates a Shiro <a href="http://shiro.apache.org/static/latest/apidocs/org/apache/shiro/subject/Subject.html"><code>Subject</code></a> which represents the user (for the current request).
Apache Isis <code>Authenticator</code> component then interacts with the <code>Subject</code> in order to check permissions.</p>
</div>
<div class="sect2">
<h3 id="shiro-configuration"><a class="anchor" href="#shiro-configuration"></a>Shiro Configuration</h3>
<div class="paragraph">
<p>To use the built-in <code>IniRealm</code>, we add the following to <code>WEB-INF/shiro.ini</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ini hljs" data-lang="ini">securityManager.realms = $iniRealm</code></pre>
</div>
</div>
<div class="paragraph">
<p>(Unlike other realms) there is no need to "define" <code>$iniRealm</code>; it is automatically available to us.</p>
</div>
<div class="paragraph">
<p>Specifying <code>$iniRealm</code> means that the usernames/passwords, roles and permissions are read from the <code>shiro.ini</code> file itself.
Specifically:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>the users/passwords and their roles from the <code>[users]</code> sections;</p>
</li>
<li>
<p>the roles are mapped to permissions in the <code>[roles]</code> section.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The format of these is described below.</p>
</div>
<div class="sect3">
<h4 id="users-section"><a class="anchor" href="#users-section"></a><code>[users]</code> section</h4>
<div class="paragraph">
<p>This section lists users, passwords and their roles.</p>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ini hljs" data-lang="ini">sven = pass, admin_role
dick = pass, user_role, analysis_role, self-install_role
bob = pass, user_role, self-install_role</code></pre>
</div>
</div>
<div class="paragraph">
<p>The first value is the password (eg "pass", the remaining values are the role(s).</p>
</div>
</div>
<div class="sect3">
<h4 id="roles-section"><a class="anchor" href="#roles-section"></a><code>[roles]</code> section</h4>
<div class="paragraph">
<p>This section lists roles and their corresponding permissions.</p>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ini hljs" data-lang="ini">user_role = *:ToDoItems:*:*,\
*:ToDoItem:*:*,\
*:TodoAppDashboard:*:*
analysis_role = *:ToDoItemAnalysis:*:*,\
*:ToDoItemsByCategoryViewModel:*:*,\
*:ToDoItemsByDateRangeViewModel:*:*
self-install_role = *:ToDoItemsFixturesService:install:*
admin_role = *</code></pre>
</div>
</div>
<div class="paragraph">
<p>The value is a comma-separated list of permissions for the role. The format is:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ini hljs" data-lang="ini">packageName:className:memberName:r,w</code></pre>
</div>
</div>
<div class="paragraph">
<p>where:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>memberName</code> is the property, collection or action name.</p>
</li>
<li>
<p><code>r</code> indicates that the member is visible</p>
</li>
<li>
<p><code>w</code> indicates that the member is usable (editable or invokable)</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>and where each of the parts of the permission string can be wildcarded using <code>*</code>.</p>
</div>
<div class="paragraph">
<p>Because these are wildcards, a '*' can be used at any level. Additionally, missing levels assume wildcards.</p>
</div>
<div class="paragraph">
<p>Thus:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ini hljs" data-lang="ini">com.mycompany.myapp:Customer:firstName:r,w # view or edit customer's firstName
com.mycompany.myapp:Customer:lastName:r # view customer's lastName only
com.mycompany.myapp:Customer:placeOrder:* # view and invoke placeOrder action
com.mycompany.myapp:Customer:placeOrder # ditto
com.mycompany.myapp:Customer:*:r # view all customer class members
com.mycompany.myapp:*:*:r # view-only access for all classes in myapp package
com.mycompany.myapp:*:*:* # view/edit for all classes in myapp package
com.mycompany.myapp:*:* # ditto
com.mycompany.myapp:* # ditto
com.mycompany.myapp # ditto
* # view/edit access to everything</code></pre>
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="paragraph">
<p>The format of the permissions string is configurable in Shiro, and Apache Isis uses this to provide an extended wildcard format, described <a href="#security:ROOT:enhanced-wildcard-permission.adoc" class="page unresolved">here</a>.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="externalized-inirealm"><a class="anchor" href="#externalized-inirealm"></a>Externalized IniRealm</h3>
<div class="paragraph">
<p>There&#8217;s no requirement for all users/roles to be defined in the <code>shiro.ini</code> file.
Instead, a realm can be defined that loads its users/roles from some other resource.</p>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ini hljs" data-lang="ini">$realm1=org.apache.shiro.realm.text.IniRealm <i class="conum" data-value="1"></i><b>(1)</b>
realm1.resourcePath=classpath:webapp/realm1.ini <i class="conum" data-value="2"></i><b>(2)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>happens to (coincidentally) be the <a href="http://shiro.apache.org/static/latest/apidocs/org/apache/shiro/realm/text/IniRealm.html">same implementation</a> as Shiro&#8217;s built-in $iniRealm</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>in this case load the users/roles from the <code>src/main/resources/webapp/realm1.ini</code> file.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Note that a URL could be provided as the <code>resourcePath</code>, so a centralized config file could be used.
Even so, the</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>If configured this way then the <code>[users]</code> and <code>[roles]</code> sections of <code>shiro.ini</code> become unused.
Instead, the corresponding sections from for <code>realm1.ini</code> are used instead.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="shiro-jdbc-realm"><a class="anchor" href="#shiro-jdbc-realm"></a>Shiro JDBC Realm</h2>
<div class="sectionbody">
<div class="paragraph">
<p>There is nothing to stop you from using some other <code>Realm</code> implementation (or indeed writing one yourself). For example, you could use Shiro&#8217;s own JDBC realm that loads user/password details from a database.</p>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
<div class="paragraph">
<p>If you are happy to use a database then we strongly recommend you use the <a href="../about.html" class="page">SecMan extension</a> instead of a vanilla JDBC; it is far more sophisticated and moreover gives you the ability to administer the system from within your Apache Isis application.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>If you go down this route, then the architecture is as follows:</p>
</div>
<div class="imageblock">
<div class="content">
<a class="image" href="_images/configuration/jdbc/configuring-shiro/configure-shiro-to-use-custom-jdbc-realm.png"><img src="_images/configuration/configuring-shiro/jdbc/configure-shiro-to-use-custom-jdbc-realm.png" alt="configure shiro to use custom jdbc realm" width="600px"></a>
</div>
</div>
<div class="paragraph">
<p>There&#8217;s quite a lot of configuration required (in <code>WEB-INF/shiro.ini</code>) to set up a JDBC realm, so we&#8217;ll break it out into sections.</p>
</div>
<div class="paragraph">
<p>First, we need to set up the connection to JDBC:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ini hljs" data-lang="ini">jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm <i class="conum" data-value="1"></i><b>(1)</b>
jof = org.apache.shiro.jndi.JndiObjectFactory <i class="conum" data-value="2"></i><b>(2)</b>
jof.resourceName = jdbc/postgres <i class="conum" data-value="3"></i><b>(3)</b>
jof.requiredType = javax.sql.DataSource
jof.resourceRef = true
jdbcRealm.dataSource = $jof <i class="conum" data-value="4"></i><b>(4)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>instantiate the JDBC realm</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>instantiate factory object to lookup DataSource from servlet container</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>name of the datasource (as configured in <code>web.xml</code>)</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>instruct JDBC realm to obtain datasource from the JNDI</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>We next need to tell the realm how to query the database. Shiro supports any schema; what matters is the input search argument and the output results.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ini hljs" data-lang="ini">jdbcRealm.authenticationQuery = \ <i class="conum" data-value="1"></i><b>(1)</b>
select password \
from users \
where username = ?
jdbcRealm.userRolesQuery = \ <i class="conum" data-value="2"></i><b>(2)</b>
select r.label \
from users_roles ur \
inner join roles r \
on ur.role_id = r.id \
where user_id = ( \
select id \
from users \
where username = ?); \
jdbcRealm.permissionsQuery= \ <i class="conum" data-value="3"></i><b>(3)</b>
select p.permission \
from roles_permissions rp \
inner join permissions p \
on rp.permission_id = p.id \
where rp.role_id = ( \
select id \
from roles \
where label = ?);
jdbcRealm.permissionsLookupEnabled=true <i class="conum" data-value="4"></i><b>(4)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>query to find password for user</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>query to find roles for user</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>query to find permissions for role</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>enable permissions lookup</td>
</tr>
</table>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
<div class="paragraph">
<p>The <code>permissionsLookupEnabled</code> is very important, otherwise Shiro just returns an empty list of permissions and your users will have no access to any features(!).</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>We also should ensure that the passwords are not stored as plain-text:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ini hljs" data-lang="ini">dps = org.apache.shiro.authc.credential.DefaultPasswordService <i class="conum" data-value="1"></i><b>(1)</b>
pm = org.apache.shiro.authc.credential.PasswordMatcher <i class="conum" data-value="2"></i><b>(2)</b>
pm.passwordService = $dps
jdbcRealm.credentialsMatcher = $pm <i class="conum" data-value="3"></i><b>(3)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>mechanism to encrypts password</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>service to match passwords</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>instruct JDBC realm to use password matching service when authenticating</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>And finally we need to tell Shiro to use the realm, in the usual fashion:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ini hljs" data-lang="ini">securityManager.realms = $jdbcRealm</code></pre>
</div>
</div>
<div class="paragraph">
<p>Using the above configuration you will also need to setup a <code>DataSource</code>. The details vary by servlet container, for example this is <a href="https://tomcat.apache.org/tomcat-8.0-doc/jndi-datasource-examples-howto.html">how to do the setup on Tomcat 8.0</a>.</p>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
<div class="paragraph">
<p>The name of the <code>DataSource</code> can also vary by servlet container; see for example <a href="http://stackoverflow.com/questions/17441019/how-to-configure-jdbcrealm-to-obtain-its-datasource-from-jndi/23784702#23784702">this StackOverflow answer</a>.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="ldap-realm"><a class="anchor" href="#ldap-realm"></a>Ldap Realm</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Apache Isis ships with an implementation of <a href="http://shiro.apache.org">Apache Shiro</a>'s <code>Realm</code> class that allows user authentication and authorization to be performed against an LDAP server.</p>
</div>
<div class="imageblock">
<div class="content">
<a class="image" href="_images/configuration/configuring-shiro/ldap/configure-shiro-to-use-isis-ldap-realm.png"><img src="_images/configuration/configuring-shiro/ldap/configure-shiro-to-use-isis-ldap-realm.png" alt="configure shiro to use isis ldap realm" width="600px"></a>
</div>
</div>
<div class="paragraph">
<p>The LDAP database stores the user/passwords and user groups, while the <code>shiro.ini</code> file is used to map the LDAP groups to roles, and to map the roles to permissions.</p>
</div>
<div class="sect2">
<h3 id="shiro-configuration-2"><a class="anchor" href="#shiro-configuration-2"></a>Shiro Configuration</h3>
<div class="paragraph">
<p>To use LDAP involves telling Shiro how to instantiate the realm. This bootstrapping info lives in the <code>WEB-INF/shiro.ini</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ini hljs" data-lang="ini">contextFactory = org.apache.isis.security.shiro.IsisLdapContextFactory
contextFactory.url = ldap://localhost:10389
contextFactory.systemUsername = uid=admin,ou=system <i class="conum" data-value="1"></i><b>(1)</b>
contextFactory.systemPassword = secret
contextFactory.authenticationMechanism = CRAM-MD5 <i class="conum" data-value="2"></i><b>(2)</b>
contextFactory.systemAuthenticationMechanism = simple
ldapRealm = org.apache.isis.security.shiro.IsisLdapRealm <i class="conum" data-value="3"></i><b>(3)</b>
ldapRealm.contextFactory = $contextFactory
ldapRealm.searchBase = ou=groups,o=mojo <i class="conum" data-value="4"></i><b>(4)</b>
ldapRealm.groupObjectClass = groupOfUniqueNames <i class="conum" data-value="5"></i><b>(5)</b>
ldapRealm.uniqueMemberAttribute = uniqueMember <i class="conum" data-value="6"></i><b>(6)</b>
ldapRealm.uniqueMemberAttributeValueTemplate = uid={0}
# optional mapping from physical groups to logical application roles
ldapRealm.rolesByGroup = \ <i class="conum" data-value="7"></i><b>(7)</b>
LDN_USERS: user_role,\
NYK_USERS: user_role,\
HKG_USERS: user_role,\
GLOBAL_ADMIN: admin_role,\
DEMOS: self-install_role
ldapRealm.permissionsByRole=\ <i class="conum" data-value="8"></i><b>(8)</b>
user_role = *:ToDoItemsJdo:*:*,\
*:ToDoItem:*:*; \
self-install_role = *:ToDoItemsFixturesService:install:* ; \
admin_role = *
securityManager.realms = $ldapRealm</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>user accounts are searched using a dedicated service account</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>SASL (CRAM-MD5) authentication is used for this authentication</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Apache Isis' implementation of the LDAP realm.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>groups are searched under <code>ou=groups,o=mojo</code> (where <code>mojo</code> is the company name)</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>each group has an LDAP objectClass of <code>groupOfUniqueNames</code></td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>each group has a vector attribute of <code>uniqueMember</code></td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b>7</b></td>
<td>groups looked up from LDAP can optionally be mapped to logical roles; otherwise groups are used as role names directly</td>
</tr>
<tr>
<td><i class="conum" data-value="8"></i><b>8</b></td>
<td>roles are mapped in turn to permissions</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The value of <code>uniqueMember</code> is in the form <code>uid=xxx</code>, with <code>xxx</code> being the uid of the user
* users searched under <code>ou=system</code>
* users have, at minimum, a <code>uid</code> attribute and a password
* the users credentials are used to verify their user/password</p>
</div>
<div class="paragraph">
<p>The above configuration has been tested against <a href="http://directory.apache.org/apacheds/">ApacheDS</a>, v1.5.7. This can be administered using <a href="http://directory.apache.org/studio/">Apache Directory Studio</a>, v1.5.3.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
<div class="title">Shiro Realm Mappings</div>
<div class="paragraph">
<p>When configuring role based permission mapping, there can only be one of these entries per realm:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ini hljs" data-lang="ini">realm.groupToRolesMappings = ...</code></pre>
</div>
</div>
<div class="paragraph">
<p>and</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ini hljs" data-lang="ini">realm.roleToPermissionsMappings = ...</code></pre>
</div>
</div>
<div class="paragraph">
<p>This forces you to put everything on one line for each of the above. This is, unfortunately, a Shiro "feature". And if you repeat the entries above then it&#8217;s "last one wins".)</p>
</div>
<div class="paragraph">
<p>To make the configuration maintainable, use "\" to separate the mappings onto separate lines in the file. Use this technique for both group to roles mapping and role to permission mapping. If you use the '&#39; after the "," that separates the key:value pairs it is more readable.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="externalizing-role-perms"><a class="anchor" href="#externalizing-role-perms"></a>Externalizing role perms</h3>
<div class="paragraph">
<p>As an alternative to injecting the <code>permissionsByRole</code> property, the role/permission mapping can alternatively be specified by injecting a resource path:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ini hljs" data-lang="ini">ldapRealm.resourcePath=classpath:webapp/myroles.ini</code></pre>
</div>
</div>
<div class="paragraph">
<p>where <code>myroles.ini</code> is in <code>src/main/resources/webapp</code>, and takes the form:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ini hljs" data-lang="ini">[roles]
user_role = *:ToDoItemsJdo:*:*,\
*:ToDoItem:*:*
self-install_role = *:ToDoItemsFixturesService:install:*
admin_role = *</code></pre>
</div>
</div>
<div class="paragraph">
<p>This separation of the role/mapping can be useful if Shiro is configured to support multiple realms (eg an LdapRealm based one and also an TextRealm)</p>
</div>
</div>
<div class="sect2">
<h3 id="active-ds-ldap-tutorial"><a class="anchor" href="#active-ds-ldap-tutorial"></a>Active DS LDAP tutorial</h3>
<div class="paragraph">
<p>The screenshots below show how to setup LDAP accounts in ApacheDS using the Apache Directory Studio.</p>
</div>
<div class="paragraph">
<p>The setup here was initially based on <a href="http://krams915.blogspot.co.uk/2011/01/ldap-apache-directory-studio-basic.html">this tutorial</a>, however we have moved the user accounts so that they are defined in a separate LDAP node.</p>
</div>
<div class="paragraph">
<p>To start, create a partition in order to hold the mojo node (holding the groups):</p>
</div>
<div class="imageblock">
<div class="content">
<a class="image" href="_images/configuration/configuring-shiro/ldap/activeds-ldap-mojo-partition.png"><img src="_images/configuration/configuring-shiro/ldap/activeds-ldap-mojo-partition.png" alt="activeds ldap mojo partition"></a>
</div>
</div>
<div class="paragraph">
<p>Create the <code>ou=groups,o=mojo</code> hierarchy:</p>
</div>
<div class="imageblock">
<div class="content">
<a class="image" href="_images/configuration/configuring-shiro/ldap/activeds-ldap-mojo-root-dse.png"><img src="_images/configuration/configuring-shiro/ldap/activeds-ldap-mojo-root-dse.png" alt="activeds ldap mojo root dse"></a>
</div>
</div>
<div class="paragraph">
<p>Configure SASL authentication. This means that the checking of user/password is done implicitly by virtue of Apache Isis connecting to LDAP using these credentials:</p>
</div>
<div class="imageblock">
<div class="content">
<a class="image" href="_images/configuration/configuring-shiro/ldap/activeds-ldap-sasl-authentication.png"><img src="_images/configuration/configuring-shiro/ldap/activeds-ldap-sasl-authentication.png" alt="activeds ldap sasl authentication"></a>
</div>
</div>
<div class="paragraph">
<p>In order for SASL to work, it seems to be necessary to put users under <code>o=system</code>. (This is why the setup is slightly different than the tutorial mentioned above):</p>
</div>
<div class="imageblock">
<div class="content">
<a class="image" href="_images/configuration/configuring-shiro/ldap/activeds-ldap-users.png"><img src="_images/configuration/configuring-shiro/ldap/activeds-ldap-users.png" alt="activeds ldap users"></a>
</div>
</div>
<div class="paragraph">
<p>Configure the users into the groups:</p>
</div>
<div class="imageblock">
<div class="content">
<a class="image" href="_images/configuration/configuring-shiro/ldap/activeds-ldap-groups.png"><img src="_images/configuration/configuring-shiro/ldap/activeds-ldap-groups.png" alt="activeds ldap groups"></a>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="enhanced-wildcard-permission"><a class="anchor" href="#enhanced-wildcard-permission"></a>Enhanced Wildcard Permission</h2>
<div class="sectionbody">
<div class="paragraph">
<p>If using the text-based <a href="#security:shiro:ini-realm.adoc" class="page unresolved"><code>IniRealm</code></a> or <a href="../shiro-realm-ldap/about.html" class="page">Apache Isis' LDAP realm</a>, then note that Shiro also allows the string representation of the permissions to be mapped (resolved) to alternative <code>Permission</code> instances.
Apache Isis provides its own <code>IsisPermission</code> which introduces the concept of a "veto".</p>
</div>
<div class="paragraph">
<p>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.</p>
</div>
<div class="paragraph">
<p>The string representation of the <code>IsisPermission</code> uses the following format:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ini hljs" data-lang="ini">([!]?)([^/]+)[/](.+)</code></pre>
</div>
</div>
<div class="paragraph">
<p>where:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>the optional <code>!</code> prefix indicates this permission is a vetoing permission</p>
</li>
<li>
<p>the optional <code>xxx/</code> prefix is a permission group that scopes any vetoing permissions</p>
</li>
<li>
<p>the remainder of the string is the permission (possibly wild-carded, with :rw as optional suffix)</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>For example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ini hljs" data-lang="ini">user_role = !reg/org.estatio.api,\
!reg/org.estatio.webapp.services.admin,\
reg/* ; \
api_role = org.estatio.api ;\
admin_role = adm/*</code></pre>
</div>
</div>
<div class="paragraph">
<p>sets up:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>the <code>user_role</code> with access to all permissions except those in <code>org.estatio.api</code> and <code>org.estatio.webapp.services.admin</code></p>
</li>
<li>
<p>the <code>api_role</code> with access to all permissions in <code>org.estatio.api</code></p>
</li>
<li>
<p>the <code>admin_role</code> with access to everything.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>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 <code>admin_role</code> and <code>user_role</code>; we would want the <code>admin_role</code> to trump the vetos of the <code>user_role</code>, in other words to give the user access to everything.</p>
</div>
<div class="paragraph">
<p>Because of the permission groups, the two "!reg/...+""" vetos in user_role only veto out selected permissions granted by the "reg/<strong>" permissions, but they do not veto the permissions granted by a different scope, namely "adm/</strong>+".</p>
</div>
<div class="paragraph">
<p>The net effect is therefore what we would want: that a user with both <code>admin_role</code> and <code>user_role</code> would have access to everything, irrespective of those two veto permissions of the <code>user_role</code>.</p>
</div>
<div class="paragraph">
<p>Finally, the Apache Isis permission resolver is specified in <code>WEB-INF/shiro.ini</code> file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ini hljs" data-lang="ini">permissionResolver = org.apache.isis.security.shiro.authorization.IsisPermissionResolver
myRealm.permissionResolver = $permissionResolver <i class="conum" data-value="1"></i><b>(1)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td><code>myRealm</code> is the handle to the configured realm, eg <code>$iniRealm</code> or <code>$isisLdapRealm</code> etc.</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="run-as"><a class="anchor" href="#run-as"></a>Run-as</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This hint shows how to temporarily change the current user as reported by Shiro.
This can be useful to support "Run As", for example.</p>
</div>
<div class="paragraph">
<p>The heavy lifting is done in <code>ShiroService</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">import org.springframework.stereotype.Service;
@Service
public class ShiroService {
public void runAs(String userName) {
SimplePrincipalCollection principals =
new SimplePrincipalCollection(userName, "jdbcRealm"); <i class="conum" data-value="1"></i><b>(1)</b>
getSubject().runAs(principals);
}
public String releaseRunAs() {
final PrincipalCollection principals = getSubject().releaseRunAs();
String username = (String)principals.asList().get(0);
return username;
}
public String getUsername() { <i class="conum" data-value="2"></i><b>(2)</b>
String principalAsString = ((String)getSubject().getPrincipal());
return principalAsString.toLowerCase();
}
public String getRealUsername() { <i class="conum" data-value="3"></i><b>(3)</b>
return userService.getUser().getName().toLowerCase();
}
public boolean isRunAs() {
return getSubject().isRunAs();
}
private static Subject getSubject() {
return org.apache.shiro.SecurityUtils.getSubject();
}
@Inject
private UserService userService;
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>"jdbcRealm" is realm as configured in Shiro config (shiro.ini).</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>The username of the currently logged in user (by which permissions are determined).
This could be the user name the real user is running as.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>The username of the real currently logged in user.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>This could be exposed in the UI using a simple <code>RunAsService</code>, for example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@DomainService(nature = NatureOfService.VIEW)
@DomainServiceLayout(menuBar = DomainServiceLayout.MenuBar.TERTIARY)
public class RunAsService {
public Dashboard runAs(User user) {
shiroService.runAs(user.getUsername());
return dashboardService.openDashboard(); <i class="conum" data-value="1"></i><b>(1)</b>
}
public List&lt;User&gt; choices0RunAs() {
return ... <i class="conum" data-value="2"></i><b>(2)</b>
}
public boolean hideRunAs() {
return shiroService.isRunAs();
}
public User releaseRunAs() {
String username = shiroService.releaseRunAs();
return usersRepository.findByUsername(username);
}
public boolean hideReleaseRunAs() {
return !shiroService.isRunAs();
}
@Inject
private ShiroService shiroService;
@Inject
private UsersRepository usersRepository;
@Inject
private DashboardService dashboardService; <i class="conum" data-value="1"></i><b>(1)</b>
}</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>go to the home page (application-specific)</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>return a list of users to run as</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Credits: adapted from <a href="https://gist.github.com/erikdehair/efa3005440ca982cca41ebe5347e82d8">this gist</a>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="caching-and-other-shiro-features"><a class="anchor" href="#caching-and-other-shiro-features"></a>Caching and other Shiro Features</h2>
<div class="sectionbody">
<div class="paragraph">
<p>We don&#8217;t want to repeat the entire <a href="http://shiro.apache.org/documentation.html">Shiro documentation set</a> here, but we should flag a number of other features that are worth checking out.</p>
</div>
<div class="sect2">
<h3 id="caching"><a class="anchor" href="#caching"></a>Caching</h3>
<div class="paragraph">
<p>To ensure that security operations does not impede performance, Shiro supports caching. For example, this sets up a simple memory-based cache manager:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-ini hljs" data-lang="ini">memoryCacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager
securityManager.cacheManager = $memoryCacheManager</code></pre>
</div>
</div>
<div class="paragraph">
<p>Other implementations can be plugged in; see the Shiro <a href="http://shiro.apache.org/caching.html">documentation</a> for further details.</p>
</div>
</div>
<div class="sect2">
<h3 id="further-reading"><a class="anchor" href="#further-reading"></a>Further Reading</h3>
<div class="ulist">
<ul>
<li>
<p>Shiro&#8217;s documentation page can be found <a href="http://shiro.apache.org/documentation.html">here</a>.</p>
</li>
<li>
<p>community-contributed articles can be found <a href="http://shiro.apache.org/articles.html">here</a>.<br></p>
<div class="paragraph">
<p>These include for instance <a href="http://meri-stuff.blogspot.co.uk/2011/04/apache-shiro-part-2-realms-database-and.html">this interesting article</a> describing how to perform certificate-based authentication (ie login using Google or Facebook credentials).</p>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
</article>
<aside class="article-aside toc" role="navigation">
<p class="toc-title">On this page</p>
<div id="article-toc"></div>
</aside>
</main>
</div>
<footer class="footer">
<div class="content">
<div class="copyright">
<p>
Copyright © 2010~2020 The Apache Software Foundation, licensed under the Apache License, v2.0.
<br/>
Apache, the Apache feather logo, Apache Isis, and the Apache Isis project logo are all trademarks of The Apache Software Foundation.
</p>
</div>
<div class="revision">
<p>Revision: SNAPSHOT</p>
</div>
</div>
</footer>
<script src="../../../_/js/site.js"></script>
<script async src="../../../_/js/vendor/highlight.js"></script>
<script src="../../../_/js/vendor/jquery-3.4.1.min.js"></script>
<script src="../../../_/js/vendor/jquery-ui-1.12.1.custom.widget-only.min.js"></script>
<script src="../../../_/js/vendor/jquery.tocify.min.js"></script>
<script>
$(function() {
$("#article-toc").tocify( {
showEffect: "slideDown",
hashGenerator: "pretty",
hideEffect: "slideUp",
selectors: "h2, h3",
scrollTo: 120,
smoothScroll: true,
theme: "jqueryui",
highlightOnScroll: true
} );
});
</script>
</body>
</html>