Add multitenancy module: Hibernate 7 @TenantId vs schema-per-tenant (shared pool + search_path, pool per tenant), JWT tenant filter, isolation failure modes on PostgreSQL 17 (Hibernate 7.4.14.Final, Spring Boot 4.1.2)
Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_017ic4b4VQVEJWetTT1dYzyJ
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
# JWT -> filter -> TenantContext -> @TenantId, real HTTP against the discriminator application
|
||||
|
||||
== the filter: who gets in
|
||||
-- no Authorization header
|
||||
GET /invoices -> HTTP 401 {"error":"missing bearer token"}
|
||||
-- garbage token
|
||||
GET /invoices -> HTTP 401 {"error":"invalid token"}
|
||||
-- token signed with a different secret
|
||||
GET /invoices -> HTTP 401 {"error":"invalid token"}
|
||||
-- expired token (exp = 10 minutes ago)
|
||||
GET /invoices -> HTTP 401 {"error":"invalid token"}
|
||||
-- valid signature, no tenant_id claim
|
||||
GET /invoices -> HTTP 403 {"error":"token carries no valid tenant"}
|
||||
-- valid signature, tenant_id = "x; drop schema public cascade"
|
||||
GET /invoices -> HTTP 403 {"error":"token carries no valid tenant"}
|
||||
-- valid signature, tenant_id = "public" (reserved name)
|
||||
GET /invoices -> HTTP 403 {"error":"token carries no valid tenant"}
|
||||
|
||||
== two tenants, same URL, different data
|
||||
-- acme creates INV-1
|
||||
POST /invoices -> HTTP 201 {"id":1,"number":"INV-1","amount":100.00,"tenant":"acme"}
|
||||
-- acme creates INV-2
|
||||
POST /invoices -> HTTP 201 {"id":2,"number":"INV-2","amount":250.00,"tenant":"acme"}
|
||||
-- globex creates INV-9
|
||||
POST /invoices -> HTTP 201 {"id":3,"number":"INV-9","amount":999.00,"tenant":"globex"}
|
||||
-- acme lists
|
||||
GET /invoices -> HTTP 200 [{"id":1,"number":"INV-1","amount":100.00,"tenant":"acme"},{"id":2,"number":"INV-2","amount":250.00,"tenant":"acme"}]
|
||||
-- globex lists
|
||||
GET /invoices -> HTTP 200 [{"id":3,"number":"INV-9","amount":999.00,"tenant":"globex"}]
|
||||
|
||||
== cross-tenant read by id
|
||||
-- globex asks for acme's INV-2 by id (2)
|
||||
GET /invoices/2 -> HTTP 404
|
||||
-- acme asks for the same id
|
||||
GET /invoices/2 -> HTTP 200 {"id":2,"number":"INV-2","amount":250.00,"tenant":"acme"}
|
||||
|
||||
== a well-formed tenant nobody has onboarded
|
||||
-- token for tenant "ghostco": GET /invoices
|
||||
GET /invoices -> HTTP 200 []
|
||||
-- token for tenant "ghostco": POST /invoices
|
||||
POST /invoices -> HTTP 201 {"id":4,"number":"INV-G","amount":1.00,"tenant":"ghostco"}
|
||||
Reference in New Issue
Block a user