Feature: #109365 - Introduce module access gates for backend modules 

See forge#109365

Description 

The previously hardcoded module access checks ( user, admin, systemMaintainer) in the backend module registration have been replaced with an extensible gate system. Each access type is now handled by a dedicated gate class implementing ModuleAccessGateInterface.

TYPO3 ships three built-in gates that preserve the existing behavior:

  • UserGate — grants access to admin users and users/groups with explicit module permissions ( be_users.userMods / be_groups.groupMods)
  • AdminGate — grants access only to admin users
  • SystemMaintainerGate — grants access only to system maintainers

Extension authors can register custom gates using the #[AsModuleAccessGate] PHP attribute. A gate receives the module and the current backend user and returns one of three results:

  • ModuleAccessResult::Granted — access is explicitly allowed
  • ModuleAccessResult::Denied — access is explicitly denied
  • ModuleAccessResult::Abstain — the gate cannot decide (not responsible for this access type)

Example: Custom module access gate 

EXT:my_extension/Classes/Module/AccessGate/EditorGate.php
namespace MyVendor\MyExtension\Module\AccessGate;

use TYPO3\CMS\Backend\Module\ModuleAccessGateInterface;
use TYPO3\CMS\Backend\Module\ModuleAccessResult;
use TYPO3\CMS\Backend\Module\ModuleInterface;
use TYPO3\CMS\Core\Attribute\AsModuleAccessGate;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;

#[AsModuleAccessGate(identifier: 'editor')]
final readonly class EditorGate implements ModuleAccessGateInterface
{
    public function decide(
        ModuleInterface $module,
        BackendUserAuthentication $user
    ): ModuleAccessResult {
        if ($module->getAccess() !== 'editor') {
            return ModuleAccessResult::Abstain;
        }
        // Custom logic: check for a specific user group
        return $user->check('groupList', '3')
            ? ModuleAccessResult::Granted
            : ModuleAccessResult::Denied;
    }
}
Copied!

The custom gate can then be referenced in the module registration:

EXT:my_extension/Configuration/Backend/Modules.php
return [
    'my_module' => [
        'access' => 'editor',
        'labels' => 'LLL:EXT:my_extension/Resources/Private/Language/locallang_mod.xlf',
        // ...
    ],
];
Copied!

Ordering gates 

Gates support before and after parameters to control their evaluation order when multiple gates are registered:

#[AsModuleAccessGate(identifier: 'editor', after: ['user'])]
final readonly class EditorGate implements ModuleAccessGateInterface
{
    // ...
}
Copied!

Impact 

Extension authors can now define custom module access strategies beyond the built-in user, admin, and systemMaintainer levels by implementing ModuleAccessGateInterface and registering it with the #[AsModuleAccessGate] attribute.

Existing module registrations using the built-in access values continue to work without changes.