Skip to content

guide access control

derochs edited this page Dec 10, 2018 · 14 revisions

Access-Control

Access-Control is a central and important aspect of Security. It consists of two major aspects:

Authentication

Definition:

Authentication is the verification that somebody interacting with the system is the actual subject for whom he claims to be.

The one authenticated is properly called subject or principal. However, for simplicity we use the common term user even though it may not be a human (e.g. in case of a service call from an external system).

To prove his authenticity the user provides some secret called credentials. The most simple form of credentials is a password.

Note
Please never implement your own authentication mechanism or credential store. You have to be aware of implicit demands such as salting and hashing credentials, password life-cycle with recovery, expiry, and renewal including email notification confirmation tokens, central password policies, etc. This is the domain of access managers and identity management systems. In a business context you will typically already find a system for this purpose that you have to integrate (e.g. via LDAP). Otherwise you should consider establishing such a system e.g. using keycloak.

We use spring-security as a framework for authentication purposes.

Therefore you need to provide an implementation of WebSecurityConfigurerAdapter:

@Configuration
@EnableWebSecurity
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {

  @Inject
  private UserDetailsService userDetailsService;
  ...
  public void configure(HttpSecurity http) throws Exception {
    http.userDetailsService(this.userDetailsService)
        .authorizeRequests().antMatchers("/public/**").permitAll()
        .anyRequest().authenticated().and()
        ...
  }
}

As you can see spring-security offers a fluent API for easy configuration. You can simply add invocations like formLogin().loginPage("/public/login") or httpBasic().realmName("MyApp"). Also CSRF protection can be configured by invoking csrf(). For further details see spring Java-config for HTTP security.

Further, you need to provide an implementation of the UserDetailsService interface. A good starting point comes with our application template.

Preserve original request anchors after form login redirect

Spring Security will automatically redirect any unauthorized access to the defined login-page. After successful login, the user will be redirected to the original requested URL. The only pitfall is, that anchors in the request URL will not be transmitted to server and thus cannot be restored after successful login. Therefore the devon4j-security module provides the RetainAnchorFilter, which is able to inject javascript code to the source page and to the target page of any redirection. Using javascript this filter is able to retrieve the requested anchors and store them into a cookie. Heading the target URL this cookie will be used to restore the original anchors again.

To enable this mechanism you have to integrate the RetainAnchorFilter as follows: First, declare the filter with

  • storeUrlPattern: an regular expression matching the URL, where anchors should be stored

  • restoreUrlPattern: an regular expression matching the URL, where anchors should be restored

  • cookieName: the name of the cookie to save the anchors in the intermediate time

You can easily configure this as code in your WebSecurityConfig as following:

RetainAnchorFilter filter = new RetainAnchorFilter();
filter.setStoreUrlPattern("http://[^/]+/[^/]+/login.*");
filter.setRestoreUrlPattern("http://[^/]+/[^/]+/.*");
filter.setCookieName("TARGETANCHOR");
http.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class);

Users vs. Systems

If we are talking about authentication we have to distinguish two forms of principals:

  • human users

  • autonomous systems

While e.g. a Kerberos/SPNEGO Single-Sign-On makes sense for human users it is pointless for authenticating autonomous systems. So always keep this in mind when you design your authentication mechanisms and separate access for human users from access for systems.

Authorization

Definition:

Authorization is the verification that an authenticated user is allowed to perform the operation he intends to invoke.

Clarification of terms

For clarification we also want to give a common understanding of related terms that have no unique definition and consistent usage in the wild.

Table 1. Security terms related to authorization
Term Meaning and comment

Permission

A permission is an object that allows a principal to perform an operation in the system. This permission can be granted (give) or revoked (taken away). Sometimes people also use the term right what is actually wrong as a right (such as the right to be free) can not be revoked.

Group

We use the term group in this context for an object that contains permissions. A group may also contain other groups. Then the group represents the set of all recursively contained permissions.

Role

We consider a role as a specific form of group that also contains permissions. A role identifies a specific function of a principal. A user can act in a role.

For simple scenarios a principal has a single role associated. In more complex situations a principal can have multiple roles but has only one active role at a time that he can choose out of his assigned roles. For KISS it is sometimes sufficient to avoid this by creating multiple accounts for the few users with multiple roles. Otherwise at least avoid switching roles at run-time in clients as this may cause problems with related states. Simply restart the client with the new role as parameter in case the user wants to switch his role.

Access Control

Any permission, group, role, etc., which declares a control for access management.

Suggestions on the access model

For the access model we give the following suggestions:

  • Each Access Control (permission, group, role, …​) is uniquely identified by a human readable string.

  • We create a unique permission for each use-case.

  • We define groups that combine permissions to typical and useful sets for the users.

  • We define roles as specific groups as required by our business demands.

  • We allow to associate users with a list of Access Controls.

  • For authorization of an implemented use case we determine the required permission. Furthermore, we determine the current user and verify that the required permission is contained in the tree spanned by all his associated Access Controls. If the user does not have the permission we throw a security exception and thus abort the operation and transaction.

  • We avoid negative permissions, that is a user has no permission by default and only those granted to him explicitly give him additional permission for specific things. Permissions granted can not be reduced by other permissions.

  • Technically we consider permissions as a secret of the application. Administrators shall not fiddle with individual permissions but grant them via groups. So the access management provides a list of strings identifying the Access Controls of a user. The individual application itself contains these Access Controls in a structured way, whereas each group forms a permission tree.

Naming conventions

As stated above each Access Control is uniquely identified by a human readable string. This string should follow the naming convention:

«app-id».«local-name»
For Access Control Permissions the «local-name» again follows the convention:
«verb»«object»
The segments are defined by the following table:
Table 2. Segments of Access Control Permission ID
Segment Description Example

«app-id»

Is a unqiue technical but human readable string of the application (or microservice). It shall not contain special characters and especially no dot or whitespace. We recommend to use lower-train-case-ascii-syntax. The identity and access management should be organized on enterprise level rather than application level. Therefore permissions of different apps might easily clash (e.g. two apps might both define a group ReadMasterData but some user shall get this group for only one of these two apps). Using the «app-id». prefix is a simple but powerful namespacing concept that allows you to scale and grow. You may also reserve specific «app-id»s for cross-cutting concerns that do not actually reflect a single app e.g to grant access to a geographic region.

shop

«verb»

The action that is to be performed on «object». We use Find for searching and reading data. Save shall be used both for create and update. Only if you really have demands to separate these two you may use Create in addition to Save. Finally, Delete is used for deletions. For non CRUD actions you are free to use additional verbs such as Approve or Reject.

Find

«object»

The affected object or entity. Shall be named according to your data-model

Product

So as an example shop.FindProduct will reflect the permission to search and retrieve a Product in the shop application. The group shop.ReadMasterData may combine all permissions to read master-data from the shop. However, also a group shop.Admin may exist for the Admin role of the shop application. Here the «local-name» is Admin that does not follow the «verb»«object» schema.

devon4j-security

The devonfw provides a ready to use module devon4j-security that is based on spring-security and makes your life a lot easier.

access-control
Figure 1. devon4j Security Model

The diagram shows the model of devon4j-security that separates two different aspects:

  • The Indentity- and Access-Management is provided by according products and typically already available in the enterprise landscape (e.g. an active directory). It provides a hierarchy of primary access control objects (roles and groups) of a user. An administrator can grant and revoke permissions (indirectly) via this way.

  • The application security defines a hierarchy of secondary access control objects (groups and permissions). This is done by configuration owned by the application (see following section). The "API" is defined by the IDs public access control objects (groups) that may be referenced from the Indentity- and Access-Management.

Access Control Config

In your application simply extend AccessControlConfig to configure your access control objects as code and reference it from your use-cases. An example config may look like this:

@Named
public class MyAccessControlConfig extends AccessControlConfig {

  public static final String APP_ID = "MyApp";

  private static final String PREFIX = APP_ID + ".";

  public static final String PERMISSION_FIND_OFFER = PREFIX + "FindOffer";

  public static final String PERMISSION_SAVE_OFFER = PREFIX + "SaveOffer";

  public static final String PERMISSION_DELETE_OFFER = PREFIX + "DeleteOffer";

  public static final String PERMISSION_FIND_PRODUCT = PREFIX + "FindProduct";

  public static final String PERMISSION_SAVE_PRODUCT = PREFIX + "SaveProduct";

  public static final String PERMISSION_DELETE_PRODUCT = PREFIX + "DeleteProduct";

  public static final String GROUP_READ_MASTER_DATA = PREFIX + "ReadMasterData";

  public static final String GROUP_MANAGER = PREFIX + "Manager";

  public static final String GROUP_ADMIN = PREFIX + "Admin";

  public MyAccessControlConfig() {

    super();
    AccessControlGroup readMasterData = group(GROUP_READ_MASTER_DATA, PERMISSION_FIND_OFFER, PERMISSION_FIND_PRODUCT);
    AccessControlGroup manager = group(GROUP_MANAGER, readMasterData, PERMISSION_SAVE_OFFER, PERMISSION_SAVE_PRODUCT);
    AccessControlGroup admin = group(GROUP_ADMIN, manager, PERMISSION_DELETE_OFFER, PERMISSION_DELETE_PRODUCT);
  }
}

In your use-case you can now reference a permission like this:

@Named
public class UcSafeOfferImpl extends ApplicationUc implements UcSafeOffer {

  @Override
  @RolesAllowed(MyAccessControlConfig.PERMISSION_SAVE_OFFER)
  public OfferEto save(OfferEto offer) { ... }
  ...
}

Access Control Schema (deprecated)

The devon4j-security module provides a simple and efficient way to define permissions and roles. The file access-control-schema.xml is used to define the mapping from groups to permissions (see example from sample app). The general terms discussed above can be mapped to the implementation as follows:

Table 3. General security terms related to devon4j access control schema
Term devon4j-security implementation Comment

Permission

AccessControlPermission

Group

AccessControlGroup

When considering different levels of groups of different meanings, declare type attribute, e.g. as "group".

Role

AccessControlGroup

With type="role".

Access Control

AccessControl

Super type that represents a tree of AccessControlGroups and AccessControlPermissions. If a principal "has" a AccessControl he also "has" all AccessControls with according permissions in the spanned sub-tree.

Example access-control-schema.xml
<?xml version="1.0" encoding="UTF-8"?>
<access-control-schema>
  <group id="ReadMasterData" type="group">
    <permissions>
      <permission id="OfferManagement_GetOffer"/>
      <permission id="OfferManagement_GetProduct"/>
      <permission id="TableManagement_GetTable"/>
      <permission id="StaffManagement_GetStaffMember"/>
    </permissions>
  </group>

  <group id="Waiter" type="role">
    <inherits>
      <group-ref>Barkeeper</group-ref>
    </inherits>
    <permissions>
      <permission id="TableManagement_ChangeTable"/>
    </permissions>
  </group>
  ...
</access-control-schema>

This example access-control-schema.xml declares

  • a group named ReadMasterData, which grants four different permissions, e.g., OfferManagement_GetOffer

  • a group named Waiter, which

    • also grants all permissions from the group Barkeeper

    • in addition grants the permission TableManagement_ChangeTable

    • is marked to be a role for further application needs.

The devon4j-security module automatically validates the schema configuration and will throw an exception if invalid.

Unfortunately, Spring Security does not provide differentiated interfaces for authentication and authorization. Thus we have to provide an AuthenticationProvider, which is provided from Spring Security as an interface for authentication and authorization simultaneously. To integrate the devon4j-security provided access control schema, you can simply inherit your own implementation from the devon4j-security provided abstract class AbstractAccessControlBasedAuthenticationProvider and register your ApplicationAuthenticationProvider as an AuthenticationManager. Doing so, you also have to declare the two Beans AccessControlProvider and AccessControlSchemaProvider as listed below, which are precondition for the AbstractAccessControlBasedAuthenticationProvider.

Example integration of devon4j-security access control schema
<bean id="AuthenticationManager" class="org.springframework.security.authentication.ProviderManager">
    <constructor-arg>
      <list>
        <ref bean="ApplicationAuthenticationProvider"/>
      </list>
    </constructor-arg>
</bean>

<bean id="AccessControlProvider" class="com.devonfw.module.security.common.base.accesscontrol.AccessControlProviderImpl"/>
<bean id="AccessControlSchemaProvider" class="com.devonfw.module.security.common.base.accesscontrol.AccessControlSchemaProviderImpl"/>

Configuration on URL level

The authorization (in terms of Spring security "access management") can be enabled seperately for different url patterns, the request will be matched against. The order of these url patterns is essential as the first matching pattern will declare the access restriction for the incoming request (see access attribute). Here an example:

Extensive example of authorization on URL level
<bean id="FilterSecurityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
    <property name="authenticationManager" ref="AuthenticationManager"/>
    <property name="accessDecisionManager" ref="FilterAccessDecisionManager"/>
    <property name="securityMetadataSource">
      <security:filter-security-metadata-source use-expressions="true">
        <security:intercept-url pattern="/" access="isAnonymous()"/>
        <security:intercept-url pattern="/index.jsp" access="isAnonymous()"/>
        <security:intercept-url pattern="/security/login*" access="isAnonymous()"/>
        <security:intercept-url pattern="/j_spring_security_login*" access="isAnonymous()"/>
        <security:intercept-url pattern="/j_spring_security_logout*" access="isAnonymous()"/>
        <security:intercept-url pattern="/services/rest/security/currentuser/" access="isAnonymous() or isAuthenticated()"/>
        <security:intercept-url pattern="/**" access="isAuthenticated()"/>
      </security:filter-security-metadata-source>
    </property>
</bean>

<bean id="FilterAccessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased">
    <constructor-arg>
      <list>
        <bean class="org.springframework.security.web.access.expression.WebExpressionVoter"/>
      </list>
    </constructor-arg>
</bean>

Configuration on Java Method level

As state of the art devon4j will focus on role-based authorization to cope with authorization for executing use case of an application. We will use the JSR250 annotations, mainly @RolesAllowed, for authorizing method calls against the permissions defined in the annotation body. This has to be done for each use-case method in logic layer. Here is an example:

public class OrdermanagementImpl extends AbstractComponentFacade implements Ordermanagement {

  @RolesAllowed(Roles.WAITER)
  public PaginatedListTo<OrderCto> findOrdersByPost(OrderSearchCriteriaTo criteria) {

    return findOrderCtos(criteria);
  }
}

Now this method can only be called if a user is logged-in that has the permission FIND_TABLE.

Check Data-based Permissions

Clone this wiki locally