> ## Documentation Index
> Fetch the complete documentation index at: https://docs.t3planet.de/en/latest/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom Access Catalogs

> Custom Access Catalogs for EXT:ns_t3af (T3AF).

Use an AI access catalog provider when your extension needs to expose module access, AI feature permissions, or record-level permissions through T3AF.

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 `FeaturePermissionGate`
* stores records that need `tables_select` or `tables_modify` enforcement

Administrators assign the resulting permissions through Backend Users > Backend usergroups > Access Lists. The optional T3AF > AI Permissions 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->grantsModuleTab($GLOBALS['BE_USER'], 'my_ext', 'content')) {
    // return 403
}
```

You can take the reference below for the function definition:

```
NITSAN\NsT3AF\Access\FeaturePermissionGate::grantsModuleTab

<?php
public function grantsModuleTab(BackendUserAuthentication $user, string $moduleKey, string $tabIdentifier): bool { }
@param \TYPO3\CMS\Core\Authentication\BackendUserAuthentication $user
@param string $moduleKey
@param string $tabIdentifier
@return bool
```

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 Permissions wizard**

Open T3AF > AI Permissions, select a backend group, and configure the wizard steps or permission matrix tabs.

**Option B — Native backend groups**

1. Grant the custom feature bit under Custom module options (T3Ai section).
2. Grant the backend module under Modules.
3. Grant `tables_select` and `tables_modify` for 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_modify` returns a 403 JSON response from `RecordAccessEnforcer`.

## Reference implementations

* `EXT:ns_t3af_extended/Classes/Access/T3afExtendedAccessCatalogProvider.php`
* `EXT:ns_t3ai/Classes/Access/T3AiAccessCatalogProvider.php`
* `EXT:ns_t3aa/Classes/Access/T3AaAccessCatalogProvider.php`
* `EXT:ns_t3cs/Classes/Access/T3CsAccessCatalogProvider.php`
* `EXT:ns_t3as/Classes/Access/T3AsAccessCatalogProvider.php`
* `EXT: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()` returns `true`.
* Flush TYPO3 caches so the DI container is rebuilt.

**Backend action is still allowed**

* Confirm the route calls `FeaturePermissionGate` or `RecordAccessEnforcer` before performing the action.
* Confirm the catalog id used in the enforcer matches the id returned by the provider.

## Related documentation

* [Extension Integration](/en/latest/en/latest/ExtNsT3AF/DeveloperGuide/ExtensionIntegration/Index)
* [AI Permissions](/en/latest/en/latest/ExtNsT3AF/Configuration/AIPermissions/Index)
