sergehuber opened a new pull request, #921:
URL: https://github.com/apache/unomi/pull/921

   Implements [UNOMI-1013](https://issues.apache.org/jira/browse/UNOMI-1013), a 
follow-up to #920 (UNOMI-1005).
   
   ## The problem
   #920 added a server-wide list of website origins that may use the visitor 
cookie. Tenants are created and deleted at runtime, often by provisioning 
systems, and each has its own websites, so an operator-maintained list cannot 
keep up: every tenant change would need a configuration change on the servers.
   
   ## What this PR does
   **Per-tenant list.** A tenant lists its own origins in its 
`publicCorsAllowedOrigins` property (a JSON array or a comma-separated string), 
set through `/cxs/tenants` when the tenant is created or updated. That endpoint 
already requires a system administrator, so tenants cannot widen their own 
access. The list follows the same matching rules as the server list, and an 
invalid entry is refused with `400`, so provisioning fails rather than a 
website silently losing its visitors.
   
   **Scoped to the tenant.** A tenant's origins only apply to requests made 
with that tenant's public API key, for both the visitor cookie 
(`RestServiceUtilsImpl.isVisitorCookieAllowed`, the single decision point from 
#920) and the credentialed CORS response. The server list still applies to 
every tenant.
   
   **Preflights.** A preflight carries no API key, so the tenant is not known 
yet. It is answered for an origin the server or any tenant lists; that only 
lets the browser send the real request, which is then checked against its own 
tenant. This is the design from the ticket.
   
   **Dynamic tenants, no per-request cost.** Tenant origins live in an 
in-memory snapshot in `PublicTrackerSettings`; requests never load tenants.
   - Loaded at activation.
   - Reloaded at once when a tenant is created, updated or deleted through this 
node (`TenantEndpoint`), or removed (`TenantLifecycleListener`).
   - Refreshed every 30 seconds by one request while the others keep the 
previous snapshot.
   - Reloaded when an unknown tenant or origin shows up, at most every 5 
seconds so requests cannot force reloads. A tenant created on another node 
therefore works on its first request.
   - A failed load keeps the previous snapshot.
   
   **Operator switch.** `org.apache.unomi.public.cors.tenantOrigins` 
(`UNOMI_PUBLIC_CORS_TENANT_ORIGINS`, default `true`) turns tenant lists off; 
only `false` does. `SameSite`, `HttpOnly` and the cookie domain stay server 
settings, since all tenants share one cookie on a host; `SameSite=None` is 
accepted once tenants may list origins.
   
   **Fix found by the IT.** In a JAX-RS response filter the current CXF message 
is the response; the security context set by `AuthenticationFilter` is on the 
request, reached through the exchange. `PublicTrackerSettings.tenantIdOf` reads 
it from there.
   
   ## Compatibility
   No change for existing deployments: tenants without the property behave 
exactly as with #920, and the server list keeps working. The new property is 
opt-in per tenant.
   
   ## Ticket test list
   | UNOMI-1013 | Test |
   |---|---|
   | An origin listed by tenant A works with tenant A's key | 
`TenantTrackerOriginsIT.aTenantsOwnOriginGetsCredentialsAndKeepsItsVisitor`, 
`PublicTrackerSettingsTenantTest.aTenantsOriginsApplyOnlyToThatTenant` |
   | The same origin with tenant B's key is not tied to the visitor's profile | 
`TenantTrackerOriginsIT.anotherTenantsOriginDoesNotUseTheVisitorCookie`, 
`PublicTrackerCorsFilterTest.actualRequestIsCheckedAgainstItsOwnTenant` |
   | With tenant lists off, only the operator list counts | 
`PublicTrackerSettingsTenantTest.theOperatorCanTurnTenantListsOff` |
   | Tenant origins follow the same matching rules | 
`PublicTrackerCorsPolicyTest.tenantOriginsAcceptAStringOrAList`, 
`invalidTenantOriginsAreReported`, `TenantEndpointTrackerOriginsTest`, 
`TenantTrackerOriginsIT.invalidOriginsAreRejectedWhenTheTenantIsCreated` |
   | All UNOMI-1005 tests still pass | `PublicTrackerCorsIT` 9/9, unchanged 
UNOMI-1005 unit tests |
   
   Also: `aNewTenantWorksAtOnceWithoutRestart`, 
`unknownOriginsCannotForceReloads`, `tenantsAreNotLoadedForEachRequest`, 
`theSnapshotIsRefreshedPeriodically`, `aFailedLoadKeepsThePreviousOrigins`, 
`theTenantIsFoundFromTheResponseMessageToo`, and 
`TenantTrackerOriginsIT.aTenantCreatedElsewhereWorksWithoutRestart`.
   
   ## Documentation
   `configuration.adoc`: new "Websites listed by each tenant" section 
(provisioning example, scope, preflights, propagation across nodes, operator 
switch), the tenant-creation example, settings list and compatibility table; 
`whats-new.adoc`; comments in `custom.system.properties` and 
`org.apache.unomi.web.cfg`.
   
   ## Test plan
   - [x] Unit (rest, 237 tests; web-servlets `WebConfigTest`)
   - [x] `TenantTrackerOriginsIT` 4/4, `PublicTrackerCorsIT` 9/9 on 
Elasticsearch
   - [x] `SingleTenantCompatibilityModeIT` 8/8, `ContextServletIT` 39/39 on 
Elasticsearch (before the last fix, which only touches the CORS response path)
   - [x] Manual (docs) builds
   - [ ] OpenSearch run (CI)
   
   ## Not in this PR
   - `TenantServiceImpl.getTenantByApiKey` loads every tenant from storage on 
every public request. That cost predates this PR and matters at the same scale; 
worth its own ticket.
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to