In a multi-tenant setup, each tenant should have its own isolated database. This separation is crucial to ensure data security and integrity, allowing each tenant to access only its own data.
Info |
---|
The case-engine scheduler and process-engine basic scheduler are exceptions. The scheduler can only be initialised once and is tenant-aware of itself. |
To create the necessary databases, connect to your PostgreSQL server and execute the following SQL statements:
Code Block |
---|
|
psql -v ON_ERROR_STOP=1 --username "postgres" --dbname "postgres" <<-EOSQL
CREATE DATABASE google_audits;
CREATE DATABASE google_cds;
CREATE DATABASE google_comments;
CREATE DATABASE google_process;
CREATE DATABASE google_timeline;
CREATE DATABASE google_trace;
CREATE DATABASE google_reports;
CREATE DATABASE apple_audits;
CREATE DATABASE apple_cds;
CREATE DATABASE apple_comments;
CREATE DATABASE apple_process;
CREATE DATABASE apple_timeline;
CREATE DATABASE apple_trace;
CREATE DATABASE apple_reports;
CREATE DATABASE scheduler;
EOSQL
|
Next, execute the SQL scripts for each tenant's database to set up the necessary tables and data structures. NOTE: use the regular creation scripts and transfer them to your postgresql server.
Code Block |
---|
|
psql -v ON_ERROR_STOP=1 --username "postgres" --dbname "google_audits" -f /db_scripts/audit.sql
psql -v ON_ERROR_STOP=1 --username "postgres" --dbname "google_cds" -f /db_scripts/cds.sql
psql -v ON_ERROR_STOP=1 --username "postgres" --dbname "google_comments" -f /db_scripts/comments.sql
psql -v ON_ERROR_STOP=1 --username "postgres" --dbname "google_process" -f /db_scripts/process.sql -f /db_scripts/process_outbox.sql
psql -v ON_ERROR_STOP=1 --username "postgres" --dbname "google_timeline" -f /db_scripts/timeline.sql -f /db_scripts/timeline_inbox.sql
psql -v ON_ERROR_STOP=1 --username "postgres" --dbname "google_trace" -f /db_scripts/trace.sql -f /db_scripts/trace_inbox.sql
psql -v ON_ERROR_STOP=1 --username "postgres" --dbname "google_reports" -f /db_scripts/reports.sql
psql -v ON_ERROR_STOP=1 --username "postgres" --dbname "apple_audits" -f /db_scripts/audit.sql
psql -v ON_ERROR_STOP=1 --username "postgres" --dbname "apple_cds" -f /db_scripts/cds.sql
psql -v ON_ERROR_STOP=1 --username "postgres" --dbname "apple_comments" -f /db_scripts/comments.sql
psql -v ON_ERROR_STOP=1 --username "postgres" --dbname "apple_process" -f /db_scripts/process.sql -f /db_scripts/process_outbox.sql
psql -v ON_ERROR_STOP=1 --username "postgres" --dbname "apple_timeline" -f /db_scripts/timeline.sql -f /db_scripts/timeline_inbox.sql
psql -v ON_ERROR_STOP=1 --username "postgres" --dbname "apple_trace" -f /db_scripts/trace.sql -f /db_scripts/trace_inbox.sql
psql -v ON_ERROR_STOP=1 --username "postgres" --dbname "apple_reports" -f /db_scripts/reports.sql
psql -v ON_ERROR_STOP=1 --username "postgres" --dbname "scheduler" -f /db_scripts/scheduler.sql |
Next we change the definitions.json of RabbitMQ to add two vHosts: Google and Apple. Afterwards we change the current vHosts of all declarations and bindings to Google. Finally we will duplicate the bindings an declarations and change on those the vHost to Apple.
Info |
---|
In the least complex situation, the RabbitMQ is shared between tenants. Each tenant uses the same queues, but in its own virtual host. This means that data is not mixed between tenants. |
An example script for RabbitMQ initialisation can be found here, it must be adjusted to create all exchanges and queues for each tenant, with a different virtual host, like the description below.
Code Block |
---|
language | js |
---|
title | definitions.json |
---|
|
{
"bindings": [
{
"arguments": {},
"destination": "dcmEventsQueue",
"destination_type": "queue",
"routing_key": "",
"source": "dcmEvents",
"vhost": "Google"
},
...
],
"exchanges": [
{
"arguments": {},
"auto_delete": false,
"durable": true,
"internal": false,
"name": "dlx.dcmMaintenanceApp",
"type": "fanout",
"vhost": "Google"
},
...
],
"permissions": [
{
"configure": ".*",
"read": ".*",
"user": "guest",
"vhost": "Google",
"write": ".*"
}
],
"policies": [],
"queues": [
{
"arguments": {},
"auto_delete": false,
"durable": true,
"name": "dcmMaintenanceAppDlq",
"vhost": "Google"
},
...
],
"vhosts": [
{
"name": "Google"
}
]
} |
Code Block |
---|
language | js |
---|
title | definitions.json |
---|
|
{
"bindings": [
{
"arguments": {},
"destination": "dcmEventsQueue",
"destination_type": "queue",
"routing_key": "",
"source": "dcmEvents",
"vhost": "Apple"
},
...
],
"exchanges": [
{
"arguments": {},
"auto_delete": false,
"durable": true,
"internal": false,
"name": "dlx.dcmMaintenanceApp",
"type": "fanout",
"vhost": "Apple"
},
...
],
"permissions": [
{
"configure": ".*",
"read": ".*",
"user": "guest",
"vhost": "Apple",
"write": ".*"
}
],
"policies": [],
"queues": [
{
"arguments": {},
"auto_delete": false,
"durable": true,
"name": "dcmMaintenanceAppDlq",
"vhost": "Apple"
},
...
],
"vhosts": [
{
"name": "Apple"
}
]
} |
To ensure that Keycloak adds the correct tenant name in the JWT token during user authentication, and to map the tenant attribute appropriately, you'll need to configure Keycloak accordingly.
A way to achieve this is to add a user-attribute to the users in Keycloak. For example using the steps below:
- Navigate to the Keycloak Admin Console:
- Go to
Users
in the left-hand menu. - Select the specific user or create a new user or select an existing user.
- Go to the
Attributes
tab. - Add a new attribute with the name
TenantID
and set its value to the appropriate tenant name. - Save the changes.
- Create a Client Mapper:
- In the Keycloak Admin Console, go to the
Clients
section and select the client for which you want to add the claim. - Go to the
Mappers
tab and click Create
. - Configure the new mapper with the following settings:
- Name:
Tenant Mapper
(or any suitable name) - Mapper Type:
User Attribute
- User Attribute:
TenantID
- Token Claim Name:
tenant
(or your preferred claim path name) - Claim JSON Type:
String
- Add to ID token:
ON
- Add to access token:
ON
- Add to userinfo:
ON
- Save the mapper.
If you decide to use a different claim path name, ensure that your application is configured to recognize this custom claim. For example, if you use custom_tenant_claim_path
instead of tenant
, you need to set the following configuration in the Runtime and DCM maintenance app:
Code Block |
---|
title | application.properties |
---|
|
blueriq:
jwt:
tenant-path: $.custom_tenant_claim_path |
This configuration allows your application to correctly parse the tenant information from the JWT token.
To transition your Blueriq Runtime environment from single tenancy to multi-tenancy, you need to configure the property files to handle connections on a per-tenant basis. Below is described how to achieve this for two tenants, Google
and Apple
.
First, enable multi-tenancy by adding the following properties to your application.properties
file:
Code Block |
---|
title | application.properties |
---|
|
blueriq.multi-tenancy.enabled=true
blueriq.multi-tenancy.allowedTenants=google,apple |
This setting tells the Blueriq Runtime that it should operate in multi-tenant mode and allows only the specified tenants (google
and apple
).
For each tenant, configure the JDBC connection settings. Use default properties for common configurations like the driver, username, and password, and then specify the JDBC URL for each tenant's specific databases.
Code Block |
---|
title | application.properties |
---|
|
blueriq.default.datasource.tenants.google.driverClassName=org.postgresql.Driver
blueriq.default.datasource.tenants.google.username=<your database username>
blueriq.default.datasource.tenants.google.password=<your database password>
blueriq.datasource.comments-sql-store.tenants.google.url=jdbc:postgresql://<database host>:<database port>/google_comments
blueriq.datasource.process-sql-store.tenants.google.url=jdbc:postgresql://<database host>:<database port>/google_process
blueriq.datasource.trace-sql-store.tenants.google.url=jdbc:postgresql://<database host>:<database port>/google_trace
blueriq.datasource.timeline-sql-store.tenants.google.url=jdbc:postgresql://<database host>:<database port>/google_timeline
blueriq.datasource.report-service.tenants.google.url=jdbc:postgresql://<database host>:<database port>/google_reports |
Code Block |
---|
title | application.properties |
---|
|
blueriq.default.datasource.tenants.apple.driverClassName=org.postgresql.Driver
blueriq.default.datasource.tenants.apple.username=<your database username>
blueriq.default.datasource.tenants.apple.password=<your database password>
blueriq.datasource.comments-sql-store.tenants.apple.url=jdbc:postgresql://<database host>:<database port>/apple_comments
blueriq.datasource.process-sql-store.tenants.apple.url=jdbc:postgresql://<database host>:<database port>/apple_process
blueriq.datasource.trace-sql-store.tenants.apple.url=jdbc:postgresql://<database host>:<database port>/apple_trace
blueriq.datasource.timeline-sql-store.tenants.apple.url=jdbc:postgresql://<database host>:<database port>/apple_timeline
blueriq.datasource.report-service.tenants.apple.url=jdbc:postgresql://<database host>:<database port>/apple_reports |
For each tenant, configure the RabbitMQ connection settings. Use default properties for common settings like host, port, username, and password. You dont need to set the queue and exchange names as they are picked up by default.
Code Block |
---|
title | application.properties |
---|
|
blueriq.default.rabbitmq.host=<rabbitmq host>
blueriq.default.rabbitmq.port=<rabbitmq port>
|
Code Block |
---|
title | application.properties |
---|
|
blueriq.default.rabbitmq.tenants.google.username=<rabbitmq username>
blueriq.default.rabbitmq.tenants.google.password=<rabbitmq password>
blueriq.default.rabbitmq.tenants.google.virtualHost=Google |
Code Block |
---|
title | application.properties |
---|
|
blueriq.default.rabbitmq.tenants.apple.username=<rabbitmq username>
blueriq.default.rabbitmq.tenants.apple.password=<rabbitmq password>
blueriq.default.rabbitmq.tenants.apple.virtualHost=Apple |
For other connections like SOAP services, REST calls, etc., follow the same pattern. Use the format:
<connection-prefix>.tenants.<tenantName>.<specific-property>=<value>
Code Block |
---|
title | application.properties |
---|
|
blueriq.soap.service.tenants.google.endpoint=<google soap endpoint>
blueriq.soap.service.tenants.apple.endpoint=<apple soap endpoint> |
To set up the Case Engine with multi-tenancy support, you will need to configure SQL connections (for the process sql stoe, trace sql store, timeline sql store, and scheduler-quartz) and RabbitMQ connections for each tenant.
Add the following settings to enable multi-tenancy:
Code Block |
---|
title | application.properties |
---|
|
blueriq.multi-tenancy.enabled=true
blueriq.multi-tenancy.allowedTenants=google,apple |
Code Block |
---|
title | application.properties |
---|
|
# Google SQL Data Source
blueriq.default.datasource.tenants.google.driverClassName=org.postgresql.Driver
blueriq.default.datasource.tenants.google.username=<your database username>
blueriq.default.datasource.tenants.google.password=<your database password>
# Google JDBC URLs
blueriq.datasource.process-sql-store.tenants.google.url=jdbc:postgresql://<database host>:<database port>/google_process
blueriq.datasource.trace-sql-store.tenants.google.url=jdbc:postgresql://<database host>:<database port>/google_trace
blueriq.datasource.timeline-sql-store.tenants.google.url=jdbc:postgresql://<database host>:<database port>/google_timeline
blueriq.datasource.scheduler-quartz.tenants.google.url=jdbc:postgresql://<database host>:<database port>/google_reports
|
Code Block |
---|
title | application.properties |
---|
|
# Apple SQL Data Source
blueriq.default.datasource.tenants.apple.driverClassName=org.postgresql.Driver
blueriq.default.datasource.tenants.apple.username=<your database username>
blueriq.default.datasource.tenants.apple.password=<your database password>
# Apple JDBC URLs
blueriq.datasource.process-sql-store.tenants.apple.url=jdbc:postgresql://<database host>:<database port>/apple_process
blueriq.datasource.trace-sql-store.tenants.apple.url=jdbc:postgresql://<database host>:<database port>/apple_trace
blueriq.datasource.timeline-sql-store.tenants.apple.url=jdbc:postgresql://<database host>:<database port>/apple_timeline
blueriq.datasource.scheduler-quartz.tenants.apple.url=jdbc:postgresql://<database host>:<database port>/apple_reports
|
Code Block |
---|
title | application.properties |
---|
|
blueriq.default.rabbitmq.host=<rabbitmq host>
blueriq.default.rabbitmq.port=<rabbitmq port>
|
Code Block |
---|
title | application.properties |
---|
|
blueriq.default.rabbitmq.tenants.google.username=<rabbitmq username>
blueriq.default.rabbitmq.tenants.google.password=<rabbitmq password>
blueriq.default.rabbitmq.tenants.google.virtualHost=Google
|
Code Block |
---|
title | application.properties |
---|
|
blueriq.default.rabbitmq.tenants.apple.username=<rabbitmq username>
blueriq.default.rabbitmq.tenants.apple.password=<rabbitmq password>
blueriq.default.rabbitmq.tenants.apple.virtualHost=Apple
|
Lastly, add the MongoDB connection details for each tenant to the application.properties
file:
Code Block |
---|
title | application.properties |
---|
|
# Google MongoDB Configuration
blueriq.default.mongodb.tenants.google.host=<mongodb host>
blueriq.default.mongodb.tenants.google.port=<mongodb port>
blueriq.default.mongodb.tenants.google.username=<mongodb username>
blueriq.default.mongodb.tenants.google.password=<mongodb password>
blueriq.case.engine.data.mongodb.tenants.google.database=google_caseEngine
blueriq.locking.mongodb.tenants.google.database=google_locks
|
Code Block |
---|
title | application.properties |
---|
|
# Apple MongoDB Configuration
blueriq.default.mongodb.tenants.apple.host=<mongodb host>
blueriq.default.mongodb.tenants.apple.port=<mongodb port>
blueriq.default.mongodb.tenants.apple.username=<mongodb username>
blueriq.default.mongodb.tenants.apple.password=<mongodb password>
blueriq.case.engine.data.mongodb.tenants.apple.database=apple_caseEngine
blueriq.locking.mongodb.tenants.apple.database=apple_locks
|
To enable multi-tenancy for the CDS and adjust the customer data SQL store connection properties, enable multi tenancy and adjust the customer data sql store datasource as follows
Code Block |
---|
language | yml |
---|
title | blueriq-customer-odata-service-v1.yml |
---|
|
blueriq:
multi-tenancy:
enabled: true
allowed-tenants: google,apple
customerdata:
datasource:
customer-data-sql-store:
driver-class-name: org.postgresql.Driver
tenants:
google:
url: jdbc:postgresql://<database host>:<database port>/google_cds
username: <your database username>
password: <your database password>
apple:
url: jdbc:postgresql://<database host>:<database port>/apple_cds
username: <your database username>
password: <your database password> |
To enable multi-tenancy for the DCM Lists Service and configure the MongoDB and RabbitMQ connections, update your configuration as follows:
Code Block |
---|
language | yml |
---|
title | blueriq-dcm-lists.yml |
---|
|
blueriq:
multi-tenancy:
enabled: true
allowed-tenants:
- google
- apple
default:
mongodb:
tenants:
google:
host: <database host>
port: <database port>
username: <database username>
password: <database password>
apple:
host: <database host>
port: <database port>
username: <database username>
password: <database password>
rabbitmq:
tenants:
google:
host: <database host>
port: <database port>
username: <database username>
password: <database password>
virtualHost: Google
apple:
host: <database host>
port: <database port>
username: <database username>
password: <database password>
virtualHost: Apple
dcm:
lists:
mongodb:
tenants:
google:
database: google_tasks
apple:
database: apple_tasks |
DCM Maintenance App
To enable multi-tenancy for the DCM Mainteance App and configure the MongoDB and RabbitMQ connections, update your configuration as follows:
Code Block |
---|
language | yml |
---|
title | blueriq-dcm-maintenance-app.yml |
---|
|
blueriq:
multi-tenancy:
enabled: true
allowed-tenants:
- google
- apple
default:
mongodb:
tenants:
google:
host: <database host>
port: <database port>
username: <database username>
password: <database password>
apple:
host: <database host>
port: <database port>
username: <database username>
password: <database password>
rabbitmq:
tenants:
google:
host: <database host>
port: <database port>
username: <database username>
password: <database password>
virtualHost: Google
apple:
host: <database host>
port: <database port>
username: <database username>
password: <database password>
virtualHost: Apple
dcm:
maitnenance:
app:
mongodb:
tenants:
google:
database: google_messages
apple:
database: apple_apple |
The final step to ensure that your multi-tenant stack functions correctly is to include the X-Tenant-ID
header in each request. This header should carry the appropriate tenant name as its value.
We recommend using NGINX as a proxy to automate this process. NGINX should be configured to add the X-Tenant-ID
header to every incoming request.
One way of achieveing this is to add the tenant to your domain (google.mydomain.com or apple.mydomain.com). This can be rewritten in NGINX to add "apple" or "google" as a header to the request. Make sure the proxy always overwrites the header, otherwise someone might set it himself in the browser.