moselwal / typo3-config
Fluent API to set environment-specific configuration for TYPO3.
Requires
- php: ^8.5.12
- composer/installers: ^2.3.0
- typo3/cms-core: ^14.0
Requires (Dev)
- ergebnis/composer-normalize: ^2.56.0
- helhum/config-loader: ^0.12 || ^1.0
- koh/dev-typo3: ^1.0
- mfc/oauth2: ^4.4.0
- moselwal/keyvalue-store: ^4.5
- php-mock/php-mock-phpunit: ^2.16.0
Suggests
- mfc/oauth2: GitLab/OAuth2 login provider for the TYPO3 backend, wired by useGitLabBackendLogin()
- moselwal/keyvalue-store: Redis/Valkey integration for caching, sessions, and locking
Provides
None
Conflicts
None
Replaces
None
README
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
_FILEenv 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:
- File path from env var (
DB_PASSWORD_FILE) - Default secret file (
/run/secrets/db_password) - Direct env var (
getenv('DB_PASSWORD')) - 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.
- Ein aktives Konto mit diesem
oauth_identifier→ dieses Konto. - Die ID ist nur mit einem gesperrten/abgelaufenen Konto verknuepft → Login abgelehnt.
- Die ID steht in
linkedAccounts→ das dort genannte, aktive und noch unverknuepfte Konto wird verknuepft (Benutzername bleibt). - Sonst → neues Konto
gitlab-<ID>. Der Name kann mit keinem lokal angelegten Konto kollidieren und vom GitLab-Benutzer nicht gesteuert werden; der Klarname steht inrealName.
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/cookieSameSitewird auflaxgesetzt. Der OAuth2-Rueckweg von GitLab ist eine Cross-Site-Navigation; unterstricthaelt der Browser das Nonce-Cookie zurueck und der RequestToken-Check schlaegt jedes Mal fehl.EXTENSIONS/oauth2wird gesetzt, damitmfc/oauth2den Login-Provider registriert. Das spart den Klick im Install-Tool und haelt die Konfiguration im Code.T3_SERVICES/auth/Mfc\OAuth2\Services\OAuth2LoginService/classNamezeigt aufMoselwal\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.