Autorisation
RBAC Yayaw, bindings groupe-rôle scopés, évaluation des policies et opérations d'autorisation dans le tableau de bord.
Vue d'ensemble
Yayaw utilise Better Auth pour l'identité, les sessions, les organisations et les enregistrements d'appartenance. L'autorisation applicative est gérée par le moteur RBAC propre à Yayaw au-dessus de ces identités.
Le modèle est basé sur les groupes:
Les utilisateurs appartiennent à des groupes.
Les groupes reçoivent des rôles via des bindings explicites.
Les rôles contiennent des policies.
Les policies accordent ou refusent des actions sur des ressources.
Les bindings et vérifications peuvent être scopés globalement, par organisation ou par ressource.
Le helper booléen can(...) est la façade utilisée par la plupart des loaders
de routes, actions serveur et services. L'évaluateur détaillé enregistre la
chaîne de décision pour les workflows de débogage et d'audit.
Concepts clés
Les ressources sont déclarées dans src/lib/server/authz/contracts.ts. Les
familles de ressources actuelles incluent:
organisations et membres
utilisateurs plateforme
clés API
plans de facturation, produits, entitlements, événements webhook et accès au code
événements d'audit du plan de contrôle (control plane)
ressources d'administration de l'autorisation
médias, pages, sections, composants, données globales, design tokens et thèmes
réglages système, organisation et préférences utilisateur
Les actions sont:
readlistcreateupdatedeleteinvitemanagepublish
manage est l'action administrative large pour une ressource. Préférez des
actions plus étroites lorsqu'une UI ou un service n'a besoin que d'un
comportement read/list/update.
Tables
Tables d'autorisation:
groupsgroup_membershipsrolesrole_policiesgroup_role_bindingsauthorization_audit_eventsauthorization_decision_logs
group_role_bindings est la table de scope clé. Un groupe peut recevoir un rôle
avec:
scope
globalscope
organizationscope
resource
C'est pourquoi l'accès organisation est modélisé comme des rôles appliqués à des groupes, et non comme des lignes directes utilisateur-permission.
Rôles seedés
Le seed crée les rôles plateforme:
Super AdminOrganization AdminOrganization ManagerOrganization MemberAuthenticated UserScreen Viewer,Screen EditoretScreen Publisher, les niveaux d'accès à un écran du tableau de bord (voir Accès à un écran)
Super Admin reçoit une couverture manage pour chaque ressource déclarée et
est bindé au groupe superadmins.
L'administration des utilisateurs plateforme utilise la paire ressource/action
user:manage. N'utilisez pas user.role de Better Auth comme autorité
d'autorisation Yayaw; les permissions Yayaw restent exclusivement
users -> groups -> roles -> policies.
Lorsqu'une organisation est créée, Yayaw crée des groupes scopés par organisation:
<organizationSlug>-admin<organizationSlug>-manager<organizationSlug>-member
Chaque groupe est bindé au rôle d'organisation correspondant avec
scopeType = "organization" et scopeId = <organizationId>.
Synchronisation d'organisation
Les hooks d'organisation Better Auth gardent les groupes d'autorisation Yayaw synchronisés:
la création d'organisation crée les groupes par défaut et assigne le créateur au groupe admin
l'acceptation d'invitation ajoute les membres au bon groupe d'organisation
les changements de rôle de membre déplacent les appartenances entre les groupes d'organisation
un membre qui part, quelle que soit la manière, quitte tous les groupes que l'organisation tient pour lui (ci-dessous)
Services clés:
src/lib/server/services/authz/organization-setup-drizzle.tssrc/lib/server/services/authz/better-auth-sync-drizzle.tssrc/lib/server/services/authz/organization-departure.tssrc/config/better-auth.config.ts
Ne créez pas de membres d'organisation Better Auth par un chemin qui contourne le service de synchronisation, sauf si le code répare aussi l'appartenance au groupe correspondante. N'en retirez pas par un chemin qui saute le départ ci-dessous.
Quitter une organisation
Chaque manière de partir retire les mêmes appartenances, trouvées par une seule
fonction (organization-departure.ts) dans la transaction de l'appelant:
| Chemin | Quand les appartenances partent |
|---|---|
Retrait d'un membre (/organization/remove-member) | En une transaction, depuis le hook afterRemoveMember de Better Auth, juste après le retrait |
Départ volontaire (/organization/leave) | La même étape, lancée après l'endpoint par createOrganizationLeavePlugin: Better Auth n'y lance aucun hook de membre |
| Administration des utilisateurs de la plateforme | Dans la transaction qui supprime l'appartenance, avec ses équipes Better Auth |
| Suppression de l'organisation | Lues avant la suppression, puisque ses écrans et ses enregistrements disparaissent en cascade avec elle, et retirées une fois celle-ci faite |
Ce qui part:
chaque groupe que l'organisation scope: ses groupes admin, manager et member, et tout autre groupe scopé sur elle;
les groupes de ses équipes;
un groupe de rôle créé avant que les groupes portent le scope de leur organisation, par son slug exact (
<organizationSlug>-admin,-managerou-member), jamais par un préfixe que le slug d'une autre organisation pourrait partager;les groupes propres de ses écrans, et des enregistrements qu'un type de ressource d'un produit lui attribue (le champ
organizationFielddu type, ou un modèle stocké par organisation), quelle que soit la manière dont cet accès a été donné, appartenances expirées comprises.
Ce qui reste: un groupe aussi scopé sur une autre organisation dont le membre fait toujours partie, les groupes globaux, et les groupes propres d'une ressource d'une autre organisation ou d'aucune. Un type dont les enregistrements ne peuvent pas être lus garde son accès et n'arrête jamais le reste. SCIM ne retire jamais d'appartenance à une organisation: Yayaw ne configure aucune projection SCIM. Supprimer un compte retire toutes ses appartenances avec lui.
Évaluation des policies
Fichiers principaux:
src/lib/server/authz/can.tssrc/lib/server/authz/policy-loader.tssrc/lib/server/authz/drizzle-group-membership-delegate.ts
Règles d'évaluation:
les ressources/actions inconnues sont refusées par défaut
les règles deny correspondantes gagnent sur les allows
le scope de binding doit correspondre au scope demandé par la vérification
les vérifications scopées par organisation exigent l'id d'organisation dans le scope
les vérifications scopées par ressource exigent l'id de ressource dans le scope
authorizeDetailed(...)retourne une chaîne de décision pour l'explicabilitécan(...)retourne un booléen pour les guards de routes et d'actions
L'autorisation côté serveur doit rester dans le chemin de mutation/requête. La visibilité de navigation UI est seulement une commodité et ne doit jamais être le seul contrôle d'accès.
Administration du tableau de bord
L'administration de l'autorisation vit à:
/dashboard/admin/authorization/dashboard/admin/authorization/groups/[groupId]/dashboard/admin/users
L'UI admin prend en charge:
l'inspection des rôles et policies
les vues liste/détail de groupes
la gestion des appartenances de groupes
la gestion des bindings groupe-rôle
le listing des utilisateurs plateforme, la maintenance des appartenances d'organisation et les mises à jour d'organisation par défaut
l'évaluation rapide de permissions
les diagnostics de décision/audit
L'écran des autorisations
/dashboard/admin/authorization (permission de route group:manage) est un
écran global du tableau de bord, dashboard.admin.authorization : une seule
copie pour toute la plateforme, que seuls les Super Admins personnalisent
(voir Tableau de bord). Une personnalisation peut changer sa
disposition, mais l'annuaire des groupes reste toujours.
Qui voit les groupes : les détenteurs d'un
group:listglobal (group:managel'accorde). Une policygroupliée à une organisation ne l'ouvre jamais : la vérification se fait sans organisation. Qu'une personne puisse gérer les groupes dépend d'ungroup:manageglobal, et chaque action sur un groupe vérifie de nouveaucan()sur le serveur.Chiffres : les groupes, les appartenances aux groupes, les liaisons groupe-rôle, les groupes SCIM et les groupes en lecture seule, comptés sur tous les groupes quelle que soit la recherche de la table, et relus aussitôt après un changement.
Annuaire des groupes : nom, slug, source, membres, liaisons et dernière mise à jour ; la lecture seule, les groupes imbriqués, les scopes et le lien peuvent être affichés. Un clic sur une ligne ouvre la page du groupe (
/dashboard/admin/authorization/groups/[groupId]). Les compteurs comprennent chaque appartenance, liaison et groupe imbriqué enregistrés, expirés compris. Aucun identifiant externe ou SCIM n'est jamais lu.Vues : la vue de l'écran (tous les groupes par nom, 20 par page), puis Groupes locaux et Groupes provisionnés (groupes SCIM et synchronisés), puis les vues enregistrées. Chacune peut être la favorite d'une personne, et les vues enregistrées peuvent être partagées avec l'organisation.
Modes : table, liste, un kanban par source (les cartes ne se déplacent pas) et graphiques.
Recherche et ordre : la recherche lit le nom et le slug ; les noms se trient comme on les lit, sans tenir compte de la casse (« Group 2 » avant « group 10 ») ; une page de la table contient au plus 100 lignes.
Actions : Créer demande un nom, un slug, un mode de politiques et une description, puis crée un groupe d'organisation par l'action générée des groupes, qui vérifie
group:createsur le serveur. Une sélection, ou tous les groupes de la vue, peuvent être exportés.Évaluateur rapide : explique une décision (une ressource, une action, une organisation facultative et une personne) et l'enregistre, pour les détenteurs d'un
group:listglobal, dont ses options ont besoin. Expliquer la décision d'une autre personne demande unauthorization-decision-log:manageglobal ; le serveur vérifie les deux de nouveau.
La surface utilisateurs permet aux superadmins d'ajouter un utilisateur à une
autre organisation sans supprimer les appartenances existantes, de modifier le
rôle d'appartenance Better Auth à une organisation, de retirer des appartenances
et de démarrer une session d'impersonation vérifiée par le RBAC Yayaw. Les
endpoints d'impersonation enveloppent intentionnellement les mécaniques de
session Better Auth derrière des vérifications user:manage et bloquent
l'impersonation d'un autre utilisateur qui possède aussi user:manage.
Utilisez cette page pour inspecter pourquoi un utilisateur peut ou ne peut pas accéder à une ressource avant de modifier les policies de seed.
Contrats de routes et d'actions
Les routes déclarent leur intention de permission dans la configuration navigation/route. Les actions de base de données générées déclarent aussi des paires ressource/action. Les tests de contrat assurent que les ressources déclarées restent dans le contrat authz canonique.
Test important:
bun test src/lib/server/authz/contracts.test.tsCela protège:
les ressources de navigation
les ressources des actions DB générées
la couverture de seed Super Admin
la couverture de réparation de l'administration utilisateur
la couverture de réparation authz des sections
les racines navigables du tableau de bord
Attentes courantes par ressource
Valeurs par défaut d'organisation:
| Ressource | Membre | Manager/Admin |
|---|---|---|
page | list/read | manage |
sections | list/read | manage |
media | list/read | manage |
code-access | list/read | manage |
Super Admin reçoit un accès manage global plateforme, y compris aux produits
de facturation, indicateurs de fonctionnalité, réglages système, modèles de
registre, utilisateurs plateforme et ressources d'autorisation.
L'accès au code exige toujours l'éligibilité de facturation. code-access:read
permet à un utilisateur d'atteindre la surface d'accès au code, mais le service
de facturation décide si les livrables payants sont déverrouillés.
Accès à un écran
Les administrateurs d'organisation gèrent tous les écrans du tableau de bord
de leur organisation (screen:manage). Depuis Organisation › Écrans,
Gérer l'accès donne à d'autres personnes un niveau sur un écran, ou sur
tous les écrans de sa section de menu :
| Niveau | Rôle | Actions screen |
|---|---|---|
| Voir | Screen Viewer | read |
| Modifier | Screen Editor | read, update : consulter le brouillon en attente, enregistrer des brouillons |
| Publier | Screen Publisher | read, update, publish : aussi publier ou abandonner le brouillon, revenir au défaut |
Ces rôles n'accordent rien seuls. Ils ne sont tenus que par un binding scopé
à un écran (scopeType = "resource", l'id dashboard_screens de l'écran) ou
à une section de menu d'une organisation (scopeType = "parent",
screen-section:<organizationId>:<section>), jamais à une organisation ni
globalement :
un membre rejoint le groupe propre de l'écran pour le niveau, un groupe par niveau, comme pour les groupes d'accès de toute ressource (un écran système reçoit d'abord une ligne vide, pour avoir un id) ;
une des équipes de l'organisation, ou son groupe des membres (tous les membres de l'organisation, administrateurs et managers compris), est bindé à l'écran au niveau ;
une équipe ou tous les membres sont bindés au niveau sur la section de l'écran, ce qui couvre tous ses écrans, présents et futurs.
Seuls les membres actuels de l'organisation, ses équipes et son groupe des
membres peuvent être nommés, et une personne tient un seul niveau sur un
écran ou une section. Chaque vérification nomme l'écran tel qu'il est
stocké : resourceId, resourceOrganizationId et sa section dans
parents. Elle exige aussi le flag screen-customization et
l'appartenance à l'organisation active ; un changement redemande la
décision sans cache et est refusé pendant une usurpation d'identité. Une
personne qui quitte l'organisation quitte les groupes propres de ses
écrans, et les équipes et le groupe des membres dont elle faisait partie.
L'écran d'un modèle de données dynamiques (sa page d'enregistrements,
dynamic-data.records.<id du modèle>) se délègue de la même façon : sa
section est la section de menu du modèle (Admin par défaut, Contenu,
Organisation ou la sienne), donc un accès sur cette section couvre tous les
écrans de modèles qu'elle contient, et un accès sur l'écran ne couvre que
celui de ce modèle. L'accès à un écran n'ouvre jamais le modèle lui-même :
qui ne peut pas lister le modèle (dynamic-data:list ou manage sur lui, ou
sur sa portée) n'a pas de page, quel que soit son niveau sur l'écran.
MCP ne change jamais qui a accès : aucun outil d'écran n'y touche, et les
outils yayaw_resource_access_* administrent les types de ressources que
déclarent les produits enregistrés, qui ne peuvent jamais être screen. Un
écran ne donne aucune donnée : chaque widget lit toujours sa source par la
vérification propre de cette source.
Ajouter une ressource
Lors de l'ajout d'une ressource:
Ajoutez-la à
RESOURCESdanssrc/lib/server/authz/contracts.ts.Ajoutez les policies de seed pour Super Admin.
Ajoutez les policies de rôles d'organisation lorsque la ressource est scopée par organisation.
Ajoutez des migrations ou du SQL de réparation pour les installations existantes si nécessaire.
Ajoutez ou mettez à jour les permissions de configuration de routes.
Ajoutez les mappings d'actions générées si la ressource a des actions de base de données.
Mettez à jour les docs anglaises et
content/llm/llm-source.mdlorsque la ressource change l'architecture ou les consignes assistant.Exécutez les tests de contrat authz.
Déboguer un accès
Lorsqu'un utilisateur ne peut pas accéder à une page:
Confirmez que l'organisation active est correcte.
Confirmez que l'appartenance Better Auth existe.
Confirmez que l'appartenance au groupe Yayaw correspondant existe.
Confirmez que le groupe a un binding de rôle scopé.
Confirmez que le rôle a une policy pour la ressource/action demandée.
Utilisez l'évaluateur rapide dans
/dashboard/admin/authorization.Vérifiez
authorization_decision_logslorsque la journalisation détaillée est activée.
Validation
Vérifications utiles après des changements authz:
bun test src/lib/server/authz/policy-loader.test.ts
bun test src/lib/server/authz/contracts.test.ts
bun test src/lib/server/services/authz/authz-service.test.ts
bun run check
bunx tsc --noEmit