= SecMan

: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.


SecMan provides an implementation of both the xref:refguide:core:index/security/authentication/Authenticator.adoc[Authenticator] and xref:refguide:core:index/security/authorization/Authorizor.adoc[Authorizor] SPIs, storing user, roles and permissions information as domain entities (persisted to relational database).
It can be used with both the JDO or JPA persistence mechanisms.

Because these features are implemented using domain entities, it means that this security information can be administered from within your Apache Isis application.
The domain model is explained in more detail <<domain-model,below>>.

If SecMan is used for authentication, user/passwords are stored in encrypted form using a configured `PasswordEncoder`.
The framework ships with xref:refguide:extensions:index/secman/encryption/jbcrypt/services/PasswordEncoderUsingJBcrypt.adoc[] that uses the link:https://www.mindrot.org/projects/jBCrypt/[jBCrypt] library.

Authorization is supported by associating users to roles, where each role has a set of permissions.
Permissions can be defined at different scopes, and can either allow or veto access to the underlying feature.
The set of permissions (to features) is derived completely from your application's metamodel; in essence the permissions are "type-safe".

SecMan can be used just for authorization, or it can be used in conjunction with any of the other xref:refguide:core:index/security/authentication/Authenticator.adoc[Authenticator] implementations, for example xref:security:shiro:about.adoc[Shiro], xref:security:spring:about.adoc[Spring] or xref:security:keycloak:about.adoc[Keycloak].
This integration is discussed in more detail <<shiro-integration,below>>.


== Users, Roles and Permissions

SecMan's xref:refguide:core:index/security/authorization/Authorizor.adoc[Authorizor] implementation uses domain entities to model users, roles and permissions.
Each permission relates to an "object feature".
Object features themselves come in three varieties:

* the most fine-grained object feature is an object member
+
for example `myapp.customer.CustomerAddress#zipCode`

* next up is an object feature representing an entire type, described using its logical type name.
+
for example `myapp.customer.CustomerAddress`.
This is usually specified using the `@javax.inject.Named` annotation.

* most general is an object feature representing a namespace
+
These consist of the portion of the logical type name up to but excluding the local type name, for example `myapp.customer`.

Permissions are inferred: a permission granted at the type level implies a permission to all the object members of the type, and a permission granted to a namespace implies a permission to all the object members of all the object types within that namespace.

Multipart namespaces also imply a hierarchy and permissions can be inferred through that hierarchy.
For example the `myapp.customer` namespace has a parent `myapp` namespace.
Permissions granted to the `myapp` namespace will be inferred by types in the child `myapp.customer` namespace.

Permissions are also either additive or subtractive: they can indicate the user has been ``ALLOW``ed access to an object member, or they have been ``VETO``ed access.

Because of permission inference, there could be more than one permission that applies to an object member, where one permission is an `ALLOW` and another permission is a `VETO`.
SecMan uses the most specific permission to determine whether access should be granted or not.
For example:

* if there is an ``VETO`` on `mycompany.customer`, but an ``ADDRESS`` on `mycompany.customer.CustomerProfile`, then access will be given to the object members in ``mycompany.customer.CustomerProfile`` because the type-level `ALLOW` takes precedence over the namespace-level ``VETO``.

* if there is an ``ALLOW`` on `mycompany.customer.CustomerAddress`, but a ``VETO`` on ``mycompany.customer.CustomerAddress#zipCode``, then access will be given to all the object members of `CustomerAddress` _except for_ `zipCode`.



[#domain-model]
== Domain Model

The diagram below shows the domain model for SecMan, and how it relates to the features obtained from the core metamodel:

.SecMan domain model
image:secman-domain-model.drawio.svg[]

SecMan's users, roles and permissions are entities, but application features are serializable value types that are derived from the Apache Isis metamodel.

Thus:

* a user (represented by xref:refguide:extensions:index/secman/applib/user/dom/ApplicationUser.adoc[ApplicationUser]) can belong to many roles (xref:refguide:extensions:index/secman/applib/role/dom/ApplicationRole.adoc[ApplicationRole])
* a role in turn holds a set of permissions (xref:refguide:extensions:index/secman/applib/permission/dom/ApplicationPermission.adoc[ApplicationPermission]).
Each such permission is either an ``ALLOW`` or a ``VETO`` to an application feature, represented by a fully qualified name
* this resolves to an xref:refguide:applib:index/services/appfeat/ApplicationFeatureId.adoc[ApplicationFeatureId] (from the core metamodel).
That feature will be either a namespace, a type or a member.
+
The core metamodel also provides xref:refguide:applib:index/services/appfeat/ApplicationFeature.adoc[ApplicationFeature] (each being identified with an xref:refguide:applib:index/services/appfeat/ApplicationFeatureId.adoc[ApplicationFeatureId] that makes it easier to navigate around the application feature hierarchy.

The domain model also shows the xref:refguide:extensions:index/secman/applib/tenancy/spi/ApplicationTenancyEvaluator.adoc[ApplicationTenancyEvaluator] interface and xref:refguide:extensions:index/secman/applib/tenancy/dom/ApplicationTenancy.adoc[ApplicationTenancy] entity.
These are to support multitenancy, discussed in the section <<Multitenancy,below>>.


== Multitenancy

In addition to users, roles and permissions, SecMan also supports link:https://en.wikipedia.org/wiki/Multitenancy[multitenancy].
The idea is that the ownership of domain objects is logically partitioned into tenants; one tenant cannot see or access the data owned by another tenant.

Implementing multitenancy requires that both data and user is "tagged" in some way, and that these tags are compared against each other to determine if the user has access to the tagged data.
This is represented in the domain model through the xref:refguide:extensions:index/secman/applib/tenancy/spi/ApplicationTenancyEvaluator.adoc[ApplicationTenancyEvaluator] SPI interface.
The idea is that the application provides its own implementation of this interface, that performs the evaluation of whether the current user can view the domain object or not (and if they can, whether the domain object members are disabled/read-only).

One simple implementation is to tag domain objects with a "path", and similarly to store a "path" for each application user.
The idea behind the xref:refguide:extensions:index/secman/applib/tenancy/dom/ApplicationTenancy.adoc[ApplicationTenancy] is simply to name these tenancies; its `atPath` property is intended to be a primary key.
The xref:refguide:extensions:index/secman/applib/user/dom/ApplicationUser.adoc[ApplicationUser] entity also has an `atPath` property.
We could therefore use this "atPath" to represent a country, eg "/GBR", "/ITA", "/FRA", "/BEL" and so on.

For example, the example below uses implements the rule that a user can always view an object within (above or below) their place in the path "hierarchy", and can edit any object "under" them in the hierarchy:

[source,java]
.ApplicationTenancyEvaluatorUsingAtPath.java
----
@Service
public class ApplicationTenancyEvaluatorUsingAtPath implements ApplicationTenancyEvaluator {

    @Override
    public boolean handles(Class<?> cls) {
        return HasAtPath.class.isAssignableFrom(cls);   // <.>
    }
    @Override
    public String hides(Object domainObject, ApplicationUser applicationUser) {
        final String objAtPath = ((HasAtPath) domainObject).getAtPath();
        if(objAtPath == null) { return null; } // show
        final String userAtPath = applicationUser.getAtPath();
        if(userAtPath == null) { return "user does not have atPath"; } // hide
        return objAtPath.startsWith(userAtPath) || userAtPath.startsWith(objAtPath) // <.>
                ? null
                : "object not visible within user's tenancy";
    }
    @Override
    public String disables(Object domainObject, ApplicationUser applicationUser) {
        final String objAtPath = ((HasAtPath) domainObject).getAtPath();
        if(objAtPath == null) { return null; } // enable
        final String userAtPath = applicationUser.getAtPath();
        if(userAtPath == null) { return "user does not have atPath"; } // disable
        return objAtPath.startsWith(userAtPath) // <.>
                ? null
                : "object not enabled within user's tenancy";
    }
}
----
<.> SecMan provides the xref:refguide:extensions:index/secman/applib/tenancy/dom/HasAtPath.adoc[HasAtPath] interface to standardize the way in which domain objects expose their "tag" (atPath) to the evaluator.
<.> can view all objects (above and below) within the user's hierarchy
+
For example:
+
[cols="2m,2m,2a"]
|===
| Object atPath | User atPath | Visibility

|/
|/ITA
|visible

|/ITA
|/ITA
|visible

|/ITA/MIL
|/ITA
|visible

|/FRA
|/ITA
|not visible

|===

<.> can edit only objects at or below the user's hierarchy
+
For example:
+
[cols="2m,2m,2a"]
|===
| Object atPath | User atPath | Outcome

|/
|/ITA
|disabled

|/ITA
|/ITA
|enabled

|/ITA/MIL
|/ITA
|enabled

|/FRA
|/ITA
|n/a (not visible)

|===

More complex implementations are possible: ultimately the "atPath" properties are just strings and so can be interpreted in whatever way makes sense.
For example, to allow a user to be able to access objects from multiple countries, we could use a format such as "/ITA;/BEL".
The implementation would parse the string and allow access for any country.

For this reason, the xref:refguide:extensions:index/secman/applib/user/dom/ApplicationUser.adoc[ApplicationUser]'s `atPath` property is _not_ a foreign key to the xref:refguide:extensions:index/secman/applib/tenancy/dom/ApplicationTenancy.adoc[ApplicationTenancy] entity.

TIP: Another implementation of xref:refguide:extensions:index/secman/applib/tenancy/spi/ApplicationTenancyEvaluator.adoc[ApplicationTenancyEvaluator] can be found in the xref:docs:demo:about.adoc[Demo App]..


.Apache Isis' multi-tenancy is only skin deep
****
It's important to realize that Apache Isis' multi-tenancy support is only skin deep.
What we mean by that is that the restricting of access to data is only performed at the presentation layer.
If a user is not permitted to view/edit an object, then it is only the viewer component prevents them from doing so; the restricted object could still have been retrieved into memory from the database.

You may therefore wish to implement multi-tenancy at a "deeper" level, at the persistence layer).
This would prevent the object from being retrieved into memory in the first place, almost certainly more performant and obviously also secure because the viewer cannot render an object that hasn't been retrieved.
One implementation (for multi-tenancy at the persistence layer) is to use capabilities of the ORM.

* xref:pjdo:ROOT:about.adoc[JDO/DataNucleus] supports link:link:https://www.datanucleus.org/products/accessplatform/jdo/persistence.html#multitenancy[multi-tenancy] through the link:https://www.datanucleus.org/products/accessplatform_5_1/jdo/annotations.html#MultiTenant_Class[@MultiTenant] annotation and `datanucleus.tenantId` or `datanucleus.tenantProvider` configuration properties.
* xref:pjpa:ROOT:about.adoc[JPA/Eclipselink] supports 3 different types of multi-tenancy, described in the documentation for the link https://www.eclipse.org/eclipselink/documentation/2.4/jpa/extensions/a_multitenant.htm[@Multitenant] annotation.

Another alternative is to move the responsibility for managing tenancy into the relational database itself.
This will obviously vary by vendor.

Another option again is rather simple: just run multiple instances of the application, one per tenancy.
****



== Password encryption

Secman leverages Spring's `org.springframework.security.crypto.password.PasswordEncoder` SPI to allow different algorithms to encrypt the user's password.

The `encryption-jbcrypt` module provides an implementation using the link:https://www.mindrot.org/projects/jBCrypt/[jBCrypt] library.


[#shiro-integration]
== Using other Authenticators

While SecMan does provide an implementation of the xref:refguide:core:index/security/authentication/Authenticator.adoc[] SPI, it's also possible to use an alternative `Authenticator` implementation, for example as provided by xref:security:shiro:about.adoc[Apache Shiro], xref:security:spring:about.adoc[Spring] or xref:security:keycloak:about.adoc[Keycloak].

For more details, see xref:security:secman:setting-up-with-shiro.adoc[], xref:setting-up-with-spring-oauth2.adoc[] and xref:setting-up-with-keycloak.adoc[].


== SecMan's structure

SecMan consists of a number of Maven submodules:

* the API module (`isis-extensions-secman-api`) defines a set of interfaces for the xref:refguide:extensions:index/secman/applib/user/dom/ApplicationUser.adoc[ApplicationUser], xref:refguide:extensions:index/secman/applib/role/dom/ApplicationRole.adoc[ApplicationRole],
xref:refguide:extensions:index/secman/applib/permission/dom/ApplicationPermission.adoc[ApplicationPermission] and xref:refguide:extensions:index/secman/applib/tenancy/dom/ApplicationTenancy.adoc[ApplicationTenancy] entities.

* the two persistence modules (`isis-extensions-secman-persistence-jpa` and `isis-extensions-secman-persistence-jdo`) provide concrete implementations of the APIs for JPA and JDO respectively.
As you might expect, they are intended for use with xref:pjpa:ROOT:about.adoc[JPA/Eclipselink] and xref:pjdo:ROOT:about.adoc[JDO/DataNucleus] persistence mechanisms respectively; use one or the other.

* the Model module (`isis-extensions-secman-model`) defines view models to represent the feature application features, and also contains business logic as mixins to the API (and therefore contributed to the appropriate concrete entity).

* the Shiro realm module (`isis-extensions-secman-shiro-realm`) provides the Shiro realm interface that delegates to the Secman database (see discussion <<shiro-integration,above>>)

* the jbcrypt encryption module (`isis-extensions-secman-encryption-jbcrypt`) provides an implementation of Spring's `org.springframework.security.crypto.password.PasswordEncoder` SPI so that passwords are persisted securely using link:https://www.mindrot.org/projects/jBCrypt/[jBCrypt].

