Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions code-samples/multitenancy/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
*
!target/*-runner
!target/*-runner.jar
!target/lib/*
!target/quarkus-app/*
45 changes: 45 additions & 0 deletions code-samples/multitenancy/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#Maven
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
release.properties
.flattened-pom.xml

# Eclipse
.project
.classpath
.settings/
bin/

# IntelliJ
.idea
*.ipr
*.iml
*.iws

# NetBeans
nb-configuration.xml

# Visual Studio Code
.vscode
.factorypath

# OSX
.DS_Store

# Vim
*.swp
*.swo

# patch
*.orig
*.rej

# Local environment
.env

# Plugin directory
/.quarkus/cli/plugins/
# TLS Certificates
.certs/
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
wrapperVersion=3.3.4
distributionType=only-script
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.16/apache-maven-3.9.16-bin.zip
distributionSha256Sum=5af3b743dd8b876b5c45da33b676251e5f1687712644abb4ee519ca56e1d89ce
35 changes: 35 additions & 0 deletions code-samples/multitenancy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# multitenancy

This project uses the [quarkus-multitenancy](https://github.com/mathias82/quarkus-multitenancy) extension form
mathias82, which is in the process of becoming an official [quarkiverse extension](https://github.com/quarkiverse/quarkus-multitenancy).

All parts that I just copied from the quarkus-multitenancy demo project are marked by a comment.

### quarkus-multitenancy extension
The quarkus-multitenancy extension provides a consistent tenant resolution in quarkus, e.g. via a HTTP-Header.
Hereby the extension lets us implement (if needed) our custom:
- TenantContext
- TenantResolver
- TenantDataSourceRegistry
- TenantResolutionContext
- ...

The extension itself comes with a filter that resolves the tenant if there is no authorization needed.
The extension supports a database-per-tenant approach.

### Tenant resolution with authorization
In order to resolve the tenant when making an authorization request it is needed to implement a custom
HTTPAuthenticationMechanism and to implement a custom TenantResolutionContext.


Implementing a custom HTTPAuthenticationMechanism is one of the intended ways when customizing Quarkus Security
([see docs](https://quarkus.io/guides/security-customization)).

The extensions build-in TenantResolutionContext expects an ContainerRequestContext, which is not available in the
authentication step. However, to resolve the Tenant via the TenantResolver we only need to extract a specific HTTP-Header,
which we can also get from the RoutingContext.


### Testing
Run `docker compose up -d` to create the databases in the `compose.yaml`, then run `mvn quarkus:dev`.
HTTP Requests for testing are in the `localhost.http`, all passwords are `password`.
34 changes: 34 additions & 0 deletions code-samples/multitenancy/compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
services:
default-db:
image: postgres
environment:
POSTGRES_USER: default
POSTGRES_PASSWORD: default
POSTGRES_DB: default
ports:
- "5432:5432"
volumes:
- ./init-db/init_default.sql:/docker-entrypoint-initdb.d/init.sql:z

tenant1-db:
image: postgres
environment:
POSTGRES_USER: user1
POSTGRES_PASSWORD: pass1
POSTGRES_DB: tenant1
ports:
- "5433:5432"
volumes:
- ./init-db/init_tenant1.sql:/docker-entrypoint-initdb.d/init.sql:z


tenant2-db:
image: postgres
environment:
POSTGRES_USER: user2
POSTGRES_PASSWORD: pass2
POSTGRES_DB: tenant2
ports:
- "5434:5432"
volumes:
- ./init-db/init_tenant2.sql:/docker-entrypoint-initdb.d/init.sql:z
10 changes: 10 additions & 0 deletions code-samples/multitenancy/init-db/init.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
CREATE SEQUENCE IF NOT EXISTS users_seq START 1;

CREATE TABLE IF NOT EXISTS users (
id BIGINT DEFAULT nextval('users_seq') PRIMARY KEY,
name VARCHAR(255),
email VARCHAR(255)
);

INSERT INTO users (id, name, email) VALUES (1, 'admin', 'admin@admin.com');
INSERT INTO users (id, name, email) VALUES (2, 'user','user@user.com');
11 changes: 11 additions & 0 deletions code-samples/multitenancy/init-db/init_default.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CREATE SEQUENCE IF NOT EXISTS users_seq START 1;

CREATE TABLE IF NOT EXISTS users (
id BIGINT DEFAULT nextval('users_seq') PRIMARY KEY,
username VARCHAR(255),
password VARCHAR(255),
role VARCHAR(255)
);

INSERT INTO users (id, username, password, role) VALUES (1, 'default_admin', '$2a$10$Uc.SZ0hvGJQlYdsAp7be1.lFjmOnc7aAr4L0YY3/VN3oK.F8zJHRG', 'admin');
INSERT INTO users (id, username, password, role) VALUES (2, 'default_user', '$2a$10$Uc.SZ0hvGJQlYdsAp7be1.lFjmOnc7aAr4L0YY3/VN3oK.F8zJHRG', 'user');
11 changes: 11 additions & 0 deletions code-samples/multitenancy/init-db/init_tenant1.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CREATE SEQUENCE IF NOT EXISTS users_seq START 1;

CREATE TABLE IF NOT EXISTS users (
id BIGINT DEFAULT nextval('users_seq') PRIMARY KEY,
username VARCHAR(255),
password VARCHAR(255),
role VARCHAR(255)
);

INSERT INTO users (id, username, password, role) VALUES (1, 'tenant1_admin', '$2a$10$Uc.SZ0hvGJQlYdsAp7be1.lFjmOnc7aAr4L0YY3/VN3oK.F8zJHRG', 'admin');
INSERT INTO users (id, username, password, role) VALUES (2, 'tenant1_user', '$2a$10$Uc.SZ0hvGJQlYdsAp7be1.lFjmOnc7aAr4L0YY3/VN3oK.F8zJHRG', 'user');
11 changes: 11 additions & 0 deletions code-samples/multitenancy/init-db/init_tenant2.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CREATE SEQUENCE IF NOT EXISTS users_seq START 1;

CREATE TABLE IF NOT EXISTS users (
id BIGINT DEFAULT nextval('users_seq') PRIMARY KEY,
username VARCHAR(255),
password VARCHAR(255),
role VARCHAR(255)
);

INSERT INTO users (id, username, password, role) VALUES (1, 'tenant2_admin', '$2a$10$Uc.SZ0hvGJQlYdsAp7be1.lFjmOnc7aAr4L0YY3/VN3oK.F8zJHRG', 'admin');
INSERT INTO users (id, username, password, role) VALUES (2, 'tenant2_user', '$2a$10$Uc.SZ0hvGJQlYdsAp7be1.lFjmOnc7aAr4L0YY3/VN3oK.F8zJHRG', 'user');
37 changes: 37 additions & 0 deletions code-samples/multitenancy/localhost.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
### GET current tenant
GET http://localhost:8080/api/tenant_users/tenant
X-Tenant: tenant1

### List Users
GET http://localhost:8080/api/tenant_users
X-Tenant: tenant1

### GET Admin resource tenant 1
GET http://localhost:8080/api/admin
X-Tenant: tenant1
Authorization: Basic dGVuYW50MV9hZG1pbjpwYXNzd29yZA==

### GET User resource tenant 1
GET http://localhost:8080/api/users/me
X-Tenant: tenant1
Authorization: Basic dGVuYW50MV91c2VyOnBhc3N3b3Jk

### GET Admin resource tenant 2
GET http://localhost:8080/api/admin
X-Tenant: tenant2
Authorization: Basic dGVuYW50Ml9hZG1pbjpwYXNzd29yZA==

### GET User resource tenant 2
GET http://localhost:8080/api/users/me
X-Tenant: tenant2
Authorization: Basic dGVuYW50Ml91c2VyOnBhc3N3b3Jk

### GET Admin resource wrong tenant
GET http://localhost:8080/api/admin
X-Tenant: tenant2
Authorization: Basic dGVuYW50MV9hZG1pbjpwYXNzd29yZA==

### GET User resource wrong tenant
GET http://localhost:8080/api/users/me
X-Tenant: tenant2
Authorization: Basic dGVuYW50MV91c2VyOnBhc3N3b3Jk
Loading