Permissions
Qui a droit à quoi. Une permission est une politique rule qui porte sur
l'acteur plutôt que sur l'action ; elle lit l'identité établie à la porte.
Tout est fermé, les grants ouvrent des opérations
Par défaut, personne n'a droit à rien. Un profil reçoit des opérations des API du tenant,
nommées par leur API : boutique.searchProducts, boutique.createOrder. Une opération qu'aucun
profil de l'identité n'a reçue n'est jamais appelée pour elle.
profiles:
anonyme:
when: "identity.authenticated == false"
grants: [boutique.searchProducts, boutique.createOrder]
premium:
when: "identity.authenticated and 'premium' in identity.plans"
grants: [boutique.searchProducts, boutique.createOrder]
support:
when: "'support' in identity.roles"
grants: [paiement.proposeRefund, paiement.createRefund]
manager:
when: "'support-manager' in identity.roles"
grants: [paiement.proposeRefund, paiement.createRefund]
- Le grant porte sur l'opération, pas sur la skill. Une même opération peut être atteinte par une skill, une tâche de playbook, le submit ou une automatisation : la garder elle, c'est les garder toutes.
- Les grants s'additionnent entre les profils d'une même identité.
- Les grants sont le seul endroit qui ouvre. Tout le reste (permissions, gardes, budgets, approbations, sous-identités) ne fait que resserrer ce qu'ils ont ouvert.
- Le visiteur anonyme n'a pas d'exception : ce qu'il peut faire, le profil
anonymele reçoit. - Les opérations forment un ensemble fermé, lu dans l'OpenAPI : un grant qui nomme une opération inconnue est refusé au chargement.
- Core vérifie le grant au moment où il appelle l'API du tenant, en relisant la config. Même si le graphe compilé se trompait, l'appel serait refusé.
Les permissions resserrent, phase par phase
Dans ce que les grants ont ouvert, une garde permission réserve un moment précis à certains
profils. Chaque phase répond à une seule question :
| phase | question | ce qui la règle |
|---|---|---|
invoke | qui peut appeler la skill ? | le grant de l'opération lue, puis une permission dans invoke.guards ; au retour, les kinds classent les items |
present | quels kinds montre-t-on, et à qui ? | une permission dans kinds.<k>.present |
select | qui peut choisir quel kind ? | une permission dans select ou kinds.<k>.select |
submit | qui peut soumettre ? | le grant de l'opération d'écriture, et submitted_by |
Une skill n'a pas de droit à elle : elle est utilisable si les opérations qu'elle appelle sont
accordées, et si ses permissions le permettent. Un playbook s'ouvre si ses tâches requises sont
faisables. Une automatisation n'a pas de profil à elle : ce qu'elle lance agit sous le
propriétaire de la room qu'elle désigne (owner), avec ses grants.
Pour une règle qui ne sert qu'une fois, une garde require sur l'identité suffit, sans créer de
profil : require: "'phone' in identity.verified".
Quand une preuve peut ouvrir la règle, Core ne refuse pas seulement : il demande
l'authentification, avant l'appel pour invoke et present, au choix pour select, au submit pour
submit, puis relance l'appel. Voir Demander l'authentification.
Ce qu'un profil voit : la permission dans present
Les clients qui ne sont pas premium ne voient pas les produits premium :
skills:
- name: articles
invoke: {method: searchProducts}
kinds:
premium:
when: "$fields.tier == 'premium'"
present: {guards: [{permission: {profiles: [premium]}}]}
present:
fields:
name: {from: name, type: text, role: title}
tier: {from: tier, type: text}
Une permission dans present est une règle de sécurité, pas d'affichage :
- Core retire l'item du résultat avant le modèle, sur les deux portes. Ni le modèle ni le widget ne le voient.
- Les comptes se font après le filtre : « 12 produits » ne trahit pas les produits cachés.
- Ce qu'on ne voit pas, on ne le choisit pas :
selecthérite depresent. Sinon, le modèle pourrait ajouter un produit premium en devinant son id. - Quand l'API du tenant sait filtrer, mieux vaut lui passer le filtre, par un paramètre fixé par
l'identité (
set_by: [identity]). Le produit premium ne sort alors jamais de chez le tenant.
per_profile.present sans permission reste de l'interface : des colonnes en plus pour un
manager. Voir Profils.
Réserver un kind au choix
skills:
- name: rembourser
invoke: {method: proposeRefund}
kinds:
gros-montant:
when: "$fields.amount > 50"
select: {guards: [{permission: {profiles: [manager]}}]} # le support voit, le manager choisit
present:
fields:
name: {from: label, type: text, role: title}
amount: {from: amount, type: price, role: billed}
Le support voit le gros remboursement et peut le signaler ; seul le manager peut le choisir.
Les règles se combinent par intersection : une action est permise seulement si toutes les couches la permettent (station, skill, playbook, profil, sous-identité). Voir Comment les couches se combinent. Que ces couches ne se contredisent pas, c'est le graphe des permissions qui le vérifie au chargement.
Qui soumet
Le droit de soumettre se règle dans le submit_guard, par submitted_by : le propriétaire vérifié
de la room, la personne qui a envoyé l'agent, l'approbateur, un rôle, ou l'agent lui-même sous
condition. Celui qui soumet doit aussi avoir le grant de l'opération d'écriture. Voir
Le submit.
Les sous-identités déléguées
Un client de la station crée des sous-identités pour ses agents, sur la page du tenant. Leurs
limites — montant, horaires, adresses, skills interdites, confirmation au-delà d'un montant — sont
des règles comme les autres. Elles ne peuvent que restreindre ; une limite qui élargirait la
station est refusée. La station déclare ce qu'elle permet de limiter dans station.delegation.restrictions.