* The ES5 Demo Gateway System is not really an on-premise system, but we will use it to mimic one
Additionally to those requirements, this time, an on-premise system is needed. I will be using the openly available ES5 system, for which you can create a user here. To test whether you have access to the system, open https://sapes5.sapdevcenter.com/sap/opu/odata/sap/SEPMRA_PO_APV/PurchaseOrders?$format=json and login with your username and password. If successful, you should be able to see information about some purchase orders.
Now that we have access to the system let's connect it to Cloud Foundry. For this purpose, we will use the SAP Cloud Connector. Follow this guide on how to install and configure a Cloud connector instance on your local machine to connect to your Cloud Foundry Subaccount. Once completed, you can check your configuration by selecting the "Connectivity" item in the navigation menu of your CF Subaccount and navigating to "Cloud Connectors". Under "Exposed Back-End Systems" you should be able to see the on-premise system with the virtual host you set up when configuring the Cloud Connector.
Once you are done setting up the connection to your backend system, use the knowledge we acquired in my last post to set up an API Provider and an API in the API Portal.
For the API Provider, navigate to Configure, click on Create and set one up called "es5" with the following configuration:
Connection:
Property | Value |
---|---|
Type | On Premise |
Host | virtuales5 |
Port | 8000 |
Location ID | (leave empty) |
Authentication | None |
Use SSL | false |
Additional Properties | None |
Catalog Service Settings:
Property | Value |
---|---|
Path prefix | /sap/opu/odata |
Service Collection URL | /iwfnd/catalogservice/ServiceCollection |
Authentication type | Basic |
Username | (your ES5 username) |
Password | (your ES5 password) |
Now, use the API Provider you just created to set up an API with the following configuration:
Property | Value |
---|---|
Select | API Provider |
API Provider | (select the provider you just created |
URL | /sap/opu/odata |
Host Alias | (choose one of the options) |
API Base Path | /es5 |
Service Type | ODATA |
Save and deploy your API. To check whether everything is working correctly, open your API's URL in your browser and authenticate with your ES5 credentials.
Since each request we want to send to our ES5 API is authenticated by the ES5 system, we will need to send our ES5 credentials with every request. The easiest way of doing so is by adding a basic authentication header to the GET
requests we are sending. Taking our previously built node application, we could extend it like so:
// Make axios get request to API url
const response = await axios.get(url, {
// Add custom authorization headers
auth: {
username: "username",
password: "password"
}
});
This would, however, require us to store our login information in every application we want to send requests from, like our Alexa skill from previously. Which will inevitably introduce security concerns since we would have to store sensitive data on potentially vulnerable third-party systems.
To avoid this issue, we can let SAP API Management handle the authentication. For that, we will safe our login information in a key-value map on the API Portal and use policies to add a basic authentication header to incoming requests. This gives us the advantage of only having to store user information on SAP systems.
To create a key-value map, you will first need to access your API Portal, navigate to Configure and switch over to the "Key Value Maps" tab. Click on Create and enter the following:
Property | Value |
---|---|
Name | Es5BasicAuthCredentials |
Encrypt Key Value Map | true |
Also, add the following two entries to the key-value map:
Key | Value |
---|---|
username | (your ES5 username) |
password | (your ES5 password) |
Save your key-value map. Setting "Encrypt Key Value Map" to true will prevent anyone from reading the set values manually, and only API Management can access them.
For our purpose, we will want to add two policies:
On the left side of your API portal, navigate to Develop and select the API you created. In the top right corner, click on Policies. Doing so will take you to the Policy Editor, where you will see an overview of the request flow.
If you want to learn more about how the flow works, I recommend checking out this blog post by Elijah Martinez.
Since we want to add two policies, first click on Edit. Next, select the PreFlow under TargetEndpoint and add (+) a "Key Value Map Operations" policy, which you can find under "Mediation Policies" on the right side of the Policy Editor.
Clicking on add (+) will prompt you with a dialogue field in which you name your policy.
Set "getEs5Credentials" as the policy name and make sure to keep Stream set to Incoming Request. Replace the preconfigured XML with:
<KeyValueMapOperations mapIdentifier="Es5BasicAuthCredentials" continueOnError="false" enabled="true" xmlns="http://www.sap.com/apimgmt">
<!-- Read parameter with key "username" and assign its value to private variable BasicAuthUsername-->
<Get assignTo="private.BasicAuthUsername" index='1'>
<Key><Parameter>username</Parameter></Key>
</Get>
<!-- Read parameter with key "password" and assign its value to private variable BasicAuthPassword-->
<Get assignTo="private.BasicAuthPassword" index='1'>
<Key><Parameter>password</Parameter></Key>
</Get>
<Scope>environment</Scope>
</KeyValueMapOperations>
<BasicAuthentication async='true' continueOnError='false' enabled='true' xmlns='http://www.sap.com/apimgmt'>
<!-- Operation can be Encode or Decode -->
<Operation>Encode</Operation>
<IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
<!-- for Encode, User element can be used to dynamically populate the user value -->
<User ref='private.BasicAuthUsername'></User>
<!-- for Encode, Password element can be used to dynamically populate the password value -->
<Password ref='private.BasicAuthPassword'></Password>
<!-- Assign to is used to assign the encoded value of username and password to a variable. This should not be used if the operation is Decode -->
<AssignTo createNew="true">request.header.Authorization</AssignTo>
</BasicAuthentication>
Notice the two policies we created and their order. First, read out the required values from the key-value map, then use those values to add a basic authentication header.
If we now make a call to our API, we should successfully get back the pruchase orders we already saw when directly accessing the ES5 system. You will notice, however, that we are now pretty much at the same point we were at the beginning of this blog post since now everyone can make calls to the API without authentication, which is not what we want. Here I want to introduce the concept of API Keys. I won't go into full detail as to what an API key exactly is, but imagine it as a unique identifier, that can be used to authenticate you, or rather your program.
If you are curious to learn more about the concept of API keys, check out this article.
With SAP API Management, two steps need to be completed to acquire an API key. First, we will need to publish the API we created in the previous step as a product. A product represents a set of one or more APIs that are published to developers who might want to work with them. If you recall, in my previous blog post, I differentiated between the API Portal and Developer Portal. By publishing an API as part of a product, we make it accessible in the Developer Portal and by doing so, to our developer colleagues.
To create a product, access your API Portal and navigate to Develop > Products and click on Create.
Under the Overview tab, enter the following properties:
Property | Value |
---|---|
Name | demo-product |
Title | Product containing ES5 API |
Description | Simple demo product publishing ES5 API |
Next, switch over to the APIs tab and add the API you created for the ES5 system in the previous steps.
Publish your product.
Now, switch over to the Developer Portal. There you should see the product you just created.
Clicking on it will show you detailed information about the product, such as the APIs contained in it. In order to acquire an API key for your API, you will need to register an application with the product you published the API in. To do that, click on Subscribe > Create a new Application.
This will prompt you with a window in which you can set up your application. Enter "demo-app" as the name. The product containing your API should already be selected.
Once you created your application, SAP API Management will automatically assign an API key to you, which you can access on your application's overview page. Here you can also regenerate your key in case it has been unintentionally leaked to the outside.
To add the API key to our request, we can simply add the key as a request header. This is easiest done by recycling the code snippet we used earlier to make a basic authentication request:
const axios = require('axios');
require('ssl-root-cas').create().addFile(__dirname + "/../certs/es5-cert.pem");
const url = "https://devrelations.test.apimanagement.eu20.hana.ondemand.com/es5";
(async function() {
// Make request to API
try {
const response = await axios.get(url, {
// Add custom ApiKey header
headers: {
"ApiKey":"erDcyxv5WsDuK1XJ4AaQ3hyLV1POSQhN"
}
});
console.log(response.data.value);
} catch(e) {
console.error(e.toJSON().stack);
}
})();
To verify the API key passed with an incoming request, we will once again create a policy in the policy editor of the API Portal. Navigate to your API and access the policy editor. Click on Edit, select the PreFlow under TargetEndpoint and create a new "Verify API Key" policy. Enter verifyApiKey
as the name of the policy and replace the pre-defined XML text with:
<!--Specify in the APIKey element where to look for the variable containing the api key-->
<VerifyAPIKey async='true' continueOnError='false' enabled='true'
xmlns='http://www.sap.com/apimgmt'>
<APIKey ref='request.header.ApiKey'/>
</VerifyAPIKey>
request.header.ApiKey
, since that is how we configured our request earlier. Also make sure the policy you just created is the first one in the PreFlow, meaning in the flow diagram it should be on the left of the two policies we created prior. If that is not the case, you can use the left and right arrows at the top of the page to rearrange the policy order.Update and save your API. Sending a request to the API without or with an invalid API key will now result in a 401 Unauthorized
response, while using a valid API key will give us access to the data.
The purpose of using API keys is to have a secure way to consume SAP data from third-party platforms. The Alexa skill from my previous blog post is an example of software running on such a platform. To adapt it to our new API and API key requirements, replace the GetApiDataIntentHandler
with:
const GetApiDataIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'GetApiDataIntent';
},
// Handle user's intent request
async handle(handlerInput) {
const baseURL = "https://devrelations.test.apimanagement.eu20.hana.ondemand.com/es5";
const path = "/sap//SEPMRA_PO_APV/PurchaseOrders?$format=json";
let speakOutput;
try {
// Get resources from API and format as readable string
const response = await axios.get(baseURL + path, {
headers: {
"ApiKey":"7iGuS5Yy28BVxfgR0yVC3fAvvyon4hLD"
}
});
const usdOrders = response.data.d.results.filter(x => x.CurrencyCode === "USD");
const totalAmount = (usdOrders.reduce((a, b) => a + parseFloat(b.GrossAmount), 0)).toFixed(2);
speakOutput = `There are ${usdOrders.length} purchase orders with a total gross amount of $${totalAmount}.`;
} catch(e) {
// Error handling
speakOutput = `An error occurred}`;
}
return handlerInput.responseBuilder
.speak(speakOutput)
.getResponse();
}
};
GetApiDataIntent
with "show me my data", will now display the number and total gross amount of purchase orders provided by the ES5 system.We have an on-premise system providing us with SAP data. We connected to that system using SAP API Management. Following the same principles as in my previous blog post, we created an API Provider and an API, to consume data from ES5. However, this time, we also added some policies to require API keys in incoming requests and adding basic authentication. This required some additional steps I did not cover in the previous blog post: publishing the API as a product and subscribing to it in the form of an application on the Developer Portal.
It should be noted however that the policy flow I created during this blog post is only supposed to be a demonstration, to show how policies can be used and what is generally possible with SAP API Management. An API key alone should not always be used as a sole mean of authentication; however, in this case, it doesn't really matter since the ES5 system only offers basic authentication anyway. If you are dealing with sensitive data you should consider making use of advanced authentication flows, such as OAuth 2.0 and JWT tokens.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
40 | |
24 | |
16 | |
10 | |
9 | |
8 | |
7 | |
7 | |
7 | |
5 |