TYPO3 technology icon

How TYPO3 Checks Permissions In The Backend

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(),
checkAuthMode()

allowed_languages

Languages in which the user can edit content

check(),
checkLanguageAccess()

file_permissions

User permissions for operations on files and folders

check(),
getFilePermissions()

webmounts

Specific pages from the page tree, including their subpages, visible to the user (in pages tree)

check(),
isInWebMount()

filemounts

List of file mounts within file storages

check(),
getFileMountRecords()

workspace_perms

Permissions to edit in the Live workspace

check(),
hasEditAccessToLiveWorkspace()

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.