APIs allow organizations to unlock data from back-end systems to spark innovation, bring products to market faster, and improve the overall quality and experience of dealing with back-end systems. More and more, industries the world over are looking for solutions that connect their back-end systems, making them all accessible via an API. When organizations rely on disparate systems and data, it becomes nearly impossible to innovate.
For example, organizations in the healthcare space are facing unprecedented challenges and have a need to integrate legacy back-end systems in order to provide the best care and experience for their care population. There are many EHR and EMR systems in use today that do not support seamless integration and it can be detrimental to the goal of such organizations if they cannot provide care in a timely and efficient manner.
Similarly, companies in the Financial Services space are gearing up for increased demand for financial products such as loans, credit cards and investments among other needs. There are many banking core systems or home-grown systems that do not support seamless integration and it can be costly to those organizations to not evolve.
Many of these organizations are stifled because they can only integrate as much as their legacy systems allow. Building out and hosting your own APIs with the power of the MuleSoft platform is one way to solve that problem.
Leading firms in both healthcare and financial services industries leverage Azure Blob Storage to power certain use cases. They may use it to store files, documents, and payloads of relevant data.
But how do you stand up an API to unlock an Azure Blob Storage in a controlled, secure, and managed manner?
Accessing Azure Blob Storage from MuleSoft
Microsoft’s Azure Blob storage is a cloud-based, binary large object (BLOB) storage option that is part of Microsoft’s Azure Cloud. It supports many different types of files, all stored in binary format. Blob storage is designed for (among other things), serving images or documents directly to a browser, storing files for distributed access, storing data for backup, etc.
While Azure Blob storage is a database, it cannot be accessed using SQL. Access is gained via the Azure Storage REST API, Azure PowerShell, Azure CLI, or via an Azure Storage client library.
Since MuleSoft interacts seamlessly with APIs, we chose to demonstrate how to leverage the Azure Storage REST API alongside Java in MuleSoft. In creating an API that abstracts the Azure Storage REST API, we are able to stand up a modern API on top of an existing API.
An API-Led Connectivity diagram of an Azure Blob System API connected to Azure Blob Storage.
Azure Storage REST API
Looking at the API documentation, you can see that there are many API calls that interact with the Blob storage database. For this example, we’ll focus on retrieving a blob.
Accounts and Containers
The Get Blob API has a request URI that contains an Account and Container value. These values are specific to the subscriber’s Blob storage setup.
Authorization
Even though the Get Blob request URI is pretty simple, it requires an authorization (SAS) token. This is the complicated part of executing this request. The SAS token is a combination of some static values (which depend on the request you’re making) and a calculated signature. It’s this signature where a little Java helps us out.
https://Account/Container/BlobName?sv=2019-02-02&ss=b&srt=co&sp=r&se=2020-05-05T20%3A11%3A04Z&st=2020-05-05T20%3A10%3A04Z&spr=https&sig=sOuAoHcp7A8fpe%2BwM5qPW9KlLzGv8hJO%2BR35LnwvFCc%3D
Everything bolded in the above request is the SAS token. It is composed of five static values, two datetime values, and the signature.
- sv = SignedVersion. Specifies the signed storage service version to use to authorize requests made with this account SAS.
- ss = SignedServices. Specifies the signed services accessible with the account SAS. Possible values include Blob (b), Queue (q), Table (t), and File (f). In this case, we’re getting a Blob, so we’re using ‘b’.
- srt = SignedResourceTypes. Specifies the signed resource types that are accessible with the account SAS. Container (c) and Object(o)
- sp = SignedPermission. Specifies the signed permissions for the account SAS. Read (r)
- st = SignedStart. The starting date time of the authorization.
- se = SignedExpiry. The ending date time of the authorization. In this case, 1 minute after that start time. Note: These are URL encoded and in a specific format. yyyy-MM-ddThh:mm:ssZ
Signature
The signature portion of the SAS taken is an encrypted string of the URL, all of the above static values, and an Account Key. The Account Key is specific to the instance of the database.
The URL and the static values are formed into a string and that string is HMAC-computed with a SHA256 algorithm using the key value. That is then Base64-encoded. The result is the signature.
Since Java provides all of those encryption functions out of the box, we’re using a small static method to compute the signature.
package com.sl;import javax.crypto.Mac;import javax.crypto.spec.SecretKeySpec;import java.io.UnsupportedEncodingException;import java.net.URLEncoder;import java.text.DateFormat;import java.text.SimpleDateFormat;import java.util.Base64;import java.util.Calendar;import java.util.Date;import java.util.TimeZone;public class GenerateSAS {public static String GetFileSAS(String resourceUrl, String accountName, String key, String azureApiVersion, String permissions, String service, String resourceType){DateFormat dateFormat = new SimpleDateFormat(“yyyy-MM-dd’T’HH:mm:ss’Z'”);dateFormat.setTimeZone(TimeZone.getTimeZone(“GMT”));Date startDate = new Date();String start = dateFormat.format(startDate);Calendar endCal = Calendar.getInstance();endCal.add(Calendar.SECOND,60);String expiry = dateFormat.format(endCal.getTime());System.out.println(expiry);String spr = “https”;String stringToSign = accountName + “\n” +permissions+”\n” +service+”\n” +resourceType+”\n” +start + “\n” +expiry + “\n” +“\n” +spr+”\n” +azureApiVersion+”\n”;String signature = getHMAC256(key, stringToSign);try{String sasToken = “sv=” + azureApiVersion +“&ss=” + service +“&srt=” + resourceType +“&sp=” + permissions +“&se=” + URLEncoder.encode(expiry, “UTF-8”) +“&st=” + URLEncoder.encode(start, “UTF-8”) +“&spr=” + spr +“&sig=” + URLEncoder.encode(signature, “UTF-8”);String urlStr = resourceUrl+”&”+sasToken;if(!resourceUrl.contains(“?”))urlStr = resourceUrl+”?”+sasToken;return urlStr;} catch (UnsupportedEncodingException e) {e.printStackTrace();}return null;}private static String getHMAC256(String accountKey, String signStr) {String signature = null;try {SecretKeySpec secretKey = new SecretKeySpec(Base64.getDecoder().decode(accountKey), “HmacSHA256”);Mac sha256HMAC = Mac.getInstance(“HmacSHA256”);sha256HMAC.init(secretKey);signature = Base64.getEncoder().encodeToString(sha256HMAC.doFinal(signStr.getBytes(“UTF-8”)));} catch (Exception e) {e.printStackTrace();}return signature;}} |
GetFileSAS is a static method that is called from the MuleSoft flow. It constructs the URL using the method parameters and the getHMAC256 method.
MuleSoft API
To access Azure Blob storage with MuleSoft API, the URL, complete with SAS token, needs to be created. Once that is done, just make a HTTP Request to the URL; the response will be the document bytes that are stored in the database.
<flow name=”GetDocument” doc:id=”a98be5e5-b69b-4008-ab6c-5d0fb134c82a” ><set-variable value=”#["https://" ++ attributes.uriParams.’Account’ ++ ".blob.core.windows.net/" ++ attributes.uriParams.’Container’ ++ "/" ++ attributes.uriParams.’FileName’]” doc:name=”Set Endpoint” doc:id=”aca62d78-aa2d-4881-b0d7-5041c176dde8″ variableName=”endpoint” /><java:invoke-static method=”GetFileSAS(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String)” doc:name=”Invoke static” doc:id=”18fb7b22-c559-4c50-bc74-13ac7a287ede” class=”com.sl.GenerateSAS” target=”fileURL” ><java:args ><![CDATA[#[{arg0 : vars.endpoint,arg1 : attributes.uriParams.Account,arg2 : p(‘secure::azure-blob-connect.accountKey’),arg3 : p(‘secure::azure-blob-connect.apiVersion’),arg4 : “r”,arg5 : “b”,arg6 : “co”}]]]></java:args></java:invoke-static><http:request method=”GET” doc:name=”Request” doc:id=”c4157fa3-a015-43a5-b220-546aa6ef0372″ config-ref=”HTTP_Request_configuration” url=”#[vars.fileURL]” target=”docBytes” /></flow> |
Let’s break down what occurred above.
Flow
The flow above first constructs the endpoint URL from the passed in Account, Container, and FileName values. It then invokes the Java static method using that URL, Account, Account Key, API Version, and the static values for permissions, service, and resource type.
The Java method returns the URL with SAS token which is then used in a HTTP Request callout. The response is stored in a variable for future use.
The following screenshot elaborates on what happens during the Invoke Static portion of the above flow.
What about the Azure Client Library?
You might ask, “we’re already using Java, why not use Azure’s client library to calculate the signature?” The reason is code size. The Azure client library is large. It’s built to execute all of the Azure REST API via code. There is no need to import that large of a codebase into your MuleSoft project when a small static method using native Java can handle what’s needed.
Final notes before you try it
Accessing a document in Azure Blob storage via MuleSoft is not complicated, but you have to pay attention to the details. The details have to be followed or access won’t be granted. Unfortunately, Azure doesn’t return specific details on why access isn’t granted. If it’s a required parameter, make sure you are including it. It could be as simple as a missing or inappropriate static value to a bad calculation of the signature. Make sure you familiarize yourself with the Azure documentation linked above.
For more solutions designed to help your organization improve agility and increase solution discovery and reuse, check out our MuleSoft solutions page