|
| 1 | +<?php |
| 2 | + |
| 3 | +/** |
| 4 | + * Server list with optional stored credentials and Auto Sign-In button. |
| 5 | + * Credentials are handled server-side — never exposed to the browser. |
| 6 | + * |
| 7 | + * Activated by: ADMINER_SERVERS_* env vars |
| 8 | + * DSN format: driver://username:password@host:port/database |
| 9 | + * Examples: |
| 10 | + * ADMINER_SERVERS_MariaDB=server://root:secret@mysql:3306/mydb |
| 11 | + * ADMINER_SERVERS_PostgreSQL=pgsql://postgres:pwd@pg:5432/app |
| 12 | + * ADMINER_SERVERS_SQLite=sqlite:///data/db.sqlite (no credentials) |
| 13 | + * |
| 14 | + * The suffix after ADMINER_SERVERS_ becomes the display name in the dropdown. |
| 15 | + */ |
| 16 | +class AdminerServerList extends Adminer\Plugin { |
| 17 | + private $servers = []; |
| 18 | + private $credentials = []; |
| 19 | + |
| 20 | + function __construct() { |
| 21 | + foreach (getenv() as $key => $value) { |
| 22 | + if (!str_starts_with($key, 'ADMINER_SERVERS_')) { |
| 23 | + continue; |
| 24 | + } |
| 25 | + $name = substr($key, strlen('ADMINER_SERVERS_')); |
| 26 | + if (!preg_match('#^(\w+)://(?:([^:@]*)(?::([^@]*))?@)?([^/:]+)(?::(\d+))?(?:/(.*))?$#', $value, $m)) { |
| 27 | + continue; |
| 28 | + } |
| 29 | + $host = $m[4] . (!empty($m[5]) ? ':' . $m[5] : ''); |
| 30 | + $this->servers[$name] = ['server' => $host, 'driver' => $m[1]]; |
| 31 | + if (!empty($m[2])) { |
| 32 | + $this->credentials[$name] = [ |
| 33 | + 'username' => urldecode($m[2]), |
| 34 | + 'password' => urldecode($m[3] ?? ''), |
| 35 | + 'db' => urldecode($m[6] ?? ''), |
| 36 | + ]; |
| 37 | + } |
| 38 | + } |
| 39 | + |
| 40 | + // Set driver from selected server on POST (same as AdminerLoginServers) |
| 41 | + if ($_POST["auth"] && isset($this->servers[$_POST["auth"]["server"]])) { |
| 42 | + $_POST["auth"]["driver"] = $this->servers[$_POST["auth"]["server"]]["driver"]; |
| 43 | + } |
| 44 | + |
| 45 | + // Handle Auto Sign-In: inject stored credentials into POST |
| 46 | + if (isset($_POST["_autologin"]) && isset($this->credentials[$_POST["auth"]["server"]])) { |
| 47 | + $cred = $this->credentials[$_POST["auth"]["server"]]; |
| 48 | + $_POST["auth"]["username"] = $cred["username"]; |
| 49 | + $_POST["auth"]["password"] = $cred["password"]; |
| 50 | + $_POST["auth"]["db"] = $cred["db"]; |
| 51 | + } |
| 52 | + } |
| 53 | + |
| 54 | + function credentials() { |
| 55 | + $server = Adminer\SERVER; |
| 56 | + if (isset($this->servers[$server])) { |
| 57 | + $host = $this->servers[$server]["server"]; |
| 58 | + if (isset($this->credentials[$server])) { |
| 59 | + $cred = $this->credentials[$server]; |
| 60 | + $username = $_GET["username"] ?: $cred["username"]; |
| 61 | + $password = Adminer\get_password() ?: $cred["password"]; |
| 62 | + return [$host, $username, $password]; |
| 63 | + } |
| 64 | + return [$host, $_GET["username"], Adminer\get_password()]; |
| 65 | + } |
| 66 | + } |
| 67 | + |
| 68 | + function login($login, $password) { |
| 69 | + if (!isset($this->servers[Adminer\SERVER])) { |
| 70 | + return false; |
| 71 | + } |
| 72 | + if (isset($this->credentials[Adminer\SERVER])) { |
| 73 | + return true; |
| 74 | + } |
| 75 | + } |
| 76 | + |
| 77 | + function loginFormField($name, $heading, $value) { |
| 78 | + if ($name == 'driver') { |
| 79 | + return ''; |
| 80 | + } |
| 81 | + if ($name == 'server') { |
| 82 | + return $heading . Adminer\html_select("auth[server]", array_keys($this->servers), Adminer\SERVER) . "\n"; |
| 83 | + } |
| 84 | + } |
| 85 | + |
| 86 | + function head() { |
| 87 | + if (isset($_GET["username"]) || empty($this->credentials)) { |
| 88 | + return; |
| 89 | + } |
| 90 | + $servers = json_encode(array_keys($this->credentials), JSON_HEX_TAG | JSON_HEX_AMP); |
| 91 | + $nonce = \Adminer\get_nonce(); |
| 92 | + echo <<<SCRIPT |
| 93 | +<script nonce="$nonce"> |
| 94 | +(function() { |
| 95 | + var serversWithCreds = $servers; |
| 96 | + document.addEventListener("DOMContentLoaded", function() { |
| 97 | + var form = document.querySelector("form"); |
| 98 | + if (!form) return; |
| 99 | + var serverSelect = form.querySelector('[name="auth[server]"]'); |
| 100 | + if (!serverSelect) return; |
| 101 | +
|
| 102 | + var hidden = document.createElement("input"); |
| 103 | + hidden.type = "hidden"; |
| 104 | + hidden.name = "_autologin"; |
| 105 | + hidden.disabled = true; |
| 106 | + form.appendChild(hidden); |
| 107 | +
|
| 108 | + var btn = document.createElement("input"); |
| 109 | + btn.type = "submit"; |
| 110 | + btn.value = "Auto Sign-In"; |
| 111 | + btn.style.display = "none"; |
| 112 | + btn.style.marginLeft = "5px"; |
| 113 | + btn.addEventListener("click", function() { |
| 114 | + hidden.disabled = false; |
| 115 | + hidden.value = "1"; |
| 116 | + }); |
| 117 | + var loginBtn = form.querySelector('[type="submit"][value="Login"]'); |
| 118 | + if (loginBtn) loginBtn.parentNode.insertBefore(btn, loginBtn.nextSibling); |
| 119 | +
|
| 120 | + function updateButton(serverName) { |
| 121 | + btn.style.display = serversWithCreds.indexOf(serverName) >= 0 ? "inline" : "none"; |
| 122 | + } |
| 123 | + serverSelect.addEventListener("change", function() { updateButton(this.value); }); |
| 124 | + updateButton(serverSelect.value); |
| 125 | + }); |
| 126 | +})(); |
| 127 | +</script> |
| 128 | +SCRIPT; |
| 129 | + } |
| 130 | +} |
0 commit comments