Search by

moselwal / typo3-config

o.hartwig

Fluent API to set environment-specific configuration for TYPO3.

Package info

github.com/ohartwig/typo3-config

pkg:composer/moselwal/typo3-config

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v5.14.0 2026-11-10 14:31 UTC

README

TYPO3 14 PHP 8.2+ License: GPL-2.0-or-later

Fluent PHP API for environment-specific TYPO3 configuration. Provides context-based presets, secure secret management, caching, logging, mailer setup, and TLS/mTLS auto-configuration.

Features

  • Context-Based Presets — Auto-configuration for Production, Development, CLI, and Testing environments
  • Secret Resolution Cascade — Secure secret loading from _FILE env vars → /run/secrets/ → getenv() → fallback
  • Caching Auto-Configuration — Redis/Valkey (via moselwal/keyvalue-store), APCu, or file backends
  • TLS/mTLS Auto-Configuration — Automatic certificate discovery for database and Redis connections
  • Mailer Setup — SMTP and Mailpit configuration helpers
  • GitLab-Backend-Login — OAuth2-Login-Provider fuer das Backend (via mfc/oauth2)
  • Logging Presets — Context-aware logging configuration
  • Image Engine — ImageMagick and GraphicsMagick configuration
  • Fluent Interface — Chainable API for readable configuration
  • TYPO3 14.x — getestet auf der aktuellen LTS-Linie

Installation

composer require moselwal/typo3-config
```text

## Quick Start

In your `config/config.php`:

```php
<?php

use Moselwal\Config;

Config::initialize()
    ->loadCoreSecrets()
    ->loadMailSecrets()
    ->autoconfigureCaching();

Secret Management

Secrets are resolved through a cascading lookup:

  1. File path from env var (DB_PASSWORD_FILE)
  2. Default secret file (/run/secrets/db_password)
  3. Direct env var (getenv('DB_PASSWORD'))
  4. Fallback parameter (optional)
# Docker secrets or Kubernetes mounts
echo "supersecret" > /run/secrets/db_password
```text

```php
Config::get()->loadCoreSecrets();

No secrets need to be committed to Git or stored in .env files.

GitLab-Login fuers Backend

useGitLabBackendLogin() haengt das TYPO3-Backend an eine GitLab-Instanz. Voraussetzung ist die Extension mfc/oauth2 — ohne sie macht die Methode nichts.

Config::initialize()
    ->loadCoreSecrets()
    ->useGitLabBackendLogin(
        gitlabServer: 'https://git.example.com',
        projectName: '',
        accessGroup: 'devops/typo3-backend-access',   // empfohlen, siehe "Zugriffsgruppe statt Projekt"
        adminUserLevel: 40,   // ab Maintainer wird der Benutzer TYPO3-Admin
    );

Die App-ID und das App-Secret kommen aus derselben Kaskade wie alle anderen Secrets — GITLAB_OAUTH_APP_ID_FILE → /run/secrets/gitlab_oauth_app_id → getenv() → Parameter, analog fuer GITLAB_OAUTH_APP_SECRET. Nichts davon gehoert in Git oder in eine .env.

Die Methode ist bewusst still. Fehlt die Extension, fehlt eines der beiden Secrets oder fehlt der Projektpfad, passiert gar nichts: kein Login-Button, keine geaenderte Cookie-Konfiguration. Der Aufruf ist damit auch auf Instanzen unbedenklich, hinter denen kein GitLab steht — er schaltet sich selbst frei, sobald die Secrets vorhanden sind.

Wer darf rein

Die Berechtigung haengt an einem GitLab-Projekt (projectName) oder an einer GitLab-Gruppe (accessGroup):

GitLab TYPO3
kein Mitglied (Access Level 0) Login wird abgelehnt
Access Level > 0 Login erlaubt
Access Level ≥ adminUserLevel TYPO3-Admin
external-Flag abgelehnt, solange blockExternalUsers gesetzt ist

Mit accessGroup gilt dieselbe Tabelle fuer die Mitgliedschaft in dieser Gruppe (siehe unten).

Feinere Zuordnung laeuft ueber be_groups: Die Extension legt dort das Feld gitlabGroup an. Traegt man dort ein GitLab-Access-Level ein (10 Guest, 20 Reporter, 30 Developer, 40 Maintainer), bekommt jeder Benutzer mit diesem Level die Gruppe beim Login zugewiesen. Greift kein Mapping, gilt defaultGroups.

overrideUser (Default an) schreibt bei jedem Login admin, usergroup, email und realName aus GitLab zurueck. Eine Herabstufung in GitLab (z. B. Maintainer → Developer) nimmt dem Benutzer damit beim naechsten Login die Admin-Rechte; geaenderte gitlabGroup-Zuordnungen kommen ebenso an. In TYPO3 von Hand vergebene Gruppen an einem GitLab-verknuepften Konto gehen dabei verloren — die Rechte solcher Konten gehoeren nach GitLab. Mit overrideUser: false gelten Admin-Flag und Gruppen nur beim Anlegen.

Zugriffsgruppe statt Projekt (accessGroup, empfohlen)

Im Projekt-Modell (nur projectName) liest die Extension das Access-Level ueber die GitLab-API mit dem Token des Benutzers. Dafuer fordert der Login den Scope read_api an — jeder Login gibt dem TYPO3-Prozess damit einen Token, der die gesamte GitLab-API im Namen des Benutzers lesen kann: alle Repositories und bei Projekten, die er maintained, auch die CI/CD-Variablen. Wer Code im TYPO3-Prozess ausfuehren kann, liest beim naechsten Login eines Maintainers die Instanz mit.

Mit accessGroup kommt das Level stattdessen aus der OpenID-Connect-Antwort von GitLab (/oauth/userinfo), und der Login fordert nur read_user openid an:

userinfo-Claim (Gruppe oder eine Elterngruppe) Level
https://gitlab.org/claims/groups/owner 50
https://gitlab.org/claims/groups/maintainer 40
https://gitlab.org/claims/groups/developer 30
sonst Mitglied laut groups 10
nichts davon, Antwort fuer einen anderen Benutzer, GitLab nicht erreichbar 0 → abgelehnt

Grenzen dieses Modells: userinfo unterscheidet Guest und Reporter nicht (beide 10, ein gitlabGroup-Mapping auf 20 greift nie), und Mitgliedschaften in einem Projekt oder ueber eine geteilte Gruppe zaehlen nicht. Fuer jede Installation also eine eigene Gruppe anlegen (z. B. devops/<site>-backend-access) und die Berechtigten dort eintragen. Ist accessGroup gesetzt, wird projectName ignoriert.

Die GitLab-Application danach auf die Scopes read_user und openid beschraenken. GitLab stellt zwar nur die angeforderten Scopes aus, aber eine Application, die mehr erlaubt, erlaubt es auch jedem anderen Client mit ihren Zugangsdaten.

Sperren bleiben in TYPO3 wirksam: Ein deaktiviertes oder abgelaufenes Konto wird von einem GitLab-Login weder entsperrt noch durch ein neues Konto ersetzt — der Login wird abgelehnt.

Welches Konto ein GitLab-Login bekommt

Massgeblich ist allein die GitLab-User-ID (unveraenderlich, von GitLab vergeben), gespeichert als be_users.oauth_identifier = gitlab|<ID>. Benutzername und E-Mail werden nie zum Zuordnen verwendet — beide waehlt der GitLab-Benutzer selbst.

  1. Ein aktives Konto mit diesem oauth_identifier → dieses Konto.
  2. Die ID ist nur mit einem gesperrten/abgelaufenen Konto verknuepft → Login abgelehnt.
  3. Die ID steht in linkedAccounts → das dort genannte, aktive und noch unverknuepfte Konto wird verknuepft (Benutzername bleibt).
  4. Sonst → neues Konto gitlab-<ID>. Der Name kann mit keinem lokal angelegten Konto kollidieren und vom GitLab-Benutzer nicht gesteuert werden; der Klarname steht in realName.

Dafuer ersetzt useGitLabBackendLogin() den Auth-Service von mfc/oauth2 ueber TYPO3_CONF_VARS/T3_SERVICES durch Moselwal\OAuth2\GitLabLoginService (gleicher Service-Key, gleiche Prioritaet). Laeuft trotzdem einmal der Upstream-Service — etwa weil ein mfc/oauth2-Update den Service-Key aendert —, verweigert Moselwal\OAuth2\GitLabResourceServer den Login, statt auf die Namenssuche zurueckzufallen.

Lokal angelegte Konten (z. B. das Betreiberkonto aus dem Admin-Bootstrap) sind per GitLab-Login nicht erreichbar, solange sie nicht ausdruecklich verknuepft sind. Wer ein solches Konto auch per GitLab nutzen will, traegt es mit der GitLab-User-ID ein:

->useGitLabBackendLogin(
    gitlabServer: 'https://git.example.com',
    projectName: 'devops/typo3-backend-access',
    linkedAccounts: ['operator' => 4711],   // TYPO3-Benutzername => GitLab-User-ID
);

Die ID steht im GitLab-Profil (bzw. GET /api/v4/users?username=<name>). Alternativ einmalig per SQL: UPDATE be_users SET oauth_identifier = 'gitlab|<ID>' WHERE username = '<name>'. Ohne Verknuepfung bekommt der Betreiber beim GitLab-Login einfach ein eigenes Konto gitlab-<ID> (Admin ab adminUserLevel); das lokale Konto bleibt als Passwort-Zugang daneben bestehen.

Bestehende Verknuepfungen (Konten mit gesetztem oauth_identifier) funktionieren unveraendert weiter und behalten ihren Benutzernamen. Vor dieser Version konnte ein Konto allerdings auch ueber Benutzername oder E-Mail verknuepft worden sein. Nach dem Update lohnt ein Blick auf alle Konten mit oauth_identifier <> '', deren Verknuepfung nicht vom GitLab-Login selbst angelegt wurde; im Zweifel oauth_identifier leeren.

Redirect-URI

Die URI, die in der GitLab-Application eingetragen wird, ist der Login-Pfad dieser Installation, nicht /typo3/index.php:

Setup Redirect-URI
eigener Backend-Host (BE/entryPoint) https://cms.example.com/login
Backend unter der Hauptdomain https://www.example.com/typo3/login

Ohne Query-String eintragen. GitLab (Doorkeeper) vergleicht die Query nur, wenn die registrierte URI selbst eine hat — und die Extension haengt bei jedem Login einen anderen request-token an.

mfc/oauth2 selbst baut den Rueckweg auf den festverdrahteten Pfad /typo3/index.php. Dieses Einstiegsskript gibt es in TYPO3 v14 nicht mehr; der Callback laeuft dort in einen 404, nachdem der Benutzer bei GitLab schon zugestimmt hat. Bis davor sieht alles gesund aus. useGitLabBackendLogin() registriert deshalb Moselwal\OAuth2\GitLabResourceServer, eine Ableitung, die den Pfad aus dem laufenden Request nimmt. Sobald ein mfc/oauth2-Release den Fehler behebt, kann die Klasse weg.

Was die Methode sonst noch anfasst

  • BE/cookieSameSite wird auf lax gesetzt. Der OAuth2-Rueckweg von GitLab ist eine Cross-Site-Navigation; unter strict haelt der Browser das Nonce-Cookie zurueck und der RequestToken-Check schlaegt jedes Mal fehl.
  • EXTENSIONS/oauth2 wird gesetzt, damit mfc/oauth2 den Login-Provider registriert. Das spart den Klick im Install-Tool und haelt die Konfiguration im Code.
  • T3_SERVICES/auth/Mfc\OAuth2\Services\OAuth2LoginService/className zeigt auf Moselwal\OAuth2\GitLabLoginService (siehe oben).

Passwort- und Passkey-Login bleiben unveraendert. GitLab kommt als dritter Weg dazu und ersetzt die anderen beiden nicht — faellt die GitLab-Instanz aus, kommt man weiterhin ins Backend.

Available Presets

Method Description
applyDefaults() Auto-selects preset based on TYPO3 context
useCliPreset() Optimized for CLI calls with debug output
useDevelopmentPreset() Development environment settings
useProductionPreset() Production with APP_ROOT (container)
useProductionPresetVHost() Production with VHost-based setup

Usage Example

Config::get()
    ->useGraphicsMagick()
    ->useMailpit()
    ->autoconfigureCaching()
    ->setPhpSettings([
        'memory_limit' => '512M',
        'max_execution_time' => 120,
    ])
    ->setConfigPathValues('SYS', [
        'defaultScheme' => 'https',
    ]);
```text

## TYPO3 Version Compatibility

| TYPO3 Version | Status |
|---------------|--------|
| v11 | Supported |
| v12 | Supported (`pagesection` cache auto-removed) |
| v13 | Supported (`imagesizes` cache auto-removed) |
| v14 | Supported |

## Architecture

src/ ├── Config.php # Main class (Singleton, Fluent API) └── ConfigInterface.php # Public contract interface tests/ ├── ConfigTestCase.php # Base test class (singleton reset, $GLOBALS isolation) ├── TestableConfig.php # Test subclass for version injection └── ... # 35+ tests with >120 assertions


- **Singleton pattern** via `Config::initialize()` / `Config::get()`
- **Namespace**: `Moselwal\` → `src/` (PSR-4)
- Late static binding (`new static()`) for project-specific extensions

## Development

```bash
composer install
composer test              # PHPUnit tests
composer test:coverage     # Tests with coverage report
composer phpstan           # PHPStan Level 5 static analysis

Dependencies

Package Type Purpose
moselwal/keyvalue-store Optional Redis/Valkey cache and session backends
moselwal/dev Dev Shared QA tooling

Related

  • keyvalue-store — Redis/Valkey integration used by the caching auto-configuration

License

GPL-2.0-or-later — see LICENSE for details. Releases up to and including v5.8.12 were published under the MIT License and remain available under it.