Skip to content

Securing the Generated Service

Consider adding security measures to your generated OData service to control access.

You can secure the generated OData services by:

  1. Authenticating each user's identity.

  2. Authorizing each user's data access.

  3. Ensuring the confidentiality of data that's passed over the network.

Caution

If you omit the -login option during service generation, the generated OData service doesn't check a user's identity or restrict a user's data access. Nor does it guarantee that data passed over the network is encrypted (via HTTPS). Apart from exceptional circumstances, such as public-access read-only services, you should configure a production application for authentication, authorization, and confidentiality. This helps to ensure compliance with the EU General Data Protection Regulation or other applicable regulations.

Authentication

Using the -login option, specify XSUAA for Cloud Foundry (or another option supported by the target environment) to ensure that only authenticated users can access the service, it also supports X509 for Principal Propagation authentication for on-premise server.

Note

The generated service uses the login-config element within the generated web.xml to enforce authentication, however, for X509 authentication the login-config element will not be added.

Authorization

Specify the Security.Roles annotation for security roles that might not be referenced in other security annotations, but might be referenced in runtime checks using custom code, for example, when you are implementing row-level authorization.

Specify the Security.ServiceUserRoles annotation (or rely on the default Everyone role which matches all authenticated users) to ensure that only authorized users will be able to access the main OData service To authorize a subset of all authenticated users, specify a role name such as ServiceUser.

Specify the Security.ViewMetricsRoles annotation (or rely on the default ViewMetrics role) to ensure that only authorized administrators can access the embedded metrics service.

Example:

<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
    <edmx:Reference Uri="vocabularies/com.sap.cloud.server.odata.sql.v1.xml">
        <edmx:Include Alias="Security" Namespace="com.sap.cloud.server.odata.security.v1"/>
    </edmx:Reference>
    <edmx:DataServices>
        <Schema Namespace="example" xmlns="http://docs.oasis-open.org/odata/ns/edm" Alias="Self">
            ...
            <EntityContainer Name="ExampleService">
                <Annotation Term="Security.Roles">
                    <Collection>
                        <String>Manager</String>
                    </Collection>
                </Annotation>
                <Annotation Term="Security.ServiceUserRoles">
                    <Collection>
                        <String>Employee</String>
                    </Collection>
                </Annotation>
                <Annotation Term="Security.ViewMetricsRoles">
                    <Collection>
                        <String>Administrator</String>
                    </Collection>
                </Annotation>
            </EntityContainer>
        </Schema>
    </edmx:DataServices>
</edmx:Edmx>

Note

The generated service uses security-constraint elements within the generated web.xml to enforce authorization.

Fine-Grained Authorization

All authorized users have permission to create, read, update, and delete any data. The following section describe how to achieve more fine-grained authorization controls.

Method-Level Authorization

Reference the security annotation vocabulary in the CSDL XML file to restrict access to data methods (action imports, function imports), and apply the Security.InvokeRoles annotation term from that vocabulary to the data methods.

Example:

<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
    <edmx:Reference Uri="vocabularies/com.sap.cloud.server.odata.sql.v1.xml">
        <edmx:Include Alias="Security" Namespace="com.sap.cloud.server.odata.security.v1"/>
    </edmx:Reference>
    <edmx:DataServices>
        <Schema Namespace="example" xmlns="http://docs.oasis-open.org/odata/ns/edm" Alias="Self">
            ...
            <Action Name="SetSalaryAction">
                ...
            </Action>
            <EntityContainer Name="ExampleService">
                ...
                <ActionImport Name="SetSalary" Action="Self.SetSalaryAction">
                    <Annotation Term="Security.InvokeRoles">
                        <Collection>
                            <String>Manager</String>
                        </Collection>
                    </Annotation>
                </ActionImport>
            </EntityContainer>
        </Schema>
    </edmx:DataServices>
</edmx:Edmx>

A user needs only one of the permitted roles to be allowed access. Specifying an empty roles collection in the roles annotation means nobody can access the service.

Table-Level Authorization

Reference the security annotation vocabulary in the CSDL XML file to restrict access to entity sets (database tables) and apply the annotation terms from that vocabulary to the entity sets.

Example:

<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
    <edmx:Reference Uri="vocabularies/com.sap.cloud.server.odata.sql.v1.xml">
        <edmx:Include Alias="Security" Namespace="com.sap.cloud.server.odata.security.v1"/>
    </edmx:Reference>
    <edmx:DataServices>
        <Schema Namespace="example" xmlns="http://docs.oasis-open.org/odata/ns/edm" Alias="Self">
            ...
            <EntityType Name="Customer">
                ...
            </EntityType>
            <EntityType Name="Order">
                ...
            </EntityType>
            <EntityContainer Name="ExampleService">
                <EntitySet Name="Customers" EntityType="Self.Customer">
                    <Annotation Term="Security.ReadRoles">
                        <Collection>
                            <String>ViewCustomer</String>
                        </Collection>
                    </Annotation>
                    <Annotation Term="Security.WriteRoles">
                        <Collection>
                            <String>EditCustomer</String>
                        </Collection>
                    </Annotation>
                    <NavigationPropertyBinding Path="Orders" Target="Orders"/>
                </EntitySet>
                ...
            </EntityContainer>
        </Schema>
    </edmx:DataServices>
</edmx:Edmx>

Available annotation terms for entity set security include the following:

  • Security.ReadWriteRoles – a collection of roles permitted for read or write access, if not overridden by the more specific Security.ReadRoles, Security.WriteRoles, Security.CreateRoles, Security.UpdateRoles or Security.DeleteRoles.

  • Security.ReadRoles – a collection of security roles permitted for read access.

  • Security.WriteRoles – a collection of security roles permitted for write access, if not overridden by the more specific Security.CreateRoles, Security.UpdateRoles, or Security.DeleteRoles.

In each case, a user needs only one of the permitted roles to be allowed access. Specifying an empty roles collection in the roles annotation means nobody can access the service. Additionally specifying the Security.ReadRoles, but not the Security.WriteRoles, causes nobody to have write access except as permitted by Security.CreateRoles, Security.UpdateRoles, or Security.DeleteRoles.

Row-Level Authorization

You may want to customize generated listener classes to implement row-level authorization. The simplest way to achieve this is to add code into a beforeQuery listener method to add extra filter conditions to the client-specified queries. Adding these query filters also prevents write access to the respective records.

For example, suppose there is an Employee entity type with a business rule that managers can access any employee's record, but regular employees can access only their own employee record. This might be coded in the EmployeeListener class.

@Override void beforeQuery(DataQuery query)
{
    if (!servlet.isUserInRole("Manager"))
    {
        // Add or extend query filter to employee's own record only.
        query.filter(Employee.email.equal(servlet.currentUser()));
    }
}

If it isn't feasible to add extra filter conditions to achieve the required level of row-level authorization, then you may need to customize the executeQuery method in the generated entity handler for the entity type to post-process the query results.

Note

Any security role name used in a servlet.isUserInRole check must reference at least one security roles annotation so that the role name is known at deployment time. Use the Security.Roles annotation, as shown in the authorization examples, if there are no other roles-related annotations that reference the required role name.

Column-Level Authorization

Customize generated listener classes to implement column-level authorization.

For example, suppose that only managers can update an employee's salary, but employees can update other details in their own employee records. In addition to the beforeQuery customization, this might be coded in the EmployeeListener class.

@Override void beforeSave(EntityValue entityValue)
{
    Employee entity = (Employee)entityValue;
    if (entity.isNewOrChanged(Employee.salary)
        && !servlet.isUserInRole("Manager"))
    {
        throw AccessDeniedException.cannotWrite(Employee.salary.getQualifiedName());
    }
}

Note

Any security role name used in a servlet.isUserInRole check must reference at least one security roles annotation so that the role name is known at deployment time. Use the Security.Roles annotation, as shown in the authorization examples, if there are no other roles-related annotations referencing the required role name.

Confidentiality

Using the -login option ensures the confidentiality of data passed over the network.

Note

The generated service uses the transport-guarantee element of CONFIDENTIAL within the generated web.xml to achieve confidentiality.

Local Apache TomEE Security

Please refer to the Apache TomEE Security documentation and Apache Tomcat Security documentation for detailed configuration instructions.

  • Configure TomEE for transport-level security.

  • Edit conf/tomcat-users.xml to create users and assign appropriate roles.

  • Your OData service should now be ready for secure access by regular users and administrators. Make sure to use an https URL when connecting.

Note: A local server is useful for basic security testing, but additional security testing should always be performed in the intended target environment.

See also: -http and -https options.

SAP Cloud Platform Security

Use the security provided by the SAP Cloud Platform to secure your generated OData service.

Note

The Develop Applications topic in the SAP Cloud Platform documentation is a good starting point for additional information about SAP Cloud Platform security.

SAP Cloud Platform Security – Mobile Services

Use these security measures to keep your application secure in the Mobile Services environment for Cloud Foundry.

The following instructions refer to the SAP Cloud Platform Mobile Services Cockpit, and are subject to change if the cockpit UI changes.

SAP Cloud Platform for Cloud Foundry usually requires a xsuaa service instance and application router creation in order to secure a deployed Java application. This is described below. If these services are to be consumed by mobile applications and SAP Cloud Platform Mobile Services for Cloud Foundry is available, the process can be simplified to these steps.

  1. Using the SAP Cloud Platform Mobile Services Cockpit, select Mobile Applications > Native/Hybrid > New.

  2. For ID and Name, enter the ID and name of your already deployed OData application with router appended (the application router cannot have the same name as the OData service). Select Save.

    The Mobile Services Cockpit creates an application router and xsuaa service instance.

  3. On the Info tab for your mobile application, select the + sign to the right of Assigned Features. Choose Mobile Connectivity and select OK.

  4. On the Mobile Connectivity Configuration tab, select the Create icon to create a destination.

  5. Enter your OData service root URL, then select No Rewriting as the rewrite mode.

  6. Skip the Custom Headers and Annotations dialog pages.

  7. Select Forward Authentication as the SSO mechanism, then select Finish.

  8. On the APIs tab for your mobile application, note the Back End URL, which is the URL that clients should use to access your OData service via the application router.

  9. On the Security tab for your mobile application, select the Edit (pencil) icon to the right of Application Settings and choose the security configuration (Basic, SAML, or OAuth) that meets your organization's security requirements. You may be able to use Basic for testing purposes depending on your identity provider service. SAML works well with desktop browser clients. Using OAuth may require more client-side configuration.

  10. Enable CSRF Protection to prevent Cross-Site Request Forgery.

  11. On the Security tab for your mobile application, select the Edit (pencil) icon to the right of Role Settings. Select Browse to open the xs-security.json file in your project folder.

  12. Select OK to save the role settings.

  13. Navigate back to your Cloud Foundry subaccount, then use Security > Role Collections to define one or more role collections. If your application uses the default ServiceUser and ViewMetrics roles, define a role collection for each one and add the corresponding role to it. Otherwise, define role collections in accordance with the roles you selected using OData annotations for authorization. For each role ServiceUser and ViewMetrics, highlight the role. Select Assign and in the pop-up specify your user id and select Assign. The ServiceUser role is for regular users of the service (that is, a regular user of the service must have this role in order to be permitted to use the service). The ViewMetrics role is for administrators who want to view the service’s performance metrics. Regular users should not be permitted to view the metrics.

  14. Navigate back to your Cloud Foundry subaccount, then select Security > Trust Configuration and select your identity provider to show the Role Collection Assignment page, where you can assign role collections to individual users.

  15. Consult the SAP Cloud Platform documentation Security > Authorization and Trust Management > Developing Security Artifacts > Set Up Security Artifacts > Bind the XSUAA Service Instance to the Application. Bind the xsuaa service instance to your OData service (it was already bound to your application router by the Mobile Services Cockpit).

  16. Restart your OData service so it recognizes the assigned xsuaa service instance.

    Your OData service should now be ready for secure access by regular users and administrators using the service root (back-end) URL shown on the APIs tab for your mobile application.

SAP Cloud Platform Security – Cloud Foundry

Use these security measures to keep your application secure in the Cloud Foundry environment.

The following instructions refer to the SAP Cloud Platform Cockpit, and are subject to change if the cockpit UI changes.

  1. Consult the SAP Cloud Platform documentation topic Security > Authorization and Trust Management > Authorization and Trust Management in the Cloud Foundry Environment.

  2. Ensure that the Authentication Method xsuaa option was used during application generation.

  3. Using the SAP Cloud Platform Cockpit, navigate to your Cloud Foundry space, then use the Service Marketplace menu option to locate the xsuaa service.

  4. Select the xsuaa service, then select Instances, and select New Instance.

  5. In the Specify Parameters page of the wizard, select Browse and select the xs-security.json file that was generated in your project folder. This file defines the scopes and roles that are needed by the application.

  6. On the Assign Application page of the wizard, choose your deployed application.

  7. On the Confirm page of the wizard, choose an instance name (for example: myapp-xsuaa) and select Finish.

  8. Restart your OData service so it can recognize the assigned xsuaa service instance.

  9. Consult the SAP Cloud Platform documentation Development > Applications in the Cloud Foundry Environment > Configure Application Router.

  10. Consult the SAP Cloud Platform documentation Security > Authorization and Trust Management > Developing Security Artifacts > Set Up Security Artifacts > Bind the XSUAA Service Instance to the Application. Bind the xsuaa service instance to your application router (it was already bound to your OData service by the prior Assign Application step). Restart your application router.

  11. Configure Role Collections and Trust Configuration as documented in the previous section.

This following additional information may be helpful as you set up XSUAA:

  • When you create your service xsuaa instance make sure to specify application as the plan.

  • The generated xs-security-json defaults to dedicated as the tenant mode. Some Cloud Foundry landscape IDPs configure for "shared" tenant-mode. In that case, change it to shared before creating the XSUAA service instance.

  • In Cloud Foundry cockpit, the Trust Configuration and Role Collections require security administrator privileges, otherwise you can't see them.

    Your OData service is now ready for secure access by regular users and administrators.

Consider using SAP Cloud Platform Mobile Services as an intermediary with your OData service so that you can enable CSRF Protection to prevent Cross-Site Request Forgery.

X509 - Principal Propagation Authentication

Using the -login option specify X509 to enable Principal Propagation authentication.

Note

This option only supports an on-premise OData service.

In this scenario, it will setup Mutual Authentication between the SAP Cloud Connector and the on-premise OData service, however the on-premise OData service does not validate the Common Name from the client certificate. The SAP Cloud Connector will put a temporary user certificate in the HTTP request header ssl_client_cert, which contains the identity of an on-demand user. A filter in the on-premise OData service will decode this header and put the identity to the User Principal.

The following instructions are to configure the Principal Propagation authentication in SAP Cloud Platform Mobile Services, SAP Cloud Connector and the on-premise OData service.

  • Firstly, configure the SAP Cloud Platform Mobile Services. Create a new Application in Native/Hybrid Mobile Applications and assign the Mobile Connectivity feature, create a new Mobile Destination in the Mobile Connectivity feature, the SSO Mechanism/Authentication should be set to Cloud Connector SSO in the Mobile Destination, for more information please refer to:

  • Secondly, configure the SAP Cloud Connector, setup trusts between SAP Cloud Platform and SAP Cloud Connector, also setup the trusts between SAP Cloud Connector and back-end server, add corresponding Subaccount, configure the Access Control in Cloud To On-Premise section, for more information please refer to:

    • Connectivity (SAP Cloud Platform Connectivity (Cloud Foundry environment))
  • Finally, setup the Mutual Authentication in the on-premise OData service, for more information please refer to the server documentation (e.g. for Apache Tomcat or TomEE).


Last update: November 16, 2020