Skip to content

Instance-Based Authorization

Policies that assign roles can be extended with attribute filters for instance-based authorization. This allows administrators of different tenants to define fine-grained policies with individual restrictions at runtime to customize the authorization model for their tenant.

Motivation

Let's imagine a scenario where we want to enable tenant administrators to give users the SalesRepresentative role but only for a specific Region and ProductCategory. For example, the following policy would grant the SalesRepresentative role only in the EU region and only for products in the Electronics category:

dcl
POLICY SalesRepresentativeEUElectronics {
    ASSIGN ROLE SalesRepresentative 
    WHERE Region = 'EU' AND ProductCategory = 'Electronics'; 
}

However, administrators can't simply write arbitrary DCL policies as free text because it's important that the application supports the conditions that are used in the policies. In this example, the application must understand how and where to apply the conditions for Region and ProductCategory.

In the next sections, we explain the necessary steps in the application to achieve this example.

Schema Definition

As a basis, attributes used in policies must be defined in a schema.dcl file. This file must be located in the DCL root folder of the CAP application.

In this file, the data type of the attributes is defined, and it can also contain additional metadata annotations, such as those for Value Help.

dcl
SCHEMA {
  @valueHelp { ... }  // details omitted for brevity
  Region : String,
  
  @valueHelp { ... } // details omitted for brevity
  ProductCategory : String,
}

Schema Generation

The Authorization Management Service (AMS) Node.js module @sap/ams can be used to generate the schema.dcl from @ams.attributes annotations in a cds model.

Restricted Role Assignments

To provide guardrails for runtime policies, it's possible to extend role assignments in the base policies with a WHERE condition. In that condition, the IS NOT RESTRICTED and IS RESTRICTED keywords can be used to list the attributes that can (must) be restricted by the tenant administrator at runtime.

IS RESTRICTED

When assigning a base policy with an attribute that IS RESTRICTED, the attribute condition evaluates to false, which typically means access by assigning the base policy isn't possible at all. To gain access, administrators must derive a runtime policy from this base policy that restricts the attribute to a specific value.

IS NOT RESTRICTED

When assigning a base policy with an attribute that IS NOT RESTRICTED, the attribute condition evaluates to true which means the base policy can be assigned for unfiltered access. However, administrators can derive a runtime policy from this base policy to restrict the attribute to a specific value.

In our example, we want to allow (but not force) the tenant administrator to restrict the Region and ProductCategory attributes, so we extend the SalesRepresentative base policy as follows:

dcl
POLICY SalesRepresentative {
    ASSIGN ROLE SalesRepresentative
    WHERE Region IS NOT RESTRICTED AND ProductCategory IS NOT RESTRICTED; 
}

WARNING

In CAP, WHERE conditions behind role assignments must only be used to determine what users with the role are allowed to see. Don't try to use it as a means to check if the role is assigned or not based on attribute conditions.

Combining Attribute Conditions

Usually, when there are multiple attributes in the WHERE condition of a rule, developers want to combine the filter conditions of all attributes using AND. However, it's also possible to use OR to combine attribute conditions.

Partial Restrictions

In both cases, it's important to consider the effects of the IS RESTRICTED and IS NOT RESTRICTED keywords. For example, administrators can decide to leave some attributes as IS RESTRICTED (IS NOT RESTRICTED) in a derived policy by restricting only a subset of attributes to specific conditions. The evaluation to false (true) of the IS RESTRICTED (IS NOT RESTRICTED) attributes will short-circuit the evaluation of the remaining attributes in the same AND (OR) clause.

For this reason, when using IS NOT RESTRICTED, we discourage the use of OR in the WHERE clause of role assignments because it can lead to unintended full access.

Runtime Policy Creation

Once the previous steps are in place, the tenant administrator can use the SCI admin cockpit to create a runtime policy from the base policy that restricts the Region and ProductCategory attributes to specific values.

TIP

For local tests, such a derived policy can be written in a DCL file inside the local DCL package.

dcl
POLICY SalesRepresentativeEUElectronics {
    USE cap.SalesRepresentative
    RESTRICT Region = 'EU', ProductCategory = 'Electronics';
}

This derived policy is equivalent to the policy defined in the Motivation section. It can be assigned to users in the same tenant like any other policy.

Annotating the CDS Model

Finally, via @ams.attributes annotations, the AMS attributes are mapped to elements (or association paths) in the cds model using compile-safe cds expressions. Whenever requests access the annotated resources, the result is filtered based on the attribute conditions computed by AMS.

js
annotate Product with @ams.attributes: { 
    ProductCategory: (category),
};

annotate SalesOrder with @ams.attributes: {
    Region: (region),
    ProductCategory: (product.category),
};

annotate Product with @restrict: [
    {
      grant: ['READ'],
      to: [ 'SalesManager', 'SalesRepresentative' ]
    }
];

annotate SalesOrder with @restrict: [
    {
      grant: [ 'CREATE', 'READ', 'UPDATE', 'DELETE' ],
      to: 'SalesManager',
    },
    {
      grant: [ 'READ' ],
      to: 'SalesRepresentative',
    },
];

TIP

ams.attributes annotations are supported on aspects, entities, and actions/functions bound to a single entity. They are the cds resources that support where conditions.

Effect of Attribute Filters

When the SalesRepresentativeEUElectronics policy is assigned to a user, the CAP modules for AMS dynamically adjusts the cds where condition to inject the attribute conditions from authorization policies.

For example, when accessing the SalesOrder entity with this policy, the AMS module will add a where condition to the privilege for the SalesRepresentative role that looks like this:

js
@restrict: [
    {
      grant: [ 'READ' ],
      to: 'SalesRepresentative',
      where: "region = 'EU' AND product.category = 'Electronics'", 
    },
]

If a user has more than one cds role that grants access to a resource, the AMS module combines the attribute conditions of all roles using OR. For example, if the user also has the SalesManager role assigned with unfiltered access, the resulting where condition on Product would effectively look like this (before being simplified by the AMS module):

js
@restrict: [
    {
      grant: ['READ'],
      to: [ 'SalesManager', 'SalesRepresentative' ],
      where: "true OR (region = 'EU' AND product.category = 'Electronics')", 
    }
]

TIP

When there is already a static where condition on a cds privilege, the AMS module combines the static where condition with the attribute conditions from authorization policies by using AND.