Contrôle d'accès par l'API REST¶
Les pages rôles et permissions et
projets décrivent le modèle dans l'interface web. Cette page donne les
limites, les règles détaillées et les appels de l'API REST (permission users.manage pour les
rôles, les rôles et projets des utilisateurs, les membres des projets et les correspondances
OIDC).
Limites¶
| Élément | Limite |
|---|---|
| Rôles créés par les administrateurs | 100 au plus |
| Rôles d'un utilisateur | 100 au plus |
| Projets d'un utilisateur | 200 au plus ; au-delà (avec les correspondances OIDC), le compte n'a aucun accès |
| Membres définis d'un coup sur un projet | 500 au plus |
| Correspondances OIDC | 500 au plus, une par valeur du claim (200 caractères au plus), chacune de 100 rôles et 50 projets au plus |
Un rôle ne porte que des droits sur tout le serveur Controller : un rôle avec reports.read,
reports.write ou findings.triage est refusé (422). Ces droits par projet se donnent avec chaque projet d'un
utilisateur (projects[].permissions, au moins un) et avec tous les projets
(all_projects_permissions). Seul un projet connu peut être donné (404 sinon).
users.manage couvre aussi les réglages OIDC, puisqu'ils décident qui reçoit quel rôle. Le rôle
admin, avec la lecture, l'écriture et le triage de tous les projets, est aussi donné par une correspondance
créée à la connexion d'un utilisateur qui porte une valeur de
SECREVIEW_OIDC_ADMIN_GROUPS (OIDC) tant qu'aucune correspondance ne la nomme ; une
correspondance qui nomme déjà cette valeur est laissée telle qu'un administrateur l'a modifiée,
une correspondance supprimée est recréée. Les valeurs du claim des rôles qu'aucune
correspondance ne nomme sont ignorées.
Rôles et utilisateurs¶
# Un rôle, puis une utilisatrice qui l'a, lit « billing » et lit et trie « crm »
curl -X POST https://secreview.example.com/api/v1/roles \
-H "Authorization: Bearer $ADMIN_TOKEN" -H "Content-Type: application/json" \
-d '{"name": "Auditor", "description": "", "permissions": ["audit.read"]}'
curl -X POST https://secreview.example.com/api/v1/users \
-H "Authorization: Bearer $ADMIN_TOKEN" -H "Content-Type: application/json" \
-d '{"username": "alice", "role_ids": [2], "projects": [{"project": "billing"}, {"project": "crm", "permissions": ["reports.read", "findings.triage"]}]}'
# La lecture de tous les projets pour le compte 7 (ses rôles et ses projets sont gardés)
curl -X PUT https://secreview.example.com/api/v1/users/7 \
-H "Authorization: Bearer $ADMIN_TOKEN" -H "Content-Type: application/json" \
-d '{"all_projects_permissions": ["reports.read"]}'
| Route | Effet |
|---|---|
GET /api/v1/permissions |
Permissions qu'un rôle peut porter, et si elles s'appliquent par projet |
GET, POST /api/v1/roles ; PUT, DELETE /api/v1/roles/{id} |
Rôles, avec le nombre d'utilisateurs et de correspondances OIDC qui donnent chacun |
POST /api/v1/users |
Crée un utilisateur avec ses rôles (role_ids), ses droits par projet sur tous les projets (all_projects_permissions) et ses projets (projects, chacun avec ses permissions, la lecture par défaut) |
PUT /api/v1/users/{id} |
Remplace les rôles, les droits par projet sur tous les projets ou les projets d'un utilisateur ; un champ absent reste inchangé, ceux de ses correspondances OIDC sont gardés |
GET /api/v1/access/projects?search=&limit= |
Projets connus dont le nom contient search (tous les projets, y compris ceux que l'appelant ne lit pas), 200 au plus |
GET, PUT /api/v1/access/projects/{project}/members |
Utilisateurs qui ont un projet, chacun avec ses permissions données sur ce projet et ses all_projects_permissions détenues sur tous les projets, par l'utilisateur ou ses correspondances OIDC (lecture seule ; un utilisateur qui n'a que tous les projets a des permissions vides et ne compte ni dans la version de la liste ni dans le PUT) |
GET, PUT /api/v1/oidc/mappings |
Correspondances OIDC, remplacées d'un bloc (OIDC) |
Un champ inconnu (comme l'ancien group_ids) est refusé (422). Les rôles intégrés reader et
admin ne peuvent être ni modifiés ni supprimés ; supprimer un autre rôle le retire des
utilisateurs et des correspondances OIDC qui le donnent (un rôle utilisé par un jeton ne peut pas
être supprimé).
curl -X PUT https://secreview.example.com/api/v1/access/projects/billing/members \
-H "Authorization: Bearer $ADMIN_TOKEN" -H "Content-Type: application/json" \
-d '{"members": [{"user_id": 7}, {"user_id": 9, "permissions": ["reports.read", "findings.triage"]}]}'
Le GET des membres d'un projet et des correspondances OIDC renvoie la version de la liste dans
l'en-tête ETag. Renvoyez-la dans If-Match avec le PUT qui remplace la liste : si un autre
administrateur l'a modifiée entre-temps, le PUT est refusé (412) au lieu d'annuler sans le
dire ce changement ; rechargez la liste et recommencez. Sans If-Match (ou avec *), la liste
est remplacée quel que soit son contenu. L'interface web l'envoie toujours.
etag=$(curl -s -o /dev/null -D - https://secreview.example.com/api/v1/oidc/mappings \
-H "Authorization: Bearer $ADMIN_TOKEN" | awk 'tolower($1) == "etag:" {print $2}' | tr -d '\r')
curl -X PUT https://secreview.example.com/api/v1/oidc/mappings \
-H "Authorization: Bearer $ADMIN_TOKEN" -H "Content-Type: application/json" \
-H "If-Match: $etag" -d '{"mappings": [{"claim": "secops", "role_ids": [2]}]}'
Chaque changement est écrit dans le journal d'audit (role.*,
user.roles_changed, user.projects_changed, oidc.mappings_changed). En CLI,
security-review-controller user set-access change les rôles et projets d'un compte, et
security-review-controller role list / role mappings liste les rôles et les correspondances
OIDC (voir CLI).
Permissions de l'appelant¶
GET /api/v1/me renvoie les permissions de l'appelant, chacune avec ses projets (null : tous
les projets), et role vaut toujours user pour une session web (le rôle du jeton sinon). Un
utilisateur qui ne gère que les jetons et les normes ne lit aucun projet. Une permission détenue
donne aussi celles qu'elle comprend : rules.manage donne rules.read, standards.manage donne
standards.read. Une session web ne passe
jamais pour un jeton admin, même avec toutes les permissions : les routes réservées aux jetons
(/metrics, les scans de pipeline) la refusent.
{"client": "alice", "role": "user", "projects": null,
"permissions": {"reports.read": null, "findings.triage": ["crm"]}, …}
Un compte sans aucune permission ne peut pas se connecter, et ses sessions ouvertes s'arrêtent.
Retirer le dernier détenteur de users.manage (supprimer son compte, ses rôles, la permission de
son rôle ou la correspondance OIDC qui la lui donne) est refusé avec 409 ou 422. Une connexion
OIDC ne la retire jamais : les valeurs du claim qui la donnaient sont gardées, avec un
avertissement dans les journaux et un événement user.roles_changed.
Projets¶
Projets connus¶
Un projet est connu dès qu'il a une ligne en base : créé par son premier rapport, un jeton qui l'atteint, une validation ou une création dans l'interface web. Une demande en attente ne rend pas un projet connu.
Lecture d'un projet demandé¶
La validation d'un projet demandé par un agent
(enrôlement) l'ajoute
aux projets de l'utilisateur demandeur avec reports.read et reports.write, jamais plus (en créant le projet s'il est nouveau ;
pour un projet existant, la demande est une demande d'accès, que seul un utilisateur doté de
users.manage valide), et créer un projet l'ajoute de la même façon à son créateur. Voir aussi
lecture du projet.
Création d'un projet¶
POST /api/v1/projects crée un projet, depuis une session de l'interface web uniquement. Le seul
jeton qui crée un projet est le jeton MCP personnel d'un utilisateur doté de projects.create,
par l'outil request_project de son agent. Le serveur Controller ajoute le projet au compte avec
reports.read et reports.write.
curl -X POST -H "Authorization: Bearer $SESSION" -H "Content-Type: application/json" \
https://secreview.example.com/api/v1/projects -d '{"project": "mobile-app"}'
# {"project": "mobile-app", "reports_total": 0, "last_report_at": null, …}
Un projet connu est refusé (409). Un projet encore en attente n'est pas encore connu : le créer
règle la demande de son créateur (les demandes des autres utilisateurs deviennent des demandes
d'accès). Chaque création est écrite dans le journal d'audit (project.created).
Adhésion automatique à un projet¶
Un utilisateur doté de projects.autojoin rejoint un projet existant par l'outil
request_project de son agent : le serveur Controller ajoute le projet au compte avec
reports.read et reports.write, règle sa demande en attente pour ce projet et écrit project.joined dans le journal d'audit. Un
utilisateur qui lit déjà 200 projets est refusé. PUT /api/v1/projects/{project}/autojoin bloque
(true) ou autorise de nouveau (false) cette adhésion automatique, avec users.manage,
depuis une session de l'interface web uniquement, pour un projet que l'appelant lit (un autre
donne 404). Un projet bloqué affiche "autojoin_blocked": true dans GET /api/v1/projects ;
les utilisateurs qui l'ont déjà rejoint gardent leur accès. Chaque changement est écrit dans le
journal d'audit (project.autojoin_changed).
curl -X PUT -H "Authorization: Bearer $SESSION" -H "Content-Type: application/json" \
https://secreview.example.com/api/v1/projects/mobile-app/autojoin -d '{"blocked": true}'
# {"project": "mobile-app", "autojoin_blocked": true}
Suppression d'un projet¶
DELETE /api/v1/projects/{project} supprime un projet, depuis une session de l'interface web
uniquement : un jeton ne supprime jamais un projet. La suppression se fait en une transaction :
- ses rapports, avec leurs findings et les commentaires de ces findings ;
- le projet dans les projets des utilisateurs ;
- le projet dans les correspondances OIDC : une correspondance qui n'a plus de projet est supprimée (jamais laissée atteindre tous les projets) ;
- le projet dans les jetons : un jeton qui n'atteint plus aucun autre projet est révoqué (jamais laissé atteindre tous les projets), les autres gardent leurs autres projets.
curl -X DELETE -H "Authorization: Bearer $SESSION" \
https://secreview.example.com/api/v1/projects/mobile-app
# {"project": "mobile-app", "reports": 3, "findings": 12, "members_removed": 2, "mappings_changed": [], …}
Le journal d'audit garde les événements du projet et enregistre project.deleted, avec les
nombres dans ses détails ; les correspondances OIDC et les jetons changés ou supprimés ont leurs
propres événements (oidc.mappings_changed, token.revoked, token.projects_changed). Un projet
inconnu donne 404.