NOTE: this blog post is intended for developers who have previous experience in developing multi-tenant CAP applications using SAP Business Application Studio, SAP BTP destinations, and the destination and XSUAA services.

Introduction

After I published this blog post, many developers reached out to me with the classical question: “does this microservice work in a multi-tenant scenario?

My first reaction was to provide them a quick and straightforward answer: “yes, as long as you properly handle the on-board and off-board processes for the subscription and the access to the appropriate subscriber XSUAA APIs to manage its users“.

Looks quite simple, right? Well, but, of course, the queries kept coming (having the same quick and straightforward answer). Then, I finally made the decision to take on the challenge and develop the multi-tenant version of the microservice to serve as a definitive reference for those who want to embarc on that adventure.

So, before you continue reading this post, I strongly recommend that you read through the first post (as this one is just its spin-off) to fully understand what’s going to be discussed in the following topics. That post explains in detail the single-tenant version of the microservice – this version just contains some specific modifications to make it multi-tenant.

Therefore, without further due, let’s dive into the service details.

Application Architecture

The business problem is exactly the same of the first post. The scenario in this post is intended to deep dive into a fully working multi-tenant version of the CAP microservice leveraging an SAP Fiori UI for subscribers. The service manages users who are assigned to specific role collections (the ones belonging to the business application that is consuming the service) reading and writing user information from BTP in the subscriber subaccount using its XSUAA service APIs. The microservice is ready to deploy on BTP, Cloud Foundry Runtime.

Here’s a diagram representing the overall architecture of the microservice:

Figure 1 – Application Architecture

You may have noticed that the contents inside the provider subaccount are exactly the same as in the architecture described in the first post.

In the subscribers subaccounts, there’s the microservice subscription and a specific destination pointing to the XSUAA APIs from the subaccount.

XSUAA manages the BTP user store (namely “Shadow Users”). In the subscriber subaccount the CAP microservice subscription is created and  will be responsible for managing the business application’s users in there. Once again, as in the original version, the microservice will “filter” users from BTP based on a set of role collections belonging to the business application.

It interacts with the XSUAA service in the subscriber through a destination to make REST API calls to the services’ user management APIs which are based on the System for Cross-Domain Identity Management (SCIM) standard.

That being said and looking to the architecture diagram, it’s obvious that each subscriber will require its own instance of the XSUAA service with the apiaccess plan (which also requires enabling Cloud Foundry and creating a CF space – no memory quota assignment is needed in this case). This is well detailed in the Git repo branch instructions.

The Fiori Elements HTML5 application will serve as the UI for the microservice’s subscribers.

Prerequisites

As in the first post, that application is already built using SAP Cloud Application Programming Model (CAP) and BTP’s XSUAA APIs. The full code can be found in this branch of the GitHub repo from SAP samples.

As previously mentioned, the intent of this blog post is just to walk through and understand the key-points of the development.

Therefore, as first prerequisite to follow-up with this post (besides reading through the first one), you must clone the repo branch strictly following the instructions from its README.md to setup the project locally.

NOTEdo not miss any single step of the instructions, otherwise you won’t be able to run and deploy the application.

Security Descriptor (xs-security.json)

The only difference in the security descriptor from the first post is that “tenant-mode” is shared instead of dedicated:

Figure 2 – Application info

We also changed the “xsappname” in case you decide to deploy the microservice in the same subaccount of the single-tenant version, otherwise it’s absolutely not mandatory.

The role collections for the generic application served by the microservice have been prefixed with GenericMtxApp instead of GenericApp also in case you want to deploy this version in the same subaccount as the single-tenant version, thus avoiding to share the same role collections between both versions. And that’s all!

To learn more about the syntax of the XSUAA security descriptor you can read this official document.

Required Services

Same as in the first post. The only exception is that, upon deployment the MTA will create an additional service to manage the on-board and off-board processes of subscriptions to the microservice: the SaaS Provisioning Service (namely saas-registry). That service is bound to the CAP backend service in the mta.yaml:

Figure%203%20-%20New%20service%20binding%20for%20the%20CAP%20backend%20servie

Figure 3 – New service binding for the CAP backend service in mta.yaml

And is configured as shown below:

Figure%204%20-%20SaaS%20Provisioning%20Service%20configuration

Figure 4 – SaaS Provisioning Service configuration

I strongly recommend you examine the mta.yaml in detail to look for the differences in relation to the single-tenant version from the first post. You’ll notice that the managed approuter has been replaced by a standalone approuter:

Figure%204%20-%20Standalone%20Approuter%20in%20mta.yaml

Figure 5 – Standalone Approuter in mta.yaml

Thus, all references to the HTML5 repo and runtime services and respective configurations have been completely removed. This also leads to changes in the approuter configuration within the xs-app.json file:

Figure%206%20-%20xs-app.json

Figure 6 – xs-app.json

Besides the replacement of all routes related to the HTML5 repo and runtime, another route has been added to point to the on-subscription and on-dependencies callbacks required by the SaaS Provisioning Service.

Destination Setup

Same as in the first post.

Package Setup

Few modifications have been made to the projects’ package.json file in relation to the first post.

First, some dependencies have been added to handle the on-board, off-board and get dependencies processes of subscription life-cycle:

Figure%207%20-%20New%20project%20dependencies

Figure 7 – New project dependencies

The @sap/cds-mtx package is also responsible for managing the different schemas (HDI containers in the case of SAP HANA Cloud) where the data model is deployed for each tenant (subscriber) to provide full data isolation.

Second, some changes have been made to the cds.requires section:

1. Indication of a “mock tenant” for each “mock user“;

Figure%208%20-%20Mock%20tenant%20for%20mock%20user

Figure 8 – Mock tenant for mock user

2. Multitenancy setup:

Figure%209%20-%20Multitenancy%20setup

Figure 9 – Multitenancy setup

3. There’s also an additional API setup which is not part of the original microservice and has been added as a bonus that will be discussed later:

Figure%2010%20-%20Additional%20API%20reference

Figure 10 – Additional API reference

Microservice Configuration

The new GenericMtxApp prefix for the role collections has also been adjusted in the corresponding environment variable in the microservice configuration:

Figure%2011%20-%20New%20role%20collections%20prefix

Figure 11 – New role collections prefix

Service Definition

In the service definition, a new action has been bound to the User entity. This has been done to execute the bonus snippet, which will be discussed later in this post:

Figure%2012%20-%20New%20action%20bound%20to%20the%20User%20entity

Figure 12 – New action bound to the User entity

Annotations for the UI

Like it has been done with the previously defined action, we also annotate the criticality of the newly added action (which is also non-critical), but with no side effect in this case:

Figure%2013%20-%20UI%20annotations%20for%20the%20new%20action

Figure 13 – UI annotations for the new action

Subscription Handling

The subscription handling process is fully implemented in the server.js file into the srv folder. That file has been entirely generated by the SAP HANA Academy CAP SaaS Multi-tenant Application Generator, selecting the options to automatically create routes and return required destination dependency in the respective callbacks, with no further modification (please, refer to the corresponding videos in the playlist from the link to better understand the generator).

Code Analysis

The application code is basically the same with the only exception that, as it’s running in a multi-tenant context, the control flag that was a simple boolean variable in the single-tenant version has been changed to a JSON object like demonstrated in the screenshots below:

Figure%2014%20-%20New%20JSON%20object%20control%20flag

Figure 14 – New JSON object control flag

Figure%2015%20-%20Control%20flag%20handling%20upon%20data%20read

Figure 15 – Control flag handling upon data read

Figure%2016%20-%20Control%20flag%20reset%20upon%20data%20refresh

Figure 16 – Control flag reset upon data refresh

Bonus Snippet!

A while ago I also wrote and published a blog posts series focusing on how to get authenticated user information using three different approaches. One of them (which is the most complete) uses the XSUAA API from the application service binding – you can check it here.

And guess what? Same old classical recurring question: “does it work in a multi-tenant context?“. Well, in this case the answer is: “yes, but with a slight modification in the code of the post to consider the authentication made in the subscriber“.

So, once again, I decided to take advantage of the effort to produce the mult-tenant version of this microservice and add the multi-tenant version of the procedure to get authenticated user information.

NOTE: to understand what I’m talking about here, please also read through the blog post, before inspecting the bonus snippet.

OK, so here’s the code executed by the previously mentioned userInfo action:

Figure%2017%20-%20userInfo%20action%20main%20code

Figure 17 – userInfo action main code

In the single-tenant version described in the post, the url in the credentials is simply filled with the one gotten from the XSUAA service binding. As this binding is done in the provider subaccount (where the service is actually deployed), when the “/userinfo” endpoint is invoked XSUAA yields error, because the authentication (handled by the approuter) has been done in the subscriber subaccount and not the provider.

Well, the trick here is simple: as subscriptions can be done only in subaccounts located in the same region as the provider we can safely assume the authentication domain is always the same for both provider and subscribers. Therefore, for XSUAA to correctly interpret the authenticated user info, we just need to change the subdomain of the API URL to point to the subscriber subdomain instead of the provider. This is exactly what the highlighted code snippet does.

And this concludes the analysis and explanation of the multi-tenant user management microservice CAP project (with bonus snippet).

Additional Resources

Here’s a list of resources to enhance your learning experience on this topic:

Conclusion

After setting up the project from this git repo branch and going through this blog post content, you’ll have learnt and experimented how to build a multi-tenant CAP microservice which will allow your business application to manage its business users autonomously for each application subscriber. Hope you have enjoyed the journey!

Please, do not hesitate to submit your questions in Q&A in SAP Communityhttps://answers.sap.com/index.html

Sara Sampaio

Sara Sampaio

Author Since: March 10, 2022

0 0 votes
Article Rating
Subscribe
Notify of
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x