innhold/custom/tools/set-publiser-password.php

101 lines
3.2 KiB
PHP
Raw Permalink Normal View History

#!/usr/bin/env php
<?php
/**
* Add, update, or remove a Basic Auth login for /publiser.
*
* Writes bcrypt-hashed entries to .htpasswd (no dependency on the
* `htpasswd` binary being installed - Apache's mod_authn_file accepts
* $2y$ hashes). Existing users are preserved: re-running with a known
* username updates just that line, any other username is appended.
*
* Usage:
* php custom/tools/set-publiser-password.php <username> <password>
* php custom/tools/set-publiser-password.php (interactive)
* php custom/tools/set-publiser-password.php --remove <username>
* php custom/tools/set-publiser-password.php --list
*/
$htpasswdPath = dirname(__DIR__, 2) . '/content/publiser/.htpasswd';
function prompt(string $question): string {
echo $question;
return trim(fgets(STDIN));
}
function promptHidden(string $question): string {
$isWindows = stripos(PHP_OS, 'WIN') === 0;
if (!$isWindows) {
system('stty -echo');
}
$value = prompt($question);
if (!$isWindows) {
system('stty echo');
echo "\n";
}
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: ');
if ($username === '' || $password === '') {
fwrite(STDERR, "Username and password are required.\n");
exit(1);
}
if (str_contains($username, ':')) {
fwrite(STDERR, "Username cannot contain ':'.\n");
exit(1);
}
$users = readUsers($htpasswdPath);
$isNew = !isset($users[$username]);
$users[$username] = password_hash($password, PASSWORD_BCRYPT);
writeUsers($htpasswdPath, $users);
echo ($isNew ? 'Added' : 'Updated') . " \"$username\". " . count($users) . " user(s) total in $htpasswdPath.\n";