Custom AI Access / Permissions
Use an AI access catalog provider when your extension needs to expose module access, AI feature permissions, or record-level permissions through AI Foundation.
Runtime enforcement uses the merged catalog from all registered providers. You do not need EXTCONF wiring for new integrations.
When to use this
Implement a provider when your extension:
exposes a backend module that should be controlled per backend user group
defines AI feature areas checked with
FeaturePermissionGatestores records that need
tables_selectortables_modifyenforcement
Administrators assign the resulting permissions through Backend Users > Groups > Access Lists. The optional AI Foundation → AI Access / Roles wizard reads the same merged catalogs.
Step 1 — Implement the provider
Implement NITSAN\NsT3AF\Contract\AiAccessCatalogProviderInterface in your extension.
namespace MyVendor\MyExt\Access;
use NITSAN\NsT3AF\Access\Dto\FeatureAccessBindingsDescriptor;
use NITSAN\NsT3AF\Access\Dto\FeaturePermissionDescriptor;
use NITSAN\NsT3AF\Access\Dto\ModuleAccessDescriptor;
use NITSAN\NsT3AF\Access\Dto\RecordPermissionDescriptor;
use NITSAN\NsT3AF\Contract\AiAccessCatalogProviderInterface;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
final class MyExtAccessCatalogProvider implements AiAccessCatalogProviderInterface
{
public function isAvailable(): bool
{
return ExtensionManagementUtility::isLoaded('my_ext');
}
public function getExtensionKey(): string
{
return 'my_ext';
}
public function getCatalogModuleKey(): string
{
return 'my_ext';
}
public function getModuleAccess(): ?ModuleAccessDescriptor
{
return new ModuleAccessDescriptor(
label: 'My Extension',
sublabel: 'AI Assistant',
description: 'Short description for the wizard module card.',
color: '#2563eb',
groupMod: 'myvendor_myext_dashboard',
extension: 'my_ext',
);
}
public function getFeaturePermissions(): array
{
return [
new FeaturePermissionDescriptor(
id: 'myFeature',
label: 'My AI Feature',
description: 'Generate content with AI.',
permBase: 'MyExt.Feature',
relevantModules: ['my_ext'],
group: 'my_ext',
extension: 'my_ext',
),
];
}
public function getRecordPermissions(): array
{
return [
new RecordPermissionDescriptor(
id: 'myExtRecords',
label: 'My Records',
tables: ['tx_myext_domain_model_item'],
relevantModules: ['my_ext'],
relevantFeatures: ['myFeature'],
readHelp: 'View records',
writeHelp: 'Create and edit records',
extension: 'my_ext',
),
];
}
public function getFeatureAccessBindings(): FeatureAccessBindingsDescriptor
{
return new FeatureAccessBindingsDescriptor(
moduleKey: 'my_ext',
legacyCardPermPrefix: 'tx_myext_dashboard_',
moduleGroupMod: 'myvendor_myext_',
tabFeatureMap: ['dashboard' => 'MyExt.Feature'],
);
}
}
Step 2 — Register the provider
Tag the provider in your extension’s Configuration/Services.yaml.
_instanceof:
NITSAN\NsT3AF\Contract\AiAccessCatalogProviderInterface:
tags: ['t3af.ai_access_catalog_provider']
MyVendor\MyExt\Access\:
resource: '../Classes/Access/'
Step 3 — Gate backend UI and routes
Use FeaturePermissionGate for tabs, cards, and feature areas.
use NITSAN\NsT3AF\Access\FeaturePermissionGate;
use TYPO3\CMS\Core\Utility\GeneralUtility;
$gate = GeneralUtility::makeInstance(FeaturePermissionGate::class);
if (!$gate->grantsT3AiTab($GLOBALS['BE_USER'], 'content')) {
// return 403
}
For suite child extensions, use the gate method that matches the module family, such as grantsT3AiTab, grantsT3AaTab, grantsT3CsTab, or grantsT3AaCard.
Use RecordAccessEnforcer before mutating records.
use NITSAN\NsT3AF\Access\RecordAccessEnforcer;
use TYPO3\CMS\Core\Utility\GeneralUtility;
$enforcer = GeneralUtility::makeInstance(RecordAccessEnforcer::class);
if ($denied = $enforcer->denyUnlessCanModifyCatalogId($GLOBALS['BE_USER'], 'myExtRecords')) {
return $denied;
}
Step 4 — Assign permissions
Administrators can assign permissions in two ways.
Option A — AI Access / Roles wizard
Open AI Foundation → AI Access / Roles, select a backend group, and configure the wizard steps or permission matrix tabs.
Option B — Native backend groups
Grant the custom feature bit under Custom module options → T3Ai.
Grant the backend module under Modules.
Grant
tables_selectandtables_modifyfor the relevant tables.
Step 5 — Verify
The merged catalog contains your feature and record rows when your extension is loaded.
A restricted editor without the feature bit cannot open gated tabs or submit protected actions.
A direct POST without
tables_modifyreturns a 403 JSON response fromRecordAccessEnforcer.
Reference implementations
EXT:ns_t3af_extended/Classes/Access/T3afExtendedAccessCatalogProvider.phpEXT:ns_t3ai/Classes/Access/T3AiAccessCatalogProvider.phpEXT:ns_t3aa/Classes/Access/T3AaAccessCatalogProvider.phpEXT:ns_t3cs/Classes/Access/T3CsAccessCatalogProvider.phpEXT:ns_t3as/Classes/Access/T3AsAccessCatalogProvider.phpEXT:ns_t3ac/Classes/Access/T3AcAccessCatalogProvider.php
Troubleshooting
Permissions do not appear in the wizard
Confirm the provider is tagged with
t3af.ai_access_catalog_provider.Confirm
isAvailable()returnstrue.Flush TYPO3 caches so the DI container is rebuilt.
Backend action is still allowed
Confirm the route calls
FeaturePermissionGateorRecordAccessEnforcerbefore performing the action.Confirm the catalog id used in the enforcer matches the id returned by the provider.