diff --git a/Makefile b/Makefile index 10e74629..ca43a2a0 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ # It is designed to be the main entry point for developers. # Use .PHONY to declare targets that are not files, to avoid conflicts. -.PHONY: install dev build prepare-export help deploy-launchpad deploy-app +.PHONY: help install dev build integrate-export prepare-export deploy-launchpad deploy-launchpad-full deploy-app admin-install admin-dev admin-build admin-deploy-dev admin-deploy-staging configure-iap-launchpad list-iap-users remove-iap-user setup-labels export-issues create-issues-from-file install-git-hooks # The default command to run if no target is specified (e.g., just 'make'). .DEFAULT_GOAL := help @@ -12,6 +12,12 @@ # --- Firebase & GCP Configuration --- GCP_DEV_PROJECT_ID := krow-workforce-dev GCP_STAGING_PROJECT_ID := krow-workforce-staging +IAP_SERVICE_ACCOUNT := service-933560802882@gcp-sa-iap.iam.gserviceaccount.com + +# --- Cloud Run Configuration --- +CR_LAUNCHPAD_SERVICE_NAME := internal-launchpad +CR_LAUNCHPAD_REGION := us-central1 +CR_LAUNCHPAD_IMAGE_URI := us-docker.pkg.dev/$(GCP_DEV_PROJECT_ID)/gcr-io/$(CR_LAUNCHPAD_SERVICE_NAME) # --- Environment Detection --- ENV ?= dev @@ -27,26 +33,191 @@ else HOSTING_TARGET := app-dev endif -# Installs all project dependencies using npm. +# Shows this help message. +help: + @echo "--------------------------------------------------" + @echo " KROW Workforce - Available Makefile Commands" + @echo "--------------------------------------------------" + @echo "" + @echo " --- CORE DEVELOPMENT ---" + @echo " make install - Installs web frontend dependencies." + @echo " make dev - Starts the local web frontend server." + @echo " make build - Builds the web frontend for production." + @echo "" + @echo " --- DEPLOYMENT ---" + @echo " make deploy-launchpad - Deploys the internal launchpad to Cloud Run." + @echo " make deploy-launchpad-full - Deploys launchpad to Cloud Run & configures IAP." + @echo " make deploy-app [ENV=staging] - Builds and deploys the main web app (default: dev)." + @echo " make admin-deploy-dev - Builds and deploys the admin web app to the DEV environment (App Engine)." + @echo " make admin-deploy-staging - Builds and deploys the admin web app to the STAGING environment (App Engine)." + @echo "" + @echo " --- CLOUD IAP (for Cloud Run Launchpad) ---" + @echo " make configure-iap-launchpad - Adds users from iap-users.txt as IAP-secured Web App Users and grants IAP Service Account Cloud Run Invoker role." + @echo " make list-iap-users - Lists all users with IAP-secured Web App User role." + @echo " make remove-iap-user USER=user:email@example.com - Removes the IAP-secured Web App User role from a user." + @echo "" + @echo " --- PROJECT MANAGEMENT & TOOLS ---" + @echo " make setup-labels - Creates/updates GitHub labels from labels.yml." + @echo " make export-issues - Exports SR&ED-eligible issues to a markdown file." + @echo " make create-issues-from-file - Bulk creates GitHub issues from a markdown file." + @echo " make install-git-hooks - Installs git pre-push hook to protect main/dev branches." + @echo "" + @echo " --- BASE44 EXPORT WORKFLOW ---" + @echo " make integrate-export - Integrates a new Base44 export from '../krow-workforce-export-latest'." + @echo " make prepare-export - Prepares a fresh Base44 export for local use." + @echo "" + @echo " make help - Shows this help message." + @echo "--------------------------------------------------" + +# --- Core Development --- install: @echo "--> Installing web frontend dependencies..." @cd frontend-web && npm install -# Starts the local development server. dev: @echo "--> Ensuring web frontend dependencies are installed..." @cd frontend-web && npm install @echo "--> Starting web frontend development server on http://localhost:5173 ..." @cd frontend-web && npm run dev -# Builds the application for production. build: @echo "--> Building web frontend for production..." @cd frontend-web && VITE_APP_ENV=$(ENV) npm run build -# Integrates a new Base44 export into the current project. -# It replaces the src directory and the index.html file in the frontend-web directory. -# Prerequisite: The new export must be in a folder named '../krow-workforce-export-latest'. +# --- Deployment --- +deploy-launchpad: + @echo "--> Building and deploying Internal Launchpad to Cloud Run..." + @echo " - Step 1: Building container image..." + @cd firebase/internal-launchpad && gcloud builds submit \ + --tag $(CR_LAUNCHPAD_IMAGE_URI) \ + --project=$(GCP_DEV_PROJECT_ID) + @echo " - Step 2: Deploying to Cloud Run..." + @gcloud run deploy $(CR_LAUNCHPAD_SERVICE_NAME) \ + --image $(CR_LAUNCHPAD_IMAGE_URI) \ + --platform managed \ + --region $(CR_LAUNCHPAD_REGION) \ + --no-allow-unauthenticated \ + --project=$(GCP_DEV_PROJECT_ID) + @echo " - Step 3: Enabling IAP on the service..." + @gcloud beta run services update $(CR_LAUNCHPAD_SERVICE_NAME) \ + --region=$(CR_LAUNCHPAD_REGION) \ + --project=$(GCP_DEV_PROJECT_ID) \ + --iap + @echo "--> ✅ Deployment to Cloud Run successful." + +deploy-launchpad-full: deploy-launchpad configure-iap-launchpad + @echo "✅ Launchpad deployed and IAP configured successfully!" + +deploy-app: build + @echo "--> Deploying Frontend Web App to [$(ENV)] environment..." + @firebase deploy --only hosting:$(HOSTING_TARGET) --project=$(FIREBASE_ALIAS) + +# --- Admin Console --- +admin-install: + @echo "--> Installing admin console dependencies..." + @cd admin-web && npm install + +admin-dev: + @echo "--> Starting admin console development server on http://localhost:5174 ..." + @cd admin-web && npm run dev -- --port 5174 + +admin-build: + @echo "--> Building admin console for production..." + @cd admin-web && VITE_APP_ENV=$(ENV) npm run build + +admin-deploy-dev: admin-build + @echo "--> Deploying Admin Web App to DEV environment (App Engine)..." + @cd admin-web && gcloud app deploy app.dev.yaml --project=$(GCP_DEV_PROJECT_ID) + +admin-deploy-staging: admin-build + @echo "--> Deploying Admin Web App to STAGING environment (App Engine)..." + @cd admin-web && gcloud app deploy app.staging.yaml --project=$(GCP_STAGING_PROJECT_ID) + +# --- Cloud IAP Configuration --- +# NOTE: These commands use the 'gcloud beta iap web' command set, which is the correct +# method for managing user access to an IAP-protected Cloud Run service. +configure-iap-launchpad: + @echo "--> Configuring IAP users for Cloud Run service [$(CR_LAUNCHPAD_SERVICE_NAME)]..." + @echo " - Granting Cloud Run Invoker role to IAP Service Account..." + @gcloud run services add-iam-policy-binding $(CR_LAUNCHPAD_SERVICE_NAME) \ + --region=$(CR_LAUNCHPAD_REGION) \ + --project=$(GCP_DEV_PROJECT_ID) \ + --member="serviceAccount:$(IAP_SERVICE_ACCOUNT)" \ + --role='roles/run.invoker' \ + --quiet + @echo " - Adding users from iap-users.txt..." + @cd firebase/internal-launchpad && \ + grep -v '^#' iap-users.txt | grep -v '^$$' | while read -r member; do \ + echo " Adding $$member as IAP-secured Web App User..."; \ + gcloud beta iap web add-iam-policy-binding \ + --project=$(GCP_DEV_PROJECT_ID) \ + --resource-type=cloud-run \ + --service=$(CR_LAUNCHPAD_SERVICE_NAME) \ + --region=$(CR_LAUNCHPAD_REGION) \ + --member="$$member" \ + --role='roles/iap.httpsResourceAccessor' \ + --quiet; \ + done + @echo "✅ IAP user configuration complete." + +list-iap-users: + @echo "--> Current IAP users for Cloud Run service [$(CR_LAUNCHPAD_SERVICE_NAME)]:" + @gcloud beta iap web get-iam-policy \ + --project=$(GCP_DEV_PROJECT_ID) \ + --resource-type=cloud-run \ + --service=$(CR_LAUNCHPAD_SERVICE_NAME) \ + --region=$(CR_LAUNCHPAD_REGION) + +remove-iap-user: + @if [ -z "$(USER)" ]; then \ + echo "❌ Error: Please specify USER=user:email@example.com"; \ + exit 1; \ + fi + @echo "--> Removing IAP access for $(USER) from Cloud Run service [$(CR_LAUNCHPAD_SERVICE_NAME)]..." + @gcloud beta iap web remove-iam-policy-binding \ + --project=$(GCP_DEV_PROJECT_ID) \ + --resource-type=cloud-run \ + --service=$(CR_LAUNCHPAD_SERVICE_NAME) \ + --region=$(CR_LAUNCHPAD_REGION) \ + --member="$(USER)" \ + --role='roles/iap.httpsResourceAccessor' \ + --quiet + @echo "✅ User removed from IAP." + +disable-iap-launchpad: + @echo "--> Disabling IAP and making Cloud Run service [$(CR_LAUNCHPAD_SERVICE_NAME)] public..." + @gcloud beta run services update $(CR_LAUNCHPAD_SERVICE_NAME) \ + --region=$(CR_LAUNCHPAD_REGION) \ + --project=$(GCP_DEV_PROJECT_ID) \ + --no-iap --quiet + @gcloud run services add-iam-policy-binding $(CR_LAUNCHPAD_SERVICE_NAME) \ + --region=$(CR_LAUNCHPAD_REGION) \ + --project=$(GCP_DEV_PROJECT_ID) \ + --member="allUsers" \ + --role='roles/run.invoker' \ + --quiet + @echo "✅ IAP disabled. The service is now public." + +# --- Project Management --- +setup-labels: + @echo "--> Setting up GitHub labels..." + @./scripts/setup-github-labels.sh + +export-issues: + @echo "--> Exporting GitHub issues to documentation..." + @./scripts/export_issues.sh + +create-issues-from-file: + @echo "--> Creating GitHub issues from file..." + @./scripts/create_issues.py + +# --- Development Tools --- +install-git-hooks: + @echo "--> Installing Git hooks..." + @ln -sf ../../scripts/git-hooks/pre-push .git/hooks/pre-push + @echo "✅ pre-push hook installed successfully. Direct pushes to 'main' and 'dev' are now blocked." + +# --- Base44 Export Workflow --- integrate-export: @echo "--> Integrating new Base44 export into frontend-web/..." @if [ ! -d "../krow-workforce-export-latest" ]; then \ @@ -69,69 +240,7 @@ integrate-export: @node scripts/patch-index-html.js @echo "--> Integration complete. Next step: 'make prepare-export'." -# Applies all necessary patches to a fresh Base44 export to run it locally. -# This is the main command for the hybrid workflow. prepare-export: @echo "--> Preparing fresh Base44 export for local development..." @node scripts/prepare-export.js @echo "--> Preparation complete. You can now run 'make dev'." - -# --- Firebase Deployment --- -deploy-launchpad: - @echo "--> Deploying Internal Launchpad to DEV project..." - @firebase deploy --only hosting:launchpad --project=dev - -deploy-app: build - @echo "--> Deploying Frontend Web App to [$(ENV)] environment..." - @firebase deploy --only hosting:$(HOSTING_TARGET) --project=$(FIREBASE_ALIAS) - -# Shows this help message. -help: - @echo "--------------------------------------------------" - @echo " KROW Workforce - Available Makefile Commands" - @echo "--------------------------------------------------" - @echo " make install - Installs web frontend dependencies." - @echo " make dev - Starts the local web frontend server." - @echo " make build - Builds the web frontend for production." - @echo " make integrate-export - Integrates a new Base44 export from '../krow-workforce-export-latest'." - @echo " make prepare-export - Prepares a fresh Base44 export for local use." - @echo "" - @echo " --- DEPLOYMENT ---" - @echo " make deploy-launchpad - Deploys the internal launchpad (always to dev)." - @echo " make deploy-app [ENV=staging] - Builds and deploys the main web app (default: dev)." - @echo "" - @echo " make help - Shows this help message." - @echo "--------------------------------------------------" - -# --- Project Management --- -setup-labels: - @echo "--> Setting up GitHub labels..." - @./scripts/setup-github-labels.sh - -export-issues: - @echo "--> Exporting GitHub issues to documentation..." - @./scripts/export_issues.sh - -create-issues-from-file: - @echo "--> Creating GitHub issues from file..." - @./scripts/create_issues.py - -# --- Development Tools --- -install-git-hooks: - @echo "--> Installing Git hooks..." - @ln -sf ../../scripts/git-hooks/pre-push .git/hooks/pre-push - @echo "✅ pre-push hook installed successfully. Direct pushes to 'main' and 'dev' are now blocked." - - -# --- Admin Console --- -admin-install: - @echo "--> Installing admin console dependencies..." - @cd admin-web && npm install - -admin-dev: - @echo "--> Starting admin console development server on http://localhost:5174 ..." - @cd admin-web && npm run dev -- --port 5174 - -admin-build: - @echo "--> Building admin console for production..." - @cd admin-web && VITE_APP_ENV=$(ENV) npm run build diff --git a/admin-web/.gcloudignore b/admin-web/.gcloudignore new file mode 100644 index 00000000..4d7d6938 --- /dev/null +++ b/admin-web/.gcloudignore @@ -0,0 +1,17 @@ +# This file specifies files that are *not* uploaded to Google Cloud +# using gcloud. It follows the same syntax as .gitignore, with the addition of +# "#!include" directives (which insert the entries of the given .gitignore-style +# file at that point). +# +# For more information, run: +# $ gcloud topic gcloudignore +# +.gcloudignore +# If you would like to upload your .git directory, .gitignore file or files +# from your .gitignore file, remove the corresponding line +# below: +.git +.gitignore + +# Node.js dependencies: +node_modules/ \ No newline at end of file diff --git a/admin-web/app.dev.yaml b/admin-web/app.dev.yaml new file mode 100644 index 00000000..93daa5fb --- /dev/null +++ b/admin-web/app.dev.yaml @@ -0,0 +1,13 @@ +runtime: nodejs20 +service: admin-web-dev + +handlers: + # Servir les fichiers statiques (js, css, images, etc.) + - url: /(.*\.(js|css|svg|png|jpg|ico|txt))$ + static_files: dist/\1 + upload: dist/.*\.(js|css|svg|png|jpg|ico|txt)$ + + # Servir l'index.html pour toutes les autres routes (pour le routing côté client de React) + - url: /.* + static_files: dist/index.html + upload: dist/index.html \ No newline at end of file diff --git a/admin-web/app.staging.yaml b/admin-web/app.staging.yaml new file mode 100644 index 00000000..968c8c89 --- /dev/null +++ b/admin-web/app.staging.yaml @@ -0,0 +1,13 @@ +runtime: nodejs20 +service: admin-web-staging + +handlers: + # Servir les fichiers statiques (js, css, images, etc.) + - url: /(.*\.(js|css|svg|png|jpg|ico|txt))$ + static_files: dist/\1 + upload: dist/.*\.(js|css|svg|png|jpg|ico|txt)$ + + # Servir l'index.html pour toutes les autres routes (pour le routing côté client de React) + - url: /.* + static_files: dist/index.html + upload: dist/index.html \ No newline at end of file diff --git a/create-missing-repository.sh b/create-missing-repository.sh new file mode 100755 index 00000000..5aa775f6 --- /dev/null +++ b/create-missing-repository.sh @@ -0,0 +1,57 @@ +#!/bin/bash +set -e + +PROJECT_ID="krow-workforce-dev" +PROJECT_NUMBER="933560802882" + +echo "============================================" +echo "Création du repository us.gcr.io manquant" +echo "============================================" +echo "" + +# 1. Créer le repository us.gcr.io qui n'existe pas +echo "1. Création du repository us.gcr.io dans Artifact Registry..." +gcloud artifacts repositories create us.gcr.io \ + --repository-format=docker \ + --location=us \ + --description="GCR compatibility layer for App Engine" \ + --project=${PROJECT_ID} || echo "Repository existe déjà" + +echo "" +echo "2. Configuration des permissions sur us.gcr.io..." + +# Permissions pour Cloud Build Service Account +gcloud artifacts repositories add-iam-policy-binding us.gcr.io \ + --location=us \ + --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \ + --role="roles/artifactregistry.reader" \ + --project=${PROJECT_ID} + +gcloud artifacts repositories add-iam-policy-binding us.gcr.io \ + --location=us \ + --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \ + --role="roles/artifactregistry.writer" \ + --project=${PROJECT_ID} + +# Permissions pour App Engine Service Account +gcloud artifacts repositories add-iam-policy-binding us.gcr.io \ + --location=us \ + --member="serviceAccount:${PROJECT_ID}@appspot.gserviceaccount.com" \ + --role="roles/artifactregistry.reader" \ + --project=${PROJECT_ID} + +echo "" +echo "3. Vérification des repositories existants..." +gcloud artifacts repositories list \ + --location=us \ + --project=${PROJECT_ID} \ + --format="table(name,format,location)" + +echo "" +echo "============================================" +echo "✅ Repository us.gcr.io créé et configuré!" +echo "============================================" +echo "" +echo "Attends 1-2 minutes puis relance:" +echo " make deploy-launchpad" +echo "" \ No newline at end of file diff --git a/firebase/internal-launchpad/.gcloudignore b/firebase/internal-launchpad/.gcloudignore new file mode 100644 index 00000000..28b93809 --- /dev/null +++ b/firebase/internal-launchpad/.gcloudignore @@ -0,0 +1,18 @@ +# This file specifies files that are *not* uploaded to Google Cloud +# using gcloud. It follows the same syntax as .gitignore, with the addition of +# "#!include" directives (which insert the entries of the given .gitignore-style +# file at that point). +# +# For more information, run: +# $ gcloud topic gcloudignore +# +.gcloudignore +# If you would like to upload your .git directory, .gitignore file or files +# from your .gitignore file, remove the corresponding line +# below: +.git +.gitignore + +# Node.js dependencies: +node_modules/ +*.log \ No newline at end of file diff --git a/firebase/internal-launchpad/Dockerfile b/firebase/internal-launchpad/Dockerfile new file mode 100644 index 00000000..84bf1d83 --- /dev/null +++ b/firebase/internal-launchpad/Dockerfile @@ -0,0 +1,28 @@ +# Utiliser nginx pour servir les fichiers statiques +FROM nginx:alpine + +# Copier les fichiers statiques +COPY index.html /usr/share/nginx/html/ +COPY assets /usr/share/nginx/html/assets/ +COPY favicon.svg /usr/share/nginx/html/ +COPY logo.svg /usr/share/nginx/html/ + +# Configuration nginx pour le routing SPA +RUN echo 'server { \ + listen 8080; \ + server_name _; \ + root /usr/share/nginx/html; \ + index index.html; \ + location / { \ + try_files $uri $uri/ /index.html; \ + } \ + # Headers de sécurité \ + add_header X-Frame-Options "SAMEORIGIN" always; \ + add_header X-Content-Type-Options "nosniff" always; \ + add_header X-XSS-Protection "1; mode=block" always; \ +}' > /etc/nginx/conf.d/default.conf + +# Nginx écoute sur le port 8080 (requis par Cloud Run) +EXPOSE 8080 + +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/firebase/internal-launchpad/app.yaml b/firebase/internal-launchpad/app.yaml new file mode 100644 index 00000000..2b635400 --- /dev/null +++ b/firebase/internal-launchpad/app.yaml @@ -0,0 +1,30 @@ +runtime: python312 +service: default +instance_class: F1 + +handlers: + - url: / + static_files: index.html + upload: index.html + secure: always + + - url: /(.*\.(ico|png|jpg|jpeg|gif|svg|webp|woff|woff2|ttf|eot|json)) + static_files: \1 + upload: (.*\.(ico|png|jpg|jpeg|gif|svg|webp|woff|woff2|ttf|eot|json)) + secure: always + expiration: "1d" + + - url: /(.*\.(js|css|map)) + static_files: \1 + upload: (.*\.(js|css|map)) + secure: always + expiration: "1d" + + - url: /(.*) + static_files: \1 + upload: (.*) + secure: always + +automatic_scaling: + max_instances: 5 + min_instances: 0 \ No newline at end of file diff --git a/firebase/internal-launchpad/iap-users.txt b/firebase/internal-launchpad/iap-users.txt new file mode 100644 index 00000000..50a03b87 --- /dev/null +++ b/firebase/internal-launchpad/iap-users.txt @@ -0,0 +1,6 @@ +# Liste des utilisateurs autorisés pour le Internal Launchpad +# Format: un email par ligne, les lignes commençant par # sont des commentaires + +user:boris@oloodi.com +# user:temporaire@oloodi.com # Décommenté pour donner accès temporaire +user:admin@krowwithus.com \ No newline at end of file diff --git a/fix-appengine-permissions-complete.sh b/fix-appengine-permissions-complete.sh new file mode 100755 index 00000000..71f5b215 --- /dev/null +++ b/fix-appengine-permissions-complete.sh @@ -0,0 +1,83 @@ +#!/bin/bash +set -e + +PROJECT_ID="krow-workforce-dev" +PROJECT_NUMBER="933560802882" +REGION="us-central1" + +echo "============================================" +echo "Fix App Engine + Artifact Registry Issue" +echo "============================================" +echo "" + +# Le problème: App Engine essaie d'accéder à us.gcr.io qui pointe maintenant vers Artifact Registry +# Mais les permissions ne sont pas correctement configurées sur TOUS les repositories nécessaires + +echo "1. Activation de l'API Container Registry (legacy GCR)..." +gcloud services enable containerregistry.googleapis.com --project=${PROJECT_ID} + +echo "" +echo "2. Configuration des permissions sur TOUS les repositories Artifact Registry..." + +# Liste de tous les repositories potentiels +REPOS=("gcr.io" "app-engine-tmp" "gae-standard") + +for REPO in "${REPOS[@]}"; do + echo "" + echo " → Repository: ${REPO}" + + # Vérifier si le repo existe + if gcloud artifacts repositories describe ${REPO} --location=us --project=${PROJECT_ID} &>/dev/null; then + echo " ✓ Repository existe" + + # Ajouter les permissions pour Cloud Build SA + gcloud artifacts repositories add-iam-policy-binding ${REPO} \ + --location=us \ + --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \ + --role="roles/artifactregistry.reader" \ + --project=${PROJECT_ID} --quiet 2>/dev/null || true + + gcloud artifacts repositories add-iam-policy-binding ${REPO} \ + --location=us \ + --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \ + --role="roles/artifactregistry.writer" \ + --project=${PROJECT_ID} --quiet 2>/dev/null || true + + # Ajouter les permissions pour App Engine SA + gcloud artifacts repositories add-iam-policy-binding ${REPO} \ + --location=us \ + --member="serviceAccount:${PROJECT_ID}@appspot.gserviceaccount.com" \ + --role="roles/artifactregistry.reader" \ + --project=${PROJECT_ID} --quiet 2>/dev/null || true + + echo " ✓ Permissions configurées" + else + echo " ⚠ Repository n'existe pas (normal)" + fi +done + +echo "" +echo "3. Permissions Storage (pour les artefacts de build)..." +gcloud projects add-iam-policy-binding ${PROJECT_ID} \ + --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \ + --role="roles/storage.objectAdmin" \ + --condition=None --quiet + +echo "" +echo "4. Permissions Cloud Build spécifiques..." +gcloud projects add-iam-policy-binding ${PROJECT_ID} \ + --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \ + --role="roles/cloudbuild.builds.builder" \ + --condition=None --quiet + +echo "" +echo "============================================" +echo "✅ Configuration terminée!" +echo "============================================" +echo "" +echo "⏱ IMPORTANT: Attends 2-3 minutes pour la propagation des permissions IAM" +echo "" +echo "Puis lance:" +echo " cd firebase/internal-launchpad" +echo " gcloud app deploy app.yaml --project=${PROJECT_ID} --no-cache" +echo "" \ No newline at end of file diff --git a/fix-project-level-permissions.sh b/fix-project-level-permissions.sh new file mode 100755 index 00000000..9be1b940 --- /dev/null +++ b/fix-project-level-permissions.sh @@ -0,0 +1,39 @@ +#!/bin/bash +set -e + +PROJECT_ID="krow-workforce-dev" +PROJECT_NUMBER="933560802882" + +echo "============================================" +echo "Permissions au niveau du PROJET" +echo "============================================" +echo "" + +echo "1. Artifact Registry Admin pour Cloud Build au niveau projet..." +gcloud projects add-iam-policy-binding ${PROJECT_ID} \ + --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \ + --role="roles/artifactregistry.admin" \ + --condition=None + +echo "" +echo "2. Storage Admin pour Cloud Build (pour staging bucket)..." +gcloud projects add-iam-policy-binding ${PROJECT_ID} \ + --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \ + --role="roles/storage.admin" \ + --condition=None + +echo "" +echo "3. Vérification des rôles du service account Cloud Build:" +gcloud projects get-iam-policy ${PROJECT_ID} \ + --flatten="bindings[].members" \ + --filter="bindings.members:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \ + --format="table(bindings.role)" + +echo "" +echo "============================================" +echo "✅ Permissions au niveau projet configurées!" +echo "============================================" +echo "" +echo "Ces permissions sont plus larges mais devraient résoudre le problème." +echo "Attends 2-3 minutes puis relance: make deploy-launchpad" +echo "" \ No newline at end of file diff --git a/issues-to-create.md b/issues-to-create.md index 1ac6bd86..831fdc83 100644 --- a/issues-to-create.md +++ b/issues-to-create.md @@ -1,110 +1,36 @@ - -# [Admin] Scaffold the Admin Console Web Application - -Labels: infra, platform:admin, priority:high, sred-eligible +# Refactor: Clean up legacy App Engine deployment artifacts +Labels: refactor, infra Milestone: Phase 1: Foundation & Dev Environment Setup -### 🎯 Objective +## Context +Now that the `internal-launchpad` service has been successfully migrated from App Engine to Cloud Run, a number of scripts, configurations, and files related to the old App Engine deployment method are obsolete. -Create the foundational scaffolding for a new, separate React application within the monorepo, which will serve as the Admin Console. This console will be the central hub for managing users, roles, and other system-level configurations. +This technical debt should be removed to clean up the repository, reduce confusion for developers, and prevent the accidental use of outdated deployment logic. -### 🔬 SR&ED Justification +## Action Items -- **Technological Uncertainty:** What is the most efficient way to set up a second, independent React application within our existing Vite-based monorepo, ensuring that dependency management, build processes, and environment variable configurations are isolated from the main `frontend-web` app but still manageable from the root `Makefile`? -- **Systematic Investigation:** We will conduct experimental development to create a new `admin-web/` directory. We will test different Vite configurations to ensure it can run concurrently with the main app. We will also experiment with `npm` workspaces or similar solutions to optimize shared, non-UI dependencies and update the root `Makefile` to orchestrate this new application. +1. **Delete Obsolete Permission Scripts:** + The following shell scripts were created to fix permission issues specific to the App Engine deployment and are no longer needed. They should be deleted: + - `fix-appengine-permissions-complete.sh` + - `fix-project-level-permissions.sh` + - `create-missing-repository.sh` -### Details +2. **Remove App Engine Configuration:** + The `app.yaml` file within the `firebase/internal-launchpad/` directory was specific to the App Engine runtime and is now ignored. It should be deleted. -This is the first step in building our critical user management interface. The Admin Console will be a completely new application, not sharing any logic with the `krowSDK` facade. It will communicate directly with Firebase services. +3. **Update Firebase Hosting Configuration:** + The `firebase.json` file still contains a hosting target named `launchpad` that points to the `firebase/internal-launchpad` directory. This configuration is no longer used since the launchpad is served by Cloud Run. + - Remove the `launchpad` target object from the `hosting` array in `firebase.json`. -### ✅ Acceptance Criteria - -- [ ] A new directory `admin-web/` is created at the project root. -- [ ] A new React + Vite application is correctly scaffolded inside `admin-web/`. -- [ ] The new admin application can be started independently using `npm run dev` from within its directory. -- [ ] Firebase SDK is installed and a basic `firebase/config.js` is created for the admin app, using its own set of environment variables (`VITE_ADMIN_FIREBASE_*`). -- [ ] A basic, protected "AdminLayout" component is created that checks if a logged-in user has the `admin` role before rendering content. -- [ ] The root `Makefile` is updated with new targets (`make admin-install`, `make admin-dev`) to manage this new application. +## Acceptance Criteria +- The three shell scripts listed above are deleted from the repository. +- The `firebase/internal-launchpad/app.yaml` file is deleted. +- The `firebase.json` file no longer contains the `launchpad` hosting configuration. +- The `make deploy-launchpad-full` command remains the sole method for deploying the launchpad. --- +## SR&ED Justification -# [Admin] Implement User Listing and Role Management +**Eligibility:** Not Eligible. -Labels: feature, platform:admin, priority:high, sred-eligible -Milestone: Phase 1: Foundation & Dev Environment Setup - -### 🎯 Objective - -Develop the core user management interface in the Admin Console, allowing administrators to view a list of all system users and modify their roles. - -### 🔬 SR&ED Justification - -- **Technological Uncertainty:** What is the most performant and secure way to query and display a potentially large list of users from Firestore in a React application, implementing real-time updates and secure role modification? Key uncertainties involve structuring Firestore security rules to ensure only admins can read the full user list and write role changes, and designing a robust UI component that prevents accidental, irreversible role changes. -- **Systematic Investigation:** We will experiment with different Firestore queries (`onSnapshot` for real-time vs. `getDocs` for static lists) to evaluate performance. We will prototype and test Firestore security rules extensively. We will also build a reusable and secure "RoleSelector" component that includes confirmation dialogs to prevent unintended administrative actions. - -### Details - -This feature is the primary function of the Admin Console MVP. It directly enables the unblocking of other teams by allowing admins to assign the correct roles (`client`, `vendor`, etc.) to newly registered users. - -### ✅ Acceptance Criteria - -- [ ] A new "Users" page is created in the Admin Console. -- [ ] The page fetches and displays a list of all users from the `users` collection in Firestore. -- [ ] The user list is displayed in a table showing at least `full_name`, `email`, and `user_role`. -- [ ] An administrator can change a user's `user_role` using a dropdown or select menu in the table. -- [ ] The role change is immediately reflected in Firestore upon selection. -- [ ] A confirmation modal is shown before the role change is finalized to prevent errors. -- [ ] Firestore security rules are implemented to ensure only authenticated users with `user_role: 'admin'` can read the `users` collection and write to the `user_role` field. - ---- - -# [Backend] Implement User Invitation Cloud Function - -Labels: feature, platform:backend, priority:high, sred-eligible -Milestone: Phase 1: Foundation & Dev Environment Setup - -### 🎯 Objective - -Create a secure, HTTP-triggered Cloud Function that handles the business logic for inviting new users to the platform. - -### 🔬 SR&ED Justification - -- **Technological Uncertainty:** What is the most secure and reliable method to generate a unique, single-use invitation token, associate it with an email and a specific role, and then securely verify this token upon user registration? The investigation will involve designing a data model for pending invitations in Firestore, implementing a secure token generation strategy (e.g., UUIDs), and creating a Cloud Function with robust input validation and IAM permissions to prevent unauthorized use. -- **Systematic Investigation:** We will experiment with different data structures in Firestore for storing pending invites (e.g., a top-level `invites` collection). We will develop and test an HTTP Cloud Function, ensuring it is protected by IAM and properly validates incoming requests (checking for admin privileges). We will also integrate an email sending service (e.g., SendGrid or Firebase's own Trigger Email extension) to dispatch the invitation emails. - -### Details - -This backend function is the engine of our invitation system. It will be called by the Admin Console and will be responsible for creating a pending invitation and sending an email to the invitee. - -### ✅ Acceptance Criteria - -- [ ] A new HTTP-triggered Cloud Function, `inviteUser`, is created in the `firebase/functions` directory. -- [ ] The function is protected and can only be called by authenticated users with an `admin` custom claim. -- [ ] The function accepts a request body containing `email` and `user_role`. -- [ ] Upon being called, it creates a new document in a `invites` collection in Firestore. -- [ ] The new document contains the invitee's email, the assigned role, a uniquely generated token, and an expiration date. -- [ ] The function sends an email to the provided address containing a unique registration link (e.g., `https://krow-workforce.com/register?invite_token=...`). - ---- - -# [Admin] Implement User Invitation UI - -Labels: feature, platform:admin, priority:high -Milestone: Phase 1: Foundation & Dev Environment Setup - -### 🎯 Objective - -Build the user interface within the Admin Console that allows administrators to invite new users to the platform. - -### Details - -This UI will serve as the frontend for the `inviteUser` Cloud Function. It provides a simple and intuitive way for admins to add new users without needing manual database intervention. - -### ✅ Acceptance Criteria - -- [ ] A new "Invite User" button or section is added to the "Users" page in the Admin Console. -- [ ] Clicking the button opens a modal or form with two fields: `Email Address` and `User Role`. -- [ ] The "User Role" field is a dropdown populated with the available roles (e.g., `client`, `vendor`, `procurement`). -- [ ] Submitting the form calls the `inviteUser` Cloud Function with the provided data. -- [ ] The UI provides clear feedback to the administrator on success or failure of the invitation. -- [ ] (Optional) A new section or page is created to display a list of pending invitations from the `invites` collection in Firestore. +**Reasoning:** This task involves code and configuration cleanup, which is considered standard software engineering practice and maintenance. It does not involve systematic investigation to resolve a technological uncertainty, nor does it lead to a technological advancement. The outcome is an improved and cleaner codebase, not new technological knowledge. \ No newline at end of file diff --git a/memo-iap.txt b/memo-iap.txt new file mode 100644 index 00000000..c1dd8f5d --- /dev/null +++ b/memo-iap.txt @@ -0,0 +1,132 @@ +# =================================================================== +# MEMO : Sécurisation d'un service Cloud Run avec IAP (Identity-Aware Proxy) +# =================================================================== +# +# Objectif : Ce document résume les étapes et les permissions nécessaires +# pour sécuriser un service Cloud Run avec IAP, en se basant sur +# l'expérience acquise avec le service 'internal-launchpad'. +# +# Date : 2025-11-16 + +# --- +# 1. PRINCIPE FONDAMENTAL ET CONTRAINTE MAJEURE +# --- +# +# L'enseignement le plus important est le suivant : +# +# > L'activation d'IAP directement sur un service Cloud Run (sans Load Balancer) +# > restreint l'accès aux utilisateurs qui font partie de la MÊME organisation +# > Google Workspace que le projet Google Cloud. +# +# Dans notre cas, le projet 'krow-workforce-dev' appartient à l'organisation +# 'krowwithus.com'. C'est pourquoi : +# - 'admin@krowwithus.com' A PU ACCÉDER au service. +# - 'boris@oloodi.com' N'A PAS PU ACCÉDER au service, même avec les bonnes permissions. +# +# Solution adoptée : Créer des comptes utilisateurs via Google Workspace ou +# Cloud Identity (qui est gratuit) dans le domaine 'krowwithus.com' pour +# tous les développeurs et administrateurs qui ont besoin d'accéder aux +# services internes. + +# --- +# 2. PRÉREQUIS INDISPENSABLES +# --- +# +# Avant de commencer, les éléments suivants doivent être configurés pour le projet : +# +# a) APIs Google Cloud activées : +# - Cloud Run API (`run.googleapis.com`) +# - Identity-Aware Proxy API (`iap.googleapis.com`) +# - Cloud Build API (`cloudbuild.googleapis.com`) +# - Artifact Registry API (`artifactregistry.googleapis.com`) +# +# Commande pour les activer : +# gcloud services enable run.googleapis.com iap.googleapis.com cloudbuild.googleapis.com artifactregistry.googleapis.com --project=krow-workforce-dev +# +# b) Écran de consentement OAuth configuré : +# - IAP utilise OAuth2 pour identifier les utilisateurs. L'écran de consentement est obligatoire. +# - URL : https://console.cloud.google.com/apis/credentials/consent?project=krow-workforce-dev +# - Type d'utilisateur : "Interne" est préférable si disponible. +# - Nom de l'application : Utiliser un nom général (ex: "Krow Workforce Platform"). +# +# c) Compte de service IAP existant : +# - Parfois, ce compte n'est pas créé automatiquement. Il est plus sûr de forcer sa création. +# +# Commande pour le créer : +# gcloud beta services identity create --service=iap.googleapis.com --project=krow-workforce-dev + +# --- +# 3. LOGIQUE DE CONFIGURATION EN 3 ÉTAPES (AUTOMATISÉE DANS LE MAKEFILE) +# --- +# +# Le processus complet pour sécuriser un service se déroule en 3 étapes séquentielles. +# +# ÉTAPE A : DÉPLOIEMENT SÉCURISÉ DU SERVICE +# ----------------------------------------- +# Le service doit être déployé en mode privé, puis IAP doit être activé dessus. +# +# 1. Déployer le service avec l'accès public désactivé : +# gcloud run deploy --image --no-allow-unauthenticated ... +# +# 2. Activer IAP sur le service : +# gcloud beta run services update --iap ... +# +# +# ÉTAPE B : AUTORISER IAP À APPELER LE SERVICE +# ------------------------------------------- +# Une fois IAP activé, le proxy IAP a besoin de la permission d'invoquer (d'appeler) +# votre service Cloud Run. Cette permission est accordée au compte de service IAP. +# +# Commande : +# gcloud run services add-iam-policy-binding \ +# --member="serviceAccount:service-@gcp-sa-iap.iam.gserviceaccount.com" \ +# --role="roles/run.invoker" +# +# +# ÉTAPE C : AUTORISER LES UTILISATEURS À PASSER LE PROXY IAP +# ------------------------------------------------------- +# C'est ici qu'on ajoute les utilisateurs finaux (qui doivent être dans l'organisation). +# Cette commande est spécifique à IAP pour Cloud Run. +# +# Commande : +# gcloud beta iap web add-iam-policy-binding \ +# --resource-type=cloud-run \ +# --service= \ +# --member="user:email@krowwithus.com" \ +# --role="roles/iap.httpsResourceAccessor" + +# --- +# 4. AUTOMATISATION VIA LE MAKEFILE +# --- +# +# Tout ce processus est géré par la commande `make deploy-launchpad-full`. +# +# a) `deploy-launchpad` s'occupe de l'ÉTAPE A. +# +# b) `configure-iap-launchpad` s'occupe des ÉTAPES B et C. +# +# - Il lit les utilisateurs depuis `firebase/internal-launchpad/iap-users.txt`. +# - Il exécute la commande de l'ÉTAPE B pour le compte de service IAP. +# - Il exécute la commande de l'ÉTAPE C pour chaque utilisateur du fichier. +# +# Pour adapter cela à 'admin-web', il faudra : +# 1. Créer des variables similaires à `CR_LAUNCHPAD_...` pour `admin-web`. +# 2. Créer de nouvelles cibles `deploy-admin-web-full`, `configure-iap-admin-web`, etc., +# en copiant la logique de celles du launchpad et en adaptant les noms de service. + +# --- +# 5. VÉRIFICATION ET DÉPANNAGE +# --- +# +# a) Lister les utilisateurs autorisés : +# La commande `make list-iap-users` est le meilleur moyen de vérifier qui a accès. +# +# b) Erreur "You don't have access" : +# - Cause 1 : L'utilisateur n'est pas dans la bonne organisation (le plus probable). +# - Cause 2 : L'utilisateur n'est pas dans le fichier `iap-users.txt` et `make configure-iap-launchpad` n'a pas été lancé. +# - Cause 3 : Problème de cache de navigateur. Toujours tester dans une nouvelle fenêtre de navigation privée. +# - Cause 4 : Délai de propagation des permissions IAM (attendre 2-3 minutes). +# +# c) Erreur "Forbidden" : +# - Cela signifie que l'authentification a réussi mais que la permission d'invoquer le service est manquante. +# - La cause la plus probable est que l'ÉTAPE B (donner la permission au compte de service IAP) a échoué ou a été oubliée.