Aller au contenu principal
Fonctionnement du SDK

Cette page n’est pas encore traduite : la version anglaise est affichée.

Access & Roles

Les rôles se composent, ils ne sont jamais codés en dur. Les permissions atomiques se combinent en rôles ; les rôles filtrent les données jusqu’à la ligne et à la colonne, et décident même quelles interfaces de module un build voit. Ce modèle remplace la séparation entre clé client et clé serveur : un identifiant désigne une identité, et ses rôles sont résolus à chaque requête.

Quand l’utiliser

  • Il vous faut un identifiant (credential) plus restreint que « client » ou « serveur » : derrière lui, des rôles composés sont résolus à chaque requête.
  • L’accès aux données doit s’arrêter aux lignes et aux colonnes : restriction par région, masquage des données personnelles, prestataires en lecture seule.
  • Un build ne doit voir que les interfaces que son rôle débloque : pour un visiteur, expulser et fermer n’existent tout simplement pas.
  • Votre UI doit griser les boutons avec exactitude : CanI évalue la même politique que celle que le serveur appliquera.
  • Inutile si les presets fournis (player, room-owner, seller, …) correspondent déjà à vos acteurs. Tous les modules les respectent par défaut ; le catalogue complet se trouve dans Concepts clés.

Qui fait quoi

ActeurCe qu’il fait sur cette page
operatordéclare les rôles et les politiques, fixe les limites par ligne et par colonne, attribue les rôles, émet les clés
match-organizerl’équipe du tournoi : détient une clé aux rôles composés, filtre les inscriptions, ne peut pas rembourser
chaque acteurvérifie CanI avant d’agir ; ne voit que les interfaces qu’il a débloquées

En bref

Déclarer entry-validator avec des limites par ligne et par colonne, l’attribuer, puis vérifier CanI avant d’agir
[Role("entry-validator")]
public class EntryValidator
{
    [Allow(Rooms.Membership.Administer)] public Permit GateEntries;
    [Allow(Data.Records.Read, table: "player_profile", rows: "banned == false",
           columns: "id, display_name")] public Permit SeeProfiles;
}

await PlayServ.Access.Grant(staffId, Roles.EntryValidator, Roles.MatchOrganizer);
var key = await PlayServ.Access.IssueKey(staffId);   // the credential names no roles

// any actor, before attempting an operation:
if (await PlayServ.Access.CanI(Commerce.Orders.Administer)) Hud.ShowRefund();
@Role('entry-validator')
export class EntryValidator {
  @Allow(Rooms.membership.administer) gateEntries: Permit;
  @Allow(Data.records.read, { table: 'player_profile', rows: 'banned == false',
                              columns: ['id', 'display_name'] }) seeProfiles: Permit;
}

await playserv.access.grant(staffId, Roles.entryValidator, Roles.matchOrganizer);
const key = await playserv.access.issueKey(staffId);  // the credential names no roles

// any actor, before attempting an operation:
if (await playserv.access.canI(Commerce.orders.administer)) hud.showRefund();
@role("entry-validator")
class EntryValidator:
    gate_entries = allow(rooms.membership.administer)
    see_profiles = allow(data.records.read, table="player_profile",
                         rows="banned == false", columns=["id", "display_name"])

await playserv.access.grant(staff_id, roles.ENTRY_VALIDATOR, roles.MATCH_ORGANIZER)
key = await playserv.access.issue_key(staff_id)   # the credential names no roles

# any actor, before attempting an operation:
if await playserv.access.can_i(commerce.orders.administer):
    hud.show_refund()
Bientôt — Go

La déclaration par attributs en Go n’est pas encore tranchée (O-1). Les exemples arriveront quand la forme d’écriture sera choisie.

// declarations compile into the same pushed model — playserv push from the UE project or CI
USTRUCT(PSRole = "entry-validator")
struct FEntryValidator
{
    GENERATED_BODY()

    UPROPERTY(PSAllow = (Atom = "Rooms.Membership.Administer"))
    FPSPermit GateEntries;

    UPROPERTY(PSAllow = (Atom = "Data.Records.Read", Table = "player_profile",
                         Rows = "banned == false", Columns = "id, display_name"))
    FPSPermit SeeProfiles;
};

// granting is an operator act; a build checks what its identity resolves to
const FPSActor Me = Client->Whoami();   // which interfaces this actor unlocks
Client->Access->CanI(TEXT("Commerce.Orders.Administer"),
    TPSOnResult<bool>::CreateWeakLambda(this, [this](const TPSResult<bool>& Result)
    {
        if (!Result.HasValue()) { return; }
        if (Result.Value()) { Hud->ShowRefund(); }
    }));
// co_await support ships later: a built-in SDK coroutine wrapper that respects Unreal's GC and threading.
// the same `[Role]` / `[Allow]` declaration as the server tab, on the Unity 2021.3 runtime
var me = playserv.Whoami();               // which interfaces this actor unlocks
if (await playserv.Access.CanI(Commerce.Orders.Administer)) hud.ShowRefund();

Une permission atomique (atom) est une paire : une ressource et l’un de quatre verbes — read, write, execute, administer. L’ensemble des verbes est fixe : un cas qui n’y entre pas conduit à scinder la ressource, pas à allonger la liste. C’est pourquoi filtrer l’inscription de quelqu’un d’autre s’écrit Rooms.Membership.Administer et non avec un verbe ValidateEntry dédié : agir sur l’appartenance d’un autre acteur relève de l’administration, alors que rejoindre soi-même correspond à Rooms.Membership.Write sur la même ressource.

Le modèle

L’ACL des données, c’est rôle × opération × prédicat de lignes × masque de champs — un seul modèle, identique qu’il soit écrit dans le code, via l’API ou dans la grille des rôles du panneau.

De quoi l’accès est fait.

TermeCe que c’est
permission atomiqueune paire ressource × verbe. Les quatre verbes sont read (récupérer, sélectionner, s’abonner), write (créer, modifier, supprimer, et agir pour soi-même — rejoindre, quitter), execute (invoquer une fonction, appliquer une capacité) et administer (agir sur les autres : expulser, fermer, forcer un changement d’état)
rôleun ensemble nommé de permissions atomiques. Il peut inclure un autre rôle
preset de rôleconstruit sur des permissions atomiques, il continue de fonctionner sans changement pour les consommateurs déjà déployés. C’est un point de départ, pas une contrainte : un projet déclare ses propres rôles à partir des mêmes permissions atomiques
prédicat de lignesquelles lignes — un prédicat booléen sur des valeurs de session
masque de champsquels champs, déclaré par rôle et par opération

Ce en quoi un identifiant se résout.

IdentifiantCe qu’il débloque
clé joueurun build du moteur la porte ; le joueur derrière elle arrive avec la connexion (sign-in), et le build voit les lignes cl de chaque tableau d’opérations
clé hôteun serveur dédié ou un client maître (master-client) la détient, et ses rôles débloquent les lignes mc
code publiés’exécute avec le rôle backend-service du projet — c’est ce que vérifie Authoritative = true sur un classement (leaderboard)
un hook enregistrén’accorde rien de plus : votre fonction garde le rôle sous lequel elle a été déployée

La légende des étiquettes (fn / cl / mc / adm) se trouve dans Concepts clés.

Ce qui vaut pour chaque vérification.

ToujoursCe que c’est
un identifiantne porte aucune liste de rôles : il désigne une identité, et les rôles sont résolus côté serveur à chaque requête. Une révocation invalide immédiatement la résolution en cache, sans attendre l’expiration de son délai de fraîcheur déclaré
la délégationchange la portée, jamais la capacité d’agir : agir au nom d’un joueur change quelles lignes sont visibles et à qui une écriture est attribuée, mais n’accorde aucune opération que l’acteur ne détenait pas déjà
le verbedit quel type d’effet, et le prédicat dit quelles lignes. Si deux cas ne diffèrent que par à qui appartient la ligne, c’est un prédicat ; si l’effet lui-même diffère, c’est une autre opération et peut-être un autre verbe — c’est pourquoi « expulser » est administer et non write avec un prédicat large
un propriétairese voit toujours lui-même, quoi que dise par ailleurs un prédicat
la visibilitén’est pas de la sécurité — une optimisation des canaux et une permission sont deux mécanismes différents, et aucun ne remplace l’autre
un module désactivén’a aucune interface : l’activation d’un module est une propriété du build, donc la génération de code n’émet rien pour un module désactivé, et un appel indisponible est une erreur de compilation plutôt qu’un refus au runtime
l'interface d'un modulesuit l’acteur plutôt que le côté (voir Qui prend les décisions) : un build room-visitor voit rejoindre, quitter et lire, un build room-owner voit en plus expulser, fermer et configurer, et whoami indique quelles interfaces l’acteur courant débloque

Qui attribue un rôle. Attribuer et révoquer un rôle à un joueur, ainsi que le rôle par défaut que le projet déclare pour un nouveau joueur, sont des opérations d’Auth & Players — ce module gère les identités, et un rôle se résout à partir de l’identité contenue dans l’identifiant.

Erreurs

  • Ce qu’un prédicat masque répond not found, pas forbidden : sinon, le refus lui-même révélerait à l’appelant que la chose existe.
  • Un droit que l’appelant ne détient pas répond forbidden lorsque l’existence de l’objet n’est pas un secret, et la réponse nomme ce qui manquait au lieu d’échouer sans explication.
  • Un champ hors du masque est absent de la réponse, et non présent mais vide : une valeur vide et une valeur masquée seraient impossibles à distinguer.
  • Un rôle qui s’inclut lui-même, directement ou par une chaîne, est une erreur de configuration — refusé au moment de la déclaration plutôt que résolu au runtime.
  • La délégation n’élargit jamais la capacité d’agir : un appel que l’acteur ne pourrait pas faire en son propre nom est refusé lorsqu’il est fait au nom d’un joueur.

Limites

Les chiffres arriveront avec le chapitre sur les limites de la plateforme ; ce qui se passe à chaque plafond est déjà fixé.

  • La taille d’une sélection sous un prédicat de lignes est bornée, et le modèle d’ACL déclare cette borne au lieu de la découvrir. Une lecture qui dépasse le plafond reçoit autant de lignes que le plafond le permet, plus un marqueur indiquant la troncature — jamais une page raccourcie en silence.
  • Le délai de fraîcheur d’une permission résolue est déclaré, et une révocation ne l’attend pas : elle invalide immédiatement.