Portal Multi-Tenancy

In API Developer Portal, Portal administrators can manage the provisioning and de-provisioning of multiple tenants using the Tenant Provisioning Service (TPS) within the Portal via a REST API.
The Tenant Provisioning Service (TPS) is a standalone service responsible to perform all the activities required to manage the provisioning of tenants within the Portal via a REST API. TPS is exposed via Ingress, API Gateway component that serves as an entry-point for API requests to internal Portal components which are not publicly accessible.
For more information about creating a tenant, see Create the API Portal Tenant.
In this article:
Overview
As a Portal Administrator, you might want to run Portal securely for multiple organizations and a large group of users. Multi-tenancy gives you the ability to separately manage several Portal entities, such as different business units, environments, and so on. In addition, you can separately manage data of these entities, for example applications, application keys, APIs, or reporting.
Multi-Tenancy provides:
  • Efficient use of existing resources and infrastructure
  • Data segregation
Multi-Tenancy Architecture
Best Practice
To use Portal Multi-tenancy in the most efficient way, we recommend the following best practices:
  • One Portal instance is controlling the tenants.
  • The Portal DB is centralized and shared between the tenants.
  • Gateway clusters are managed separately for each tenant and each GW cluster has an OTK DB. One Portal tenant can have multiple proxy Gateways.
  • Multiple tenants cannot point to the same OTK DB as there might be a synchronization issue. Separate OTK databases are needed but they can reside in the same server with proper performance tuning and scaling.
  • By defining the Gateway cluster that the Portal tenant will enroll with, you will determine the set of OTK information that synchronizes with the runtime Gateway.
Follow the recommended VM requirements as specified for a single tenant. To run more than one tenant, review and set up the VM requirements accordingly.
Data Segregation
The following table shows sharing and separation of resources in various deployment scenarios:
Single Tenant on Single Portal Instance
Multiple Tenants on Single Portal Instance
Multiple Portal Instances
Server Resources (CPU, Disk, Memory, Network, etc.)
Dedicated
Shared
Separated
Application Keys
Account Plans
API Plans
Organizations
PAPI Calls
Custom Page Branding
Users
Roles
Themes and Branding
Dedicated
Separated per tenant
Separated per instance
Authentication Schemes
Dedicated
Separated/Shared
Separated/Shared
Analytics
Dedicated
Shared
Separated/Shared
OTK DB
Dedicated
Separated (can be shared)
Separated (can be shared)
Support for Proxy Gateways in Multi-Environments
Federated Deployment
Federated Deployment and/or Environment per tenant
Federated Deployment and/or Environment per instance
Example Use Case
My Company has a complex organizational structure. They want to use a Portal instance but must separate the business units, Shoe Division and Handbag Division as well as Applications and APIs for these divisions.
The following diagram shows how My Company uses two Portal Tenants within one Portal instance:
When you create a tenant, alias is not supported. You can only define one Portal domain on Portal. You can call your tenant differently but cannot change the domain name.
Example:
tenant1.mycompany.com
tenant2.mycompany.com
Backup and Restore
With multiple tenants, you can only backup and restore the full DB. You cannot back up individual tenants.
Manage Multiple Tenants
The Portal Administrator can perform the following actions from any location with access to Portal APIs:
  • GET a tenant or a list of all tenants, or information for a tenant (read all, read one, OpenAPI specification)
  • POST a tenant provisioning request (create a tenant)
  • DELETE a tenant
  • PUT (a TPS job retry after fail)
All TPS calls require mutual authentication using certificate and key. To make secure calls, you must create a certificate. For more information, see
Using "Manage Private Keys" for SSL Certificates
in Gateway documentation.
The default name of the Ingress host is apim.
Action
HTTP Method
URL
Read one tenant
GET
https://<ingress_host>:38443/provision/tenants/<tenant_id>/status
https://<ingress_host>:38443/provision/tenants/v2/<tenant_id>
Read all tenants
GET
https://<ingress_host>:38443/provision/tenants
OpenAPI specification for a tenant
GET
https://<ingress_host>:38443/provision/tenants/<tenant_id>/swagger/<api_name>
Create a tenant
POST
https://<ingress_host>:38443/provision/tenants/<tenant_id>/swagger/<api_name>
Delete a tenant
DELETE
https://<ingress_host>:38443/provision/tenants/<tenant_id>
Retry after fail
PUT
https://<ingress_host>:38443/provision/tenants/<tenant_id>/status
https://<ingress_host>:38443/provision/tenants/v2/<tenant_id>?detailed=true
GET
Request
curl --user admin:password --insecure --request GET -w "\nStatus Code: %{HTTP_CODE}%\n" https://apim.dev.mycompany.com:38443/provision/tenants/apim/status
Response:
{ "code":200, "message":"Successfully fetched Tenant Details", { "tenantInfo":{ "tenantId": "tenant1", "portalHost": "tenant1.mycompany.com", "tenantType": "ON-PREM", "adminEmail": "[email protected]", "tenantStatus":"ACTIVE" }, "componentStatus":[ { "componentName":"portal.data", "status":"SUCCESS", "statusMesage":"Successfully bootstrapped" }, { "componentName":"authenticator", "status":"SUCCESS", "statusMesage":"Successfully bootstrapped" } .... ] } }
Request for v2:
curl --user admin:password --insecure --request GET -w "\nStatus Code: %{HTTP_CODE}%\n" https://apim.dev.mycompany.com:38443/provision/tenants/v2/apim
Response:
{ "uuid":"d790dc77-fad8-11e6-9a60-0242ac11000f", "tenantId":"apim", "tenantStatus":"ACTIVE", "portalHost":"apim.dev.mycompany.com", "tenantType":"ON-PREM", "userDetails":{}, "provisioningStatus":"COMPLETED", "provisioningMessage":"Provisioning of Tenant succeeded", "enrollmentStatus": { "enrollmentUrl":"https://enroll.dev.mycompany.com:9443/enroll/apim?sckh=mMgIYYTbxcTD37pEbDOYphCfe1v8aetd09YTlRHNfrg&token=", "enrolled":true }, "provisioningDetails": [ { "jobName":"PortalApiProvisioningJob", "jobStatus":"COMPLETED", "jobCreateDate":1564672969060 }, { "jobName":"PortalCustomPageApiProvisioningJob", "jobStatus":"COMPLETED", "jobCreateDate":1564672971693 }, { "jobName":"PortalMetricsApiProvisioningJob", "jobStatus":"COMPLETED","jobCreateDate":1564672970850 }, { "jobName":"PortalRbacApiProvisioningJob", "jobStatus":"COMPLETED", "jobCreateDate":1564672969632 }, { "jobName":"PortalLoginApiProvisioningJob", "jobStatus":"COMPLETED", "jobCreateDate":1564672997192 }, { "jobName":"ThemeSettingCreation", "jobStatus":"COMPLETED", "jobCreateDate":1564672971956 } ] }
POST
Request:
curl --user admin:password --insecure --request POST -k -w "\nStatus Code: %{HTTP_CODE}%\n" https://apim.dev.mycompany.com:38443/provision/tenants
Successful Response:
{ "uuid": "<Tenant Unique Identifier>", "tenantId": "<alpha-numeric tenantID>", "portalName": "<Company Name>", "auditLogLevel": "INFO", "performanceLogLevel": "INFO", "portalLogLevel": "INFO", "adminEmail": "<Customer Email>", "noReplyEmail": "[email protected]", "tenantType": "TRIALS", "multiclusterEnabled": true, "termOfUse": "string", "subdomain": "<portal-sub-domain>", "cqAuthorHost": null, "portalAppTenantI18NUri": "/admin/dict.json", "portalAppLoginUri": "/admin/login", "portalAppHomeUri": "/admin/app/home", "portalAppNavigationPrimaryUri": "/admin/navigation", "portalAppExtUserDashboardUri": "/admin/app/dashboard", "portalAppLogoutDefaultTargetUri": "/homeRedirect", "portalAppMobileCSSUri": "", "portalAppDesktopCSSUri": "", "cqAuthorAuthUri": null, "trialsPeriod": 30, "status": "ACTIVE", "hybridState": 2, "portalHost": "<Host>" }
Failure response:
{ "message": "Invalid entity arguments.", "errors": { "tenantType": "may not be empty" } }
DELETE
Request:
curl --user admin:password --insecure --request DELETE -w "\nStatus Code: %{HTTP_CODE}%\n" https://apim.dev.mycompany.com:38443/provision/tenants/tenant1
Response:
  • 204 No Content - delete returned without error
  • 500 Internal Server Error - unexpected error
PUT
Enables admin user to retry one or more TPS scheduler jobs after tenant provisioning fail.
Example of Using PUT after Tenant Provisioning Fail
As an administrator, you start a tenant provisioning with create operation call to TPS:
curl --request POST -k https://<ingress_host>:38443/provision/tenants
A failure to provision a job and a backend component occurs.
To check the status of jobs and backend components, follow these steps:
  1. Check the status for jobs.
    curl --user admin:password --insecure --request PUT -k -w "\nStatus Code: %{HTTP_CODE}%\n" https://apim.dev.mycompany.com:38443/provision/tenants/<tenant_id>/status
    Example failure response:
    { "uuid":"6c4790ef-f0af-4443-90de-69997fb54c8f","tenantId": "tenant01", "tenantStatus":"ACTIVE", "portalHost":"tenant1.dev.mycompany.com", "tenantType":"ON-PREM", "userDetails": { },"provisioningStatus":"FAILED", "provisioningMessage":"Provisioning of Tenant failed", "enrollmentStatus": { "enrollmentUrl":null,"enrolled":false}, "provisioningDetails": [ { "jobName":"PortalApiProvisioningJob", "jobStatus":"COMPLETED", "jobCreateDate":1566341161426 }, { "jobName":"PortalCustomPageApiProvisioningJob", "jobStatus":"COMPLETED", "jobCreateDate":1566341161534 }, { "jobName":"PortalMetricsApiProvisioningJob", "jobStatus":"COMPLETED", "jobCreateDate":1566341161730 }, { "jobName":"ThemeSettingCreation", "jobStatus":"FAILED", "jobCreateDate":1566341161921 } ] }
  2. Check the status for backend components.
    curl --user admin:password --insecure --request PUT -k -w "\nStatus Code: %{HTTP_CODE}%\n" https://apim.dev.mycompany.com:38443/provision/tenants/v2/<tenant_id>?detailed=true
    Example failure response:
    { "respCode":200,"respMessage":"Successfully fetched tenant details", "tenantStatusResponse": { "tenantInfo": { "uuid":"7b97755e-c186-4202-aa61-373b7148c192", "tenantId":"tenant1", "tenantType":"ON-PREM", "adminEmail":"[email protected]", "portalHost":"tenant1.dev.ca.com", "subdomain":"dev.mycompany.com", "tenantStatus":"FAILED" } ,"componentResponse": [ { "componentName":"AUTHENTICATOR", "status":"FAILURE", "statusMessage":"foo", "customAttributes":{} }, { "componentName": "ANALYTICS-SERVER", "status":"FAILURE", "statusMessage":"foo", "customAttributes":{} } ] } }
  3. Run the sample retry for jobs.
    curl --cert~/your-cert.pem:your-password --key~/your-key.pem --insecure --request PUT -k https://dev.mycompany.com:38443/provision/tenants/tenant1/status -d { "provisioningDetails":[ { "jobName":"ThemeSettingCreation", "jobStatus":"FAILED" }, { "jobName":"PortalCustomPageApiProvisioningJob", "jobStatus":"FAILED" } ] }
  4. Run the sample retry for backend components.
    curl --cert~/your-cert.pem:your-password --key~/your-key.pem --insecure --request PUT -k https://apim.dev.ca.com:38443/provision/tenants/v2 -d ' { "action": "REPROVISION", "tenantId": "tenant1", "componentList": ["AUTHENTICATOR","ANALYTICS-SERVER"] } ' -H "Accept: application/json" -H "Content-Type: application/json"
  5. Check the status again. One of the following messages will appear:
    • 200 OK - successful
    • 400 Bad Request - failed validation, for example,  job status does not have a valid value
    • 404 Not Found - the tenant ID and job name combination was not found
    • 500 Internal Server Error - an unexpected error occurred
If you are still experiencing issues, you can delete and create a tenant.
Follow these steps:
  1. Delete a tenant using DELETE.
  2. Create a tenant again using POST.