Developing Applications Running on SAP BTP Using SAP HANA Cloud

Explaining Application Security

Objectives
After completing this lesson, you will be able to:

After completing this lesson, you will be able to:

  • Application Security

Describe Cloud Foundry Security Concept

In this lesson we will look at the Authentication steps involved in Application Security.

The following diagram shows the architecture with the components that are responsible for business user authentication, authorization management, and security.

Main Blocks of Application Security

App Router

Its tasks are:

  • The App Router is the central entry point for our application it dispatches requests to our back end microservices, thus acting as a reverse proxy. The back end microservices should not be directly accessible by the client.
  • The App Router can serve static content such as web pages, SAPUI5, or another client-side code.
  • The App Router manages the authentication flows for our entire application.

For authentication (who the user is) and authorization (what the user is allowed to do), the App Router takes all incoming, unauthenticated request and initiates an OAuth2 flow (authorization code grant) with the Extended Services for User Account and Authentication (XSUAA) service of the SAP BTP in the Cloud Foundry environment .

Install via Service Marketplace

The App Router is a Node.js component, distributed via the publicly available SAP NPM registry

The Application Router's Design-Time Descriptor: xs-app.json

The SAP Authorization and Trust Management service lets you manage user authorizations and trust to identity providers. Identity providers are the user base for applications. You can use an identity authentication tenant, an SAP on-premise system, or a custom corporate identity provider. User authorizations are managed using technical roles at the application level, which can be aggregated into business-level groups and role collections for large-scale cloud scenarios.

OAuthCloud Foundry Applications use OAuth 2.0, When business users access an application, the application router acts as OAuth client and redirects their request to the OAuth authorization server for authentication. Runtime containers act as resource servers, using the container security API of the relevant container (for example, Java, Nodejs) to validate the token issued by the OAuth authorization server.

JSON Web Token (JWT)A JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self\u0002contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.

Although JWTs can be encrypted to also provide secrecy between parties, we will focus on signed tokens. Signed tokens can verify the integrity of the claims contained within it, while encrypted tokens hide those claims from other parties. When tokens are signed using public/private key pairs, the signature also certifies that only the party holding the private key is the one that signed it.

When should you use JSON Web Tokens?

Here are some scenarios where JSON Web Tokens are useful:

Authorization

This is the most common scenario for using JWT. Once the user is logged in, each subsequent request will include the JWT, allowing the user to access routes, services, and resources that are permitted with that token. Single Sign On is a feature that widely

uses JWT nowadays, because of its small overhead and its ability to be easily used across different domains.

Information Exchange

JSON Web Tokens are a good way of securely transmitting information between parties. Because JWTs can be signed-for example, using public/private key pairs-you can be sure the senders are who they say they are. Additionally, as the signature is calculated using

the header and the payload, you can also verify that the content hasn't been tampered with.

Extended Services - User Account and Authentication (XSUAA) service

The Extended Services - User Account and Authentication (XSUAA) service is one of the most important components to deal with when developing your own applications on Cloud Foundry. It takes care to authenticate and authorize your users and assign the right principals to your user’s session so your application can:

Identify the user by Email, UserId, First and Lastname

Check its roles (scopes) to decide if a user is allowed to do something or prohibit its action

The XSUAA is an internal development of SAP. SAP took the base of the open source UAA OAuth2 Provider of Cloud Foundry and extended it with SAP specific features to be used in SAP applications. One important thing is that the XSUAA does NOT store "real" users. This is why the XSUAA needs to trust an external Identity Provider (IdP).

SAP BTP XSUAA Overview Feature Set B.pptx

Note
This graphic only applies to SAP Business Technology Platform cloud management tools Feature Set B. Check out User and Member Management in the SAP Help Portal pages to better understand the differences between Feature Set A and Feature Set B in regard to user management.

In Feature Set B, your SAP BTP global account has its own XSUAA tenant. This XSUAA tenant by default has a trust relationship to the SAP ID Service. The SAP ID Service manages a large base of users, that have created a user account with SAP. You can add users, that exist in the SAP ID Service, as members to your global and subaccount. In order for these users to be able to perform administrative tasks, they need to be assigned with corresponding role-collections. There is a set of default platform role-collections, like Global Account Administrator, Global Account Viewer, Subaccount Administrator or Cloud Connector Administrator that you can use for the purpose of assigning SAP BTP account management authorizations.

Contrary to the platform users from the SAP ID Service, your business users can also be provided via your own corporate identity provider. These are the users you want to provide access to your business applications. These might be SaaS applications provided by SAP, like SAP Business Application Studio or SAP Workflow Management, or your own applications that you develop on the SAP BTP. The business applications have their own role-collections, like e.g. Business_Application_Studio_Developer, WorkflowManagementAdmin, or any custom role-collection that you create for your application. It is planned by the end of 2021 to also support corporate identity providers for platform users.

Role-Collections, Roles and Scopes

The following image shows the relationships between role-collections, roles and scopes.

Role-Collections, Roles and Scopes

Scopes

Scopes are arbitrary values that express authorizations / access rights in an application or service. Scopes need to be prefixed with an xsappname to make them uniquely identifiable.

Roles

Roles are entities that hold several scopes. Scopes can be put in multiple roles, so you are not limited to have scopes sitting in just one role.

Role-Collections

Role-collections contain one or more roles. A role can be used in multiple role-collections. But it is totally fine to have for example a role-collection called Admin that only has an admin role.

Role-collections are stored as an assignment in the XSUAA and are THE entity that can be assigned to a certain business user.

How does it work in practice?

Watch this video to learn more about the scope, role-collections, and roles. In this video, you can see that there are different personas. One is the developer working within a project and space. The other persona is an admin taking care of the CF account as a security admin.

Scope, Role-Collections, and Roles

When you as a developer build a new business application, you define scopes and pre-bundle them in role-templates.

You perform these definitions in the so called application security descriptor (xs-security.json) file.

You use the xs-security.json file to create an instance of the XSUAA service, which is bound to the corresponding business application(s).

The role-template definitions translate into roles. You as an administrator assemble these roles into role-collections and assign them to the business users of your application.

What is a xs-security.json? To simplify things, let’s just call the xs-security.json the "declaration of your app’s security".

The following xs-security.json is an excerpt of the office supplies application being built in our example.

Code snippet
{
  "xsappname": "HC_OFF_SUPPLIES",
  "tenant-mode": "dedicated",
  "scopes": [
    {
      "name": "$XSAPPNAME.Vendor",
      "description": "Supplier"
    },
    {
      "name": "$XSAPPNAME.ProcurementManager",
      "description": "Manager"
    }
  ],
  "attributes": [],
  "role-templates": [
    {
      "name": "Vendor",
      "description": "Supplier",
      "scope-references": [
        "$XSAPPNAME.Vendor"
      ],
      "attribute-references": []
    },
    {
      "name": "ProcurementManager",
      "description": "Manager",
      "scope-references": [
        "$XSAPPNAME.ProcurementManager"
      ],
      "attribute-references": []
    }
  ]
}
Expand

You have to tell the XSUAA service how to call your application (xsappname) and further define your scopes and role-templates. The scopes are being used within the application to check concrete permissions whenever a user tries to perform a certain action.

Security using CAP

Authorization means restricting access to data.

In CAP we do this by adding respective declarations to CDS models, which are then enforced in service implementations. By adding such declarations, we essentially revoke all default access and then grant individual privileges.

Authentication as Prerequisite

In essence, authentication verifies the user’s identity and the presented claims such as granted roles and tenant membership.

Briefly, authentication reveals who uses the service. In contrast, authorization controls how the user can interact with the application’s resources according to granted privileges. As the access control needs to rely on verified claims, authentication is a prerequisite to authorization.

From perspective of CAP, the authentication method is freely configurable and typically uses central identity and authentication services of the underlying platform.

For the local development scenario, CAP brings a built-in authentication on basis of mock users.

User Roles

As basis for access control, you can design conceptual roles that are application specific. Such a role should reflect how a user can interact with the application. For instance, the role Vendorcould describe users who can read sales articles. In contrast, a ProcurementManagercan have full access to sales articles. Users can have several roles, which are assigned by an administrative user in the platform’s authorization management solution.

Note

CDS-based authorization deliberately refrains from using technical concepts such as scopes as in OAuth in favor of user roles, which are closer to the conceptual domain of business applications. This also results in much smaller JWT tokens.

Pseudo Roles

Frequently, it’s required to define access rules that aren’t based on an application-specific user role, but rather on the authentication level of the request.

For instance, a service could be accessible not only for identified, but also for anonymous (for example, unauthenticated) users. Such roles are called pseudo roles as they aren’t assigned by user administration, but are added at runtime automatically.

The following predefined pseudo roles are currently supported by CAP:

  • authenticated-userrefers to (named or unnamed) users who have presented a valid authentication claim such as a logon token.
  • system-user denotes an unnamed user used for technical communication.
  • anyrefers to all users including anonymous ones (that means, public access without authentication).

Restrictions

By default, CDS services have no access control. Hence, depending on the configured authentication, CDS services are initially open for anonymous users.

To protect resources according to your business needs, you can define restrictions that make the runtime enforce proper access control. Alternatively, you can add custom authorization logic by means of authorization enforcement API.

Restrictions can be defined on different CDS resources:

  • Services
  • Entities
  • (un)bound actions and functions

You can influence the scope of a restriction by choosing an adequate hierarchy level in the CDS model.

For instance, a restriction on service level applies to all entities in the service.

Additional restrictions on entities or actions can further limit authorized requests. See section combined restrictions for more details. Beside the scope, restrictions can limit access to resources with regards to different dimensions:

  • The event of the request, that is, the type of the operation (what?)
  • The roles of the user (who?)
  • Filter-condition on instances to operate on (which?)

Restricting Events with @readonly and @insertonly

Annotate entities with @readonly or @insertonly to statically restrict allowed operations for all users as demonstrated in the example:

Code snippet
service Catalogservice { 
@readonly entity Products {...}  
@insertonly entity Suppliers {...}
}
Expand
Note

Note that both annotations introduce access control on entity level. In contrast, for sake of input validation, you can make use of @readonly also on property level.

Restricting Roles with @requires

  • @requires: allows specifying one or more user roles (as a single string or an array of strings) that the current user must be assigned.

Access Control with @restrict

  • @restrict: allows fine-grained control through an array of privileges given as grant statements in the form {grant:<operation>, to:<roles>, ...}
Code snippet
service CatalogService @(requires: 'authenticated-user')
{    
    @odata.draft.enabled :true      
    entity Suppliers @(restrict : [
     { grant : [ 'READ' ], to : [ 'Vendor' ] },
     { grant : [ '*' ], to : [ 'ProcurementManager' ] }
     ])as projection on officesupplies.Suppliers;
    entity Products @(restrict : [
     { grant : [ 'READ' ], to : [ 'Vendor' ] },
     { grant : [ '*' ], to : [ 'ProcurementManager' ] } 
     ])as projection on officesupplies.Products;
 
};
Expand

whereas the properties are:

  • grant one or more events the privilege applies to
  • to:one or more user roles the privilege applies to (optional)
  • where: a filter condition that further restricts access on instance level (optional)

Following values are supported:

  • grantaccepts all standard CDS events (such as READ, CREATE, UPDATE, and DELETE) as well as action and function names. WRITE is a virtual event for all standard CDS events with write semantic (CREATE, DELETE, UPDATE, UPSERT) and * is a wildcard for all events.
  • The toproperty lists all user roles or pseudo roles the privilege applies to. Note that pseudo-role any does apply for all users and is the default if no value is provided.
  • The where-clause can contain a boolean expression in CQL-syntax that filters the instances the event applies to. As it allows user values (name, attributes etc.) and entity data as input, it’s suitable for dynamic authorizations based on the business domain. Supported expressions and typical use cases are presented in instance-based authorization.

A privilege is met, if and only if all properties are fulfilled for the current request. In the following example, only orders can be read by an Vendor who meets Buyer element of the instance:

Code snippet
entity Suppliers @(restrict: [
    { grant: 'READ', to: 'Vendor', where: 'Buyer = $user' }
  ]) {/*...*/}
Expand

If a privilege contains several events, only one of them needs to match the request event to comply with the privilege. The same holds, if there are multiple roles defined in the to property:

Code snippet
service Catalogservice @(restrict: [ 
    { grant:['READ', 'WRITE'], to: ['Vendor', 'ProcurementManager'] } 
  ]) {/*...*/}
Expand

In this example, all users that have role VendororProcurementManger can read or write on Catalogservice.

You can build restrictions based on multiple privileges:

Code snippet
entity Suppliers @(restrict: [
    { grant: ['READ','WRITE'], to: 'Vendor' },
    { grant: 'READ', where: 'buyer = $user' }
  ]) {/*...*/}
Expand

A request passes such a restriction if at least one of the privileges is met. In this example, Admin users can read and write entity Suppliers. But also a user can read all Suppliers, which have a buyer property that matches the request user.

Similarly, the filter conditions of matched privileges are combined with logical OR:

Code snippet
entity Suppliers @(restrict: [
    { grant: 'READ', to: 'Vendor', where: 'country = $user.country' },
    { grant: ['READ','WRITE'], where: 'CreatedBy = $user' },
  ]) {/*...*/}
Expand

Here an Vendor user can read all Suppliers with matching country or which has been created by him- or herself.

Annotations such as @requires or @readonly are just convenience shortcuts for @restrict, for example:

  • @requires: 'Vendor'is equivalent to@restrict: [{grant:'*', to: 'Vendor'}]
  • @readonlyis the same as@restrict: [{ grant:'READ' }]

Supported Combinations with CDS Resources Restrictions can be defined on different types of CDS resources, but there are some limitations with regards to supported privileges:

Supported Combinations with CDS Resources
Note

1Node.js supports static expressionsthat don’t have any reference to the modelsuch as

where: $user.level = 2.

Restrictions and Draft Mode

Basically, the access control for entities in draft mode differs from the general restriction rules that apply to (active) entities. A user, who has created a draft, should also be able to edit (UPDATE) or cancel the draft (DELETE). The following rules apply:

  • If a user has the privilege to create an entity (CREATE), he or she also has the privilege to create a new draft entity and update, delete, and activate it.
  • If a user has the privilege to update an entity (UPDATE), he or she also has the privilege to put it into draft mode and update, delete, and activate it.
  • Draft entities can only be edited by the creator user.
Note

As a result of the derived authorization rules for draft entities, you don’t need to take care of draft events when designing the CDS authorization model.

Enable Authentication Support

To enable authentication support in CAP, a node.js module called passport needs to be installed.

Install the passport module. (the --save part makes sure it’s also added as a dependency to your project's package.json)

Code snippet
npm install --save passport
Expand

Add Users for Local Testing

Once the authorization checks have been added to the CAP model, they apply not only when deployed to the cloud but also for local testing. Therefore, you will need a way to log in to the application locally.

CAP allows you to add local users for testing as part of the cds configuration. We use the .cdsrc.json file to add the users.

The .cdsrc.json file can be used to store project configurations like in the package.json file.

Code snippet
{
 "[development]": {
 "auth": {
    "passport": {
        "strategy": "mock",
        "users": {
            "vendor@tester.sap.com": {
            "password": "initial",
            "ID": "Vendor",
            "userAttributes": {
            "email": "vendor@tester.sap.com"
        },
        "roles": ["Vendor"]
        },
            "ProcurementManager@tester.sap.com": {
            "password": "initial",
            "ID": "ProcurementManager",
            "userAttributes": {
            "email": "ProcurementManager@tester.sap.com"
        },
        "roles": ["ProcurementManager"]
            }
          }  
         }
        }
    }
 }"]
 }
 }
 }
 }
 }
 }
Expand
Note

There’s no logout functionality yet. To clear the basic authentication login data from the browser cache, you can either clear the browser cache or simply close all browser windows.

Define Restrictions and Roles in CDS

Business Example

Business Example

Set Up SAP Authorization and Trust Management

Set Up SAP Authorization and Trust Management

Exercise

Set Up SAP Authorization and Trust Management

Start Exercise

Code File - Define Restrictions and Roles in CDS

Business Example

If the Procedure has not been created, remove the Line

function get_supplier_info() returns array of Suppliers;

Business Example

Code snippet
using hc450.officesupplies as officesupplies from '../db/schema';

service CatalogService
{    
     @odata.draft.enabled :true      
    entity Suppliers @(restrict : [
     { grant : [ 'READ' ], to : [ 'Vendor' ] },
     { grant : [ '*' ], to : [ 'ProcurementManager' ] }
     ])as projection on officesupplies.Suppliers;
    entity Products @(restrict : [
     { grant : [ 'READ' ], to : [ 'Vendor' ] },
     { grant : [ '*' ], to : [ 'ProcurementManager' ] } 
     ])as projection on officesupplies.Products;
     
     function get_supplier_info() returns array of Suppliers;
 };
Expand

Business Example

cdsrc.json
Code snippet
{
 "[development]": {
 "auth": {
    "passport": {
        "strategy": "mock",
        "users": {
            "vendor@tester.sap.com": {
            "password": "initial",
            "ID": "Vendor",
            "userAttributes": {
            "email": "vendor@tester.sap.com"
        },
        "roles": ["Vendor"]
        },
            "ProcurementManager@tester.sap.com": {
            "password": "initial",
            "ID": "ProcurementManager",
            "userAttributes": {
            "email": "ProcurementManager@tester.sap.com"
        },
        "roles": ["ProcurementManager"]
            }
          }  
         }
        }
    }
 }
Expand

Business Example

xs-security.json For Information only this will be generated.
Code snippet
{
  "xsappname": "HC_OFF_SUPPLIES",
  "tenant-mode": "dedicated",
  "scopes": [
    {
      "name": "$XSAPPNAME.Vendor",
      "description": "Vendor"
    },
    {
      "name": "$XSAPPNAME.ProcurementManager",
      "description": "ProcurementManager"
    }
  ],
  "attributes": [],
  "role-templates": [
    {
      "name": "Vendor",
      "description": "generated",
      "scope-references": [
        "$XSAPPNAME.Vendor"
      ],
      "attribute-references": []
    },
    {
      "name": "ProcurementManager",
      "description": "generated",
      "scope-references": [
        "$XSAPPNAME.ProcurementManager"
      ],
      "attribute-references": []
    }
  ]
}
Expand

Business Example

mta.yanl code for reference
Code snippet
# ------------------------------------------------------------
  - name: HC_OFF_SUPPLIES-xsuaa
# ------------------------------------------------------------
    type: org.cloudfoundry.managed-service
    parameters:
      service-plan: application
      service: xsuaa
      path: ./xs-security.json
      config:
        xsappname: 'HC_OFF_SUPPLIES-${space}'
        role-collections:
          - name: 'Vendor-${space}'
            description: Read Access
            role-template-references:
              - $XSAPPNAME.Vendor
          - name: 'ProcurementManager-${space}'
            description: All Access
            role-template-references:
              - $XSAPPNAME.ProcurementManager
Expand

Create an Approuter

Business Example

Business Example

Code File Create an Approuter

Business Example

Business Example

Code snippet
{
  "name": "approuter",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node node_modules/@sap/approuter/approuter.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@sap/approuter": "^10.8.0"
  },
  "engines": {
    "node": "^14.0.0"
  }
}
Expand

Business Example

Code snippet
{	
	"welcomeFile": "/app/osfiori/webapp/index.html",
	"authenticationMethod": "route",
	"sessionTimeout": 30,
	"logout": {
	"logoutEndpoint": "/do/logout",
	"logoutPage": "/"
   },
	"routes": [
   	{
	"source": "^/app/(.*)$",
	"target": "$1",
	"localDir": "resources",
	"authenticationType": "xsuaa"
	},     
	{
	"source": "/catalog/(.*)",
	"destination": "srv-binding",
	"authenticationType": "xsuaa"
	}
  ]
}
Expand

Add the UI and Approuter Module to the MTA

Add the UI and Approuter Module to the MTA

Exercise

Add the UI and Approuter Module to the MTA

Start Exercise

Code File Add the UI and Approuter Module to the MTA

Business Example

Business Example

mta.yaml code for Reference this code is very sensitive for spaces and indentation.
Code snippet
_schema-version: '3.1'
ID: HC_OFF_SUPPLIES
version: 3.0.0
description: A simple CAP project.
parameters:
  enable-parallel-deployments: true
build-parameters:
  before-all:
    - builder: custom
      commands:
        - npm install --production
        - npx -p @sap/cds-dk cds build --production
modules:
# --------------------- SERVER MODULE ------------------------
  - name: HC_OFF_SUPPLIES-srv
# ------------------------------------------------------------  
    type: nodejs
    path: gen/srv
    requires:
      - name: HC_OFF_SUPPLIES-db
      - name: HC_OFF_SUPPLIES-xsuaa
      - name: HC_OFF_SUPPLIES-destination
    provides:
      - name: srv-api
        properties:
          srv-url: '${default-url}'
 # --------------------- SIDECAR MODULE ------------------------         
  - name: HC_OFF_SUPPLIES-db-deployer
 # ------------------------------------------------------------   
    type: hdb
    path: gen/db
    parameters:
      buildpack: nodejs_buildpack
    requires:
      - name: HC_OFF_SUPPLIES-db
      - name: HC_OFF_SUPPLIES-xsuaa
# ------------------------------------------------------------      
  - name: HC_OFF_SUPPLIES-approuter
# ------------------------------------------------------------  
    type: nodejs
    path: approuter
    requires:
      - name: HC_OFF_SUPPLIES-xsuaa
      - name: srv-api
        group: destinations
        properties:
          forwardAuthToken: true
          strictSSL: true
          name: srv-binding
          url: '~{srv-url}'
    build-parameters:
          requires:
            - name: HC_OFF_SUPPLIES-app
              artifacts:
                - ./*
              target-path: resources
# ------------------------------------------------------------
  - name: HC_OFF_SUPPLIES-app
# ------------------------------------------------------------  
    type: html5
    path: app
    build-parameters:
      supported-platforms: []
resources:
# ------------------------------------------------------------
  - name: HC_OFF_SUPPLIES-db
# ------------------------------------------------------------  
    type: com.sap.xs.hdi-container
    parameters:
      service: hana
      service-plan: hdi-shared
    properties:
      hdi-service-name: '${service-name}'
# ------------------------------------------------------------
  - name: HC_OFF_SUPPLIES-xsuaa
# ------------------------------------------------------------
    type: org.cloudfoundry.managed-service
    parameters:
      service-plan: application
      service: xsuaa
      path: ./xs-security.json
      config:
        xsappname: 'HC_OFF_SUPPLIES-${space}'
        role-collections:
          - name: 'Vendor-${space}'
            description: Read Access
            role-template-references:
              - $XSAPPNAME.Vendor
          - name: 'ProcurementManager-${space}'
            description: All Access
            role-template-references:
              - $XSAPPNAME.ProcurementManager
# ------------------------------------------------------------  
  - name: HC_OFF_SUPPLIES-destination
# ------------------------------------------------------------  
    type: org.cloudfoundry.managed-service
    parameters:
      service: destination
      service-plan: lite
Expand

Assign Role Collections

Assign Role Collections

Exercise

Assign Role Collections

Start Exercise

Save progress to your learning plan by logging in or creating an account