Security | capire

Describes authentication and authorization specific for CAP Java.

Info

This chapter appends CAP Java sepcifc information only. Consult the comprehensive Security Guide first to learn about CAP Security features in general.

Authentication

Auto Configuration

To enable auto-configuration for authentication based on platform services, following two conditions need to be met:

  1. Required Maven dependencies available.
  2. Binding to a corresponding service instance (XSUAA and/or IAS) is available at runtime.

Warning

Only if both, the library dependencies and an XSUAA or IAS service binding are in place, the CAP Java SDK activates a Spring security configuration, which enforces authentication for all endpoints automatically.

Maven Dependencies

To ensure the proper maven dependencies, we recommend using the cds-starter-cloudfoundry or the cds-starter-k8s starter bundle. Both can be active for the local scenario.

Runtime Maven dependencies required for authentication
  • cds-feature-identity
  • org.springframework.boot:spring-boot-starter-security
  • com.sap.cloud.security:resourceserver-security-spring-boot-starter that brings spring-security library

Service Bindings

Additionally, your application must be bound to corresponding service instances depending on your scenario.
The following list describes which service must be bound depending on the tokens your application should accept:

  • only accept tokens issued by XSUAA --> bind your application to an XSUAA service instance
  • only accept tokens issued by IAS --> bind your application to an IAS service instance
  • accept tokens issued by XSUAA and IAS --> bind your application to service instances of both types.

Unique Binding

CAP Java picks only a single binding of each type. If you have multiple XSUAA or IAS bindings, choose a specific binding with property cds.security.xsuaa.binding respectively cds.security.identity.binding. Choose an appropriate XSUAA service plan to fit the requirements. For instance, if your service should be exposed as technical reuse service, make use of plan broker.

Custom Authentication

Authenticated Endpoints

By default, the auto-configuration covers

  • Protocol adapter endpoints (managed by CAP such as OData V4/V2 or custom protocol adapters)
  • Remaining custom endpoints (not managed by CAP such as custom REST controllers or Spring Actuators)

There are several application parameters in section cds.security.authentication that influence the behaviour of the auto-configuration wit hregards to the affected endpoints:

Configuration PropertyDescriptionDefault
authenticateUnknownEndpointsDetermines, if security configurations enforce authentication for endpoints not managed by protocol-adapters.true
authenticateMetadataEndpointsDetermines, if OData $metadata endpoints enforce authentication.true

Authentication Modes

The property cds.security.authentication.mode controls the strategy used for authentication of protocol-adapter endpoints. There are four possible values:

Configuration PropertyDescription
neverNo endpoint requires authentication. All protocol-adapter endpoints are considered public.
model-relaxedAuthentication is derived from the authorization annotations @requires and @restrict. If no such annotation is available, the endpoint is considered public.
model-strictAuthentication is derived from the authorization annotations @requires and @restrict. If no such annotation is available, the endpoint is authenticated. An explicit @requires: 'any' makes the endpoint public (Default).
alwaysAll endpoints require authentication.

By default the authentication mode is set to model-strict to comply with secure-by-default. In that case you can use the annotation @requires: 'any' on service-level to make the service and its entities public again. You can only make an endpoint public if the full endpoint path is also considered public. For example you can only make an entity public, if the service that contains it is also considered public.

Tip

The authentication mode has no impact on the authorization behaviour.

Overrule Partially

If you want to explicitly change the automatic security configuration, you can add an additional Spring security configuration on top that overrides the default configuration by CAP. This can be useful if an alternative authentication method is required for specific endpoints of your application.

As the default security configurations provided by CAP act as the last line of defense and handle any request by default, you need to ensure that your custom security configurations have higher precedence. At the SecurityFilterChain bean method, set the @Order annotation with a lower numeric value, for example 1:

java

@Configuration
@EnableWebSecurity
public class AppSecurityConfig {

  @Bean
  @Order(1) // needs to have higher priority than CAP security config
  public SecurityFilterChain appFilterChain(HttpSecurity http) throws Exception {
    return http
      .securityMatcher(AntPathRequestMatcher.antMatcher("/public/**"))
      .csrf(c -> c.disable()) // don't insist on csrf tokens in put, post etc.
      .authorizeHttpRequests(r -> r.anyRequest().permitAll())
      .build();
  }

}

Due to the custom configuration, all URLs matching /public/** are opened for public access.

_

Be cautious with the configuration of the HttpSecurity instance in your custom configuration. Make sure that only the intended endpoints are affected.

Another typical example is the configuration of Spring Actuators. For example a custom configuration can apply basic authentication to actuator endpoints /actuator/**:

java

@Configuration
@EnableWebSecurity
public class ActuatorSecurityConfig {

  @Bean
  @Order(1)
  public SecurityFilterChain actuatorFilterChain(HttpSecurity http)
      throws Exception {
    return http
      .securityMatcher(AntPathRequestMatcher.antMatcher("/actuator/**"))
      .httpBasic(Customizer.withDefaults())
      .authenticationProvider(/* basic auth users with PasswordEncoder */)
      .authorizeHttpRequests(r -> r.anyRequest().authenticated())
      .build();
  }

}

Overrule Fully

In case you want to write your own custom security configuration that acts as a last line of defense and handles any request you need to disable the CAP security configurations by setting cds.security.authentication.authConfig.enabled: false, as Spring Security forbids registering multiple security configurations with an any request security matcher.

If you even want to deactivate OAuth token validation for XSUAA or IAS, e.g. to establish an own authentication strategy, the following properties can be used:

Configuration PropertyDescriptionDefault
cds.security.xsuaa.enabledWhether automatic XSUAA security configuration is enabled.true
cds.security.identity.enabledWhether automatic IAS security configuration is enabled.true

CAP Users

CAP is not bound to any specific authentication method or user representation such as those introduced with XSUAA or IAS; it runs requests based on a user abstraction. The CAP user of a request is represented by a UserInfo object that can be retrieved from the RequestContext as explained in the authentication guide.

Mock Users

By default, CAP Java creates a security configuration, which accepts mock users for test purposes.

Tip

Mock users are only initialized if the org.springframework.boot:spring-boot-starter-security dependency is present in the pom.xml file of your service.

Preconfigured Mock Users

For convenience, the runtime creates default mock users reflecting the pseudo roles:

NameRolePassword
authenticatedauthenticated-userempty
systemsystem-userempty
privilegedprivileged modeempty

For example, requests sent during a Spring MVC unit test with annotation @WithMockUser("authenticated") will pass authorization checks that require authenticated-user. The privileged user will pass any authorization checks.

There are several properties to control behavioud of mock users:

Configuration PropertyDescriptionDefault
cds.security.mock.defaultUsersActivates creation of pre-defined mock users at startup.true
cds.security.mock.enabledActivates mock users.false in production profile, true otherwise.

Custom Mock Users

You can also define mock users explicitly. This mock user configuration only applies if:

  • The service runs without a service binding (non-production mode)
  • Mock users are defined in the active application configuration

Define the mock users in a Spring profile, which may be only active in local testing, as in the following example:

yaml

---
spring:
  config.activate.on-profile: default
cds:
  security:
    mock:
      users:
        - name: Viewer-User
          tenant: CrazyCars
          roles:
            - Viewer
          attributes:
            Country: [GER, FR]
          additional:
            email: myviewer@crazycars.com
          features:
            - cruise
            - park

        - name: Admin-User
          password: admin-pass
          privileged: true
          features:
            - "*"
  • Mock user with name Viewer-User is a typical business user with SaaS tenant CrazyCars who has the assigned role Viewer and user attribute Country ($user.Country evaluates to the value list [GER, FR]). This user also has the additional attribute email, which can be retrieved with UserInfo.getAdditionalAttribute("email"). The features cruise and park are enabled for this mock user.
  • Admin-User is a user running in privileged mode. Such a user is helpful in tests that bypasses all authorization handlers.

A setup for Spring MVC-based tests based on the given mock users and the CDS model from above could look like this:

java

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class BookServiceOrdersTest {
	String ORDERS_URL = "/odata/v4/BooksService/Orders";

	@Autowired
	private MockMvc mockMvc;

	@Test
	@WithMockUser(username = "Viewer-User")
	public void testViewer() throws Exception {
		mockMvc.perform(get(ORDERS_URL)).andExpect(status().isOk());
	}
	@Test
	public void testUnauthorized() throws Exception {
		mockMvc.perform(get(ORDERS_URL)).andExpect(status().isUnauthorized());
	}
}

Mock Tenants

A tenants section allows to specify additional configuration for the mock tenants. In particular it is possible to assign features to tenants:

yaml

---
spring:
  config.activate.on-profile: test
cds:
  security:
    mock:
      users:
        - name: Alice
          tenant: CrazyCars
      tenants:
        - name: CrazyCars
          features:
            - cruise
            - park

The mock user Alice is assigned to the mock tenant CrazyCars for which the features cruise and park are enabled.

Custom Users

Therefore, if you bring your own authentication, you must transform the authenticated user and inject it as UserInfo to the current request. This is done by means of UserInfoProvider interface that can be implemented as Spring bean as demonstrated in Registering Global Parameter Providers. More frequently you might have the requirement to just adapt the request's UserInfo which is possible with the same interface:

java

@Component
public class CustomUserInfoProvider implements UserInfoProvider {

    private UserInfoProvider defaultProvider;

    @Override
    public UserInfo get() {
        ModifiableUserInfo userInfo = UserInfo.create();
        if (defaultProvider != null) {
            UserInfo prevUserInfo = defaultProvider.get();
            if (prevUserInfo != null) {
                userInfo = prevUserInfo.copy();
            }
        }
        if (userInfo != null) {
           /* any modification of the resolved user goes here: */
           XsuaaUserInfo xsuaaUserInfo = userInfo.as(XsuaaUserInfo.class);
           userInfo.setName(xsuaaUserInfo.getEmail() + "/" +
                            xsuaaUserInfo.getOrigin()); // normalizes name
        }

        return userInfo;
    }

    @Override
    public void setPrevious(UserInfoProvider prev) {
        this.defaultProvider = prev;
    }
}

In the example, the CustomUserInfoProvider defines an overlay on the default XSUAA-based provider (defaultProvider). The overlay redefines the user's name by a combination of email and origin.