Hallo zusammen,
das Policy-Framework von Neos und Flow ist leider manchmal schwer zu verwenden - auch für mich Ich versuche hier mal, nicht einfach ne Lösung hinzuschreiben, sondern etwas detaillierter zu erklären wie es sich verhält.
tl;dr: Die gesamte Lösung ist im Beispiel für Regel 4 enthalten.
Regel 1: Alle Nodes, die NICHT von einem PrivilegeTarget gematcht werden, sind erlaubt
Beispiel: Ich darf alle Methoden aufrufen, wo es kein MethodPrivilege gibt, welches den Zugriff einschränkt.
Bei Method Privileges (Rechten auf Methodenaufrufe) lässt sich dies durch die Implementierung erklären: Für alle Methoden, welche von einem MethodPrivilege “erreicht” werden, generiert Flow Code à la:
public function myProtectedFunction() {
checkUserIsAllowedToCallThisMethod();
return parent::myProtectedFunction(); // call original function
}
Für alle anderen Methoden (wo es kein Privilege Target gibt), wird kein Proxy-Code generiert, das heißt die sind normal erlaubt.
Bei Nodes verhält es sich analog. Bezogen auf Nodes heißt das: Wenn ein Node von KEINEM Node Privilege Target gematcht wird, ist der Zugriff erlaubt.
Regel 2: Alle Nodes, die von einem Privilege Target gematcht werden, sind verboten
Wenn wir jetzt anfangen, ein Privilege Target zu definieren, dann ändern wir quasi den Default von “Erlauben”, auf “Verbieten”, es sei denn der Nutzer hat die entsprechende Rolle.
Als Beispiel:
privilegeTargets:
'Neos\Neos\Security\Authorization\Privilege\EditNodePrivilege':
'Vendor.Project:AllNodes':
matcher: 'TRUE'
Nur durch dieses PrivilegeTarget (ohne Rollenzuweisung) wird dafür gesorgt, dass KEIN node im System editiert werden kann.
Regel 3: Immer mit GRANT arbeiten
Wenn ich bspw. nun dafür sorgen will, dass mein Administrator weiterhin alle Nodes bearbeiten kann, muss ich ihm das AllNodes
privilege target zuweisen:
roles:
'Vendor.Project:AccessSite2':
privileges:
-
privilegeTarget: 'Vendor.Project:AllNodes'
permission: GRANT
Hierbei eine Empfehlung: Bitte nicht mit DENY arbeiten, sondern immer nur mit GRANT.
Exkurs: Grant vs. Deny
Grant ist composable, d.h. wenn ich zwei PrivilegeTargets mit Grant erlaube, dann darf ich exakt das, was Privilege Target 1 erlaubt, plus das, was Privilege Target 2 erlaubt.
Wenn ich nun anfange, deny zu verwenden, kann ich aus meinem Set von erlaubten Privilege Targets wieder etwas “ausschneiden”, was “doch” verboten sein soll.
Sobald ich Deny verwende, ist meine Policy nicht mehr composable, da ein Deny immer “gewinnt”, ganz egal was ich noch an Grants habe.
Deny ist als escape hatch (letztes Mittel) gedacht, da man es manchmal zur Umsetzung besonders komplexer Regeln benötigt.
Regel 4: Wenn auf einen Node mehrere PrivilegeTargets zutreffen, ist der Zugriff erlaubt, sobald der Nutzer eines dieser PrivilegeTargets granted hat …
… Diese Regel gilt nur, wenn man kein deny verwendet (s.o.; composability).
Das bedeutet - jetzt kommen wir dem konkreten Use Case näher:
privilegeTargets:
'Neos\Neos\Security\Authorization\Privilege\EditNodePrivilege':
'Vendor.Project:AllNodes':
matcher: 'TRUE'
'Vendor.Project:AccessSite2':
matcher: 'isDescendantNodeOf("3f0bc644-4a48-11e9-8646-d663bd873d93")'
roles:
'Vendor.Project:AccessSite2':
privileges:
-
privilegeTarget: 'Vendor.Project:AccessSite2'
permission: GRANT
'Neos.Neos:Administrator':
privileges:
-
privilegeTarget: 'Vendor.Project:AllNodes'
permission: GRANT
Was passiert nun hier?
- durch die
AllNodes
Regel müssen für alle Nodes die Rechte explizit gesetzt werden.
- wenn ich die Rolle Administrator habe, habe ich ein Recht auf alle Nodes
- wenn ich die Rolle AccessSite2 habe, ist alles für mich verboten (wg. der AllNodes-Regel), außer dem Teilbaum, der durch AccessSite2 definiert ist - diese nodes sind erlaubt.
zusätzliche Problematik: Einschränken des Seitenbaumes
Es war ja gefordert, dass zusätzlich noch der Seitenbaum eingeschränkt werden soll auf den entsprechenden Teilbaum, den der Redakteur sehen kann.
Mit dem Recht von gerade eben sieht der Redakteur weiterhin den gesamten Baum, er darf aber nur noch in seinem Teilbaum editieren.
Wir haben zwar ein NodeTreePrivilege
, mit dem Verhalten bin ich aber nicht so sehr happy: Dieses erbt nämlich von EditNodePrivilege, d.h. wenn ich eine Seite im Node Tree sehe, dann kann ich sie automatisch auch editieren.
IMHO ist das ein Thema, was wir im Core ändern müssen: Wir müssen zusätzlich ermöglichen, eine Seite im Seitenbaum readonly anzuzeigen - über einen extra privilege type.
Soweit ich weiß, ist es nämlich momentan nicht standardisiert möglich, den “Root”-Node des Seitenbaumes zu manipulieren, also zu sagen “bitte beginne an Seite X” mit dem Rendering.
Ich hatte dieses Verhalten mal auf der neos.io Seite gesehen, aber das ist IMHO kein standardisiertes Verhalten, auf das man sich verlassen sollte.
Da ich mir da nicht sicher bin, ob es hier eine gute Lösung gibt, habe ich die Frage selbst mal auf "Start Point" in the Neos Document Tree gestellt.
Closing Thoughts
Ich hoffe, diese Beschreibung hier hilft ein bisschen IMHO ist das Thema definitiv einer der Schwachpunkte von Neos. Das Policy Framework intern ist recht “low-level”, und für Endbenutzer und diese Common Cases wie den, den du beschrieben hast, leider etwas komplex.
Aus dem Grunde bin ich gerade auch dabei, ein Package zu schreiben, um diese Art von Einschränkungen auch per Backend Module zu machen. Ich kann da zeitlich noch nichts versprechen, aber wenn da jemand mithelfen will, das wäre super cool (Einen Prototypen habe ich lokal schon, der zeigt dass das Package grundsätzlich funktioniert).
Hätte jemand Lust, die Inhalte aus diesem Post in Backend Permissions - Manual - Guide - Neos Docs einfließen zu lassen? Das wäre super (dann bitte hier oder im Slack in guild-documentation melden)
Viele Grüße,
Sebastian