Search

Advanced Concepts

Find here an overview of advanced concepts.

Content

Security

With respect to Web services, authentication is the act of proving the validity of a presented user claim passed with the request. This typically comprises verifying the request user’s identity, tenant, and additional claims like granted roles. Briefly, authentication controls who is using the service. In contrast, authorization makes sure that the user has the required privileges to access the requested resources. Hence, authorization is about controlling what the user is doing with the service.

Obviously, a proper authorization needs to rely on a processed authentication, which asserts the user claims. In addition, invalid requests have to be rejected as soon as possible by the server in order to keep the resource impact as low as possible and make denial of service attacks much more complex. Consequently, authentication needs to be one of the first steps in the process pipeline of a request. This is the reason why it’s not integral part of the CAP runtime and needs to be configured on application level, for example, by adding additional dependencies. Currently, the CAP Java Runtime supports XSUAA authentication out of the box, but custom authentication can be configured in a flexible manner. Find details about configuring authentication in section Authentication.
However, a comprehensive authorization service is fully implemented by the runtime and is available wither by following a declarative approach through the CDS model or by explicitly making usage of the Authorization API. All about authorization is summarized in section Authorization.

Authentication

As described before, the Java runtime offers out-of-the-box support for the XSUAA authentication service on SAP Cloud Platform, which is based on OAuth 2.0 protocol. Shortly, the web flow following the OAuth protocol involves three agents: the resource owner (user), the resource server (web service), and the authentication server (XSUAA server). As first step the resource owner retrieves a signed access token from the resource server. This token contains all relevant user claims such as user identity, tenant identifier, and granted privileges. Requests to the resource server (web service) bear the token in the authorization header and gets validated before the request is being processed. On successful authentication, the user information is available in the scope of the request.

Spring Boot

The most convenient way to configure authentication is adding a Maven dependency to the XSUAA library in the pom.xml of the service:

<dependency>
	<groupId>com.sap.cloud.security.xsuaa</groupId>
	<artifactId>xsuaa-spring-boot-starter</artifactId>
	<version>${xsuaa.version}</version>
</dependency>

as well as the dependency to the XSUAA feature:

<dependency>
	<groupId>com.sap.cds</groupId>
	<artifactId>cds-feature-xsuaa</artifactId>
</dependency>

At runtime, the XSUAA library additionally needs to be supplied with a proper UAA service configuration, which is accomplished by a service binding to a UAA service instance. Find more details about UAA service configuration on SAP Cloud Platform below.

In case the CDS Runtime recognizes the required dependencies and the UAA service is configured correctly, a Spring security configuration is activated which basically enforces XSUAA authentication for all nonpublic endpoints of CAP services. As a result, these services aren’t accessible without presenting a valid token. There are several Spring properties to control the default behaviour of the security configuration:

CAP Java Runtime configures authentication for the exposed service endpoints according to the CDS model. Note that unrestricted service endpoints are accessible for anonymous users.

If you want to overwrite the security configuration for specific endpoints, you can add an additional Spring security configuration with a higher priority as done in the following example:

import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
@Order(1) // needs to be evaluated before the standard security configuration
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.requestMatchers().antMatchers("/public/**").and()
			.csrf().disable() // don't insist on csrf tokens in put, post etc.
		.authorizeRequests()
			.anyRequest().permitAll();
	}
}

Here all URLs matching /public/** are opened for public access.

Plain Java and SAP Java Buildpack

If plain Java deployed as war archive on SAP Cloud Platform with the SAP Java Buildpack, you need to add the Maven dependency to XSUAA in the following way:

<dependency>
	<groupId>com.sap.cloud.security.xsuaa</groupId>
	<artifactId>api</artifactId>
	<version>${xsuaa.version}</version>
	<scope>provided</scope>
</dependency>

Note that the dependency scope is provided here as the SAP Java Buildpack adds the required XSUAA library at deployment time. Additional configuration is needed to activate XSUAA authentication in the web.xmlfile for the Tomcat server as shown in the following snippet:

<web-app>
<display-name>sample</display-name>
  <login-config>
    <auth-method>XSUAA</auth-method>
  </login-config>
</web-app>

Please find more details on XSUAA sample application for SAP Java Buildpack.

UI Flows

HTTP endpoints with activated OAuth-based authentication require a bearer token in each request. As explained before, resource owners need to fetch a valid token from the authorization server. For UI-based flows, the token retrieval is typically handled by a UI frontend application, which redirects HTTP requests, that miss an access token, to a dedicated logon page. The frontend can cache the token and append it in subsequent request being forwarded to the backend service.

This approach is shown in the Application Router example for XSUAA.

Authorization

In contrast to authentication, authorization can be fully handled in the CAP Java Runtime. Preferred way is the declarative approach via the CDS Model, which allows you to control the access to CDS Services and Entities on a fine-granular level. CDS Service level, you can specify which roles are required for the request user to be able to access the service. However, on service entity level you can even define which roles are required to send a specific event. A precise description of the general authorization concept in CAP can be found here.

In the current version of CAP Java Runtime, the following limitations apply:

Create XSUAA Configuration

Compile your CDS model with authentication annotations into a full xs-security.json. For this, run:

mkdir gen
cds compile srv/ --to xsuaa,json > gen/xs-security.json

Note how in xs-security.json the different CDS roles CDS model has been mapped to XSUAA scopes and role template. It’s recommended to add this compile step to your build.

To manually create an XSUAA service with name cds-service-uaa based on this configuration and bind it to the application cds-application, run:

cf create-service xsuaa application cds-service-uaa -c gen/xs-security.json
cf bind-service cds-application cds-service-uaa

Both manual steps aren’t necessary when you model the respective XSUAA service and its dependencies in a deployment manifest before pushing to the cloud.

Set Up the Roles for the Application in the Cloud Cockpit

Once you’ve deployed your application to SAP Cloud Platform, you need set up the roles and role collections in the SAP Cloud Cockpit and assign them to users. This adds the necessary authorization information to the access token when the user logs on to your application through the XSUAA and Application Router.

Following configuration steps need to be done in the SAP Cloud Platform Cockpit within your Subaccount:

  1. Add the roles to role collections

    Navigate to Security -> Role Collections. Click on New Role Collection, enter a name, and confirm. Now, click on the new role collection and press Add Role. Choose the application, role template, and role to finish the creation step.

    In case you added a where clause in the @restrict annotation in the CDS model (which is currently not supported by the runtime), it might be necessary to create a role with corresponding attributes in advance. More detailed documentation can be found in section Building Roles and Role Collections for Applications in SAP Cloud Platform documentation.

  2. Assign the role collections to users

    The user role assignment is done in Security -> Trust Configuration. Select an IDP and add the assignment to role collection to specific users. More detailed documentation can be found in section Assign Role Collections in SAP Cloud Platform documentation..

Mock Users

If security is activated in Spring as described before, the CAP Java Runtime creates a security configuration, which accepts mock users for testing purposes. The mock user configuration only applies if:

You can define mock users in a Spring profile, which should be only active during testing as in the following example:

---
spring:
  profiles: testing
cds:
  security:
    mock:
      users:
        - name: authenticated-user
          password: pass1
        - name: viewer-user
          password: pass2
          roles: 
            - viewer  

The first user with name ‘authenticate-user’ has id ‘0815’ and password ‘pass1’. No roles are granted. In contrast, user ‘viewer-user’ has role ‘viewer’. All users can log in using basic authentication.

In addition, Spring property cds.security.mock.enabled = false will switch off any mock user configuration.

Data Protection and Privacy

Currently, there’s no out-of-the-box support for managing personal data. From the perspective of applications build on CAP, the CAP Java Runtime acts like a container that stores and serves data as defined in the CDS model. Hence, applications need to add required functionality - for example, by means of handlers - to fulfill general requirements with regards to personal data. Data stored by the Persistence Service is protected from unauthorized access - provided adequate access restrictions are declared in the CDS Model as already described.

Using CDS QL with a Static CDS Model

Static Model in the Query Builder

The Query Builder API allows you to dynamically create CDS QL queries using entity and element names given as strings:

Select.from("my.bookshop.Books")
  .columns("title")
  .where(book -> book.to("author").get("name").eq("Edgar Allan Poe"));

This query is constructed dynamically. It’s checked only at runtime that the entity my.bookshop.Authors actually exists and that it has the element name. Moreover, the developer of the query doesn’t get any code completion at design time. These disadvantages are avoided by using a static model to construct the query.

Model Interfaces

The static model is a set of interfaces that reflects the structure of the CDS model in Java (like element references with their types, associations, etc.) and allow to fluently build queries in a type-safe way. For every entity in the model, the model contains a corresponding StructuredType interface, which represents this type. As an example, for this CDS model:

namespace my.bookshop;

entity Books {
  key ID : Integer;
  title  : String(111);
  author : Association to Authors;
}

entity Authors {
  key ID : Integer;
  name   : String(111);
  books  : Association to many Books on books.author = $self;
}

the following model interfaces are generated:

@CdsName("my.bookshop.Books")
public interface Books_ extends StructuredType<Books_> {
  ElementRef<Integer> ID();
  ElementRef<String> title();
  Authors_ author();
  Authors_ author(Function<Authors_, Predicate> filter);
}
@CdsName("my.bookshop.Authors")
public interface Authors_ extends StructuredType<Authors_> {
  ElementRef<Integer> ID();
  ElementRef<String> name();
  Books_ books();
  Books_ books(Function<Books_, Predicate> filter);
}

Accessor Interfaces

The corresponding Data is captured in a Data Model similar to Java Beans. These beans are interfaces generated by the framework and providing the data access methods - getters and setters, and containing the CDS element names as well. The instances of the data model are created by the CDS QL Execution Engine (see example bellow).

Note the following naming convention: the model interfaces, which represent the structure of the CDS Model, always end with underscore, for example Books_. The accessor interface, which refers to Data Model, is simply the name of the CDS Entity - Books.

The following Data Model Interface is generated for Books:

@CdsName("my.bookshop.Books")
public interface Books extends CdsData {

  String ID = "ID";
  String TITLE = "title";
  String AUTHOR = "author";

  Integer getID();
  void setID(Integer id);
  
  String getTitle();
  void setTitle(String title);

  Authors getAuthor();
  void setAuthor(Map<String, ?> author);
}

Usage

In the query builder, the interfaces reference entities. The interface methods can be used in lambda expressions to reference elements or to compose path expressions:

Select<Books_> query = Select.from(Books_.class)			// Note the usage of model interface Books_ here
  .columns(book -> book.title())
  .where  (book -> book.author().name().eq("Edgar Allan Poe"));
  
List<Books> books = dataStore.execute(query).listOf(Books.class);	// After executing the query the result can be converted to a typed representation List of Books.

Generate the Static Model

The static model and accessor interfaces can be generated using the CDS4j Maven Plugin.

Setup

Configure the plugin in the <plugins> section of the pom.xml file:

<build>
  <plugins>
    <plugin>
      <groupId>com.sap.cds</groupId>
      <artifactId>cds4j-maven-plugin</artifactId>
      <version>${cds4j.version}</version>
      <configuration>
        <outputDirectory>${project.basedir}/src/gen</outputDirectory>
      </configuration>
      <executions>
        <execution>
          <id>cds4j-generate-model</id>
          <phase>generate-resources</phase>
          <goals>
            <goal>generate</goal>
          </goals>
          <configuration>
            <csnFile>${project.basedir}/src/main/resources/model.json</csnFile>
            <basePackage>my.model</basePackage>
            <eventContext>true</eventContext>
            <methodStyle>BEAN/FLUENT</methodStyle>
            <excludes>
              <exclude>my.bookshop.*</exclude>
              <exclude>my</exclude>
            </excludes>
          </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

Configuration

Below is a list of configuration options that the CDS4j Maven plugin accepts:

//BEAN
@CdsName("my.bookshop.User")
public interface User extends Row {
  String NAME = "name";

  String getName();

  void setName(String id);
}

//FLUENT
@CdsName("my.bookshop.User")
public interface User extends Row {
  String NAME = "name";

  String name();

  User name(String id);
}

Usage Without POM

The plugin can also be used without a Maven project that is, without a pom.xml file.

The following example generates the static model from the CSN file model.json into the folder out:

mvn com.sap.cds:cds4j-maven-plugin:1.2.1:generate -DcsnFile=model.json -DoutputDirectory=out

WARNING: Currently, the generator doesn’t support using Java reserved Keywords as identifiers in the CDS model.