librelad 3ecf213cab refactor(de-sudo): docker calls via runFileOp/dockerCommandRun, drop sudo
Container-plane docker now routes through the mode-aware helpers instead of
sudo: simple calls (exec/ps/run/build/images/inspect/port/logs across ~15
app/check scripts) -> runFileOp docker (rootless socket as the install user;
rooted via the docker group). The cd && docker compose paths drop the sudo on
the rooted branch (the rootless branch already used dockerCommandRunInstallUser
-- byte-identical now, manager-ready later); gluetun, which had no rootless
branch, now uses dockerCommandRun so force-recreate works in both modes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-24 16:29:22 +01:00

116 lines
5.1 KiB
Bash

#!/bin/bash
# Bookstack stores users in MySQL/SQLite via Laravel/Eloquent. We use
# `php artisan tinker` to call the same User model the web UI does, so
# password hashing + role assignment match exactly what Bookstack expects.
_bookstackArtisan() {
local container="bookstack"
local php_body="$1"
shift
runFileOp docker exec -i -w /app/www "$@" "$container" php artisan tinker --execute="$php_body" 2>&1
}
authAdapter_bookstack_setPassword() {
local email="$1" password="$2"
[[ -z "$email" ]] && { isError "Email is required."; return 1; }
[[ -z "$password" ]] && password=$(generateRandomPassword)
local out
out=$(_bookstackArtisan '
$u = \BookStack\Users\Models\User::where("email", getenv("EZ_EMAIL"))->first();
if (!$u) { echo "EZ_USER_NOT_FOUND"; exit; }
$u->password = bcrypt(getenv("EZ_PASS"));
$u->save();
echo "EZ_RESET_OK:" . $u->name;
' -e "EZ_EMAIL=$email" -e "EZ_PASS=$password")
if echo "$out" | grep -q EZ_USER_NOT_FOUND; then
isError "No Bookstack user with email '$email'."; return 1
fi
if ! echo "$out" | grep -q EZ_RESET_OK; then
isError "Bookstack reset failed: $out"; return 1
fi
if [[ "$email" == "${CFG_BOOKSTACK_ADMIN_USER:-}" ]]; then
authPersistCfg bookstack ADMIN_PASSWORD "$password"
fi
isSuccessful "Bookstack password set for $email — New password: $password"
}
authAdapter_bookstack_createUser() {
local email="$1" password="$2" name="$3" isAdmin="$4"
[[ -z "$email" ]] && { isError "Email is required."; return 1; }
[[ -z "$name" ]] && name="${email%@*}"
[[ -z "$password" ]] && password=$(generateRandomPassword)
local roleSlug="public"
[[ "$isAdmin" == "true" ]] && roleSlug="admin"
local out
out=$(_bookstackArtisan '
if (\BookStack\Users\Models\User::where("email", getenv("EZ_EMAIL"))->exists()) { echo "EZ_USER_EXISTS"; exit; }
$u = new \BookStack\Users\Models\User();
$u->name = getenv("EZ_NAME"); $u->email = getenv("EZ_EMAIL");
$u->password = bcrypt(getenv("EZ_PASS")); $u->email_confirmed = true;
$base = preg_replace("/[^a-z0-9]+/", "-", strtolower($u->name ?: explode("@", $u->email)[0]));
$base = trim($base, "-"); if ($base === "") $base = "user";
$slug = $base; $i = 1;
while (\BookStack\Users\Models\User::where("slug", $slug)->exists()) { $i++; $slug = $base . "-" . $i; }
$u->slug = $slug;
$u->save();
$role = \BookStack\Users\Models\Role::where("system_name", getenv("EZ_ROLE"))->first();
if ($role) $u->attachRole($role);
echo "EZ_USER_CREATED:" . $u->id;
' -e "EZ_EMAIL=$email" -e "EZ_NAME=$name" -e "EZ_PASS=$password" -e "EZ_ROLE=$roleSlug")
if echo "$out" | grep -q EZ_USER_EXISTS; then isError "User '$email' already exists."; return 1; fi
if ! echo "$out" | grep -q EZ_USER_CREATED; then isError "Bookstack create failed: $out"; return 1; fi
if [[ "$isAdmin" == "true" && -z "${CFG_BOOKSTACK_ADMIN_USER:-}" ]]; then
authPersistCfg bookstack ADMIN_USER "$email"
authPersistCfg bookstack ADMIN_PASSWORD "$password"
fi
isSuccessful "Bookstack user created — Email: $email — Password: $password"
}
authAdapter_bookstack_listUsers() {
_bookstackArtisan '
foreach (\BookStack\Users\Models\User::all() as $u) {
$roles = $u->roles->pluck("display_name")->join(",");
echo "EZ_USER\t" . $u->email . "\t" . $u->name . "\t" . ($roles ?: "—") . "\n";
}
'
}
authAdapter_bookstack_deleteUser() {
local email="$1"
[[ -z "$email" ]] && { isError "Email is required."; return 1; }
local out
out=$(_bookstackArtisan '
$u = \BookStack\Users\Models\User::where("email", getenv("EZ_EMAIL"))->first();
if (!$u) { echo "EZ_USER_NOT_FOUND"; exit; }
$u->delete();
echo "EZ_USER_DELETED";
' -e "EZ_EMAIL=$email")
echo "$out" | grep -q EZ_USER_NOT_FOUND && { isError "No Bookstack user with email '$email'."; return 1; }
echo "$out" | grep -q EZ_USER_DELETED || { isError "Bookstack delete failed: $out"; return 1; }
isSuccessful "Bookstack user '$email' deleted."
}
authAdapter_bookstack_setAdmin() {
local email="$1" isAdmin="$2"
[[ -z "$email" ]] && { isError "Email is required."; return 1; }
local target="${isAdmin}"; [[ "$target" != "true" ]] && target="false"
local out
out=$(_bookstackArtisan '
$u = \BookStack\Users\Models\User::where("email", getenv("EZ_EMAIL"))->first();
if (!$u) { echo "EZ_USER_NOT_FOUND"; exit; }
$admin = \BookStack\Users\Models\Role::where("system_name", "admin")->first();
if (!$admin) { echo "EZ_ROLE_NOT_FOUND"; exit; }
if (getenv("EZ_TARGET") === "true") { $u->attachRole($admin); }
else { $u->detachRole($admin); }
echo "EZ_OK";
' -e "EZ_EMAIL=$email" -e "EZ_TARGET=$target")
echo "$out" | grep -q EZ_USER_NOT_FOUND && { isError "No Bookstack user '$email'."; return 1; }
echo "$out" | grep -q EZ_OK || { isError "Bookstack role change failed: $out"; return 1; }
isSuccessful "Bookstack user '$email' admin → $target."
}