Skip to content

feat(ra-console): gestion du registre des opérateurs depuis la console - #69

Closed
PhilippeVienne wants to merge 3 commits into
feat/ra-console-quorumfrom
feat/ra-console-registry-actions
Closed

PhilippeVienne wants to merge 3 commits into
feat/ra-console-quorumfrom
feat/ra-console-registry-actions

Conversation

@PhilippeVienne

Copy link
Copy Markdown
Contributor

Objet

Gestion du registre des opérateurs depuis la console (docs/WEBUI.md §5, §10), suite des étapes 3 et 4. Empilée sur #67 (feat/ra-console-quorum) → #66 → #65 → #64. Ordre de merge : #64, #65, #66, #67, cette PR (rebase --onto origin/dev <ancienne tête> à chaque étape).

ra-console relaie, sur le schéma exact des étapes 3 et 4 (challenge par POST /api/v1/webauthn/challenge, puis exécution sans corps, cible comparée par ca-server avant toute consommation), les quatre actions de registre qu'oe_actions exécute déjà :

Route Action Cible contrôlée
POST /api/v1/operators invite_operator le type seulement (l'opérateur n'existe pas encore)
POST /api/v1/credentials/{credential_id}/confirm confirm_key credential_id
POST /api/v1/credentials/{credential_id}/revoke revoke_key credential_id
POST /api/v1/operators/{name}/role set_role operator (par son nom)
  • oe_actions::Expect gagne credential_id et operator ; le contrôle de cible devient générique : la cible du corps figé est comparée au champ d'attente correspondant, tout autre champ d'attente renseigné est refusé.
  • Jeton d'invitation : rendu une seule fois dans result.invite_token de la réponse d'exécution, jamais journalisé (ni par ca-server ni par la console — vérifié par test).
  • Rôle admin (création, élévation, changement du rôle d'un administrateur) : deux administrateurs par la politique de ca-server ; la première signature rend AWAITING_QUORUM, la seconde passe par /api/v1/quorum/{id}/sign, dont frozen_at_this_stage/relayed_at_this_stage acceptent désormais les actions de registre et qui joint la cible correspondante.
  • Nouvelles routes isolées dans bin/ra-console/src/registry_routes.rs ; http.rs ne change que le routeur, le filtre d'actions, la cible des co-signatures et quelques pub(crate).
  • docs/RA-CONSOLE.md à jour.

Décisions à valider

  • Confirmation d'une clé : le §5 prévoit POST /api/v1/operators/onboarding/{token}/confirm, mais l'action signée est ConfirmKey { credential_id, key_fingerprint } et le jeton d'invitation est déjà consommé à ce stade (et ne doit pas repasser dans une URL). Route retenue : POST /api/v1/credentials/{credential_id}/confirm, cohérente avec la révocation de clé.
  • Changement de rôle : le §5 prévoit /operators/{id}/role ; le corps signé désigne l'opérateur par son nom (SetRole { operator }). Route retenue : /operators/{name}/role, pour que la cible de la route soit exactement celle du corps figé.
  • Invitation : aucune cible dans la route (seul le type est contrôlé) : une assertion d'invitation ne peut servir qu'à l'invitation figée pour laquelle elle a été émise, mais la route ne nomme pas l'invité.
  • Le filtre d'actions de la console (relayed_at_this_stage) couvre maintenant toute l'énumération fermée d'oe_actions ; une action ajoutée plus tard ne sera relayée qu'une fois nommée.

Limites

  • Pas de liste des clés en attente de confirmation côté console (l'empreinte est transmise hors bande par l'invité, §10) ; pas de libre-service (ajout/retrait de ses propres clés).

Vérifications

  • bin/ra-console/tests/action_challenge.rs, contre le vrai service d'actions de ca-server et PostgreSQL : invitation → enregistrement par le relais existant (clé en attente) → confirmation par l'admin → connexion de l'invitée ; jeton absent du journal de la console ; révocation de clé où les deux clés appartiennent au même opérateur (seul credential_id distingue) ; changement de rôle où l'action et le rôle sont identiques (seul l'opérateur distingue) ; élévation admin à deux (AWAITING_QUORUM puis co-signature). Tests unitaires d'Expect étendus.
  • Mutation (5 mutants, tous tués) : comparaison de credential_id neutralisée ; comparaison d'operator neutralisée ; jeton ajouté au journal de relais ; validation de forme de l'identifiant de clé retirée ; cible de l'opérateur non jointe à la co-signature.
  • cargo fmt --check, cargo clippy --workspace --all-targets -- -D warnings (1.97 et 1.98.1), cargo test --workspace avec PostgreSQL, cargo audit --ignore RUSTSEC-2023-0071 : verts.

Revue humaine obligatoire

Voir PROVENANCE.md. Chaque case est cochée par le
contributeur humain qui valide la PR, après l'avoir fait lui-même.

  • Revue d'architecture validée par l'humain
  • Code relu et tests unitaires/intégration vérifiés localement
  • Absence de dépendances tierces incompatibles avec la double licence EUPL-1.2 / AGPL-3.0 (make licenses)
  • Validation de l'apport intellectuel et de la paternité humaine sur la modification

Assistance par IA

  • Cette PR a été produite avec l'assistance de Claude Code : les commits concernés portent la remorque Co-authored-by: Claude <noreply@anthropic.com>, auteur et committer restent humains, et scripts/provenance.py archive a été lancé
  • Cette PR a été écrite sans assistance par IA

PhilippeVienne and others added 3 commits September 30, 2026 16:15
…pe 4a)

docs/WEBUI.md §15 étape 4, §8 : la console prépare `revoke_certificate`
et relaie la signature par `POST /api/v1/certificates/{serial}/revoke`.
La politique de ca-server exige deux ca_operateur distincts : la
première signature est enregistrée, rien n'est révoqué
(AWAITING_QUORUM, 1/2). La co-signature est l'étape 4b.

- oe_actions::Expect gagne `serial` : ca-server compare le certificat de
  la route au corps figé avant toute consommation, comme pour une
  décision ; une cible sans rapport avec le type d'action est refusée.
- ra-console : relay_assertion factorise le relais d'une assertion
  (décisions et révocation) ; numéro de série exigé sous forme
  canonique (hexadécimal minuscule, 20 octets au plus) avant relais.
- Tests : harnais doté d'une vraie CA sur PostgreSQL et du révocateur de
  ca-server ; première signature sans révocation, mauvaise cible,
  forme non canonique, refus pour un ra_operateur. Deux mutations tuées.

Co-authored-by: Claude <noreply@anthropic.com>
…tape 4b)

docs/WEBUI.md §8, §15 étape 4 : deux ca_operateur distincts révoquent
ensemble depuis la console.

- Co-signature : `POST /api/v1/webauthn/challenge` accepte
  `{"action_id"}` (action existante, non exécutée, proposée à ce stade),
  puis `POST /api/v1/quorum/{action_id}/sign`. oe_actions::Expect gagne
  `action_id`, comparé avant toute consommation : une co-signature ne
  compte que pour l'action pour laquelle son challenge a été émis.
- Salle d'attente : `GET /api/v1/quorum?state=PENDING` lit `actions` et
  `decision_evidence` de ca-server en lecture seule (décision de
  l'utilisateur) : corps figé, empreinte, signatures, signataires. Le
  rôle de la console gagne SELECT sur `actions` (aucun secret n'y
  figure) ; le test de schéma est mis à jour.
- Écart assumé avec le §8, en plus sûr : pas de tables de collecte, la
  console ne conserve jamais d'assertion (ca-server enregistre chaque
  signature au fil de l'eau). WEBUI.md §8 dit ce qui est construit.
- Tests : révocation à deux de bout en bout, seconde signature du même
  opérateur refusée, exécution unique, co-signature présentée pour une
  autre action sur le même certificat refusée. Deux mutations tuées.
- Mise à jour d'un déploiement : rejouer ra_console_grants.sql.

Co-authored-by: Claude <noreply@anthropic.com>
docs/WEBUI.md §5, §10 : ra-console relaie, sur le schéma des étapes 3 et
4 (challenge puis exécution, cible contrôlée par ca-server), les actions
de registre qu'oe_actions exécute déjà : invite_operator, confirm_key,
revoke_key, set_role.

- Routes (module registry_routes) : POST /api/v1/operators,
  POST /api/v1/credentials/{credential_id}/confirm et /revoke,
  POST /api/v1/operators/{name}/role. Le jeton d'invitation n'existe que
  dans result.invite_token de la réponse, jamais journalisé.
- oe_actions::Expect gagne credential_id et operator ; le contrôle de
  cible devient générique (une cible d'un autre type est refusée), avant
  toute consommation de la cérémonie. Une invitation n'a pas de cible.
- Élever au rôle admin ou changer celui d'un administrateur : deux
  administrateurs, co-signature par /api/v1/quorum/{id}/sign (la route
  de signature joint désormais la cible des actions de registre).
- Tests de bout en bout : invitation puis enregistrement puis
  confirmation, jeton absent du journal de la console ; révocation de
  clé et changement de rôle où seule la cible distingue ; élévation admin
  à deux. Cinq mutations tuées.

Co-authored-by: Claude <noreply@anthropic.com>
@PhilippeVienne
PhilippeVienne force-pushed the feat/ra-console-registry-actions branch from 3d451fe to 38b2299 Compare September 30, 2026 14:15
@PhilippeVienne
PhilippeVienne force-pushed the feat/ra-console-quorum branch 2 times, most recently from 9e06cda to 84a7de1 Compare September 30, 2026 15:12
@PhilippeVienne
PhilippeVienne deleted the branch feat/ra-console-quorum September 30, 2026 15:52
@PhilippeVienne

Copy link
Copy Markdown
Contributor Author

Remplacée par #85 (base dev) : fermée par GitHub à la suppression de sa branche de base après la fusion de #83.

PhilippeVienne added a commit that referenced this pull request Sep 30, 2026
docs/WEBUI.md §10, §15 étape 6 : un administrateur gère le registre
depuis la console, chaque écriture étant une action signée que ca-server
juge (routes de #69, fusionnées dans cette branche).

- `GET /api/v1/operators` : opérateurs, clés, clés en attente, en lecture
  seule ; l'empreinte d'une clé en attente est recalculée avec
  oe_actions::key_fingerprint, la fonction même de ca-server (test Rust :
  identique à celle remise à l'invitée).
- Écran des opérateurs : invitation (jeton affiché une seule fois),
  confirmation d'une clé en attente après déclaration de comparaison de
  l'empreinte hors bande, révocation de clé (motif obligatoire),
  changement de rôle (admin : second administrateur en salle de quorum).
  Boutons désactivés pour les non-administrateurs (affichage seulement).
- Harnais e2e : un administrateur et un opérateur de test. Parcours :
  invitation, changement de rôle, révocation de clé ; consultation seule
  pour un non-administrateur (mutation tuée).

Co-authored-by: Claude <noreply@anthropic.com>
PhilippeVienne added a commit that referenced this pull request Sep 30, 2026
docs/WEBUI.md §10, §15 étape 6 : un administrateur gère le registre
depuis la console, chaque écriture étant une action signée que ca-server
juge (routes de #69, fusionnées dans cette branche).

- `GET /api/v1/operators` : opérateurs, clés, clés en attente, en lecture
  seule ; l'empreinte d'une clé en attente est recalculée avec
  oe_actions::key_fingerprint, la fonction même de ca-server (test Rust :
  identique à celle remise à l'invitée).
- Écran des opérateurs : invitation (jeton affiché une seule fois),
  confirmation d'une clé en attente après déclaration de comparaison de
  l'empreinte hors bande, révocation de clé (motif obligatoire),
  changement de rôle (admin : second administrateur en salle de quorum).
  Boutons désactivés pour les non-administrateurs (affichage seulement).
- Harnais e2e : un administrateur et un opérateur de test. Parcours :
  invitation, changement de rôle, révocation de clé ; consultation seule
  pour un non-administrateur (mutation tuée).

Co-authored-by: Claude <noreply@anthropic.com>
PhilippeVienne added a commit that referenced this pull request Sep 30, 2026
…e) (#91)

docs/WEBUI.md §10, §15 étape 6 : un administrateur gère le registre
depuis la console, chaque écriture étant une action signée que ca-server
juge (routes de #69, fusionnées dans cette branche).

- `GET /api/v1/operators` : opérateurs, clés, clés en attente, en lecture
  seule ; l'empreinte d'une clé en attente est recalculée avec
  oe_actions::key_fingerprint, la fonction même de ca-server (test Rust :
  identique à celle remise à l'invitée).
- Écran des opérateurs : invitation (jeton affiché une seule fois),
  confirmation d'une clé en attente après déclaration de comparaison de
  l'empreinte hors bande, révocation de clé (motif obligatoire),
  changement de rôle (admin : second administrateur en salle de quorum).
  Boutons désactivés pour les non-administrateurs (affichage seulement).
- Harnais e2e : un administrateur et un opérateur de test. Parcours :
  invitation, changement de rôle, révocation de clé ; consultation seule
  pour un non-administrateur (mutation tuée).

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant