From 7cd0a1ff1bfbfb27e35b5e060c5e11f7c54404af Mon Sep 17 00:00:00 2001 From: Lorenzo Sanesi Date: Sat, 25 Jul 2026 12:10:54 +0200 Subject: [PATCH] Fix db, keyclaok .env.example e docker compose --- .env.example | 41 +++++++++++----- db-init/init-eventi-db.sql | 5 ++ db-init/init-magazzino-db.sql | 5 ++ docker-compose.yml | 73 +++++++++++++++++++++++++++- keycloak/README.md | 23 +++++---- keycloak/realm-export.json | 79 +++++++++++++++++++++++++++---- scouthub-attivita-be/.env.example | 6 +-- 7 files changed, 198 insertions(+), 34 deletions(-) create mode 100644 db-init/init-eventi-db.sql create mode 100644 db-init/init-magazzino-db.sql diff --git a/.env.example b/.env.example index dad3452..b80a890 100644 --- a/.env.example +++ b/.env.example @@ -2,26 +2,41 @@ POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres +# Database di scouthub-attivita-be. Sulla stessa istanza vivono anche +# scouthub_home / scouthub_magazzino / scouthub_eventi (creati da db-init/ +# al primo avvio), usati rispettivamente da home-backend/magazzino-backend/ +# eventi-backend con le stesse credenziali POSTGRES_USER/POSTGRES_PASSWORD. POSTGRES_DB=scouthub POSTGRES_PORT=5432 -ATTIVITA_BACKEND_PORT=8080 - -# Deve restare 8080: il frontend Angular ha l'URL dell'API hardcoded -# in src/environments/environment.ts (apiUrl: 'http://localhost:8080') -ATTIVITA_FRONTEND_PORT=4200 - KEYCLOAK_DB_PASSWORD=keycloak_change_me KEYCLOAK_ADMIN_PASSWORD=admin_change_me -# Se in locale esegui anche scouthub-home-be fuori da Docker (npm run dev), -# il suo PORT di default (8082) è già scelto per non collidere con questo. -KEYCLOAK_PORT=8081 +# Keycloak ha una porta tutta sua, separata dai range frontend/backend qui sotto. +KEYCLOAK_PORT=6999 -# Deve restare 8082: scouthub-home-fe ha l'URL dell'org-service hardcoded -# in src/environments/environment.ts (orgServiceApiBaseUrl: 'http://localhost:8082') -HOME_BACKEND_PORT=8082 -HOME_FRONTEND_PORT=4201 +# Porte frontend, a partire da 7000, in ordine home/attivita/magazzino/calendario. +# Nessuna e' hardcoded nel codice: i frontend Angular ricevono gli URL (Keycloak, +# API, cross-frontend) come build ARG Docker valorizzati da queste variabili +# (vedi build.args dei rispettivi servizi in docker-compose.yml). +HOME_FRONTEND_PORT=7000 +ATTIVITA_FRONTEND_PORT=7001 +MAGAZZINO_FRONTEND_PORT=7002 +EVENTI_FRONTEND_PORT=7003 + +# Porte backend, a partire da 8000, stesso ordine home/attivita/magazzino/calendario. +HOME_BACKEND_PORT=8000 +ATTIVITA_BACKEND_PORT=8001 +MAGAZZINO_BACKEND_PORT=8002 +EVENTI_BACKEND_PORT=8003 # Deve coincidere con il secret del client "scouthub-home-be" in keycloak/realm-export.json KEYCLOAK_ORG_SERVICE_CLIENT_SECRET=change_me + +# Deve coincidere con il secret del client "scouthub-magazzino-be" in keycloak/realm-export.json +KEYCLOAK_MAGAZZINO_CLIENT_SECRET=change_me + +# Deve coincidere con il secret del client "scouthub-eventi-be" in keycloak/realm-export.json +# (secret condiviso anche con scouthub-attivita-be e scouthub-magazzino-be, che lo usano per +# chiamare POST /eventi/:id/risorse su scouthub-eventi-be) +KEYCLOAK_EVENTI_CLIENT_SECRET=change_me diff --git a/db-init/init-eventi-db.sql b/db-init/init-eventi-db.sql new file mode 100644 index 0000000..b2e5c1e --- /dev/null +++ b/db-init/init-eventi-db.sql @@ -0,0 +1,5 @@ +-- scouthub-eventi-be usa un database separato sulla stessa istanza Postgres di "db". +-- Eseguito solo al primo avvio del container (volume dati vuoto). +SELECT 'CREATE DATABASE scouthub_eventi' +WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'scouthub_eventi') +\gexec diff --git a/db-init/init-magazzino-db.sql b/db-init/init-magazzino-db.sql new file mode 100644 index 0000000..de739d6 --- /dev/null +++ b/db-init/init-magazzino-db.sql @@ -0,0 +1,5 @@ +-- scouthub-magazzino-be usa un database separato sulla stessa istanza Postgres di "db". +-- Eseguito solo al primo avvio del container (volume dati vuoto). +SELECT 'CREATE DATABASE scouthub_magazzino' +WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'scouthub_magazzino') +\gexec diff --git a/docker-compose.yml b/docker-compose.yml index ea8acae..41380d1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,7 +10,8 @@ services: - "${POSTGRES_PORT}:5432" volumes: - scouthub_db_data:/var/lib/postgresql/data - # Crea anche il DB scouthub_home usato da scouthub-home-be (solo al primo init). + # Crea anche i DB scouthub_home / scouthub_magazzino / scouthub_eventi + # usati dagli altri backend, sulla stessa istanza (solo al primo init). - ./db-init:/docker-entrypoint-initdb.d:ro healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"] @@ -39,6 +40,9 @@ services: attivita-frontend: build: context: ./scouthub-attivita-fe + args: + API_URL: http://localhost:${ATTIVITA_BACKEND_PORT} + KEYCLOAK_BASE_URL: http://localhost:${KEYCLOAK_PORT} restart: unless-stopped ports: - "${ATTIVITA_FRONTEND_PORT}:80" @@ -70,12 +74,79 @@ services: home-frontend: build: context: ./scouthub-home-fe + args: + KEYCLOAK_BASE_URL: http://localhost:${KEYCLOAK_PORT} + ORG_SERVICE_API_BASE_URL: http://localhost:${HOME_BACKEND_PORT} + ATTIVITA_FE_BASE_URL: http://localhost:${ATTIVITA_FRONTEND_PORT} restart: unless-stopped ports: - "${HOME_FRONTEND_PORT}:80" depends_on: - home-backend + magazzino-backend: + build: + context: ./scouthub-magazzino-be + restart: unless-stopped + environment: + PORT: ${MAGAZZINO_BACKEND_PORT} + DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/scouthub_magazzino?schema=public + KEYCLOAK_BASE_URL: http://keycloak:8080 + KEYCLOAK_REALM: scouthub + KEYCLOAK_MAGAZZINO_CLIENT_ID: scouthub-magazzino-be + KEYCLOAK_MAGAZZINO_CLIENT_SECRET: ${KEYCLOAK_MAGAZZINO_CLIENT_SECRET} + ports: + - "${MAGAZZINO_BACKEND_PORT}:${MAGAZZINO_BACKEND_PORT}" + depends_on: + db: + condition: service_healthy + keycloak: + condition: service_started + + magazzino-frontend: + build: + context: ./scouthub-magazzino-fe + args: + KEYCLOAK_BASE_URL: http://localhost:${KEYCLOAK_PORT} + MAGAZZINO_API_BASE_URL: http://localhost:${MAGAZZINO_BACKEND_PORT} + restart: unless-stopped + ports: + - "${MAGAZZINO_FRONTEND_PORT}:80" + depends_on: + - magazzino-backend + + eventi-backend: + build: + context: ./scouthub-eventi-be + restart: unless-stopped + environment: + PORT: ${EVENTI_BACKEND_PORT} + DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/scouthub_eventi?schema=public + KEYCLOAK_BASE_URL: http://keycloak:8080 + KEYCLOAK_REALM: scouthub + KEYCLOAK_EVENTI_CLIENT_ID: scouthub-eventi-be + KEYCLOAK_EVENTI_CLIENT_SECRET: ${KEYCLOAK_EVENTI_CLIENT_SECRET} + KEYCLOAK_AUTHORIZED_SERVICE_CLIENTS: scouthub-eventi-be + ports: + - "${EVENTI_BACKEND_PORT}:${EVENTI_BACKEND_PORT}" + depends_on: + db: + condition: service_healthy + keycloak: + condition: service_started + + eventi-frontend: + build: + context: ./scouthub-eventi-fe + args: + KEYCLOAK_BASE_URL: http://localhost:${KEYCLOAK_PORT} + EVENTI_API_BASE_URL: http://localhost:${EVENTI_BACKEND_PORT} + restart: unless-stopped + ports: + - "${EVENTI_FRONTEND_PORT}:80" + depends_on: + - eventi-backend + keycloak-db: image: postgres:16-alpine restart: unless-stopped diff --git a/keycloak/README.md b/keycloak/README.md index 01b4f69..1008be6 100644 --- a/keycloak/README.md +++ b/keycloak/README.md @@ -4,21 +4,26 @@ Realm `scouthub` con feature Organizations abilitata, importato automaticamente all'avvio di Keycloak (`--import-realm` nel `docker-compose.yml` alla radice). Basato su `keycloak/realm.json` (bozza di riferimento), con l'aggiunta del -ruolo `admin-centrale` richiesto da `POST /gruppi` in `scouthub-home-be` -(placeholder temporaneo, vedi il TODO in `src/routes/gruppi.routes.ts`). +ruolo `admin` richiesto da `POST /gruppi` e `GET /gruppi` in +`scouthub-home-be`: creazione/elenco diretti restano riservati ad `admin`, +mentre un utente normale passa dal flusso self-service di richiesta +creazione gruppo (`POST /richieste-creazione-gruppo`), che alla review +positiva invoca la stessa `createGruppo()` internamente. Contiene già: -- i realm role usati dal backend: `admin-centrale`, `capo-gruppo`, - `capo-unita`, `censito`, più il placeholder `manage-organizations` +- i realm role usati dal backend, in gerarchia composite (`admin` include + `capo-gruppo` include `capo-unita` include `capo` include `censito`; `admin` + include anche `moderatore`): `admin`, `capo-gruppo`, `capo-unita`, `capo`, + `censito`, `moderatore`, più il placeholder `manage-organizations` - il client scope `organization` (aggiunge id/attributi della Organization nei token OIDC) - il client confidential `scouthub-home-be` (service account abilitato, usato dal backend per il flow client-credentials verso le Admin REST API — il suo `clientId`/`secret` devono corrispondere a `KEYCLOAK_ORG_SERVICE_CLIENT_ID`/`_SECRET` in `scouthub-home-be/.env.example`) -- il client pubblico `scouthub-frontend` (login utente, redirect su - `http://localhost:4200/*`) +- il client pubblico `scouthub-frontend` (login utente, condiviso da tutti i + frontend Angular del progetto, redirect su `http://localhost:7000-7003/*`) Passi manuali ancora da fare dopo l'import (non automatizzabili in un realm export senza conoscere gli id generati a runtime): @@ -49,15 +54,15 @@ I passaggi da fare sono: - manage-users - view-users 9. Clicca Assign. -10. Dopo fatto questo bisogna fare un user admin-centrale +10. Dopo fatto questo bisogna fare un user admin -I passaggi da fare per admin-centrale: +I passaggi da fare per admin: 1. Apri http://localhost:8081/admin, login admin/admin, cambia realm in scouthub. 2. Menu laterale → Users → clicca su movioletto@yahoo.it. 3. Vai sul tab "Role mapping". 4. Clicca "Assign role". 5. Assicurati che il filtro sia su "Filter by realm roles" (dovrebbe esserlo di default per gli utenti normali). -6. Cerca e seleziona admin-centrale. +6. Cerca e seleziona admin. 7. Clicca Assign. ⚠️ Il secret del client `scouthub-home-be` (`CAMBIA-QUESTO-SECRET-IN-UN-VAULT`) diff --git a/keycloak/realm-export.json b/keycloak/realm-export.json index aa49e1c..b285234 100644 --- a/keycloak/realm-export.json +++ b/keycloak/realm-export.json @@ -11,11 +11,33 @@ "roles": { "realm": [ - { "name": "admin-centrale", "description": "Placeholder temporaneo: crea nuovi gruppi scout (Organization). Vedi TODO in src/routes/gruppi.routes.ts" }, - { "name": "capo-gruppo", "description": "Gestisce il proprio gruppo scout (Organization): membri, inviti, ruoli" }, - { "name": "capo-unita", "description": "Gestisce attivita' e materiali della propria branca" }, + { + "name": "admin", + "description": "Puo' fare tutto su tutte le organizzazioni. Ex admin-centrale. Unico ruolo abilitato a POST/GET /gruppi diretti; gli utenti normali passano dal flusso self-service di richiesta creazione gruppo", + "composite": true, + "composites": { "realm": ["capo-gruppo", "moderatore"] } + }, + { + "name": "capo-gruppo", + "description": "Gestisce il proprio gruppo scout (Organization): membri, inviti, ruoli", + "composite": true, + "composites": { "realm": ["capo-unita"] } + }, + { + "name": "capo-unita", + "description": "Gestisce attivita' e materiali della propria unita' (branca)", + "composite": true, + "composites": { "realm": ["capo"] } + }, + { + "name": "capo", + "description": "Vede e modifica parzialmente le cose della propria unita' (branca). Permessi identici a capo-unita per ora, da differenziare in futuro", + "composite": true, + "composites": { "realm": ["censito"] } + }, { "name": "censito", "description": "Accesso in sola consultazione" }, - { "name": "manage-organizations", "description": "Placeholder locale: il ruolo reale e' il client role manage-organizations su realm-management" } + { "name": "manage-organizations", "description": "Placeholder locale: il ruolo reale e' il client role manage-organizations su realm-management" }, + { "name": "moderatore", "description": "Puo' approvare/rifiutare proposte da moderare nei vari servizi (es. catalogo materiali del magazzino, in futuro validazione attivita' prima della pubblicazione). Ex admin-catalogo" } ] }, @@ -44,13 +66,22 @@ "standardFlowEnabled": true, "directAccessGrantsEnabled": false, "redirectUris": [ - "http://localhost:4200/*", - "http://localhost:4201/*" + "http://localhost:7000/*", + "http://localhost:7001/*", + "http://localhost:7002/*", + "http://localhost:7002/silent-check-sso.html", + "http://localhost:7003/*", + "http://localhost:7003/silent-check-sso.html" ], "webOrigins": [ - "http://localhost:4200", - "http://localhost:4201" + "http://localhost:7000", + "http://localhost:7001", + "http://localhost:7002", + "http://localhost:7003" ], + "attributes": { + "post.logout.redirect.uris": "http://localhost:7000/*##http://localhost:7001/*##http://localhost:7002/*##http://localhost:7003/*" + }, "defaultClientScopes": [ "openid", "basic", @@ -59,6 +90,38 @@ "email", "organization" ] + }, + { + "clientId": "scouthub-magazzino-be", + "name": "Servizio di gestione catalogo magazzino (magazzino-be)", + "enabled": true, + "protocol": "openid-connect", + "publicClient": false, + "standardFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "CAMBIA-QUESTO-SECRET-IN-UN-VAULT", + "redirectUris": [], + "attributes": { + "note": "Usato solo server-to-server dal backend scouthub-magazzino-be verso le Admin REST API. Non esporre mai il secret al frontend." + } + }, + { + "clientId": "scouthub-eventi-be", + "name": "Servizio di gestione eventi (eventi-be)", + "enabled": true, + "protocol": "openid-connect", + "publicClient": false, + "standardFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "CAMBIA-QUESTO-SECRET-IN-UN-VAULT", + "redirectUris": [], + "attributes": { + "note": "Usato solo server-to-server dal backend scouthub-eventi-be verso le Admin REST API. Il secret va condiviso anche con attivita-be e magazzino-be, che lo usano come client credentials per chiamare POST /eventi/:id/risorse su scouthub-eventi-be. Non esporre mai il secret al frontend." + } } ] } diff --git a/scouthub-attivita-be/.env.example b/scouthub-attivita-be/.env.example index 9b2e791..399e3bc 100644 --- a/scouthub-attivita-be/.env.example +++ b/scouthub-attivita-be/.env.example @@ -1,11 +1,11 @@ -PORT=8080 -CORS_ORIGIN=http://localhost:4200 +PORT=8001 +CORS_ORIGIN=http://localhost:7001 DATABASE_URL=postgresql://postgres:postgres@localhost:5432/scouthub?schema=public # Keycloak (realm condiviso con scouthub-home-be/-fe, vedi keycloak/realm-export.json # alla radice del monorepo): usato solo per verificare i token, nessun client # id/secret necessario qui. -KEYCLOAK_BASE_URL=http://localhost:8081 +KEYCLOAK_BASE_URL=http://localhost:6999 KEYCLOAK_REALM=scouthub # Database dedicato ai test automatici (Jest): tenuto separato dal DB di sviluppo