Explaining Application Security

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

After completing this lesson, you will be able to:

  • Explain 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.

Approuter

Approuter – is Node JS library that is available in Public NPM. It represents a single entry point to your application.

Its tasks are:

  • The Approuter 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 Approuter can serve static content such as web pages, SAPUI5, or another client-side code.
  • The Approuter 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 .

Main Properties on root level

  • authenticationMethod

    This property indicates which authentication will be applied for this xs-app.json. Can be none (means that all routes are not protected) and route (authentication type will be chosen according to definition in a particular route). The default value is route .

  • logout

    By using this property, you can define two important thing about your business application central logout handling.

    logoutEndpoint – contains some internal path. When accessing this path your application will trigger central logout procedure. Triggering central logout will destroy user session data in Approuter, call XSUAA in order to remove user session on their side and also will call logout paths of destinations that are defined for this specific application (please refer to property destinations).

    logoutpage – can be internal path or absolute external URL. Value of this field describes so called "landing page" page address, that user will be redirected in the browser after central logout.

  • destinations

    This property indicates destinations endpoints that need to be called during session/central logout in order to destroy sessions on their side.

  • services

    The same as destinations. Services can implement their specific logout logic and approuter will trigger these endpoints during central logout / session timeout scenario.

Routing

One of important capabilities of AppRouter is to be a reverse proxy for your application. In order to achieve that you need to model correctly property routes. That property is an array of Objects. Each object represents one particular route.

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.

Note

You can set up and run your own application router or you can use the application router that is managed by SAP (for more information see Managed Application Router).

SAP recommends running your standalone app router only in advanced cases, for example when application router extensibility is required

The managed application router enables you to access and run HTML5 applications in a cloud environment without the need to maintain your own runtime infrastructure.

The managed application router is the HTML5 applications runtime capability that is provided by the following products:

  • SAP Work Zone

  • SAP Launchpad service

  • SAP Cloud Portal

To use the managed application router, you must be subscribed to one of these services.

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

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.

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.

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": []
    }
  ]
}
Copy code

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 SAP Cloud Application Programming Model

Authorization means restricting access to data.

In SAP Cloud Application Programming Model 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 Vendor could describe users who can read sales articles. In contrast, a ProcurementManager can 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 SAP Cloud Application Programming Model:

  • authenticated-user refers 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.
  • any refers 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 {...}
}
Copy code
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;
 
};
Copy code

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:

  • grant accepts 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' }
  ]) {/*...*/}
Copy code

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'] } 
  ]) {/*...*/}
Copy code

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' }
  ]) {/*...*/}
Copy code

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' },
  ]) {/*...*/}
Copy code

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:

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
Copy code

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.

SAP Cloud Application Programming Model 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"]
            }
          }  
         }
        }
    }
 }"]
 }
 }
 }
 }
 }
 }
Copy code
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

Set Up SAP Authorization and Trust Management

Code File - Define Restrictions and Roles in CDS

If the Procedure has not been created, remove the Line

function get_supplier_info() returns array of Suppliers;

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;
 };
Copy code

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"]
            }
          }  
         }
        }
    }
 }
Copy code

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": []
    }
  ]
}
Copy code

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
Copy code

Create an Approuter

Code File Create an Approuter

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"
  }
}
Copy code
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"
	}
  ]
}
Copy code

Add the UI and Approuter Module to the MTA

Code File Add the UI and Approuter Module to the MTA

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
Copy code

Assign Role Collections

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

Login or Register