Restricting API Operations in S/4HANA Cloud using SAP API Management
APIs in SAP S/4HANA Cloud are activated via communication arrangements, which are linked to communication scenarios and subsequently communication users. When exposing APIs in SAP S/4HANA Cloud via communication arrangements, there is no option to configure which Create/Read/Update/Delete (CRUD) operations are available on the API for the communication user. For example, if you want to give certain users the ability to pull invoice data into Excel via OData feed they would need the communication user associated to the API. This user has the ability to create, release and reverse invoices. Or you may have a side by side application on SAP BTP to show data that you only want to grant read only access for the data.
You can use SAP API Management as an API Proxy to restrict these operations and further enhance security in addition to monitoring the APIs. SAP API Management lets you publish, promote, and oversee APIs in a secure and scalable environment on SAP BTP.
In addition to restricting the operations available, another advantage is the ability to configure additional authentication mechanisms for user groups. For example, different username/passwords, API Keys, etc.
There are 3 ways to create APIs based on an S/4HANA system:
- Discover API from the SAP API Business Hub and use the copy function.
- Discover API from S/4HANA API Provider (this will read from OData metadata)
- Create the API from scratch.
In this blog, I’ll demonstrate option 1, which is the ideal way to create the API. I’ll expose the Supplier Invoice API via an API proxy and remove the update/delete methods in addition to configuring authentication via an HTTP header, apikey.
First, open SAP API Management click on the Discover icon and search for S/4HANA Cloud.
Discover via API Management application
Find the API that you want to proxy, in this example the Supplier Invoice OData API and click on the Copy button.
In the Copy API dialog box, change the Service URL to the one with variables in it and change the hostname of the S/4HANA Cloud tenant.
Edit the API using the Develop icon in the API portal.
Click on the Resources tab and you can see all of the operations available on the API.
Click the pencil icon next in the Header Data section. You can use the X button to remove the POST and DELETE methods.
Also remove the Batch and Service Operations.
In order to setup authkey authentication, assign the Verify API Key policy to the PreFlow Proxy Endpoint. For the APIKey value, we will use header authentication so the value should be: request.header.APIKey. This blog explains the end to end process as well.
Next, we configure basic authentication between API Management and the S/4HANA Cloud backend in the TargetEndpoint PreFlow. This blog outlines the exact steps so I won’t repeat them here.
After saving and deploying your proxy, the next step is to create a new Product from the API. This will make the API visible in your developer portal.
After creating your product, go to your developer portal and create a new application based on this proxy.
After deploying the application you can now access the URL of your API. Using a tool like Postman, we can query supplier invoices.
Now, if I try to create a new invoice I receive a Method Not Allowed, which is what we’d expect.
That’s it, you have successfully created an API that restricts the functions of the backend S/4HANA Cloud API.
Currently, there is a small issue when using the approach above where you need to fix the DefaultFaultFlow in the TargetEndPoint to properly raise the 405 Method Not Allowed.
In the API Policy editor, Export the API and then in the APITargetEndPoint folder of the extracted zip edit the default.xml file and add the following snippet to the DefaultFaultFlow ConditionalFlow section.
<request> <isRequest>true</isRequest> <steps> <step> <policy_name>defaultRaiseFaultPolicy</policy_name> <condition></condition> <sequence>1</sequence> </step> </steps> </request>
I believe if we can specifically assign the OAuth scopes (Read & Delete) to applications while subscribing to products it will then save our effort creating multiple API proxies for each operation type and have more granular access controls but unfortunately this feature is not available in SAP API Management yet.
Like in your blog above you have removed the Delete operation to grant only Read access but what if I have two applications which needs to consume the same API where one needs Read only access and other need both Read and Delete access? If we go with the approach you suggested above I would then need to create two API Proxies which will have two different base paths to refer same resource and two API Products, isn't it?
Please correct me if I misunderstood your blog.
I haven't tested this but yes, I believe your interpretation is correct and you will need to API proxies against the product API to accomplish this requirement.
Here's a way to achieve this (i.e. a single API Proxy can check for the permissions to access the resource for multiple consuming apps):
1. Deploy the scopes on the Product that are needed to be distributed to the APIs (e.g. product.read, product.write, product.delete etc.)
2. As a developer portal administrator, define a process that will determine the assignment of the relevant scope(s) to a subscribing app. There could be multiple ways to do this. for eg. maintaining a manual list (i.e App1 for developer1 needs product.read, App2 for developer2 needs product.read and product.write scopes) or by injecting the scopes as custom attributes during product subscription.
3. Configure an /oauth/token API Proxy with an OAuthV2 policy that will generate an access token for the scopes that the application developer should mandatorily present.
4. Configure a series of OAuthV2 verification policies that will verify the scopes (and the access token). Based on the policy execution status, either set a flag or use the internal policy status code to determine if the resource/operation should be permitted or disallowed.
5. Use this flag as a logical operator on the conditional string that will finally do the resource check as shown in the blog.