Commit Graph

57 Commits

Author SHA1 Message Date
hsiegeln
3a6b94c1eb fix: remove server health wait from bootstrap (no compose server)
All checks were successful
CI / build (push) Successful in 1m3s
CI / docker (push) Successful in 10s
Bootstrap was stuck waiting for cameleer3-server which no longer exists
in docker-compose. Removed server wait loop and SERVER_ENDPOINT config.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 08:54:37 +02:00
hsiegeln
3efae43879 feat: clean control plane — remove all example tenant resources
All checks were successful
CI / build (push) Successful in 58s
CI / docker (push) Successful in 11s
- Removed cameleer3-server and cameleer3-server-ui from docker-compose
  (tenants provision their own server instances via the vendor console)
- Removed viewer/camel user from bootstrap (tenant users created during
  provisioning)
- Removed Phase 7 server OIDC configuration (provisioned servers get
  OIDC config from env vars, claim mappings via Logto Custom JWT)
- Removed server-related env vars from bootstrap (SERVER_ENDPOINT, etc.)
- Removed jardata volume from dev overlay

Clean slate: docker compose up gives you Traefik + PostgreSQL +
ClickHouse + Logto + SaaS platform + vendor seed. Everything else
(servers, tenants, users) created through the vendor console.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 08:24:28 +02:00
hsiegeln
aa663a9c9e feat: vendor sidebar section, remove example tenant, add Logto link
All checks were successful
CI / build (push) Successful in 1m4s
CI / docker (push) Successful in 48s
- Sidebar: Tenants moved into expandable "Vendor" section with
  sub-items for Tenants and Identity (Logto console link)
- Bootstrap: removed example organization creation (Phase 6 org)
  — tenants are now created exclusively via the vendor console
- Removed BootstrapDataSeeder (no auto-seeded tenant/license)
- Bootstrap log updated to reflect clean-slate approach

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 08:19:46 +02:00
hsiegeln
6a81053d37 feat: integrate vendor seed into bootstrap with VENDOR_SEED_ENABLED switch
All checks were successful
CI / build (push) Successful in 1m13s
CI / docker (push) Successful in 49s
Phase 12 in logto-bootstrap.sh creates saas-vendor global role + vendor
user when VENDOR_SEED_ENABLED=true. Enabled by default in dev overlay.
Also restores GlobalFilterProvider + CommandPaletteProvider (required by
DS TopBar internally).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 22:30:06 +02:00
hsiegeln
cc792ae336 refactor: rename CAMELEER_EXPORT_ENDPOINT to CAMELEER_SERVER_URL in runtime-base
Some checks failed
CI / build (push) Successful in 1m7s
CI / docker (push) Has been cancelled
Standardize env var naming. The agent reads CAMELEER_SERVER_URL
to configure -Dcameleer.export.endpoint.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 22:06:48 +02:00
hsiegeln
bb8c68a5ca feat: seed claim mapping rules in bootstrap after OIDC config
All checks were successful
CI / build (push) Successful in 53s
CI / docker (push) Successful in 14s
After configuring the server's OIDC settings, the bootstrap now seeds
claim mapping rules so Logto roles (server:admin, server:operator) map
to server RBAC roles (ADMIN, OPERATOR) automatically. Rules are
idempotent — existing mappings are checked by matchValue before creating.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 12:21:28 +02:00
hsiegeln
f14affcc1e fix: verify Management API readiness before proceeding in bootstrap
All checks were successful
CI / build (push) Successful in 48s
CI / docker (push) Successful in 14s
Logto's OIDC endpoint may respond before the Management API is fully
initialized. Add a retry loop that checks GET /api/roles returns valid
JSON before making any API calls. Fixes intermittent bootstrap failure
on cold starts with 'Cannot index string with string "name"'.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 18:18:08 +02:00
hsiegeln
5d04a154f9 refactor: deployment infrastructure cleanup (4 fixes)
Some checks failed
CI / build (push) Failing after 46s
CI / docker (push) Has been skipped
1. Docker socket security: remove root group from Dockerfile, use
   group_add in docker-compose.yml for runtime-only socket access

2. M2M server communication: create ServerApiClient using Logto
   client_credentials grant with API resource scope. Add M2M server
   role in bootstrap. Replace hacky admin/admin login in
   AgentStatusService.

3. Async deployment: extract DeploymentExecutor as separate @Service
   so Spring's @Async proxy works (self-invocation bypasses proxy).
   Deploy now returns immediately, health check runs in background.

4. Bootstrap: M2M server role (cameleer-m2m-server) with server:admin
   scope, idempotent creation outside the M2M app creation block.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 17:08:37 +02:00
hsiegeln
a5c881a4d0 fix: skip bootstrap on subsequent restarts if already complete
Check for spaClientId and m2mClientSecret in the cached bootstrap
file. If both exist, exit immediately instead of re-running all
phases. Delete /data/logto-bootstrap.json to force a re-run.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 15:36:43 +02:00
hsiegeln
3d41d4a3da feat: 4-role model — owner, operator, viewer + vendor-seed
All checks were successful
CI / build (push) Successful in 57s
CI / docker (push) Successful in 47s
Redesign the role model from 3 roles (platform-admin, admin, member)
to 4 clear personas:

- owner (org role): full tenant control — billing, team, apps, deploy
- operator (org role): app lifecycle + observability, no billing/team
- viewer (org role): read-only observability
- saas-vendor (global role, hosted only): cross-tenant platform admin

Bootstrap changes:
- Rename org roles: admin→owner, member→operator, add viewer
- Remove platform-admin global role (moved to vendor-seed)
- admin user gets owner role, camel user gets viewer role
- Custom JWT maps: owner→server:admin, operator→server:operator,
  viewer→server:viewer, saas-vendor→server:admin

New docker/vendor-seed.sh for hosted SaaS environments only.
Remove sidebar user/logout link (TopBar handles logout).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 13:49:16 +02:00
hsiegeln
67b35a25d6 feat: configure Logto Custom JWT and update server OIDC rolesClaim
All checks were successful
CI / build (push) Successful in 1m17s
CI / docker (push) Successful in 14s
- Change rolesClaim from "scope" to "roles" to match the custom claim
  injected by the Logto Custom JWT script
- Add Phase 7b: configure Logto Custom JWT for access tokens that maps
  org roles (admin→server:admin, member→server:viewer) and global roles
  (platform-admin→server:admin) into a standard "roles" claim
- Add additionalScopes field to OIDC config

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 10:17:04 +02:00
hsiegeln
c32a606a91 fix: add X-Forwarded-Proto to admin-tenant bootstrap calls only
All checks were successful
CI / build (push) Successful in 48s
CI / docker (push) Successful in 8s
ADMIN_ENDPOINT is now HTTPS so admin-tenant calls need the
forwarded proto header. Default-tenant calls stay unchanged.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 00:48:11 +02:00
hsiegeln
774db7ba53 revert: restore to last working state (b3ac8a6)
All checks were successful
CI / build (push) Successful in 47s
CI / docker (push) Successful in 9s
Revert all Traefik port 3002 and ADMIN_ENDPOINT changes that broke
bootstrap. Admin console HTTPS access needs a different approach.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 00:07:17 +02:00
hsiegeln
a2119b8bfd fix: remove Host header from admin tenant bootstrap calls
All checks were successful
CI / build (push) Successful in 48s
CI / docker (push) Successful in 9s
ADMIN_ENDPOINT is http://localhost:3002, but bootstrap sent
Host: PUBLIC_HOST:3002 which didn't match. Let curl use the
default Host from LOGTO_ADMIN_ENDPOINT (logto:3002) which Logto
resolves to the admin tenant internally.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 00:05:33 +02:00
hsiegeln
f276953b03 fix: revert ADMIN_ENDPOINT to HTTP, remove X-Forwarded-Proto
All checks were successful
CI / build (push) Successful in 50s
CI / docker (push) Successful in 25s
Internal Docker traffic is HTTP. Traefik handles TLS termination
for external access. TRUST_PROXY_HEADER lets Logto detect HTTPS
from Traefik's forwarded headers automatically.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 23:59:49 +02:00
hsiegeln
c8ec1da328 fix: only use X-Forwarded-Proto on admin tenant calls (port 3002)
All checks were successful
CI / build (push) Successful in 48s
CI / docker (push) Successful in 9s
Default tenant (port 3001) works without it — adding it caused
Internal server error. Only the admin tenant needs it because
ADMIN_ENDPOINT changed to HTTPS.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 23:58:16 +02:00
hsiegeln
a3af667f76 debug: log api_get response for bootstrap troubleshooting
All checks were successful
CI / build (push) Successful in 48s
CI / docker (push) Successful in 7s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 23:56:08 +02:00
hsiegeln
251d8eb8e1 fix: add X-Forwarded-Proto to all bootstrap API helpers
All checks were successful
CI / build (push) Successful in 48s
CI / docker (push) Successful in 9s
All Logto endpoints are configured with HTTPS but bootstrap calls
internal HTTP. Every curl call needs the forwarded proto header.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 23:54:51 +02:00
hsiegeln
5f560e9f33 fix: add X-Forwarded-Proto to bootstrap admin endpoint calls
All checks were successful
CI / build (push) Successful in 46s
CI / docker (push) Successful in 9s
Logto's ADMIN_ENDPOINT is now HTTPS but bootstrap calls the internal
HTTP endpoint directly. TRUST_PROXY_HEADER needs X-Forwarded-Proto
to resolve the correct scheme.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 23:53:29 +02:00
hsiegeln
b3ac8a6bcc fix: set admin tenant sign-in mode to SignIn after user creation
All checks were successful
CI / build (push) Successful in 47s
CI / docker (push) Successful in 8s
Admin tenant defaults to Register mode (onboarding flow). Since we
create the admin user via API, we need to switch to SignIn mode so
the custom sign-in UI can authenticate against the admin console.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 23:46:36 +02:00
hsiegeln
c354d2e74f fix: assign 'user' base role for admin console access
All checks were successful
CI / build (push) Successful in 58s
CI / docker (push) Successful in 11s
The admin tenant requires both the 'user' role (base access) and
'default:admin' role (Management API). Missing the 'user' role
causes a 403 at the identification step.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 23:43:07 +02:00
hsiegeln
9dbdda62ce fix: use m-admin token for admin tenant console user creation
All checks were successful
CI / build (push) Successful in 47s
CI / docker (push) Successful in 16s
The m-default token has audience https://default.logto.app/api which
is rejected by port 3002's admin tenant API. Use m-admin client with
audience https://admin.logto.app/api instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 23:37:51 +02:00
hsiegeln
65d2c7c764 debug: log admin tenant API response for bootstrap troubleshooting
All checks were successful
CI / build (push) Successful in 48s
CI / docker (push) Successful in 1m3s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 23:35:26 +02:00
hsiegeln
bc42fa7172 fix: create admin console user on Logto admin tenant (port 3002)
All checks were successful
CI / build (push) Successful in 58s
CI / docker (push) Successful in 9s
The admin console runs on a separate tenant with its own user store.
Previous approach tried to assign a non-existent 'admin:admin' role
on the default tenant. Now creates the user on the admin tenant via
port 3002, assigns 'default:admin' role for Management API access,
and adds to t-default organization.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 23:28:40 +02:00
hsiegeln
ea3723958e fix: bootstrap file permission denied, use PNG favicon
All checks were successful
CI / build (push) Successful in 47s
CI / docker (push) Successful in 2m45s
- Change chmod 600 to 644 on bootstrap JSON (cameleer user needs read)
- Use PNG favicon instead of SVG (currentColor invisible in browser tab)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 22:25:15 +02:00
hsiegeln
f81cd740b7 fix: security hardening — remove dead routes, add JWT audience validation
All checks were successful
CI / build (push) Successful in 48s
CI / docker (push) Successful in 2m49s
- Remove broken observe/dashboard Traefik routes (server accessed via /server only)
- Remove unused acme volume
- Add JWT audience claim validation (https://api.cameleer.local) in SecurityConfig
- Secure bootstrap output file with chmod 600
- Add dev-only comments on TLS_SKIP_VERIFY and credential logging

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 19:15:03 +02:00
hsiegeln
7d6e78afa3 fix: add /server/login?local to Traditional app post-logout redirect URIs
All checks were successful
CI / build (push) Successful in 54s
CI / docker (push) Successful in 2m48s
The server-ui logout redirects to /server/login?local but this URI was
not whitelisted in Logto, causing the post-logout redirect to fail.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 17:47:12 +02:00
hsiegeln
ad97a552f6 refactor: no builds in Dockerfiles, CI builds all artifacts
Some checks failed
CI / build (push) Successful in 59s
CI / docker (push) Failing after 11s
Dockerfiles now only COPY pre-built artifacts:
- Dockerfile (SaaS): just COPY target/*.jar, no multi-stage build
- ui/sign-in/Dockerfile (Logto): just FROM logto + COPY dist/
- Removed docker/logto.Dockerfile (had node build stage)

CI pipeline builds everything:
- docker job: builds frontend, JAR, sign-in UI, then packages
  into images using the simple Dockerfiles
- Uses cameleer-build:1 (has node + maven + docker)
- build job: also builds sign-in UI for testing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 15:39:19 +02:00
hsiegeln
972f9b5f38 feat: custom Logto image + auto-redirect to sign-in
All checks were successful
CI / build (push) Successful in 39s
CI / docker (push) Successful in 40s
- Add docker/logto.Dockerfile: builds custom Logto image with sign-in
  UI baked into /etc/logto/packages/experience/dist/
- Remove sign-in-ui init container, signinui volume, CUSTOM_UI_PATH
  (CUSTOM_UI_PATH is Logto Cloud only, not available in OSS)
- Remove sign-in build stage from SaaS Dockerfile (now in logto.Dockerfile)
- Remove docker/saas-entrypoint.sh (no longer needed)
- LoginPage auto-redirects to Logto OIDC on mount instead of showing
  "Sign in with Logto" button — seamless sign-in experience

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 15:12:11 +02:00
hsiegeln
df220bc5f3 feat: custom Logto sign-in UI with Cameleer branding
All checks were successful
CI / build (push) Successful in 40s
CI / docker (push) Successful in 50s
Replace Logto's default sign-in page with a custom React SPA that
matches the cameleer3-server login page using @cameleer/design-system.

- New Vite+React app at ui/sign-in/ with Experience API integration
- 4-step auth flow: init → verify password → identify → submit
- Design-system components: Card, Input, Button, FormField, Alert
- Same witty random subtitles as cameleer3-server LoginPage
- Dockerfile: add sign-in-frontend build stage, copy dist to image
- docker-compose: CUSTOM_UI_PATH on Logto, shared signinui volume
- SaaS entrypoint copies sign-in dist to shared volume on startup
- Add .gitattributes for LF line endings

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 11:43:22 +02:00
hsiegeln
51cdca95c4 feat: server role mapping, Logto admin access, sign-in branding
Some checks failed
CI / build (push) Successful in 38s
CI / docker (push) Has been cancelled
- Add server:admin/operator/viewer scopes to bootstrap and org roles
- Grant SaaS admin Logto console access via admin:admin role
- Configure sign-in experience with Cameleer branding (colors + logos)
- Add rolesClaim and audience to server OIDC config
- Add server scopes to PublicConfigController for token inclusion
- Permit logo SVGs in SecurityConfig (fix 401 on /platform/logo.svg)
- Add cameleer3 logo SVGs (light + dark) to ui/public/

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 10:45:19 +02:00
hsiegeln
5a8d38a946 fix: enable skip consent on Traditional app for first-party SSO
All checks were successful
CI / build (push) Successful in 1m3s
CI / docker (push) Successful in 8s
SonarQube Analysis / sonarqube (push) Successful in 1m24s
Without this, Logto returns consent_required when the server tries
SSO because the scopes were never explicitly granted.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 01:30:25 +02:00
hsiegeln
d74aafc7b3 fix: update Traditional app redirect URIs for Traefik routing
All checks were successful
CI / build (push) Successful in 46s
CI / docker (push) Successful in 8s
Server OIDC callback is at /oidc/callback (without /server/ prefix due
to strip-prefix). Register both variants until server reads
X-Forwarded-Prefix.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 01:10:40 +02:00
hsiegeln
5981a3db71 fix: patch server-ui entrypoint to fix sed ordering bug
All checks were successful
CI / build (push) Successful in 1m19s
CI / docker (push) Successful in 9s
The server-ui's entrypoint inserts <base href> THEN rewrites all
href="/" — including the just-inserted base tag, causing doubling.
Patched entrypoint rewrites asset paths first, then inserts <base>.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 23:53:03 +02:00
hsiegeln
1b2c962261 fix: root → /platform/ redirect via Traefik file config
All checks were successful
CI / build (push) Successful in 40s
CI / docker (push) Successful in 5s
Docker-compose label escaping mangles regex patterns. Use a separate
Traefik dynamic config file instead — clean regex, proper 302 redirect.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 23:30:38 +02:00
hsiegeln
4997f7a6a9 feat: move SaaS app to /platform base path, Logto becomes catch-all
All checks were successful
CI / build (push) Successful in 48s
CI / docker (push) Successful in 41s
Eliminates all Logto path enumeration in Traefik. Routing is now:
- /platform/* → cameleer-saas (SPA + API)
- /server/* → server-ui
- /* (catch-all) → Logto (sign-in, OIDC, assets, everything)

Spring context-path handles backend prefix transparently. No changes
needed in controllers, SecurityConfig, or interceptors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 23:06:41 +02:00
hsiegeln
9568e7f127 feat: single-domain path-based routing (no subdomains required)
All checks were successful
CI / build (push) Successful in 46s
CI / docker (push) Successful in 41s
Move SPA assets from /assets/ to /_app/ (Vite assetsDir config) so
Traefik can route /assets/* to Logto without conflict. All services
on one hostname with path-based routing:

- /oidc/*, /interaction/*, /assets/* → Logto
- /server/* → server-ui (prefix stripped)
- /api/* → cameleer-saas
- /* (catch-all) → cameleer-saas SPA

Customer needs only 1 DNS record. Server gets OIDC_JWK_SET_URI for
Docker-internal JWK fetch (standard Spring split config).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 21:10:03 +02:00
hsiegeln
e167d5475e feat: production-ready TLS with self-signed cert init container
All checks were successful
CI / build (push) Successful in 39s
CI / docker (push) Successful in 40s
Standard OIDC architecture: subdomain routing (auth.HOST, server.HOST),
TLS via Traefik, self-signed cert auto-generated on first boot.

- Add traefik-certs init container (generates wildcard self-signed cert)
- Enable TLS on all Traefik routers (websecure entrypoint)
- HTTP→HTTPS redirect in traefik.yml
- Host-based routing for all services (no more path conflicts)
- PUBLIC_PROTOCOL env var (https default, configurable)
- Protocol-aware redirect URIs in bootstrap
- Protocol-aware UI fallbacks

Customer bootstrap: set PUBLIC_HOST + DNS records + docker compose up.
For production TLS, configure Traefik ACME (Let's Encrypt).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:14:25 +02:00
hsiegeln
0472528cd6 fix: move Logto to auth.localhost subdomain to avoid /assets path conflict
All checks were successful
CI / build (push) Successful in 39s
CI / docker (push) Successful in 38s
Logto's login page references /assets/* which conflicts with the SPA's
assets at the same path. Using Host-based routing (auth.localhost) gives
Logto its own namespace - all paths on that subdomain go to Logto,
eliminating the conflict. *.localhost resolves to 127.0.0.1 and is a
secure context.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:42:17 +02:00
hsiegeln
c58ca34b2c fix: route all public traffic through Traefik at localhost:80
All checks were successful
CI / build (push) Successful in 38s
CI / docker (push) Successful in 39s
Logto ENDPOINT now points at Traefik (http://localhost) instead of
directly at port 3001. All services share the same base URL, eliminating
OIDC issuer mismatches and crypto.subtle secure context issues.

- Remove :3001 from all public-facing Logto URLs
- Add cameleer3-server-ui to Traefik at /server/ with prefix strip
- Dashboard link uses /server/ path instead of port 8082
- Bootstrap Host headers match Logto ENDPOINT (no port)
- Redirect URIs simplified (Traefik handles port 80)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:32:36 +02:00
hsiegeln
e90ca29920 fix: centralize public hostname into single PUBLIC_HOST env var
All checks were successful
CI / build (push) Successful in 39s
CI / docker (push) Successful in 36s
All public-facing URLs (Logto OIDC, redirect URIs, dashboard links) now
derive from PUBLIC_HOST in .env instead of scattered localhost references.
Resolves Docker networking ambiguity where localhost inside containers
doesn't reach the host machine.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:07:20 +02:00
hsiegeln
423803b303 fix: use Docker-internal URL for server OIDC issuer in bootstrap
All checks were successful
CI / build (push) Successful in 38s
CI / docker (push) Successful in 5s
Bootstrap was sending LOGTO_PUBLIC_ENDPOINT (http://localhost:3001)
as the OIDC issuer URI to the server. Inside Docker, localhost is
unreachable. Changed to LOGTO_ENDPOINT (http://logto:3001).

Also: .env must set LOGTO_ISSUER_URI=http://logto:3001/oidc (not
localhost) since this env var feeds cameleer3-server's OIDC decoder.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:38:02 +02:00
hsiegeln
cfb16d5048 fix: bootstrap OIDC config — add retry and null guard
All checks were successful
CI / build (push) Successful in 37s
CI / docker (push) Successful in 7s
Phase 7 server health check failed intermittently due to timing.
Add 3-attempt retry loop with 2s sleep. Guard against jq returning
literal "null" string for TRAD_SECRET. Add debug logging for both
preconditions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:31:41 +02:00
hsiegeln
9b77f810c1 fix: use correct health endpoint and HTTP method for server integration
All checks were successful
CI / build (push) Successful in 40s
CI / docker (push) Successful in 31s
ConnectivityHealthCheck: /actuator/health → /api/v1/health (actuator
now requires auth on cameleer3-server after OIDC was added).

Bootstrap: POST → PUT for /api/v1/admin/oidc (server expects PUT,
POST returned 405 causing OIDC config to silently fail).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 15:59:24 +02:00
hsiegeln
051f7fdae9 feat: auth hardening — scope enforcement, tenant isolation, and docs
All checks were successful
CI / build (push) Successful in 38s
CI / docker (push) Successful in 39s
Add @PreAuthorize annotations to all API controllers (14 endpoints
across 6 controllers) enforcing OAuth2 scopes: apps:manage, apps:deploy,
billing:manage, observe:read, platform:admin.

Enforce tenant isolation: TenantResolutionFilter now rejects cross-tenant
access on /api/tenants/{id}/* paths. New TenantOwnershipValidator checks
environment/app ownership for paths without tenantId. Platform admins
bypass both layers.

Fix frontend: OrgResolver split into two useEffect hooks so scopes
refresh on org switch. Scopes now served from /api/config (single source
of truth). Bootstrap cleaned — standalone org permissions removed.

Update docs/architecture.md, docs/user-manual.md, and CLAUDE.md to
reflect all auth hardening changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 15:32:53 +02:00
hsiegeln
277d5ea638 feat: define OAuth2 scopes on API resource and assign to Logto roles
Creates 10 API resource scopes (platform:admin + 9 tenant-level) on the
Cameleer SaaS API resource, assigns all to platform-admin role, creates
matching organization scopes, and wires them declaratively to org roles
(admin gets all, member gets deploy/observe). All operations are idempotent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 14:01:43 +02:00
hsiegeln
4da9cf23cb infra: add OIDC config to bootstrap output, stop reading Logto DB for secrets
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 12:44:27 +02:00
hsiegeln
00ee8876c1 fix: move DB seeding from bootstrap script to Java ApplicationRunner
All checks were successful
CI / build (push) Successful in 40s
CI / docker (push) Successful in 33s
SonarQube Analysis / sonarqube (push) Successful in 1m21s
The bootstrap script runs before cameleer-saas (Flyway), so tenant
tables don't exist yet. Moved DB seeding to BootstrapDataSeeder
ApplicationRunner which runs after Flyway migrations complete.
Reads bootstrap JSON and creates tenant/environment/license if missing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 02:55:43 +02:00
hsiegeln
827e388349 feat: bootstrap 2 users, tenant, org-scoped tokens, platform admin UI
All checks were successful
CI / build (push) Successful in 40s
CI / docker (push) Successful in 39s
Bootstrap script now creates:
- SaaS Owner (admin/admin) with platform-admin role
- Tenant Admin (camel/camel) in Example Tenant org
- Traditional Web App for cameleer3-server OIDC
- DB records: tenant, default environment, license
- Configures cameleer3-server OIDC via its admin API
All credentials configurable via env vars.

Backend:
- Fix LogtoManagementClient resource URL (https://default.logto.app/api)
- Add getUserRoles/getUserOrganizations to LogtoManagementClient
- Add GET /api/me endpoint (user info, platform admin status, tenants)
- Add GET /api/tenants list-all for platform admins
- Remove insecure X-header forwarding from Traefik

Frontend:
- Org-scoped tokens: getAccessToken(resource, orgId) for tenant context
- OrgResolver component populates org store from /api/me
- useOrganization Zustand store (currentOrgId + currentTenantId)
- Platform admin sidebar section + AdminTenantsPage
- View Dashboard link points to cameleer3-server on port 8081

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 02:50:51 +02:00
hsiegeln
84667170f1 fix: register API resource in Logto for JWT access tokens
All checks were successful
CI / build (push) Successful in 38s
CI / docker (push) Successful in 39s
Logto returns opaque access tokens when no resource is specified.
Added API resource creation to bootstrap, included resource indicator
in /api/config, and SPA now passes resource parameter in auth request.
Also fixed issuer-uri to match Logto's public endpoint.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 01:01:32 +02:00