Search by

therealworld / adminer-module

therealworld

Integration of the database tool Adminer (https://www.adminer.org/) into the OXID backend

Package info

bitbucket.org/therealworld/adminer-module

Homepage

Type:oxideshop-module

pkg:composer/therealworld/adminer-module

Statistics

Installs: 5 557

Dependents: 0

Suggesters: 0

v2.1.1 2026-10-27 19:31 UTC

README

Deutsche Fassung: README.de.md

vendor-logo the-real-world.de

description

Opens Adminer (version 6.1.2, MySQL build, German interface) from the OXID backend. Server, database user and database name are taken from the shop's config.inc.php and filled in; the database password still has to be typed into the Adminer login. The module adds no settings.

  • Composer package: therealworld/adminer-module
  • OXID module id: trwadminer

Requirements and installation

  • OXID eShop CE >=v7.4, PHP ^8.3
  • therealworld/tools-plugin >=v3.2
  • Apache with .htaccess support recommended (see Limits)
composer require therealworld/adminer-module
vendor/bin/oe-console oe:module:activate trwadminer

Usage

Backend menu Service → DB Adminer.

On the first call the module copies the bundled Adminer files into the shop's document root at <sShopDir>/admin/Adminer/, renames the Adminer entry file to a random name (<uid>.php) and writes an .htaccess next to it that only allows that one file (Apache 2.4 Require syntax, with an Order/Allow fallback for Apache 2.2). The backend then redirects to <sShopURL>/admin/Adminer/<uid>.php?server=...&username=...&db=.... Later calls reuse the copy as long as the .htaccess exists and the copy still matches the shipped files: every call compares a fingerprint of src/Adminer/ (file names and contents) with the one stored in the copy (.trwadminer-fingerprint). After a module update that changed Adminer or the plugin, the next call deletes the old copy and creates a new one with a new random file name.

The bundled Adminer plugin (adminer-plugins/OxidExtension.php):

  • lists only the shop database in the database selection (cosmetic, not an access restriction — Adminer still accepts anything the database user may see)
  • sends X-Frame-Options: SameOrigin, so Adminer can be shown inside the backend frame
  • uses <sShopDir>/import/adminer.sql as the file for Adminer's "import from server" function

Binding to the backend login

The copy only runs for browsers that opened it through the backend. Every call of Service → DB Adminer (reachable for logged-in admins only) creates a random token and sets it as cookie trwadminer_access (HttpOnly, SameSite=Strict, Secure on https, path limited to the copy directory /admin/Adminer/). The copy stores only the SHA-256 hash of the token with its expiry (.trwadminer-tokens.php, one entry per token; expired entries are removed when the next token is issued, at most 100 entries). Several admins and browsers therefore work independently.

Before Adminer starts, adminer-guard.php checks the cookie against the stored hashes (hash_equals) and their expiry, without loading OXID. If the cookie is missing, invalid or expired, the copy answers 403 with a short hint to the menu entry. The check sits in the entry file as well as in adminer-plugins.php, which Adminer loads before it connects, so it also applies when adminer-de.php is called directly.

  • Expiry: fixed 2 hours from opening the menu entry; using Adminer does not extend it. Afterwards call Service → DB Adminer again, which issues a new token and overwrites the cookie.
  • Logout: logging out of the OXID backend does not revoke the token, it stays valid until it expires (the cookie expires at the same time). Adminer's own logout only ends the database login. To invalidate all tokens at once: deactivate/activate the module (deletes the copy) or delete admin/Adminer/.trwadminer-tokens.php.

Activating and deactivating the module deletes the copy in admin/Adminer/ completely; the next call creates a new one with a new random file name.

Limits

  • The copy and the .htaccess sit in a public directory. Web servers that ignore .htaccess (e.g. nginx) do not restrict the directory; the files of the copy can then be called directly. Adminer itself still only starts with a valid token (see above), and the token store is a PHP file with exit in its first line that holds hashes only. nginx does serve the fingerprint (.trwadminer-fingerprint) then, unless it blocks dot files.
  • The cookie is set by the backend and only applies to its host. If the backend runs on a host other than sShopURL (e.g. its own sAdminSSLURL), the cookie does not reach the copy and the copy answers 403.
  • The binding is to the browser, not to the backend session: a stolen cookie stays valid until it expires (see Logout).
  • The paths are built from sShopDir/sShopURL plus a fixed /admin/. A shop whose admin directory is named differently has to override AdminerConfig::getAdminerPath().

Updating Adminer (maintainers)

  1. Download the MySQL/German build from adminer.org.
  2. Replace src/Adminer/adminer-de.php with it (keep the file name, or adjust the include in the wrapper src/Adminer/36ba9de7be2f8a24dc54424f309d8bc0.php). The wrapper's file name is irrelevant for the shop: it is renamed to a random name when it is copied.
  3. Nothing else to do in the shop: the next call of Service → DB Adminer notices the changed files and replaces the copy.

For developers

  • Controller AdminerController (TheRealWorld\AdminerModule\Application\Controller\Admin, controller key adminercontroller, lowercase as in menu.xml): checks the fingerprint, copies, randomises, issues the access token and redirects in render(); on a copy or token error it shows the error template @trwadminer/admin/error.
  • AdminerAccessToken: issues tokens (cookie, hash store, lifetime LIFETIME). The checking side src/Adminer/adminer-guard.php runs without OXID and has to keep cookie name, file name and format in sync.
  • AdminerConfig: source path (src/Adminer/), target path and target URL. Overload getAdminerPath() for a non-standard admin directory.
  • AdminerEvents: onActivate/onDeactivate remove the copy.
  • src/Adminer/adminer-plugins.php is loaded by Adminer itself and registers OxidExtension.
  • No database tables, no migrations, no class extensions.