Skip to Content
Technical Articles

SAP Cloud Platform API Management – Self-Service Access Control

Lately a customer asked me if it was possible to check if an API consumer was actually reading only his data – within API Management. Although this is a typical use case for principal propagation (user ID is propagated to the backend, in which his rights are being enforced) I found the idea interesting.

More interesting is the fact that this use case demonstrates the flexibility of SAP API Management when it comes to customer-specific requests.

If you want to implement this use case, you’ll need to have access to an SAP Cloud Platform API Management environment and you should know the basics of API Management.

Detailed use-case description

As stated above, a company wants to expose data in a self-service mode to customers. This is done via an API that is managed centrally by API Management.

The company wants to enforce security on all levels, hence they also want API Management to check if the customer is accessing only his data.

Let’s imagine the API is an OData API. In order to retrieve data for the user (for instance a sales order), the application will call the resource like this:

/SalesOrderSet('0500000000')

To perform authentication, the application could send basic auth, an api key or a token to API Management. From these we could get the customer name.

But in order to simplify our example, we will use a query parameter containing the customer ID calling the resource:

customerIDParam = 0100000000

If the customer ID sent to the API proxy is matching the response payload (ie. if the customer requests his own sales order), everything is fine.

But let’s imagine someone reverse-engineers the App or Website and tries to access the sales order of other customers by altering the API call. This should be intercepted in API Management (and in the backend system using principal propagation).

Let’s see how to do this.

Create your API Provider and API Proxy

I will assume that you have a REST API backend in place. For testing purposes, you could use the SAP Dev ES5 system, an internet-available SAP dummy system. Here is how to get an account: https://developers.sap.com/tutorials/gateway-demo-signup.html 

In your API Management service, create the API Provider as follows:

Connection:
Type: Internet
Host: sapes5.sapdevcenter.com
Port: 443
Use SSL: checked
Catalog Service:
Path Prefix:/sap/opu/odata
Service Collection URL: /IWFND/CATALOGSERVICE/ServiceCollection
Trust All: unchecked
Authentication type: BASIC
Username: <your SAP DEV ES5 username>
Password: <your password>
With this in place, you can create your API proxy, using the “Discover” feature and selecting the “GWSAMPLE_BASIC” API.
I used the following API basepath:
/v1/AccessControlTest

Get the customer ID from parameter

As described before, we’ll need to get the customer ID first. This one is passed by the application as a query parameter. Obviously, for productive scenarios, you may want to use API key, OAuth or JWT tokens from which you can can get the user information.

For now, open the Policy Editor of your API proxy.

Select the “Sales Order Set” resource (by clicking on the blue “0” next to the name). This resource contains a sales order for a specific customer and can be filtered as needed.

Add an “Extract Variable” policy on the request:

  <ExtractVariables async="true" continueOnError="false" enabled="true" xmlns='http://www.sap.com/apimgmt'>
      <QueryParam name="customerIDParam">
          <Pattern ignoreCase="true">{customerIDParam}</Pattern>
      </QueryParam>
      <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
  </ExtractVariables>

This policy will store the customer ID in a variable for later usage.

Get the customer ID from the response

Now we’ll get the customer ID from the sales order API call response.

We’ll do this using another extract variable.

Select the “Sales Order Set” resource.

Add an “Extract Variable” policy on the response:

  <ExtractVariables async="false" continueOnError="false" enabled="true" xmlns='http://www.sap.com/apimgmt'>
      <Source>response</Source>
      <XMLPayload>
       <Namespaces>
            <Namespace prefix="d">http://schemas.microsoft.com/ado/2007/08/dataservices</Namespace>
      </Namespaces>
       <Variable name="customerIDPayload">
       <XPath>//d:CustomerID</XPath>
       </Variable>
    </XMLPayload>
  </ExtractVariables>

This policy will store the customer ID in a variable for later usage.

Compare actual customer ID and response customer ID

Because you cannot compare two variables directly in a proxy expression, we’ll use a small javascript that will do the trick.

Add a “JavaScript” policy on the response:

<Javascript async="false" continueOnError="false" enabled="true" timeLimit="200" xmlns='http://www.sap.com/apimgmt'>
	<ResourceURL>jsc://compareCustomerIDs.js</ResourceURL>
</Javascript> 

Add the actual Java Script in the “Scripts” section of the Policy Editor (left-hand menu) with the same name as above.

The code I used (I am no JS-god…) is as follows:

var customerIDParam = context.getVariable("customerIDParam");
var customerIDPayload = context.getVariable("customerIDPayload");
print("customerIDParam: " + customerIDParam);
print("customerIDPayload: " + customerIDPayload);

if (customerIDParam != customerIDPayload){
    context.setVariable("differentCustomerID", "true");
}
else{
    context.setVariable("differentCustomerID", "false"); 
}

As you can see, we are setting another variable “differentCustomerID” to hold the result of the comparison of the 2 customer IDs.

Add a conditional fault

Last step: let’s stop execution of the API proxy and raise a 403 error if different customer IDs are detected.

Add a “Raise Fault” policy at the end of the response lane:

<RaiseFault async="true" continueOnError="false" enabled="true" xmlns="http://www.sap.com/apimgmt">
	<FaultResponse>
		<Set>
			<Headers/>
			<Payload contentType="text/plain"> </Payload> <StatusCode>403</StatusCode>
			<ReasonPhrase>Unauthorized access</ReasonPhrase>
		</Set>
	</FaultResponse>
	<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</RaiseFault>

Now the trick is to raise this error only if the customer IDs are not the same, ie. if the variable created in the JavaScript is “false”.

Simply add the following condition to the “Raise Fault” policy execution:

(differentCustomerID == "true")

Your flow should look like this:

 

Testing time!

It is now time to test our little example.

I always recommend to use Postman to do API testing: you can thoroughly configure, save and organise your requests as needed.

Testing with the same customer ID in request and response

You will need to setup authentication for the backend system (eg. basic for SAP Dev ES5) in your API call.

Then use the following URL to call your API Management proxy:

https://<your APIM>.hana.ondemand.com:443/v1/AccessControlTest//SalesOrderSet(‘0500000000’)?customerIDParam=0100000000

The SalesOrderSet 0500000000 is the one of customer 0100000000.

No problem.

Testing with the different customer ID in request and response

Let’s see what happens if I try to request data from another sales order

https://<your APIM>.hana.ondemand.com:443/v1/AccessControlTest//SalesOrderSet(‘0500000001‘)?customerIDParam=0100000000

The API proxy now throws an error: customer ID in the request param is not identical to the one in the response.

Wrap-up

As you can see, it is quite simple to add specific features in API Management, as request by your organization. Especially security is a very important aspect of API Management, but security should be implemented on every level. This includes verification of the access rights in the backend system/API, not only in the API Management layer.

Be the first to leave a comment
You must be Logged on to comment or reply to a post.