Yayaw
Documentation

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:

  1. Les utilisateurs appartiennent à des groupes.

  2. Les groupes reçoivent des rôles via des bindings explicites.

  3. Les rôles contiennent des policies.

  4. Les policies accordent ou refusent des actions sur des ressources.

  5. 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:

  • read

  • list

  • create

  • update

  • delete

  • invite

  • manage

  • publish

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:

  • groups

  • group_memberships

  • roles

  • role_policies

  • group_role_bindings

  • authorization_audit_events

  • authorization_decision_logs

group_role_bindings est la table de scope clé. Un groupe peut recevoir un rôle avec:

  • scope global

  • scope organization

  • scope 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 Admin

  • Organization Admin

  • Organization Manager

  • Organization Member

  • Authenticated User

  • Screen Viewer, Screen Editor et Screen 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.ts

  • src/lib/server/services/authz/better-auth-sync-drizzle.ts

  • src/lib/server/services/authz/organization-departure.ts

  • src/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:

CheminQuand 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 plateformeDans la transaction qui supprime l'appartenance, avec ses équipes Better Auth
Suppression de l'organisationLues 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, -manager ou -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 organizationField du 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.ts

  • src/lib/server/authz/policy-loader.ts

  • src/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:list global (group:manage l'accorde). Une policy group lié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'un group:manage global, et chaque action sur un groupe vérifie de nouveau can() 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:create sur 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:list global, dont ses options ont besoin. Expliquer la décision d'une autre personne demande un authorization-decision-log:manage global ; 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.ts

Cela 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:

RessourceMembreManager/Admin
pagelist/readmanage
sectionslist/readmanage
medialist/readmanage
code-accesslist/readmanage

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 :

NiveauRôleActions screen
VoirScreen Viewerread
ModifierScreen Editorread, update : consulter le brouillon en attente, enregistrer des brouillons
PublierScreen Publisherread, 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:

  1. Ajoutez-la à RESOURCES dans src/lib/server/authz/contracts.ts.

  2. Ajoutez les policies de seed pour Super Admin.

  3. Ajoutez les policies de rôles d'organisation lorsque la ressource est scopée par organisation.

  4. Ajoutez des migrations ou du SQL de réparation pour les installations existantes si nécessaire.

  5. Ajoutez ou mettez à jour les permissions de configuration de routes.

  6. Ajoutez les mappings d'actions générées si la ressource a des actions de base de données.

  7. Mettez à jour les docs anglaises et content/llm/llm-source.md lorsque la ressource change l'architecture ou les consignes assistant.

  8. Exécutez les tests de contrat authz.

Déboguer un accès

Lorsqu'un utilisateur ne peut pas accéder à une page:

  1. Confirmez que l'organisation active est correcte.

  2. Confirmez que l'appartenance Better Auth existe.

  3. Confirmez que l'appartenance au groupe Yayaw correspondant existe.

  4. Confirmez que le groupe a un binding de rôle scopé.

  5. Confirmez que le rôle a une policy pour la ressource/action demandée.

  6. Utilisez l'évaluateur rapide dans /dashboard/admin/authorization.

  7. Vérifiez authorization_decision_logs lorsque 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