IFTTT via directives
Gato GraphQL biedt de mogelijkheid om IFTTT-strategieën (If This Then That, "als dit dan dat") te implementeren via directives. Deze directives worden dynamisch aan de query toegevoegd wanneer een bepaald veld of een bepaalde directive aanwezig is in de query.
In het algemeen zijn IFTTT regels die acties activeren wanneer een bepaalde gebeurtenis plaatsvindt. In ons geval zien de paren van gebeurtenis/actie er als volgt uit:
- Als "veld X gevonden in de query", dan "voeg directive Y toe aan veld X"
- Als "directive Z gevonden in de query", dan "voer directive Y uit voor/na directive Z"
Het dynamisch toevoegen van IFTTT-directives aan het schema is een recursief proces: zo'n directive kan zelf ook een eigen set IFTTT-directives geconfigureerd hebben, die ook aan de directive-keten worden toegevoegd.
Waar het wordt gebruikt
Achter de schermen gebruiken clients in Gato GraphQL dit mechanisme om het GraphQL-schema te configureren.
Zo stelt Access Control je in staat te selecteren welke toegangscontrolregels je wilt toepassen op operaties, velden en directives. Het zijn de IFTTT-regels die ervoor zorgen dat deze regels worden toegepast op die elementen van het GraphQL-schema.

In het algemeen zijn dit enkele gebruiksscenario's:
Definieer de cache control max-age per veld
Voeg een @CacheControl-directive toe aan alle velden en pas de waarde van de parameter maxAge aan: 1 jaar voor het veld url van Post, en 1 uur voor het veld title.
Toegangscontrole instellen
Voeg een @validateDoesLoggedInUserHaveAnyRole-directive toe aan het veld email van het type User, zodat alleen beheerders het e-mailadres van de gebruiker kunnen opvragen.
Toegangscontrole synchroniseren met cache control
Door directives aan elkaar te koppelen, kunnen we ervoor zorgen dat wanneer er wordt gevalideerd of de gebruiker toegang heeft tot een veld/directive, het antwoord niet wordt gecached. Bijvoorbeeld:
- Voeg directive
@validateIsUserLoggedIntoe aan veldme - Voeg directive
@CacheControlmet argumentwaarde0voormaxAgetoe aan directive@validateIsUserLoggedIn.
Beveiliging versterken
Voeg een @validateIsUserLoggedIn-directive toe aan directive @translate, om te voorkomen dat kwaadwillenden queries uitvoeren tegen de GraphQL-service die de server kunnen overbelasten en de kosten kunnen opdrijven (in dit geval is @translate gebaseerd op Google Translate, waarvoor betaald wordt per gebruik)
Hoe het werkt
Hoe voegen we directives toe aan het schema via IFTTT? Stel dat we bijvoorbeeld een aangepaste directive @authorize(role: String!) willen maken, om te valideren dat de gebruiker die veld myPosts opvraagt de verwachte rol author heeft, of anders een foutmelding toont.
Als we het schema via SDL hadden aangemaakt, zou het er zo uitzien:
directive @authorize(role: String!) on FIELD_DEFINITION
type User {
myPosts: [Post] @authorize(role: "author")
}De IFTTT-regel definieert dezelfde bedoeling als de bovenstaande SDL: wanneer veld myPosts wordt opgevraagd, voer directive @authorize(role: "author") daarop uit. Wanneer veld myPosts vervolgens in de query wordt gevonden, voegt de engine automatisch @authorize(role: 'author') toe aan dat veld in de uitvoerbare query.
IFTTT-regels kunnen ook worden geactiveerd bij het tegenkomen van een directive, niet alleen een veld. We kunnen bijvoorbeeld de regel instellen: "Wanneer directive @translate in de query wordt gevonden, voer directive @cache(time: 3600) uit op dat veld".
Het toevoegen van IFTTT-directives aan de query is een recursief proces: het activeert een nieuwe gebeurtenis die door de IFTTT-regels wordt verwerkt, waarbij mogelijk andere directives aan de query worden toegevoegd, enzovoort.
Zo zorgt de regel "Wanneer directive @cache wordt gevonden, voer directive @log uit" voor een logvermelding over de uitvoering van het veld, en activeert dan een nieuwe gebeurtenis met betrekking tot deze nieuw toegevoegde directive.
Instellen via PHP-code
Het type User heeft velden roles en capabilities, die als gevoelige informatie kunnen worden beschouwd en daarom niet toegankelijk mogen zijn voor willekeurige gebruikers.
We kunnen directive @validateDoesLoggedInUserHaveAnyRole aan deze twee velden toevoegen, geconfigureerd om te valideren dat alleen een gebruiker met een bepaalde rol (geconfigureerd via een omgevingsvariabele) er toegang toe heeft. De configuratie wordt aangeleverd via een CompilerPass:
$accessControlManagerDefinition = $containerBuilderWrapper->getDefinition(AccessControlManagerInterface::class);
if ($roles = Environment::anyRoleLoggedInUserMustHaveToAccessRolesFields()) {
$accessControlManagerDefinition->addMethodCall(
'addEntriesForFields',
[
UserRolesAccessControlGroups::ROLES,
[
[RootObjectTypeResolver::class, 'roles', $roles],
[UserObjectTypeResolver::class, 'roles', $roles],
[RootObjectTypeResolver::class, 'capabilities', $roles],
[UserObjectTypeResolver::class, 'capabilities', $roles],
]
]
);
}
if ($capabilities = Environment::anyCapabilityLoggedInUserMustHaveToAccessRolesFields()) {
$accessControlManagerDefinition->addMethodCall(
'addEntriesForFields',
[
UserCapabilitiesAccessControlGroups::CAPABILITIES,
[
[RootObjectTypeResolver::class, 'roles', $capabilities],
[UserObjectTypeResolver::class, 'roles', $capabilities],
[RootObjectTypeResolver::class, 'capabilities', $capabilities],
[UserObjectTypeResolver::class, 'capabilities', $capabilities],
]
]
);
}Bij het uitvoeren van de query krijgen niet-ingelogde gebruikers en gebruikers zonder de vereiste rollen geen toegang tot die velden.