Search by

uandi / ui-permissions

Flimmerkiste

Deployable TYPO3 backend user permissions via YAML configuration files

Package info

github.com/uandi/ui-permissions

Homepage

Documentation

Type:typo3-cms-extension

pkg:composer/uandi/ui-permissions

Statistics

Installs: 3 050

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 1

14.1.1 2026-10-02 17:01 UTC

This package is auto-updated.

Last update: 2026-10-02 17:03:56 UTC


README

QA

Deployable TYPO3 Permissions

TYPO3 extension to declaratively manage backend user group permissions as code.

If you have a TYPO3 project with any level of permissions for backend users you probably ran into one of the following problems:

  • Creating the permissions in the TYPO3 backend takes a lot of time and clicks
  • If you have different environments like INT, PPE and PROD, you would have to create the permissions on each environment
  • You deployed something to PROD and forgot about the permissions until the customer calls because he can't see the new feature

This extension introduces deployable YAML descriptions of permissions that are gathered from all active extensions, merged, and written into the database via a CLI command. This makes permission changes reviewable (VCS), testable, and reproducible across environments.

What it does

  • Collects all files matching *.permissions.yaml from every active TYPO3 extension (recursive scan)
    • These YAML files basically contain abstractions of be_groups and sys_filemounts records.
  • Merges all found configurations deterministically
  • Writes/updates corresponding records in:
    • sys_filemounts
    • be_groups
  • Resolves relations (e.g., be_groups.subgroup, be_groups.file_mountpoints) by referencing other items by their logical keys

Goals

  • Treat backend permissions as configuration-as-code
  • Enable safe deployments without manual backend tweaking
  • Keep environments (INT/PPE/PROD) in sync
  • Provide a clear, readable schema that is easy to review in pull requests
  • If you know how TYPO3 permissions work and how they are stored in the DB, you'll have no trouble writing the YAML abstractions

Installation

composer require uandi/ui-permissions

Extension configuration (Settings → Extension Configuration)

  • pidBeGroups (int): PID to store/update be_groups records
  • pidSysFilemounts (int): PID to store/update sys_filemounts records
  • createFilemountDirectories (bool): Create directories for filemounts if missing

These values are used by the repositories when inserting/updating records.

CLI usage

Run via TYPO3 CLI. This command is ideally executed during deployments.

# Apply the current YAML permissions to the database
vendor/bin/typo3 ui_permissions:update
# Generate YAML files from the permissions that already exist in the database
vendor/bin/typo3 ui_permissions:export

Migrating an existing project

ui_permissions:export is the inverse of ui_permissions:update: it reads the existing be_groups and sys_filemounts records and writes them as *.permissions.yaml files. This is meant to bootstrap the configuration of a project whose permissions were built in the TYPO3 backend.

# Print the whole permission setup as YAML to review it first
vendor/bin/typo3 ui_permissions:export

# Store the derived permission keys in the database, then write one file per permission key
vendor/bin/typo3 ui_permissions:export --write-permission-keys --output packages/my_sitepackage/Configuration/Permissions
Option Description
-o, --output Directory to write the files to. Relative paths are resolved from the project root, EXT: paths are supported. Without this option the YAML is printed to stdout.
--single-file Write everything into one Permissions.permissions.yaml instead of one file per permission key.
-g, --group Limit the export to the given be_groups, identified by uid, permission key or title. Repeatable. Only the filemounts that these groups reference are exported.
--include-hidden Also export disabled records. They are skipped by default, because the hidden state has no representation in the YAML abstraction.
--write-permission-keys Store the generated permission keys in the database (see below). No other field is touched.
-f, --force Overwrite existing files. Without it, existing files are reported as skipped.

Permission keys of existing records

Records that were created through the TYPO3 backend have no permission_key, so the export derives one from the record title (Redakteure Süd becomes Redakteure_Sued, duplicates get a numeric suffix).

ui_permissions:update matches a record by its permission_key and falls back to matching a record whose title equals the permission key. A derived key that differs from the title would therefore create a second record instead of updating the existing one. The export lists exactly those records and recommends a re-run with --write-permission-keys, which stores the derived keys on the existing records first.

What the export covers

Without --group every non-deleted group and filemount is exported, and subgroup and file_mountpoints are written back as permission keys instead of uids. Fields with no representation in the YAML abstraction (for example hidden or category_perms) are not exported, but every record that uses one of them is reported so it can be handled manually. The same applies to references that cannot be resolved and, on installations upgraded from TYPO3 v11 or below, to denied values in explicit_allowdeny.

The generated files are a starting point, not a finished configuration. Review the permission keys against the naming conventions below, move the files into the extensions they belong to, and replace environment specific values such as db_mountpoints page uids before committing.

Where to place YAML files

While the collector scans the whole extension path, the recommended location is:

  • EXT:your_ext/Configuration/Permissions/YourName.permissions.yaml for general purposes
  • EXT:your_ext/ContentBlocks/ContentElements/*/permissions/YourName.permissions.yaml for ContentBlock elements.

Any filename ending with .permissions.yaml is picked up.

Naming conventions (permission files and keys)

Based on the “TYPO3 Backend User Management” concept (naming-for-purpose), use clear, prefixed keys. The map key of each item becomes its durable permission_key (and should also inform the filename):

  • R_<Name>: Role groups that aggregate capabilities via subgroup, e.g. R_Editors, R_EditorsInChief
  • ACL_<Name>: Access control scopes for single tables/features/CTypes, e.g. ACL_tt_content_common, ACL_contentelement_gallery
  • FM_<Name>: Filemount definitions and helper groups that attach filemounts, e.g. FM_ProjeectIcons
  • DB_<Name>: Database (page tree) mounts, e.g. DB_Projeect
  • PG_<Name>: Page-related presets, e.g. PG_Projeect
  • L_<Name>: Language-related presets for accessible languages, e.g. L_Spanish

Further reading

Recommendations

  • Keep keys unique and stable; they act as IDs across environments
  • Name files accordingly, e.g. R_Editors.permissions.yaml containing a be_groups: R_Editors: ... definition
  • Compose roles (R_) from ACL_ and FM_* using subgroup and fileMountpoints

YAML schema

As was already noted, the YAML files are abstractions of the database records. So the top level keys describe which table we're dealing with:

  • sys_filemounts: map of filemounts by a unique key ("permission key")
  • be_groups: map of backend groups by a unique key ("permission key")

Each item’s map key becomes its permission_key in the database and is used to reference between items.

sys_filemounts item fields

  • permission_key (string, optional - map key will be used if missing)
  • title (string, optional - map key will be used if missing)
  • description (string, optional)
  • identifier (string, e.g. 1:/assets)
  • readOnly (bool, default: false)

Legacy support: If identifier is missing, the pair base + path will be converted to identifier (base:path).

be_groups item fields (selection)

  • permission_key (string, optional - map key will be used if missing)
  • title (string, optional - map key will be used if missing)
  • description (string, optional)
  • tablesModify (array|string CSV)
  • tablesSelect (array|string CSV)
  • pagetypesSelect (array|int|string CSV)
  • nonExcludeFields (map table: [field, ...] or strings/CSV)
  • explicitAllowdeny (map of tables → fields → [values])
    • Legacy support: explicitAllowdeny: { allow: { table: { field: [values] } } } also accepted
  • dbMountpoints (array|int|string CSV of page IDs)
  • fileMountpoints (array of filemount permission keys)
  • filePermissions (array|string CSV)
  • subgroup (array|string CSV of other be_group permission keys)
  • groupMods (array|string CSV)
  • availableWidgets (array|string CSV of dashboard widget identifiers, requires EXT:dashboard)
  • TSconfig (string)
  • tsconfigIncludes (array|string CSV of EXT:-paths to TSconfig files)
  • allowedLanguages (array|string CSV of sys_language uids)
  • customOptions (array|string CSV)
  • mfaProviders (array|string CSV)
  • workspacePerms (bool|int, default: false - grants editing in the live workspace)

Only fields that are present in the YAML are written to the database. Omitting a field does not reset it, the value that is already stored in the record is kept. Unknown keys are silently ignored.

Fields whose database column does not exist in the current installation are skipped instead of letting the whole record fail, and ui_permissions:update reports them at the end of its run. Optional system extensions add their own columns to the permission tables, for example availableWidgets which only exists when EXT:dashboard is installed. This way the same configuration can be shared across projects that do not all install the same system extensions.

Some fields are resolved after initial persistence (e.g., subgroup, fileMountpoints). See Classes/Domain/Repository/BackendUserGroupRepository.php for some insights.

Merge behavior

Merging is custom (see ConfigurationCollector::mergeConfiguration()):

  • Associative arrays are merged recursively; later values override earlier scalars
  • List arrays are concatenated and array_unique()-ed

This enables layering configurations across multiple extensions.

Examples

Examples of permissions files

Example ACL for a content element (CType)

be_groups:
  ACL_contentelement_gallery:
    tables_select: tt_content
    tables_modify: tt_content
    explicit_allowdeny:
      tt_content:
        CType:
          - contentelement_gallery

Example role composed of many ACLs

be_groups:
  R_Editors:
    pagetypes_select: 1, 3, 4, 199, 254
    subgroup:
      - ACL_form_formframework
      - ACL_pages_common
      - ACL_sys_file_metadata
      - ACL_sys_file_reference
      - ACL_tt_content_common
      - ACL_tt_content_shortcut

      # Content Elements
      - ACL_contentelement_gallery
      - ACL_contentelement_media
      - ACL_contentelement_quote
      - ACL_contentelement_stage
      - ACL_contentelement_teaser
      - ACL_contentelement_textmedia

      # Grids
      - ACL_contentelement_grid2columns50-50
      - ACL_contentelement_grid3columns33-33-33
      - ACL_contentelement_grid4columns25-25-25-25
    groupMods:
      - web_layout
      - media_management
      - user_setup

Example filemounts (FM_*) and their respective be_groups

sys_filemounts:
  FM_ProjeectIcons:
    title: 'Assets'
    description: 'Base Fileadmin Folder for Icons'
    identifier: '1:/user_upload/Projeect/Icons/'

  FM_ProjeectIcons_ReadOnly:
    title: 'Icons'
    description: 'Base Fileadmin Folder for Icons (Read Only)'
    identifier: '1:/user_upload/Projeect/Icons/'
    read_only: 1

be_groups:
  FM_ProjeectIcons:
    file_mountpoints:
      - FM_ProjeectIcons

  FM_ProjeectIcons_ReadOnly:
    file_mountpoints:
      - FM_ProjeectIcons_ReadOnly

Example database mount (DB_*)

be_groups:
  DB_Projeect:
    db_mountpoints: 1

Example TSconfig includes

Instead of inlining everything into TSconfig, reference deployable TSconfig files. The paths are stored as given, so they stay valid across environments.

be_groups:
  R_Editors:
    tsconfig_includes:
      - 'EXT:ui_sitepackage/Configuration/user.tsconfig'
      - 'EXT:ui_sitepackage/Configuration/TsConfig/User/Editors.tsconfig'

Note that TYPO3 stores tsconfig_includes in a varchar(255) column, so the resulting comma-separated list of paths has to stay below that limit.

Tips

  • Keep permission keys unique and stable; they become the durable identifier (permission_key) across environments
  • Split large setups into multiple files; merging will combine them
  • Prefer arrays over CSV for readability, but both are supported in many fields

What's next

  • Maybe try to introduce some level of plausibility/error checks to make it easier to find misconfigurations in the YAML abstraction files