Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,52 +21,52 @@ jobs:
matrix:
include:
- dockerfile: ./adminer-dg/Dockerfile
context: ./adminer-dg
context: .
tag: dg
platforms: linux/amd64,linux/arm64

- dockerfile: ./adminer-editor/Dockerfile
context: ./adminer-editor
context: .
tag: editor
platforms: linux/amd64,linux/arm64

- dockerfile: ./adminer-full/Dockerfile
context: ./adminer-full
context: .
tag: full
platforms: linux/amd64,linux/arm64

- dockerfile: ./adminer-mongo/Dockerfile
context: ./adminer-mongo
context: .
tag: mongo
platforms: linux/amd64,linux/arm64

- dockerfile: ./adminer-mysql/Dockerfile
context: ./adminer-mysql
context: .
tag: mysql
platforms: linux/amd64,linux/arm64

- dockerfile: ./adminer-oracle-11/Dockerfile
context: ./adminer-oracle-11
context: .
tag: oracle-11
platforms: linux/amd64

- dockerfile: ./adminer-oracle-12/Dockerfile
context: ./adminer-oracle-12
context: .
tag: oracle-12
platforms: linux/amd64

- dockerfile: ./adminer-oracle-19/Dockerfile
context: ./adminer-oracle-19
context: .
tag: oracle-19
platforms: linux/amd64

- dockerfile: ./adminer-postgres/Dockerfile
context: ./adminer-postgres
context: .
tag: postgres
platforms: linux/amd64,linux/arm64

- dockerfile: ./adminer-full/Dockerfile
context: ./adminer-full
context: .
tag: latest
platforms: linux/amd64,linux/arm64

Expand Down
51 changes: 51 additions & 0 deletions .plugins/adminer-autologin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

/**
* Auto-login to a single database server. Skips the login form entirely.
*
* Activated by: ADMINER_AUTOLOGIN_SERVER env var
* DSN format: driver://username:password@host:port/database
* Example: server://root:secret@mysql:3306/mydb
*/
class AdminerAutologin extends Adminer\Plugin {
private $config;

function __construct() {
$dsn = getenv('ADMINER_AUTOLOGIN_SERVER');
if (!$dsn || !preg_match('#^(\w+)://(?:([^:@]*)(?::([^@]*))?@)?([^/:]+)(?::(\d+))?(?:/(.*))?$#', $dsn, $m)) {
return;
}
$host = $m[4] . (!empty($m[5]) ? ':' . $m[5] : '');
$this->config = [
'driver' => $m[1],
'username' => urldecode($m[2] ?? ''),
'password' => urldecode($m[3] ?? ''),
'server' => $host,
'db' => urldecode($m[6] ?? ''),
];

// Inject login data on first visit (before Adminer processes the request)
if (!isset($_GET["username"]) && empty($_POST["auth"]) && !isset($_GET["server"])) {
$_POST["auth"] = [
"driver" => $this->config["driver"],
"server" => $this->config["server"],
"username" => $this->config["username"],
"password" => $this->config["password"],
"db" => $this->config["db"],
"permanent" => "",
];
}
}

function credentials() {
if ($this->config) {
return [$this->config["server"], $this->config["username"], $this->config["password"]];
}
}

function login($login, $password) {
if ($this->config) {
return true;
}
}
}
130 changes: 130 additions & 0 deletions .plugins/adminer-server-list.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<?php

/**
* Server list with optional stored credentials and Auto Sign-In button.
* Credentials are handled server-side — never exposed to the browser.
*
* Activated by: ADMINER_SERVERS_* env vars
* DSN format: driver://username:password@host:port/database
* Examples:
* ADMINER_SERVERS_MariaDB=server://root:secret@mysql:3306/mydb
* ADMINER_SERVERS_PostgreSQL=pgsql://postgres:pwd@pg:5432/app
* ADMINER_SERVERS_SQLite=sqlite:///data/db.sqlite (no credentials)
*
* The suffix after ADMINER_SERVERS_ becomes the display name in the dropdown.
*/
class AdminerServerList extends Adminer\Plugin {
private $servers = [];
private $credentials = [];

function __construct() {
foreach (getenv() as $key => $value) {
if (!str_starts_with($key, 'ADMINER_SERVERS_')) {
continue;
}
$name = substr($key, strlen('ADMINER_SERVERS_'));
if (!preg_match('#^(\w+)://(?:([^:@]*)(?::([^@]*))?@)?([^/:]+)(?::(\d+))?(?:/(.*))?$#', $value, $m)) {
continue;
}
$host = $m[4] . (!empty($m[5]) ? ':' . $m[5] : '');
$this->servers[$name] = ['server' => $host, 'driver' => $m[1]];
if (!empty($m[2])) {
$this->credentials[$name] = [
'username' => urldecode($m[2]),
'password' => urldecode($m[3] ?? ''),
'db' => urldecode($m[6] ?? ''),
];
}
}

// Set driver from selected server on POST (same as AdminerLoginServers)
if ($_POST["auth"] && isset($this->servers[$_POST["auth"]["server"]])) {
$_POST["auth"]["driver"] = $this->servers[$_POST["auth"]["server"]]["driver"];
}

// Handle Auto Sign-In: inject stored credentials into POST
if (isset($_POST["_autologin"]) && isset($this->credentials[$_POST["auth"]["server"]])) {
$cred = $this->credentials[$_POST["auth"]["server"]];
$_POST["auth"]["username"] = $cred["username"];
$_POST["auth"]["password"] = $cred["password"];
$_POST["auth"]["db"] = $cred["db"];
}
}

function credentials() {
$server = Adminer\SERVER;
if (isset($this->servers[$server])) {
$host = $this->servers[$server]["server"];
if (isset($this->credentials[$server])) {
$cred = $this->credentials[$server];
$username = $_GET["username"] ?: $cred["username"];
$password = Adminer\get_password() ?: $cred["password"];
return [$host, $username, $password];
}
return [$host, $_GET["username"], Adminer\get_password()];
}
}

function login($login, $password) {
if (!isset($this->servers[Adminer\SERVER])) {
return false;
}
if (isset($this->credentials[Adminer\SERVER])) {
return true;
}
}

function loginFormField($name, $heading, $value) {
if ($name == 'driver') {
return '';
}
if ($name == 'server') {
return $heading . Adminer\html_select("auth[server]", array_keys($this->servers), Adminer\SERVER) . "\n";
}
}

function head() {
if (isset($_GET["username"]) || empty($this->credentials)) {
return;
}
$servers = json_encode(array_keys($this->credentials), JSON_HEX_TAG | JSON_HEX_AMP);
$nonce = \Adminer\get_nonce();
echo <<<SCRIPT
<script nonce="$nonce">
(function() {
var serversWithCreds = $servers;
document.addEventListener("DOMContentLoaded", function() {
var form = document.querySelector("form");
if (!form) return;
var serverSelect = form.querySelector('[name="auth[server]"]');
if (!serverSelect) return;

var hidden = document.createElement("input");
hidden.type = "hidden";
hidden.name = "_autologin";
hidden.disabled = true;
form.appendChild(hidden);

var btn = document.createElement("input");
btn.type = "submit";
btn.value = "Auto Sign-In";
btn.style.display = "none";
btn.style.marginLeft = "5px";
btn.addEventListener("click", function() {
hidden.disabled = false;
hidden.value = "1";
});
var loginBtn = form.querySelector('[type="submit"][value="Login"]');
if (loginBtn) loginBtn.parentNode.insertBefore(btn, loginBtn.nextSibling);

function updateButton(serverName) {
btn.style.display = serversWithCreds.indexOf(serverName) >= 0 ? "inline" : "none";
}
serverSelect.addEventListener("change", function() { updateButton(this.value); });
updateButton(serverSelect.value);
});
})();
</script>
SCRIPT;
}
}
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build-all: build-full build-dg build-editor build-mongo build-mysql build-postgr

_docker-build-%: TAG=$*
_docker-build-%:
docker buildx build --platform ${DOCKER_PLATFORMS} -t ${DOCKER_IMAGE}:${TAG} ./adminer-${TAG}
docker buildx build --platform ${DOCKER_PLATFORMS} -t ${DOCKER_IMAGE}:${TAG} -f ./adminer-${TAG}/Dockerfile .

build-full: _docker-build-full
build-dg: _docker-build-dg
Expand Down
50 changes: 50 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,56 @@ You should take a look to the official github profile (https://github.com/dg/adm

![Adminer DG](.docs/assets/adminer-dg.png)

## Plugins

Adminer plugins can be enabled via environment variables. All plugins are disabled by default. Available for all image variants except `dg`.

### Autologin

Skips the login form and connects directly to a database server.

| Variable | Description |
|---|---|
| `ADMINER_PLUGIN_AUTOLOGIN=1` | Enable the autologin plugin |
| `ADMINER_AUTOLOGIN_SERVER` | DSN connection string |

DSN format: `driver://username:password@host:port/database`

```sh
docker run \
--rm \
-p 8080:80 \
-e ADMINER_PLUGIN_AUTOLOGIN=1 \
-e ADMINER_AUTOLOGIN_SERVER=server://root:secret@mysql:3306/mydb \
dockette/adminer:full
```

Supported drivers: `server` (MySQL/MariaDB), `pgsql`, `sqlite`, `mongo`, `oracle`, `elastic`.

### Server List

Displays a dropdown of pre-configured database servers with an **Auto Sign-In** button. Credentials are handled server-side and never exposed to the browser.

| Variable | Description |
|---|---|
| `ADMINER_PLUGIN_SERVER_LIST=1` | Enable the server list plugin |
| `ADMINER_SERVERS_<Name>` | DSN for each server (suffix becomes the display name) |

```sh
docker run \
--rm \
-p 8080:80 \
-e ADMINER_PLUGIN_SERVER_LIST=1 \
-e ADMINER_SERVERS_MySQL=server://root:secret@mysql:3306/mydb \
-e ADMINER_SERVERS_PostgreSQL=pgsql://postgres:pwd@pg:5432/app \
-e ADMINER_SERVERS_DevDB=server://dev@devhost:3306 \
dockette/adminer:full
```

Servers without credentials in the DSN (e.g. `server://devhost:3306`) appear in the dropdown but require manual login. The **Auto Sign-In** button only appears for servers with stored credentials.

> **Note:** Autologin takes precedence over Server List. If both plugins are enabled, only Autologin is activated.

## Themes

You can apply a theme by setting the `ADMINER_THEME` environment variable:
Expand Down
2 changes: 1 addition & 1 deletion adminer-dg/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ RUN echo '@community http://nl.alpinelinux.org/alpine/v3.22/community' >> /etc/a
apk del wget ca-certificates && \
rm -rf /var/cache/apk/*

ADD ./entrypoint.sh /entrypoint.sh
ADD ./adminer-dg/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

WORKDIR /srv
Expand Down
4 changes: 3 additions & 1 deletion adminer-editor/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@ RUN echo '@community http://nl.alpinelinux.org/alpine/v3.22/community' >> /etc/a
mkdir -p /srv/designs && \
mv /tmp/adminer-$ADMINER_EDITOR_VERSION/designs/* /srv/designs/ 2>/dev/null || true && \
rm -rf /tmp/* && \
mkdir -p /srv/adminer-plugins && \
ln -s /usr/bin/php84 /usr/bin/php && \
apk del wget unzip ca-certificates && \
rm -rf /var/cache/apk/*

ADD ./entrypoint.sh /entrypoint.sh
ADD ./adminer-editor/entrypoint.sh /entrypoint.sh
ADD ./.plugins/ /srv/plugins-available/
RUN chmod +x /entrypoint.sh

WORKDIR /srv
Expand Down
9 changes: 9 additions & 0 deletions adminer-editor/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ if [ -n "${ADMINER_THEME}" ]; then
fi
fi

# Activate plugins (ADMINER_PLUGIN_<name>=1, all disabled by default)
if [ "${ADMINER_PLUGIN_AUTOLOGIN}" = "1" ]; then
cp /srv/plugins-available/adminer-autologin.php /srv/adminer-plugins/
echo "[adminer] Plugin 'autologin' activated."
elif [ "${ADMINER_PLUGIN_SERVER_LIST}" = "1" ]; then
cp /srv/plugins-available/adminer-server-list.php /srv/adminer-plugins/
echo "[adminer] Plugin 'server-list' activated."
fi

# Set default values if not provided
MEMORY=${MEMORY:-256M}
UPLOAD=${UPLOAD:-2048M}
Expand Down
3 changes: 2 additions & 1 deletion adminer-full/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ RUN echo '@community http://nl.alpinelinux.org/alpine/v3.22/community' >> /etc/a
apk del wget unzip ca-certificates && \
rm -rf /var/cache/apk/*

ADD ./entrypoint.sh /entrypoint.sh
ADD ./adminer-full/entrypoint.sh /entrypoint.sh
ADD ./.plugins/ /srv/plugins-available/
RUN chmod +x /entrypoint.sh

WORKDIR /srv
Expand Down
9 changes: 9 additions & 0 deletions adminer-full/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ else
echo "[adminer] No driver plugins directory found at /srv/plugins/drivers, skipping..."
fi

# Activate plugins (ADMINER_PLUGIN_<name>=1, all disabled by default)
if [ "${ADMINER_PLUGIN_AUTOLOGIN}" = "1" ]; then
cp /srv/plugins-available/adminer-autologin.php /srv/adminer-plugins/
echo "[adminer] Plugin 'autologin' activated."
elif [ "${ADMINER_PLUGIN_SERVER_LIST}" = "1" ]; then
cp /srv/plugins-available/adminer-server-list.php /srv/adminer-plugins/
echo "[adminer] Plugin 'server-list' activated."
fi

# Copy theme CSS files based on ADMINER_THEME environment variable
if [ -n "${ADMINER_THEME}" ]; then
THEME_DIR="/srv/designs/${ADMINER_THEME}"
Expand Down
4 changes: 3 additions & 1 deletion adminer-mongo/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ RUN echo '@community http://nl.alpinelinux.org/alpine/v3.22/community' >> /etc/a
mkdir -p /srv/designs && \
mv /tmp/adminer-$ADMINER_VERSION/designs/* /srv/designs/ 2>/dev/null || true && \
rm -rf /tmp/* && \
mkdir -p /srv/adminer-plugins && \
ln -s /usr/bin/php84 /usr/bin/php && \
apk del wget unzip ca-certificates && \
rm -rf /var/cache/apk/*

ADD ./entrypoint.sh /entrypoint.sh
ADD ./adminer-mongo/entrypoint.sh /entrypoint.sh
ADD ./.plugins/ /srv/plugins-available/
RUN chmod +x /entrypoint.sh

WORKDIR /srv
Expand Down
Loading
Loading