Eigene Rollen und Berechtigungen sauber modellieren
Magento 2 steuert jeden Klick im Backend über ein hierarchisches Admin-ACL-System: acl.xml deklariert den Resource-Baum, Rollen unter System, Permissions, User Roles binden Administratoren an genau die Ressourcen, die sie benötigen, und isAllowed()-Prüfungen in Controllern, Blocks und ViewModels setzen diese Rechte zur Laufzeit durch. Wer eigene Module baut, ohne eigene ACL-Ressourcen zu deklarieren, öffnet neue Admin-Funktionen standardmäßig für jede Rolle mit Zugriff auf die übergeordnete Ressource, ein Risiko, das oft erst beim nächsten Rollen-Audit sichtbar wird. Dieser Beitrag zeigt, wie man den Resource-Baum korrekt aufbaut, eigene ACL-Ressourcen für Custom-Module modelliert, Berechtigungen in Controllern und ViewModels prüft und die häufigsten Fallstricke rund um acl.xml, system.xml-Bindungen und Config-Caching vermeidet.
Inhaltsverzeichnis
- 1. Was Admin-ACL in Magento 2 wirklich regelt
- 2. Der Resource-Baum in acl.xml: Vererbung und sortOrder
- 3. Eigene ACL-Ressourcen für ein Custom-Modul deklarieren
- 4. Rollen und Benutzer: Wie ACL-Ressourcen im Rollen-Grid wirken
- 5. isAllowed() in Controller, Block und ViewModel prüfen
- 6. _isAllowed() im Custom Admin-Controller überschreiben
- 7. system.xml und ACL: Config-Sections richtig binden
- 8. ACL-Caching und Neuladen von Berechtigungen
- 9. Typische Fehler bei der Admin-ACL-Modellierung
- 10. Zusammenfassung
- 11. FAQ
1. Was Admin-ACL in Magento 2 wirklich regelt
Die Admin-ACL von Magento 2 ist die zentrale Zugriffskontrolle für das Backend: Sie entscheidet, welcher Menüpunkt in der Admin-Navigation sichtbar ist, welche Controller-Aktion ausgeführt werden darf und welcher Konfigurationsbereich unter Stores, Configuration überhaupt angezeigt wird. Anders als die Kundengruppen-Berechtigungen im Storefront ist die Admin-ACL vollständig hierarchisch aufgebaut: Jede Ressource hängt an einem Elternknoten, bis hinauf zur Wurzel Magento_Backend::admin. Diese Baumstruktur wird über acl.xml-Dateien aus allen aktiven Modulen zu einem einzigen Resource-Baum zusammengeführt.
Für Entwickler bedeutet das konkret: Ein eigenes Modul, das eine neue Admin-Seite, eine Export-Funktion oder einen zusätzlichen Konfigurationsbereich mitbringt, muss diese Funktionalität als eigene ACL-Ressource im Baum verankern. Geschieht das nicht, erbt die neue Funktion automatisch die Berechtigung ihres nächsten deklarierten Elternknotens, im schlechtesten Fall direkt Magento_Backend::admin. Damit ist die Aktion für jede Rolle sichtbar und ausführbar, die irgendeinen administrativen Zugriff hat, unabhängig davon, ob das gewollt war. Ein sauber modelliertes Admin-ACL-Konzept ist deshalb kein Nice-to-have, sondern eine Grundvoraussetzung für jedes produktiv eingesetzte Custom-Modul.
2. Der Resource-Baum in acl.xml: Vererbung und sortOrder
Jedes Modul kann unter etc/acl.xml eigene Knoten in den globalen Resource-Baum einhängen. Das Schema verlangt ein <acl>-Root-Element mit genau einem <resources>-Container, in dem beliebig tief verschachtelte <resource>-Elemente stehen. Jede Ressource besitzt eine id im Format Vendor_Module::identifier, einen title, der im Rollen-Grid als Label erscheint, und ein optionales sortOrder-Attribut, das die Reihenfolge innerhalb der Geschwisterknoten bestimmt. Die Konvention, sortOrder-Werte in Zehnerschritten zu vergeben, hat sich bewährt: Sie lässt Raum, später weitere Ressourcen zwischen bestehende einzufügen, ohne den gesamten Baum umnummerieren zu müssen.
Die Vererbung im Resource-Baum betrifft vor allem die Darstellung im Rollen-Grid: Wird ein Elternknoten in der Baum-Auswahl deaktiviert, verschwinden alle Kindknoten automatisch mit. Zur Laufzeit gilt das aber nicht automatisch umgekehrt: Eine Rolle, der explizit nur eine Kind-Ressource zugewiesen wurde, hat damit noch keinen automatischen Zugriff auf die Eltern-Ressource, wenn diese separat per isAllowed() geprüft wird. Magento persistiert bei der Rollenzuweisung exakt die im Baum markierten Resource-IDs in der Tabelle authorization_rule, nicht implizit den gesamten Vererbungspfad. Diese Unterscheidung zwischen visueller Baum-Vererbung im Rollen-Grid und tatsächlicher Rechteprüfung zur Laufzeit ist einer der am häufigsten missverstandenen Aspekte der Admin-ACL.
3. Eigene ACL-Ressourcen für ein Custom-Modul deklarieren
Für ein eigenes Modul empfiehlt sich ein flacher, klar benannter Ressourcenbaum: ein Root-Knoten für das Modul selbst, darunter je ein Knoten für Dashboard, Grid-Ansichten, kritische Einzelaktionen wie Löschen oder Export, und ein separater Knoten für die Konfiguration. Diese Granularität erlaubt es später, einer Rolle zum Beispiel nur Lesezugriff auf ein Grid zu geben, aber die Lösch-Aktion gezielt zu entziehen, ohne dafür eine komplett neue Rolle anlegen zu müssen. Die folgende acl.xml zeigt diesen Aufbau für ein Modul, das Redirects verwaltet.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
<acl>
<resources>
<resource id="Magento_Backend::admin">
<resource id="Mironsoft_SeoSuite::seosuite" title="SEO Suite" sortOrder="200">
<resource id="Mironsoft_SeoSuite::seosuite_dashboard" title="Dashboard" sortOrder="10"/>
<resource id="Mironsoft_SeoSuite::seosuite_redirects" title="Redirects verwalten" sortOrder="20">
<resource id="Mironsoft_SeoSuite::seosuite_redirects_delete" title="Redirects loeschen" sortOrder="10"/>
</resource>
<resource id="Mironsoft_SeoSuite::seosuite_config" title="Konfiguration" sortOrder="30"/>
</resource>
</resource>
</resources>
</acl>
</config>
Wichtig ist die konsequente Einhaltung des Vendor_Module::identifier-Formats bei jeder einzelnen ID. Ein Tippfehler oder ein fehlendes Präfix führt nicht zu einem Fehler beim Deploy, sondern zu einer Ressource, die im Baum erscheint, deren isAllowed()-Prüfung im Code aber niemals greift, weil der referenzierte String nicht existiert. Diese Klasse von Fehlern fällt in der Regel erst auf, wenn ein Kunde meldet, dass eine eingeschränkte Rolle trotzdem Zugriff auf eine Funktion hat, die eigentlich gesperrt sein sollte.
4. Rollen und Benutzer: Wie ACL-Ressourcen im Rollen-Grid wirken
Der Bereich System, Permissions, User Roles ist die Oberfläche, über die Administratoren ACL-Rollen definieren und den zuvor deklarierten Resource-Baum als Checkbox-Tree den Rollen zuweisen. Jede Rolle besitzt entweder die Option „All“, die pauschal Zugriff auf den kompletten Baum gewährt, oder „Custom“, bei der einzelne Ressourcen im Tree markiert werden. Intern legt Magento für jede Rolle einen Eintrag in authorization_role an, und für jede zugewiesene Ressource einen Datensatz in authorization_rule mit role_id, resource_id und der Permission allow. Benutzer selbst werden im Bereich User Roles einer Rolle zugeordnet und erben damit exakt deren Ressourcen-Set.
Für die Praxis heißt das: Admin-Berechtigungen sind nicht direkt am Benutzer, sondern immer über die Rolle definiert. Ein Standard-Fehler bei Migrationsprojekten ist es, Benutzer testweise auf „All“ zu setzen, um schneller voranzukommen, und diese großzügige Rolle dann versehentlich produktiv zu übernehmen. Wer ACL-Rollen sauber modelliert, definiert stattdessen von Anfang an mehrere abgestufte Rollen, zum Beispiel eine Rolle mit reinem Lesezugriff auf Reports, eine Rolle mit Schreibzugriff auf Katalogdaten, und eine schmale Administratorrolle, die tatsächlich nur die eigenen Custom-Module vollständig verwalten darf.
5. isAllowed() in Controller, Block und ViewModel prüfen
Über den Injectable Service Magento\Framework\AuthorizationInterface lässt sich an praktisch jeder Stelle im Code prüfen, ob die aktuell eingeloggte Admin-Session Zugriff auf eine bestimmte ACL-Ressource hat. Die Methode isAllowed(string $resourceId): bool wertet dabei den Baum aus, der zur eingeloggten Rolle in authorization_rule hinterlegt ist. Das ist besonders relevant, wenn eine Aktion zwar über einen Controller erreichbar ist, ihr Button aber nur unter bestimmten Bedingungen im Template erscheinen soll, etwa ein Löschen-Button in einer Redirects-Übersicht.
<?php
declare(strict_types=1);
namespace Mironsoft\SeoSuite\ViewModel;
use Magento\Framework\Authorization;
use Magento\Framework\View\Element\Block\ArgumentInterface;
/**
* ViewModel to expose ACL-based visibility flags to templates.
*/
class RedirectPermissions implements ArgumentInterface
{
/**
* @param Authorization $authorization Magento authorization service for ACL checks
*/
public function __construct(
private readonly Authorization $authorization
) {
}
/**
* Checks whether the current admin user may delete redirects.
*
* @return bool
*/
public function canDeleteRedirects(): bool
{
return $this->authorization->isAllowed('Mironsoft_SeoSuite::seosuite_redirects_delete');
}
}
Diese Art der Prüfung ist rein kosmetisch: Sie blendet UI-Elemente aus, ersetzt aber niemals die serverseitige Durchsetzung. Ein Benutzer, der die Backend-URL der Lösch-Aktion direkt aufruft, würde ohne zusätzliche Absicherung im Controller trotzdem die Aktion ausführen können, selbst wenn der Button im Template unsichtbar war. Deshalb gehört jede isAllowed()-Prüfung im Template oder ViewModel immer zusammen mit einer entsprechenden Prüfung auf Controller-Ebene, nicht als Ersatz dafür.
6. _isAllowed() im Custom Admin-Controller überschreiben
Jeder Backend-Controller, der von Magento\Backend\App\Action erbt, deklariert über die Konstante ADMIN_RESOURCE die Standard-Ressource, gegen die Magento vor Ausführung der execute()-Methode automatisch prüft. Reicht diese einfache Prüfung nicht aus, etwa weil eine Aktion sowohl die spezifische Lösch-Ressource als auch die übergeordnete Grid-Ressource voraussetzen soll, überschreibt man die geschützte Methode _isAllowed() und kombiniert mehrere isAllowed()-Aufrufe.
<?php
declare(strict_types=1);
namespace Mironsoft\SeoSuite\Controller\Adminhtml\Redirect;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\Controller\ResultFactory;
use Magento\Framework\Controller\ResultInterface;
use Magento\Framework\Exception\NoSuchEntityException;
use Mironsoft\SeoSuite\Api\RedirectRepositoryInterface;
/**
* Deletes a single redirect entity from the admin grid.
*/
class Delete extends Action
{
/**
* ACL resource required to access this controller.
*/
public const ADMIN_RESOURCE = 'Mironsoft_SeoSuite::seosuite_redirects_delete';
/**
* @param Context $context Backend action context
* @param RedirectRepositoryInterface $redirectRepository Repository for redirect entities
*/
public function __construct(
Context $context,
private readonly RedirectRepositoryInterface $redirectRepository
) {
parent::__construct($context);
}
/**
* Executes the delete action after the ACL check in _isAllowed() has passed.
*
* @return ResultInterface
* @throws NoSuchEntityException
*/
public function execute(): ResultInterface
{
$id = (int) $this->getRequest()->getParam('id');
$this->redirectRepository->deleteById($id);
$result = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
return $result->setPath('*/*/');
}
/**
* Overrides the default ACL check to additionally require the parent
* "redirects" resource, not only the delete-specific leaf resource.
*
* @return bool
*/
protected function _isAllowed(): bool
{
return $this->_authorization->isAllowed(self::ADMIN_RESOURCE)
&& $this->_authorization->isAllowed('Mironsoft_SeoSuite::seosuite_redirects');
}
}
Der Dispatch-Mechanismus von Magento\Backend\App\AbstractAction ruft _isAllowed() vor jedem Controller-Aufruf automatisch auf und liefert bei false eine 403-Antwort samt Redirect auf die Login- oder Denied-Seite, ohne dass execute() überhaupt erreicht wird. Diese zentrale Durchsetzung ist der eigentliche Kern der Admin-ACL: Sie greift unabhängig davon, ob ein Frontend-Entwickler daran gedacht hat, den Löschen-Button im Template zu verstecken.
7. system.xml und ACL: Config-Sections richtig binden
Konfigurationsbereiche unter Stores, Configuration werden über system.xml deklariert, und auch hier greift die Admin-ACL: Jede <section> kann über ein <resource>-Kindelement an eine ACL-Ressource gebunden werden. Fehlt dieses Element, fällt Magento auf die generische Ressource Magento_Config::config zurück, dieselbe Ressource, die praktisch jede Rolle mit Zugriff auf irgendeinen Konfigurationsbereich besitzt. Für sensible Einstellungen, etwa API-Zugangsdaten oder Feature-Flags mit Auswirkung auf Preise, ist eine eigene, engere Ressource fast immer die richtige Wahl.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
<system>
<tab id="mironsoft" translate="label" sortOrder="500">
<label>Mironsoft</label>
</tab>
<section id="mironsoft_seosuite" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="1">
<label>SEO Suite</label>
<tab>mironsoft</tab>
<resource>Mironsoft_SeoSuite::seosuite_config</resource>
<group id="general" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Allgemein</label>
<field id="enabled" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Aktiviert</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
</group>
</section>
</system>
</config>
Die Bindung wirkt auf zwei Ebenen zugleich: Sie entscheidet, ob die Section überhaupt im linken Navigationsbaum der Konfigurationsseite auftaucht, und sie wird zusätzlich beim Speichern der Konfigurationswerte im Magento\Config\Model\Config-Save-Controller geprüft. Eine Rolle ohne die passende Admin-Berechtigung sieht die Section entweder gar nicht oder erhält beim Versuch, Werte zu speichern, eine Fehlermeldung, selbst wenn sie über einen direkten Link auf die URL zugreift.
8. ACL-Caching und Neuladen von Berechtigungen
Der Resource-Baum aus allen acl.xml-Dateien wird, wie die meisten deklarativen XML-Konfigurationen in Magento, über den config-Cache-Typ zwischengespeichert. Nach jeder Änderung an einer acl.xml, sei es eine neue Ressource, ein geänderter sortOrder oder ein umbenanntes title-Attribut, muss dieser Cache-Bereich geleert werden, damit die Änderung im Rollen-Grid sichtbar wird. Wichtig ist die Abgrenzung: Die eigentliche Rollenzuweisung in authorization_rule liegt in der Datenbank und ist von diesem Cache vollständig unabhängig, ein Cache-Flush löscht also niemals versehentlich bestehende Rollenrechte.
# ACL resource tree is part of the "config" cache type
bin/magento cache:status | grep config
# Flush only the config cache after every acl.xml change
bin/magento cache:flush config
# Verify a role's persisted permissions directly in the database
bin/mysql -e "SELECT resource_id, permission FROM authorization_rule WHERE role_id = 2;"
In der Praxis reicht während der Entwicklung oft ein gezieltes bin/magento cache:flush config, ein vollständiger cache:flush ist nur nötig, wenn parallel weitere XML-Konfigurationen geändert wurden. Nach einem Deployment über setup:upgrade wird der Cache ohnehin automatisch invalidiert, sodass neue Admin-ACL-Ressourcen aus frisch ausgerollten Modulen im Rollen-Grid direkt nach dem ersten Seitenaufruf erscheinen.
9. Typische Fehler bei der Admin-ACL-Modellierung
Der mit Abstand häufigste Fehler ist eine Controller-Action ohne eigenen Eintrag in acl.xml. Da Magento in diesem Fall stillschweigend die geerbte Standard-Ressource verwendet, meist Magento_Backend::admin selbst, fällt der Fehler nicht als Exception auf, sondern als schleichendes Sicherheitsproblem: Jede Rolle mit irgendeinem administrativen Zugriff kann die Aktion ausführen, unabhängig von der eigentlich gewünschten Einschränkung. Ein zweiter, verwandter Fehler betrifft system.xml-Sections ohne <resource>-Element, die dadurch an die viel zu breite generische Konfigurationsberechtigung gebunden bleiben.
Die folgende Tabelle stellt die häufigsten Fallstricke den empfohlenen Admin-ACL-Mustern gegenüber, mit denen sich diese Probleme von Anfang an vermeiden lassen.
| Szenario | Falsches Vorgehen | Empfohlenes Admin-ACL-Pattern | Auswirkung |
|---|---|---|---|
| Neue Controller-Action | Kein eigener acl.xml-Eintrag | Eigene Resource-ID + ADMIN_RESOURCE setzen | Verhindert Zugriff durch alle Rollen |
| system.xml Section | Kein <resource>-Element | Eigene Resource explizit binden | Verhindert Zugriff über generische Config-Rolle |
| Resource-ID Format | Ohne Vendor_Module-Präfix | Vendor_Module::identifier konsequent nutzen | Vermeidet stille isAllowed()-Fehltreffer |
| Resource-ID umbenennen | Ohne Migration der Rollen | Setup-Patch für Rollen-Neuzuweisung | Verhindert Rechteverlust bestehender Rollen |
| Cache nach acl.xml-Änderung | Kein Cache-Flush | bin/magento cache:flush config | Neue Ressource sofort im Rollen-Grid sichtbar |
Ein weiterer, subtilerer Fehler entsteht, wenn eine Resource-ID im Rahmen eines Refactorings umbenannt wird, ohne die bestehenden Rolleneinträge in authorization_rule zu migrieren. Die alte ID bleibt dann als verwaister Datensatz in der Datenbank stehen, während die neue Ressource für keine einzige Rolle aktiv zugewiesen ist, mit dem Ergebnis, dass selbst Administratorrollen nach dem Deployment plötzlich keinen Zugriff mehr auf die entsprechende Funktion haben. Ein Setup-Patch, der alte und neue Resource-IDs synchron hält, ist hier die einzige sichere Lösung.
10. Zusammenfassung
Admin-ACL in Magento 2 ist ein hierarchischer Resource-Baum, der aus allen aktiven acl.xml-Dateien zusammengeführt wird. Jedes Custom-Modul muss eigene Ressourcen deklarieren, statt sich auf die geerbte Standard-Berechtigung zu verlassen, sonst öffnet eine neue Funktion sich standardmäßig für jede Rolle mit Zugriff auf die übergeordnete Ressource. Die eigentliche Durchsetzung passiert nicht im Rollen-Grid, sondern zur Laufzeit über isAllowed() im Injectable Service AuthorizationInterface, über ADMIN_RESOURCE und _isAllowed() in Backend-Controllern, und über das <resource>-Element in system.xml-Sections.
Wer die drei häufigsten Fehler vermeidet, hat den Großteil der Arbeit bereits erledigt: jede Controller-Action bekommt eine eigene ACL-Ressource, jede sensible Config-Section wird explizit gebunden statt an die generische Magento_Config::config-Ressource zu fallen, und der config-Cache wird nach jeder Änderung an acl.xml gezielt geleert. Für eine nachhaltige Rollenstruktur lohnt sich außerdem, von Anfang an mehrere abgestufte Rollen statt einer einzigen breiten Administratorrolle zu definieren.
Admin-ACL in Magento 2, das Wichtigste auf einen Blick
Resource-Baum
Eigene Ressourcen unter Vendor_Module::identifier in acl.xml deklarieren, nie auf die geerbte Standard-Berechtigung verlassen.
Laufzeit-Durchsetzung
ADMIN_RESOURCE und _isAllowed() im Controller sind die eigentliche Absicherung, UI-Checks per isAllowed() nur kosmetisch.
system.xml-Bindung
Jede sensible Config-Section bekommt ein eigenes <resource>-Element statt der generischen Config-Ressource.
Cache & Migration
Nach jeder acl.xml-Änderung cache:flush config, bei Resource-ID-Umbenennung bestehende Rollen per Setup-Patch migrieren.
11. FAQ: Admin-ACL in Magento 2
1Was ist Admin-ACL in Magento 2 genau?
2Was passiert ohne eigene ACL-Ressource?
3Wofür dient sortOrder in acl.xml?
4Reicht isAllowed() im Template als Schutz?
5Wann überschreibe ich _isAllowed()?
6Wie binde ich system.xml an eine ACL-Ressource?
7Welcher Cache muss geleert werden?
8Was ist der häufigste ACL-Fehler?
9Was passiert bei einer Resource-ID-Umbenennung?
10Unterschied zu Kundengruppen-Berechtigungen?
Mironsoft
Magento-2-Backend-Security, ACL-Audits und Rollenkonzepte
Admin-ACL, die wirklich schützt, statt nur zu verwalten?
Wir prüfen bestehende acl.xml-Strukturen, modellieren fehlende ACL-Ressourcen für Custom-Module und bauen abgestufte Rollenkonzepte, die Admin-Berechtigungen exakt an den tatsächlichen Bedarf binden.
ACL-Audit
Vollständige Prüfung aller Controller, system.xml-Sections und Resource-IDs auf fehlende Bindungen
Rollen-Modellierung
Abgestufte Admin-Rollen entlang echter Verantwortlichkeiten statt pauschaler All-Zugriffe
Security-Review
Custom-Module-Audit auf _isAllowed()-Overrides und konsistente Vendor_Module-Formate