Overview
One of the most basic, yet crucial and complex aspects of TYPO3 is the Access Control List, or more generally, setting appropriate permissions for backend users. In this article, we will delve deeper into the technical details of backend user permission verification. With multiple settings responsible for access control, it's essential to thoroughly check them to determine a user's access level. This includes access to modules, pages, content elements, and even visibility and editability of individual fields in forms.
The scope of this article does not include users with Admin or Super User rights, as they have default access to the entire TYPO3 backend. Instead, we will focus on normal backend users and how their access to various functionalities of the backend is verified.
If you are interested in a detailed description of all the possible permissions that TYPO3 offers (in the context of backend users), including how they function and are stored, please refer to our previous article.
Creating backend user
Before any access check can be performed, TYPO3 needs to know exactly in the context of which user the request is being made. Information about the authenticated backend user is stored inside the $GLOBALS['BE_USER'] global variable. But where does it come from?
Let's examine the flow of creating a backend user object in the TYPO3, by going through some important request processing steps. If you are a developer who has worked with TYPO3, you're likely familiar with the $GLOBALS['BE_USER'] object. This object is an instance of the \TYPO3\CMS\Core\Authentication\BackendUserAuthentication class, which extends the \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication. Before we delve deeper into its functionalities, let's first examine how it's created.
It all begins within the \TYPO3\CMS\Backend\Middleware\BackendUserAuthenticator middleware. Here inside the process() function, a new instance of the \TYPO3\CMS\Core\Authentication\BackendUserAuthentication class is created and assigned to the global $GLOBALS['BE_USER']. From this point, we can access it via $GLOBALS variables.
After the BackendUserAuthentication class instance is created, its start() method is called on it. This method takes \Psr\Http\Message\ServerRequestInterface as a parameter and is responsible for:
- verifying if a session cookie exists; if absent, initiating one
- confirming if a username and password have been submitted; if so, proceeding with user authentication
- identifying any sessions linked to a user and examining their timeout details, among other factors
- conducting garbage collection and establishing no-cache headers
- upon successful user authentication, the database record of the user (in array format) is assigned to the internal variable user, allowing access through $GLOBALS['BE_USER']->user
We will not dive deeper into this middleware here. It is worth mentioning though, that things like login rate limit, multi factor authentication, backend user aspect and backend session garbage collection are handled there. For more details please investigate the class code.
Determine if backend user can view selected page
Now, let's assume that you are already an authenticated user, either through a standard TYPO3 backend user account or one created by an external user provider. The origin doesn't matter; our focus is to explore the process of authorization. We will examine how the access verification process is conducted, specifically in the context of accessing the Page module and viewing a single page from the tree.
When we click the Page module link in the main menu, the request is sent to the route /module/web/layout, which by default points to \TYPO3\CMS\Backend\Controller\PageLayoutController\PageLayoutController->mainAction(). This initiates the standard backend request handling, which generally proceeds as follows (for default TYPO3 installation, without custom middlewares etc.):
Entry point
\TYPO3\CMS\Backend\Http\Application->run()
Series of middlewares
\TYPO3\CMS\Core\Middleware\VerifyHostHeader
\TYPO3\CMS\Core\Middleware\NormalizedParamsAttribute
\TYPO3\CMS\Backend\Middleware\LockedBackendGuard
\TYPO3\CMS\Backend\Middleware\ForcedHttpsBackendRedirector
\TYPO3\CMS\Backend\Middleware\ContentSecurityPolicyReporter
\TYPO3\CMS\Backend\Middleware\BackendRouteInitialization
\TYPO3\CMS\Core\Middleware\RequestTokenMiddleware
\TYPO3\CMS\Reactions\Http\Middleware\ReactionResolver
\TYPO3\CMS\Backend\Middleware\BackendUserAuthenticator
\TYPO3\CMS\Backend\Middleware\BackendModuleValidator
\TYPO3\CMS\Backend\Middleware\OutputCompression
\TYPO3\CMS\Backend\Middleware\ContentSecurityPolicyHeaders
\TYPO3\CMS\Backend\Middleware\AdditionalResponseHeaders
\TYPO3\CMS\Backend\Middleware\SudoModeInterceptor
\TYPO3\CMS\Backend\Middleware\SiteResolver
\TYPO3\CMS\Core\Middlewar\ResponsePropagation
Reaching target
\TYPO3\CMS\Backend\Http\RequestHandler->handle()
\TYPO3\CMS\Backend\Http\RouteDispatcher->dispatch()
\TYPO3\CMS\Backend\Controller\PageLayoutController->mainAction()
Now, let's take a look at some of these classes and their functions. The \TYPO3\CMS\Backend\Middleware\BackendRouteInitialization middleware sets the routing, route, and target attributes in the PSR-7 request object. The target attribute points to TYPO3\CMS\Backend\Controller\PageLayoutController::mainAction.
Then, as already mentioned in the Creating backend user section, when the \TYPO3\CMS\Backend\Middleware\BackendUserAuthenticator middleware is called, our backend user object is initialized and we have access to $GLOBALS['BE_USER']. From this point, we know which backend user is attempting to access the target, in this case, the PageLayoutController.
Let's now explore the \TYPO3\CMS\Backend\Middleware\BackendModuleValidator middleware, where module access validation is carried out. This process includes checking whether the module is registered, assessing workspace restrictions, and verifying if the current backend user has access by calling \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication->check($type, $value). Here, $type is set to module, and $value is the identifier of the target module. The workings and role of the check() method will be discussed in the upcoming sections of this article.
Another crucial check performed in this middleware is determining if the backend user has the necessary page access permissions to view the selected page. This is achieved by calling the \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication->getPagePermsClause($perms) function. Next, TYPO3 also checks if the selected page falls within the webmounts set up for the user, ensuring that the user has read access to this page. It's important to note that this page access check is only initiated if a page has been selected through the pages tree and passed through the id query parameter. Not all modules use the pages tree, and even the Page module, by default, does not select any page when first loaded if the session does not store information about a selected page.
If any of those validations fail, a RuntimeException will be thrown.
At this stage, TYPO3 confirms that the current user has access to the Page module and possesses the required permissions to view the selected page. Now, the \TYPO3\CMS\Backend\Http\RouteDispatcher takes action, directing the user to their target, which is the \TYPO3\CMS\Backend\Controller\PageLayoutController::mainAction.
We won't be citing many other examples of how TYPO3 verifies access, such as to the file management module and file operations, the ability to edit content elements in various languages, or listing records. Instead, we will focus in the latter part of the article on the methods used to check access in the backend.
The heart for checking permissions - BackendUserAuthentication
In previous sections, the $GLOBALS['BE_USER'] global variable was mentioned frequently. This was not a coincidence, as the object it stores, \TYPO3\CMS\Core\Authentication\BackendUserAuthentication, is used throughout the TYPO3 backend to check user access rights to various elements of the panel, whether when rendering graphical interfaces or verifying access during data processing. So, as the headline for this section suggests, we can refer to it as the heart of checking user access rights.
Now, let's dive into the \TYPO3\CMS\Core\Authentication\BackendUserAuthentication class and see what functions it offers, mainly in the context of checking user access rights.
$GLOBALS['BE_USER']->check($type, $value);
This function allows to check if the backend user has access rights to any items from this groups:
Group | Description | Check through |
modules | Backend modules accessible through Modules menu | check() |
available_widgets | Widgets displayed on the Dashboard | check() |
mfa_providers | Providers available for multi-factor authentication | check() |
tables_select | Database tables that the user can list/view | check() |
tables_modify | Database tables that the user can modify | check() |
pagetypes_select | Page types that the user can select and use | check() |
non_exclude_fields | Form fields across various tables visible and editable by the user | check() |
explicit_allowdeny | Explicitly allowed options for restricted select fields | check(), |
allowed_languages | Languages in which the user can edit content | check(), |
file_permissions | User permissions for operations on files and folders | check(), |
webmounts | Specific pages from the page tree, including their subpages, visible to the user (in pages tree) | check(), |
filemounts | List of file mounts within file storages | check(), |
workspace_perms | Permissions to edit in the Live workspace | check(), |
custom_option | Custom options provided by extensions etc. | check() |
If you have read our previous article (if not, we encourage you to do so) [Access Control List in TYPO3], you will notice that these are the Access Control Options that can be set for backend user groups (and some for individual users) through the Backend Users module. Their names correspond to the columns in the database tables be_groups and be_users. In fact, these are the keys of a table stored under the groupData property of the \TYPO3\CMS\Core\Authentication\BackendUserAuthentication class. Each item in this table is a summary of the permissions for the backend user and each backend group to which he belongs. The value is a comma-separated list of elements the user has access to. For instance, if the user has access to the backend modules List (set through a backend group) and Pages (set directly for the user), under the modules key there would be a value like web_list,web_layout. Armed with this knowledge, let's see how it works in practice.
Check user access to List module
$GLOBALS['BE_USER']->check('modules', 'web_list');
Check user access to Typo3 News widget
$GLOBALS['BE_USER']->check('available_widgets', 't3news');
Check user access to Recovery Codes MFA provider
$GLOBALS['BE_USER']->check('mfa_providers', 'recovery-codes');
Check if user can read from tt_content table
$GLOBALS['BE_USER']->check('tables_select', 'tt_content');
Check if user can modify pages table
$GLOBALS['BE_USER']->check('tables_modify', 'pages');
Check user access to page type Shortcut
$GLOBALS['BE_USER']->check('pagetypes_select', 4);
Check user access to field Align (header_posision) from tt_content table
$GLOBALS['BE_USER']->check('non_exclude_fields', 'tt_content:header_position');
Check user access to select ‘bullets’ content type (from select list)
$GLOBALS['BE_USER']->checkAuthMode('tt_content', 'CType', 'bullets');
Check user access to edit in given language
$GLOBALS['BE_USER']->checkLanguageAccess(1);
Retrieve user permissions for file and folder operations
$GLOBALS['BE_USER']->getFilePermissions();
Verify if the page id or record is within the user's webmounts
$GLOBALS['BE_USER']->isInWebMount($idOrRow);
Check user access to read page
$GLOBALS['BE_USER']->doesUserHaveAccess($pageRecord, 1);
The examples given above are just a few of the many functions provided by the $GLOBALS['BE_USER'] object, which allow for the verification of access to various functionalities offered by the TYPO3 backend. If you are interested in the details of their implementation or want to learn about other available functions, we encourage you to review the code of the \TYPO3\CMS\Core\Authentication\BackendUserAuthentication class.
See official documentation.