From 6525b561c2b3c40cfca47500d141e6fe324b82ce Mon Sep 17 00:00:00 2001 From: Ruben Date: Sun, 30 Aug 2026 22:24:16 +0200 Subject: [PATCH] Enhance publiser password management tool Add support for multiple users in the password utility. New features allow listing existing users and removing specific users from the `.htpasswd` file without overwriting others. --- custom/tools/set-publiser-password.php | 65 +++++++++++++++++++++----- docs/publiser-tool.md | 8 ++-- 2 files changed, 59 insertions(+), 14 deletions(-) diff --git a/custom/tools/set-publiser-password.php b/custom/tools/set-publiser-password.php index 780e02a..f6c3d55 100644 --- a/custom/tools/set-publiser-password.php +++ b/custom/tools/set-publiser-password.php @@ -1,14 +1,18 @@ #!/usr/bin/env php * php custom/tools/set-publiser-password.php (interactive) + * php custom/tools/set-publiser-password.php --remove + * php custom/tools/set-publiser-password.php --list */ $htpasswdPath = dirname(__DIR__, 2) . '/content/publiser/.htpasswd'; @@ -31,8 +35,50 @@ function promptHidden(string $question): string { return $value; } +function readUsers(string $path): array { + if (!is_file($path)) return []; + $users = []; + foreach (file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [] as $line) { + [$user, $hash] = array_pad(explode(':', $line, 2), 2, ''); + if ($user !== '') $users[$user] = $hash; + } + return $users; +} + +function writeUsers(string $path, array $users): void { + $lines = []; + foreach ($users as $user => $hash) { + $lines[] = "$user:$hash"; + } + file_put_contents($path, $lines ? implode("\n", $lines) . "\n" : ''); + // World-readable: the web server user (www-data, varies by host/container) + // needs read access. This is safe - Apache denies direct HTTP access to + // this exact filename (content/publiser/.htaccess), and reaching it any + // other way already requires shell/filesystem access to the server. + chmod($path, 0644); +} + $args = array_slice($argv, 1); +if (($args[0] ?? '') === '--list') { + $users = array_keys(readUsers($htpasswdPath)); + echo $users ? implode("\n", $users) . "\n" : "No users configured yet.\n"; + exit(0); +} + +if (($args[0] ?? '') === '--remove') { + $username = $args[1] ?? prompt('Username to remove: '); + $users = readUsers($htpasswdPath); + if (!isset($users[$username])) { + fwrite(STDERR, "No such user: $username\n"); + exit(1); + } + unset($users[$username]); + writeUsers($htpasswdPath, $users); + echo "Removed \"$username\". " . count($users) . " user(s) remain.\n"; + exit(0); +} + $username = $args[0] ?? prompt('Username: '); $password = $args[1] ?? promptHidden('Password: '); @@ -46,12 +92,9 @@ if (str_contains($username, ':')) { exit(1); } -$hash = password_hash($password, PASSWORD_BCRYPT); -file_put_contents($htpasswdPath, "$username:$hash\n"); -// World-readable: the web server user (www-data, varies by host/container) -// needs read access. This is safe - Apache denies direct HTTP access to -// this exact filename (content/publiser/.htaccess), and reaching it any -// other way already requires shell/filesystem access to the server. -chmod($htpasswdPath, 0644); +$users = readUsers($htpasswdPath); +$isNew = !isset($users[$username]); +$users[$username] = password_hash($password, PASSWORD_BCRYPT); +writeUsers($htpasswdPath, $users); -echo "Wrote $htpasswdPath for user \"$username\".\n"; +echo ($isNew ? 'Added' : 'Updated') . " \"$username\". " . count($users) . " user(s) total in $htpasswdPath.\n"; diff --git a/docs/publiser-tool.md b/docs/publiser-tool.md index d5a6529..ce3d422 100644 --- a/docs/publiser-tool.md +++ b/docs/publiser-tool.md @@ -25,13 +25,15 @@ Standalone admin app for authoring, managing, and scheduling `content/nyheter/` `content/publiser/.htaccess` sets `DirectorySlash On` (the site-wide default is `Off` - see Critical section below) and forwards the `Authorization` header via `RewriteRule ... [E=HTTP_AUTHORIZATION:...]` for SAPIs (LSAPI/CGI/FastCGI, used on cPanel) that strip it by default. -Auth itself is checked in PHP (`publiserRequireAuth()` in `index.php`), not via Apache's `AuthUserFile` - that directive needs an absolute path that differs between the podman dev container and cPanel, so a bcrypt `.htpasswd` (same format `mod_authn_file` would use) is read and verified manually instead. Set the login with: +Auth itself is checked in PHP (`publiserRequireAuth()` in `index.php`), not via Apache's `AuthUserFile` - that directive needs an absolute path that differs between the podman dev container and cPanel, so a bcrypt `.htpasswd` (same format `mod_authn_file` would use) is read and verified manually instead. Manage logins with: ```bash -php custom/tools/set-publiser-password.php +php custom/tools/set-publiser-password.php # add, or update if it exists +php custom/tools/set-publiser-password.php --list +php custom/tools/set-publiser-password.php --remove ``` -This writes `content/publiser/.htpasswd` (gitignored, `0644` - see comment in the script for why world-readable is fine here). +Supports multiple users - adding/updating one username preserves everyone else's entry in `content/publiser/.htpasswd` (gitignored, `0644` - see comment in the script for why world-readable is fine here). There's no per-user attribution anywhere in the tool (all edits are just "whoever is logged in"); this only lets more than one person log in. ## Content Model