From 11e4a4d60afad909ff0c34076c7b8f58b383f23e Mon Sep 17 00:00:00 2001 From: Yvv Date: Mon, 2 Mar 2026 03:09:40 +0100 Subject: [PATCH] Dev mode: panneau connexion rapide avec 4 profils pre-configures Ajout d'un panneau dev sous le login (Alice=membre, Bob=forgeron, Charlie=comite tech, Dave=observateur) pour tester les differents roles sans keypair Ed25519. Endpoint GET /auth/dev/profiles renvoie les profils uniquement en ENVIRONMENT=development. Co-Authored-By: Claude Opus 4.6 --- backend/app/routers/auth.py | 47 +++++++- backend/app/services/auth_service.py | 29 ++++- frontend/app/pages/login.vue | 166 +++++++++++++++++++++++++++ 3 files changed, 237 insertions(+), 5 deletions(-) diff --git a/backend/app/routers/auth.py b/backend/app/routers/auth.py index 3549eec..dadd844 100644 --- a/backend/app/routers/auth.py +++ b/backend/app/routers/auth.py @@ -27,6 +27,38 @@ from app.services.auth_service import ( router = APIRouter() +# ── Dev profiles (only available when ENVIRONMENT == "development") ───────── +DEV_PROFILES = [ + { + "address": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "display_name": "Alice (Membre WoT)", + "wot_status": "member", + "is_smith": False, + "is_techcomm": False, + }, + { + "address": "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + "display_name": "Bob (Forgeron)", + "wot_status": "member", + "is_smith": True, + "is_techcomm": False, + }, + { + "address": "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmkP7j4bJa3zN7d8tY", + "display_name": "Charlie (Comite Tech)", + "wot_status": "member", + "is_smith": True, + "is_techcomm": True, + }, + { + "address": "5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy", + "display_name": "Dave (Observateur)", + "wot_status": "unknown", + "is_smith": False, + "is_techcomm": False, + }, +] + # ── In-memory challenge store (short-lived, no persistence needed) ────────── # Structure: { address: { "challenge": str, "expires_at": datetime } } _pending_challenges: dict[str, dict] = {} @@ -113,8 +145,11 @@ async def verify_challenge( # 5. Consume the challenge del _pending_challenges[payload.address] - # 6. Get or create identity - identity = await get_or_create_identity(db, payload.address) + # 6. Get or create identity (apply dev profile if available) + dev_profile = None + if settings.ENVIRONMENT == "development": + dev_profile = next((p for p in DEV_PROFILES if p["address"] == payload.address), None) + identity = await get_or_create_identity(db, payload.address, dev_profile=dev_profile) # 7. Create session token token = await create_session(db, identity) @@ -125,6 +160,14 @@ async def verify_challenge( ) +@router.get("/dev/profiles") +async def list_dev_profiles(): + """List available dev profiles for quick login. Only available in development.""" + if settings.ENVIRONMENT != "development": + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Not available") + return DEV_PROFILES + + @router.get("/me", response_model=IdentityOut) async def get_me( identity: DuniterIdentity = Depends(get_current_identity), diff --git a/backend/app/services/auth_service.py b/backend/app/services/auth_service.py index 1bbf36d..ae33162 100644 --- a/backend/app/services/auth_service.py +++ b/backend/app/services/auth_service.py @@ -82,15 +82,38 @@ async def get_current_identity( return identity -async def get_or_create_identity(db: AsyncSession, address: str) -> DuniterIdentity: - """Get an existing identity by address or create a new one.""" +async def get_or_create_identity( + db: AsyncSession, + address: str, + dev_profile: dict | None = None, +) -> DuniterIdentity: + """Get an existing identity by address or create a new one. + + If dev_profile is provided, apply the profile attributes on create or update. + """ result = await db.execute(select(DuniterIdentity).where(DuniterIdentity.address == address)) identity = result.scalar_one_or_none() if identity is None: - identity = DuniterIdentity(address=address) + kwargs: dict = {"address": address} + if dev_profile: + kwargs.update({ + "display_name": dev_profile.get("display_name"), + "wot_status": dev_profile.get("wot_status", "unknown"), + "is_smith": dev_profile.get("is_smith", False), + "is_techcomm": dev_profile.get("is_techcomm", False), + }) + identity = DuniterIdentity(**kwargs) db.add(identity) await db.commit() await db.refresh(identity) + elif dev_profile: + # Update existing identity with dev profile data + identity.display_name = dev_profile.get("display_name", identity.display_name) + identity.wot_status = dev_profile.get("wot_status", identity.wot_status) + identity.is_smith = dev_profile.get("is_smith", identity.is_smith) + identity.is_techcomm = dev_profile.get("is_techcomm", identity.is_techcomm) + await db.commit() + await db.refresh(identity) return identity diff --git a/frontend/app/pages/login.vue b/frontend/app/pages/login.vue index f57807c..47a4db9 100644 --- a/frontend/app/pages/login.vue +++ b/frontend/app/pages/login.vue @@ -1,11 +1,65 @@ @@ -121,6 +176,30 @@ onMounted(() => { {{ auth.loading ? 'Verification...' : 'Se connecter' }} + +
+
+ + Mode Dev — Connexion rapide +
+
+ +
+
+