diff --git a/docs/development/local-keycloak.md b/docs/development/local-keycloak.md index 96ea3c4..6fb4d59 100644 --- a/docs/development/local-keycloak.md +++ b/docs/development/local-keycloak.md @@ -67,35 +67,46 @@ The kustomization generates a ConfigMap from `dls-realm.json` and mounts it at ` The realm export at `keycloak-mock/dls-realm.json` defines: -- **Realm**: `dls` — matches `VITE_KEYCLOAK_REALM` defaults so no env change needed. -- **Client**: `SmartEM` — public client, standard flow, PKCE S256. -- **Valid redirect URIs**: `http://localhost:5173/*` and `http://localhost:5174/*` (the smartem app and legacy app dev ports). +- **Realm**: `dls` — matches the value the SPA's runtime `config.json` ships with by default. +- **Clients**: + - `SmartEM_User` — public client for the browser SPA (standard flow, PKCE S256). This is what the SPA sends in `client_id` and what tokens carry in their `azp` claim. + - `SmartEM_Agent` — confidential client for the Windows agent (client-credentials grant). Service-to-service, not user-facing. +- **Valid redirect URIs** (`SmartEM_User`): `http://localhost:5173/*`, `http://localhost:5174/*`, and `http://localhost:30100/*` — the Vite dev ports (smartem app and legacy app) plus the k3s NodePort the SPA pod is exposed on. - **Web origins**: same hosts (required for silent SSO iframe checks once that's enabled in the frontend). - **Custom claim mapper**: `fedId` — the AuthProvider in the SmartEM frontend reads `idTokenParsed.fedId`. The mapper picks up the user attribute and emits it as an ID-token claim. - **Seeded users**: - `devuser` / `devpass` — generic, fedId `dev12345`. - - `valuser` / `valpass` — for Val Redchenko, fedId `val99999`. The mock does not implement the full DLS user model (groups, roles, federated identity). Add more users or roles by editing `dls-realm.json` directly. ## Pointing the frontend at it -In `smartem-frontend/apps/smartem/.env.local`: +The SPA loads its Keycloak config at boot from a runtime `/config.json`, not from `VITE_*` env vars. For `npm run dev:smartem` Vite serves the file from `apps/smartem/public/config.json`; in k8s deploys the SPA pod's nginx serves the file from the `smartem-frontend-config` ConfigMap mount (overriding the one baked into the image). Either way the SPA reads the same shape. -```env -VITE_KEYCLOAK_URL=http://localhost:8080 # compose, or k8s with port-forward -VITE_KEYCLOAK_REALM=dls -VITE_KEYCLOAK_CLIENT_ID=SmartEM -VITE_AUTH_ENABLED=true +Edit `smartem-frontend/apps/smartem/public/config.json`: + +```json +{ + "keycloak": { + "url": "http://localhost:30090", + "realm": "dls", + "clientId": "SmartEM_User" + }, + "authEnabled": true +} ``` -For the k8s form without port-forward, substitute `http://:30090` for the URL. With a single-node k3s cluster, `node-ip` is the host's IP. +Use `http://localhost:30090` for the k3s NodePort. For docker-compose Keycloak or a port-forwarded pod, use `http://localhost:8080`. If Vite runs on a host other than the one with the cluster, use `http://:30090`. + +The config is fetched with `cache: 'no-store'` and applied before the SPA mounts, so a browser reload picks up edits without restarting Vite. + +## Disabling auth entirely (Vite dev only, with caveat) -After changing `.env.local`, restart the Vite dev server (env values are read at startup). +Set `authEnabled: false` in `apps/smartem/public/config.json` and the `AuthGate` (`apps/smartem/src/auth/AuthGate.tsx`) short-circuits — the SPA renders without contacting Keycloak at all. Useful for pure UI iteration. -## Disabling auth entirely +**Caveat:** the backend (`smartem-decisions`) always enforces Bearer-token validation on non-exempt requests since smartem-decisions#285 — there is no opt-out. With `authEnabled: false` the SPA renders, but every `/api/` call returns 401. This mode is only useful when paired with MSW (`VITE_ENABLE_MOCKS=true` in `apps/smartem/.env.local`), or for views that don't fetch from the backend. -For UI work that doesn't need to exercise auth, set `VITE_AUTH_ENABLED=false`. The `AuthGate` short-circuits and the SPA renders without contacting Keycloak at all. This is a deliberately separate path from "Keycloak is unavailable" — the latter is an error state to recover from, the former is a development convenience. +This is a deliberately separate path from "Keycloak is unavailable" — the latter is an error state to recover from, the former is a dev convenience for offline/mocked UI work. ## Editing the realm diff --git a/env-examples/.env.example.k8s.development b/env-examples/.env.example.k8s.development index 91ade40..d5bb131 100644 --- a/env-examples/.env.example.k8s.development +++ b/env-examples/.env.example.k8s.development @@ -28,12 +28,14 @@ ADMINER_PORT=8080 # Use "*" for development to allow all origins CORS_ALLOWED_ORIGINS=* -# Keycloak OIDC integration (in-cluster mock). +# Keycloak OIDC integration (in-cluster mock). Auth is unconditional on the +# backend (smartem-decisions#285); there is no opt-out flag. # KEYCLOAK_VERIFY_ISS=false because tokens are minted with the browser-facing # URL (localhost:30090) while the pod fetches JWKS via in-cluster DNS # (keycloak-service:8080). -KEYCLOAK_AUTH_REQUIRED=false +# KEYCLOAK_ALLOWED_AZP is the comma-separated azp allow-list; leave unset to +# accept any valid token from the realm (typical for dev). KEYCLOAK_URL=http://keycloak-service:8080 KEYCLOAK_REALM=dls -KEYCLOAK_CLIENT_ID=SmartEM KEYCLOAK_VERIFY_ISS=false +# KEYCLOAK_ALLOWED_AZP=SmartEM_User,SmartEM_Agent diff --git a/env-examples/.env.example.k8s.staging b/env-examples/.env.example.k8s.staging index ae0068a..1d225d7 100644 --- a/env-examples/.env.example.k8s.staging +++ b/env-examples/.env.example.k8s.staging @@ -29,11 +29,11 @@ ADMINER_PORT=8080 # Example: "https://staging.example.com,https://staging-app.example.com" CORS_ALLOWED_ORIGINS=https://staging.example.com -# Keycloak OIDC integration -# KEYCLOAK_AUTH_REQUIRED=true makes the backend reject non-exempt requests -# without a valid Bearer token. KEYCLOAK_URL must reach the realm host. -KEYCLOAK_AUTH_REQUIRED=true +# Keycloak OIDC integration. Auth is unconditional on the backend +# (smartem-decisions#285); there is no opt-out flag. KEYCLOAK_URL must reach +# the realm host. KEYCLOAK_ALLOWED_AZP is the comma-separated azp allow-list; +# unset means any valid token from the realm is accepted. KEYCLOAK_URL=https://identity-test.diamond.ac.uk KEYCLOAK_REALM=dls -KEYCLOAK_CLIENT_ID=SmartEM KEYCLOAK_VERIFY_ISS=true +KEYCLOAK_ALLOWED_AZP=SmartEM_User,SmartEM_Agent diff --git a/k8s/environments/development/configmap.yaml b/k8s/environments/development/configmap.yaml index 550aebd..ac72167 100644 --- a/k8s/environments/development/configmap.yaml +++ b/k8s/environments/development/configmap.yaml @@ -18,9 +18,9 @@ data: # service. KEYCLOAK_VERIFY_ISS=false because tokens are issued with the # browser-facing URL (localhost:30090) while the pod fetches JWKS via # in-cluster DNS (keycloak-service:8080) - signing key matches but the - # iss claim does not. - KEYCLOAK_AUTH_REQUIRED: "false" + # iss claim does not. To restrict which realm clients can call /api/, + # set KEYCLOAK_ALLOWED_AZP (e.g. "SmartEM_User,SmartEM_Agent"); unset + # means any valid token from the realm is accepted. KEYCLOAK_URL: "http://keycloak-service:8080" KEYCLOAK_REALM: "dls" - KEYCLOAK_CLIENT_ID: "SmartEM" KEYCLOAK_VERIFY_ISS: "false" diff --git a/k8s/environments/development/kustomization.yaml b/k8s/environments/development/kustomization.yaml index bf74692..fd9e031 100644 --- a/k8s/environments/development/kustomization.yaml +++ b/k8s/environments/development/kustomization.yaml @@ -11,6 +11,7 @@ resources: - adminer.yaml - smartem-http-api.yaml - smartem-worker.yaml +- smartem-frontend.yaml - ../../../keycloak-mock namePrefix: "" diff --git a/k8s/environments/development/smartem-frontend.yaml b/k8s/environments/development/smartem-frontend.yaml new file mode 100644 index 0000000..e1a2135 --- /dev/null +++ b/k8s/environments/development/smartem-frontend.yaml @@ -0,0 +1,96 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: smartem-frontend-config + namespace: smartem-decisions +data: + # Fetched at SPA boot from /config.json. The URL must be browser-reachable; + # NodePort 30090 maps to the in-cluster keycloak-service:8080. authEnabled + # is true because the backend always enforces Bearer-token validation + # (smartem-decisions#285 removed the KEYCLOAK_AUTH_REQUIRED escape hatch); + # the SPA must complete the login ceremony to talk to /api/. + config.json: | + { + "keycloak": { + "url": "http://localhost:30090", + "realm": "dls", + "clientId": "SmartEM_User" + }, + "authEnabled": true + } + +--- + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: smartem-frontend + namespace: smartem-decisions + labels: + app: smartem-frontend +spec: + replicas: 1 + selector: + matchLabels: + app: smartem-frontend + template: + metadata: + labels: + app: smartem-frontend + spec: + tolerations: + - key: node.kubernetes.io/disk-pressure + operator: Exists + effect: NoSchedule + containers: + - name: smartem-frontend + image: ghcr.io/diamondlightsource/smartem-frontend:latest + imagePullPolicy: Always + ports: + - containerPort: 80 + env: + # The SPA pod's nginx reverse-proxies /api/ to this in-cluster host. + # FQDN required: nginx's explicit `resolver` directive in the SPA image + # doesn't consult /etc/resolv.conf's search list, so the short name + # `smartem-http-api-service` returns NXDOMAIN. + - name: BACKEND_HOST + value: "smartem-http-api-service.smartem-decisions.svc.cluster.local" + volumeMounts: + # Overrides the placeholder config.json shipped in the image. + - name: frontend-config + mountPath: /usr/share/nginx/html/config.json + subPath: config.json + resources: + requests: + memory: "64Mi" + cpu: "50m" + limits: + memory: "128Mi" + cpu: "200m" + volumes: + - name: frontend-config + configMap: + name: smartem-frontend-config + items: + - key: config.json + path: config.json + +--- + +apiVersion: v1 +kind: Service +metadata: + name: smartem-frontend-service + namespace: smartem-decisions + labels: + app: smartem-frontend +spec: + selector: + app: smartem-frontend + ports: + - name: http + port: 80 + targetPort: 80 + nodePort: 30100 + protocol: TCP + type: NodePort diff --git a/k8s/environments/production/ingress.yaml b/k8s/environments/production/ingress.yaml new file mode 100644 index 0000000..50d9f7b --- /dev/null +++ b/k8s/environments/production/ingress.yaml @@ -0,0 +1,22 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: smartem-frontend-ingress + namespace: smartem-decisions-production + annotations: + # Adjust to the cluster's ingress controller (nginx-ingress shown). + nginx.ingress.kubernetes.io/proxy-body-size: "10m" +spec: + rules: + # The SPA pod's nginx reverse-proxies /api/ to smartem-http-api-service + # internally, so a single route covering / is sufficient. + - host: smartem.diamond.ac.uk + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: smartem-frontend-service + port: + number: 80 diff --git a/k8s/environments/production/kustomization.yaml b/k8s/environments/production/kustomization.yaml index e2e8404..a581877 100644 --- a/k8s/environments/production/kustomization.yaml +++ b/k8s/environments/production/kustomization.yaml @@ -10,6 +10,8 @@ resources: - elasticsearch.yaml - smartem-http-api.yaml - smartem-worker.yaml +- smartem-frontend.yaml +- ingress.yaml namePrefix: "" namespace: smartem-decisions-production diff --git a/k8s/environments/production/smartem-frontend.yaml b/k8s/environments/production/smartem-frontend.yaml new file mode 100644 index 0000000..4dc2907 --- /dev/null +++ b/k8s/environments/production/smartem-frontend.yaml @@ -0,0 +1,89 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: smartem-frontend-config + namespace: smartem-decisions-production +data: + # Fetched at SPA boot from /config.json. Browser-reachable Keycloak URL + # for the DLS production realm. The clientId must match the realm's + # public client and appear in the backend's KEYCLOAK_ALLOWED_AZP + # allow-list (set in smartem-config). + config.json: | + { + "keycloak": { + "url": "https://identity.diamond.ac.uk", + "realm": "dls", + "clientId": "SmartEM_User" + }, + "authEnabled": true + } + +--- + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: smartem-frontend + namespace: smartem-decisions-production + labels: + app: smartem-frontend +spec: + replicas: 1 + selector: + matchLabels: + app: smartem-frontend + template: + metadata: + labels: + app: smartem-frontend + spec: + imagePullSecrets: + - name: ghcr-secret + containers: + - name: smartem-frontend + image: ghcr.io/diamondlightsource/smartem-frontend:latest + ports: + - containerPort: 80 + env: + # FQDN required: nginx's explicit `resolver` directive in the SPA image + # doesn't consult /etc/resolv.conf's search list, so the short name + # `smartem-http-api-service` returns NXDOMAIN. + - name: BACKEND_HOST + value: "smartem-http-api-service.smartem-decisions-production.svc.cluster.local" + volumeMounts: + - name: frontend-config + mountPath: /usr/share/nginx/html/config.json + subPath: config.json + resources: + requests: + memory: "64Mi" + cpu: "50m" + limits: + memory: "128Mi" + cpu: "200m" + volumes: + - name: frontend-config + configMap: + name: smartem-frontend-config + items: + - key: config.json + path: config.json + +--- + +apiVersion: v1 +kind: Service +metadata: + name: smartem-frontend-service + namespace: smartem-decisions-production + labels: + app: smartem-frontend +spec: + selector: + app: smartem-frontend + ports: + - name: http + port: 80 + targetPort: 80 + protocol: TCP + type: ClusterIP diff --git a/k8s/environments/staging/configmap.yaml b/k8s/environments/staging/configmap.yaml index 8c88d39..7573654 100644 --- a/k8s/environments/staging/configmap.yaml +++ b/k8s/environments/staging/configmap.yaml @@ -14,11 +14,13 @@ data: HTTP_API_PORT: "8000" ADMINER_PORT: "8080" CORS_ALLOWED_ORIGINS: "https://staging.example.com" - # Keycloak OIDC integration. KEYCLOAK_AUTH_REQUIRED=true rejects all - # non-exempt requests that don't carry a valid Bearer token. Defaults - # below target the DLS test realm; override per environment via .env. - KEYCLOAK_AUTH_REQUIRED: "true" + # Keycloak OIDC integration. The backend rejects every non-exempt + # request that doesn't carry a valid Bearer token (always-on since + # smartem-decisions#285). Defaults below target the DLS test realm; + # override per environment via .env. KEYCLOAK_ALLOWED_AZP is the + # comma-separated azp allow-list; unset means any valid token from + # the realm is accepted. KEYCLOAK_URL: "https://identity-test.diamond.ac.uk" KEYCLOAK_REALM: "dls" - KEYCLOAK_CLIENT_ID: "SmartEM" KEYCLOAK_VERIFY_ISS: "true" + KEYCLOAK_ALLOWED_AZP: "SmartEM_User,SmartEM_Agent" diff --git a/k8s/environments/staging/ingress.yaml b/k8s/environments/staging/ingress.yaml new file mode 100644 index 0000000..0ff8fc2 --- /dev/null +++ b/k8s/environments/staging/ingress.yaml @@ -0,0 +1,22 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: smartem-frontend-ingress + namespace: smartem-decisions-staging + annotations: + # Adjust to the cluster's ingress controller (nginx-ingress shown). + nginx.ingress.kubernetes.io/proxy-body-size: "10m" +spec: + rules: + # The SPA pod's nginx reverse-proxies /api/ to smartem-http-api-service + # internally, so a single route covering / is sufficient. + - host: smartem-staging.diamond.ac.uk + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: smartem-frontend-service + port: + number: 80 diff --git a/k8s/environments/staging/kustomization.yaml b/k8s/environments/staging/kustomization.yaml index d92de12..d20b281 100644 --- a/k8s/environments/staging/kustomization.yaml +++ b/k8s/environments/staging/kustomization.yaml @@ -10,6 +10,8 @@ resources: - elasticsearch.yaml - smartem-http-api.yaml - smartem-worker.yaml +- smartem-frontend.yaml +- ingress.yaml namePrefix: "" namespace: smartem-decisions-staging diff --git a/k8s/environments/staging/smartem-frontend.yaml b/k8s/environments/staging/smartem-frontend.yaml new file mode 100644 index 0000000..16d1fa6 --- /dev/null +++ b/k8s/environments/staging/smartem-frontend.yaml @@ -0,0 +1,89 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: smartem-frontend-config + namespace: smartem-decisions-staging +data: + # Fetched at SPA boot from /config.json. Browser-reachable Keycloak URL + # for the DLS test realm. The clientId must match the realm's public + # client and appear in the backend's KEYCLOAK_ALLOWED_AZP allow-list + # (set in smartem-config). + config.json: | + { + "keycloak": { + "url": "https://identity-test.diamond.ac.uk", + "realm": "dls", + "clientId": "SmartEM_User" + }, + "authEnabled": true + } + +--- + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: smartem-frontend + namespace: smartem-decisions-staging + labels: + app: smartem-frontend +spec: + replicas: 1 + selector: + matchLabels: + app: smartem-frontend + template: + metadata: + labels: + app: smartem-frontend + spec: + imagePullSecrets: + - name: ghcr-secret + containers: + - name: smartem-frontend + image: ghcr.io/diamondlightsource/smartem-frontend:latest + ports: + - containerPort: 80 + env: + # FQDN required: nginx's explicit `resolver` directive in the SPA image + # doesn't consult /etc/resolv.conf's search list, so the short name + # `smartem-http-api-service` returns NXDOMAIN. + - name: BACKEND_HOST + value: "smartem-http-api-service.smartem-decisions-staging.svc.cluster.local" + volumeMounts: + - name: frontend-config + mountPath: /usr/share/nginx/html/config.json + subPath: config.json + resources: + requests: + memory: "64Mi" + cpu: "50m" + limits: + memory: "128Mi" + cpu: "200m" + volumes: + - name: frontend-config + configMap: + name: smartem-frontend-config + items: + - key: config.json + path: config.json + +--- + +apiVersion: v1 +kind: Service +metadata: + name: smartem-frontend-service + namespace: smartem-decisions-staging + labels: + app: smartem-frontend +spec: + selector: + app: smartem-frontend + ports: + - name: http + port: 80 + targetPort: 80 + protocol: TCP + type: ClusterIP diff --git a/k8s/ingress.yaml b/k8s/ingress.yaml deleted file mode 100644 index bc49d88..0000000 --- a/k8s/ingress.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: smartem-http-api-ingress - namespace: smartem-decisions - annotations: - nginx.ingress.kubernetes.io/rewrite-target: / -spec: - rules: - - host: smartem-api.your-domain.com # Update with your actual domain - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: smartem-http-api-service - port: - number: 80 diff --git a/keycloak-mock/README.md b/keycloak-mock/README.md index 1cb43f1..8f8505d 100644 --- a/keycloak-mock/README.md +++ b/keycloak-mock/README.md @@ -8,7 +8,7 @@ Two deployment forms; pick whichever fits your workflow. | File | Purpose | |------|---------| -| `dls-realm.json` | Realm export. Single source of truth, consumed by both forms. Realm `dls`, client `SmartEM`, two seeded users. | +| `dls-realm.json` | Realm export. Single source of truth, consumed by both forms. Realm `dls`, clients `SmartEM_User` + `SmartEM_Agent`, seeded `devuser`. | | `docker-compose.yml` | Standalone Compose deployment. Fastest path to a running Keycloak. | | `keycloak.yaml` | Kubernetes Deployment + Services. | | `kustomization.yaml` | Kustomize config; loads the realm JSON as a ConfigMap. Referenced as a base by `k8s/environments/development/kustomization.yaml`, also works standalone. | @@ -16,13 +16,14 @@ Two deployment forms; pick whichever fits your workflow. ## Realm contents - **Realm**: `dls` -- **Client**: `SmartEM` (public, PKCE S256, standard flow) -- **Redirect URIs**: `http://localhost:5173/*`, `http://localhost:5174/*` +- **Clients**: + - `SmartEM_User` — public, PKCE S256, standard flow. Browser SPA. + - `SmartEM_Agent` — confidential, client-credentials grant. Windows agent. +- **Redirect URIs** (`SmartEM_User`): `http://localhost:5173/*`, `http://localhost:5174/*`, `http://localhost:30100/*` - **Web Origins**: same hosts - **Custom claim**: `fedId` (protocol mapper from user attribute), to mirror DLS realm claims - **Users**: - `devuser` / `devpass` (Dev User, fedId `dev12345`) - - `valuser` / `valpass` (Val Redchenko, fedId `val99999`) ## Compose @@ -50,18 +51,22 @@ Once running, the Keycloak service is reachable inside the cluster at `http://ke ## Pointing the frontend at it -In `smartem-frontend/apps/smartem/.env.local`: +The SPA loads Keycloak config at boot from a runtime `/config.json`, not from `VITE_*` env vars. For `npm run dev:smartem` the file served is `smartem-frontend/apps/smartem/public/config.json`; in k8s deploys it's overridden by the `smartem-frontend-config` ConfigMap mount. -```env -VITE_KEYCLOAK_URL=http://localhost:8080 # compose / port-forward -# or -VITE_KEYCLOAK_URL=http://:30090 # k8s NodePort -VITE_KEYCLOAK_REALM=dls -VITE_KEYCLOAK_CLIENT_ID=SmartEM -VITE_AUTH_ENABLED=true +Edit `smartem-frontend/apps/smartem/public/config.json`: + +```json +{ + "keycloak": { + "url": "http://localhost:30090", + "realm": "dls", + "clientId": "SmartEM_User" + }, + "authEnabled": true +} ``` -Then `npm run dev:smartem` from the smartem-frontend repo root. +Use `http://localhost:30090` for the k3s NodePort, or `http://:30090` if Vite is serving from a different host. Then `npm run dev:smartem` from the smartem-frontend repo root. Config is fetched with `cache: 'no-store'` and applied before the SPA mounts, so a browser reload picks up edits without restarting Vite. ## Editing the realm diff --git a/keycloak-mock/dls-realm.json b/keycloak-mock/dls-realm.json index e4897ab..793baaa 100644 --- a/keycloak-mock/dls-realm.json +++ b/keycloak-mock/dls-realm.json @@ -24,15 +24,17 @@ "serviceAccountsEnabled": false, "redirectUris": [ "http://localhost:5173/*", - "http://localhost:5174/*" + "http://localhost:5174/*", + "http://localhost:30100/*" ], "webOrigins": [ "http://localhost:5173", - "http://localhost:5174" + "http://localhost:5174", + "http://localhost:30100" ], "attributes": { "pkce.code.challenge.method": "S256", - "post.logout.redirect.uris": "http://localhost:5173/*##http://localhost:5174/*" + "post.logout.redirect.uris": "http://localhost:5173/*##http://localhost:5174/*##http://localhost:30100/*" }, "defaultClientScopes": [ "openid", diff --git a/scripts/k8s/dev-k8s.sh b/scripts/k8s/dev-k8s.sh index d2601ed..63e1a9b 100755 --- a/scripts/k8s/dev-k8s.sh +++ b/scripts/k8s/dev-k8s.sh @@ -302,19 +302,18 @@ ensure_app_configmap() { local http_api_port="${HTTP_API_PORT:-}" local adminer_port="${ADMINER_PORT:-}" local cors_allowed_origins="${CORS_ALLOWED_ORIGINS:-}" - local keycloak_auth_required="${KEYCLOAK_AUTH_REQUIRED:-}" local keycloak_url="${KEYCLOAK_URL:-}" local keycloak_realm="${KEYCLOAK_REALM:-}" - local keycloak_client_id="${KEYCLOAK_CLIENT_ID:-}" local keycloak_verify_iss="${KEYCLOAK_VERIFY_ISS:-}" + local keycloak_allowed_azp="${KEYCLOAK_ALLOWED_AZP:-}" # Check if any env vars are set local has_env_overrides=false if [[ -n "$postgres_host" ]] || [[ -n "$postgres_port" ]] || [[ -n "$postgres_db" ]] || \ [[ -n "$rabbitmq_host" ]] || [[ -n "$rabbitmq_port" ]] || \ [[ -n "$http_api_port" ]] || [[ -n "$adminer_port" ]] || [[ -n "$cors_allowed_origins" ]] || \ - [[ -n "$keycloak_auth_required" ]] || [[ -n "$keycloak_url" ]] || [[ -n "$keycloak_realm" ]] || \ - [[ -n "$keycloak_client_id" ]] || [[ -n "$keycloak_verify_iss" ]]; then + [[ -n "$keycloak_url" ]] || [[ -n "$keycloak_realm" ]] || \ + [[ -n "$keycloak_verify_iss" ]] || [[ -n "$keycloak_allowed_azp" ]]; then has_env_overrides=true fi @@ -338,32 +337,35 @@ ensure_app_configmap() { http_api_port="${http_api_port:-8000}" adminer_port="${adminer_port:-8080}" cors_allowed_origins="${cors_allowed_origins:-*}" - keycloak_auth_required="${keycloak_auth_required:-false}" keycloak_url="${keycloak_url:-http://keycloak-service:8080}" keycloak_realm="${keycloak_realm:-dls}" - keycloak_client_id="${keycloak_client_id:-SmartEM}" keycloak_verify_iss="${keycloak_verify_iss:-false}" log_info "Creating application ConfigMap for environment: $DEPLOY_ENV" log_info "POSTGRES_HOST=$postgres_host, POSTGRES_PORT=$postgres_port, POSTGRES_DB=$postgres_db" log_info "RABBITMQ_HOST=$rabbitmq_host, RABBITMQ_PORT=$rabbitmq_port" log_info "HTTP_API_PORT=$http_api_port, CORS_ALLOWED_ORIGINS=$cors_allowed_origins" - log_info "KEYCLOAK_AUTH_REQUIRED=$keycloak_auth_required, KEYCLOAK_URL=$keycloak_url, KEYCLOAK_REALM=$keycloak_realm" + log_info "KEYCLOAK_URL=$keycloak_url, KEYCLOAK_REALM=$keycloak_realm, KEYCLOAK_ALLOWED_AZP=${keycloak_allowed_azp:-}" + + local configmap_args=( + --from-literal=POSTGRES_HOST="$postgres_host" + --from-literal=POSTGRES_PORT="$postgres_port" + --from-literal=POSTGRES_DB="$postgres_db" + --from-literal=RABBITMQ_HOST="$rabbitmq_host" + --from-literal=RABBITMQ_PORT="$rabbitmq_port" + --from-literal=HTTP_API_PORT="$http_api_port" + --from-literal=ADMINER_PORT="$adminer_port" + --from-literal=CORS_ALLOWED_ORIGINS="$cors_allowed_origins" + --from-literal=KEYCLOAK_URL="$keycloak_url" + --from-literal=KEYCLOAK_REALM="$keycloak_realm" + --from-literal=KEYCLOAK_VERIFY_ISS="$keycloak_verify_iss" + ) + if [[ -n "$keycloak_allowed_azp" ]]; then + configmap_args+=(--from-literal=KEYCLOAK_ALLOWED_AZP="$keycloak_allowed_azp") + fi kubectl create configmap smartem-config \ - --from-literal=POSTGRES_HOST="$postgres_host" \ - --from-literal=POSTGRES_PORT="$postgres_port" \ - --from-literal=POSTGRES_DB="$postgres_db" \ - --from-literal=RABBITMQ_HOST="$rabbitmq_host" \ - --from-literal=RABBITMQ_PORT="$rabbitmq_port" \ - --from-literal=HTTP_API_PORT="$http_api_port" \ - --from-literal=ADMINER_PORT="$adminer_port" \ - --from-literal=CORS_ALLOWED_ORIGINS="$cors_allowed_origins" \ - --from-literal=KEYCLOAK_AUTH_REQUIRED="$keycloak_auth_required" \ - --from-literal=KEYCLOAK_URL="$keycloak_url" \ - --from-literal=KEYCLOAK_REALM="$keycloak_realm" \ - --from-literal=KEYCLOAK_CLIENT_ID="$keycloak_client_id" \ - --from-literal=KEYCLOAK_VERIFY_ISS="$keycloak_verify_iss" \ + "${configmap_args[@]}" \ --namespace="$NAMESPACE" log_success "Application ConfigMap created with .env overrides" @@ -477,8 +479,10 @@ print_access_urls() { echo -e "${GREEN}🌐 Access URLs:${NC}" echo -e " ${BLUE} Adminer (Database UI):${NC} http://localhost:30808" echo -e " ${BLUE} RabbitMQ Management:${NC} http://localhost:30673" + echo -e " ${BLUE} Keycloak (mock):${NC} http://localhost:30090" echo -e " ${BLUE} SmartEM HTTP API:${NC} http://localhost:30080" echo -e " ${BLUE} API Documentation:${NC} http://localhost:30080/docs" + echo -e " ${BLUE} SmartEM Frontend:${NC} http://localhost:30100" echo "" echo -e "${YELLOW}💡 Quick Commands:${NC}" echo -e " View all resources: ${BLUE}kubectl get all -n $NAMESPACE${NC}"