227 lines
6.4 KiB
Bash
Executable File
227 lines
6.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
repo_root="$(cd "${script_dir}/.." && pwd)"
|
|
source "${script_dir}/lib-compose-project.sh"
|
|
|
|
compose_file=""
|
|
env_file=""
|
|
app_service="app"
|
|
broker_service="broker"
|
|
repair_mode="0"
|
|
|
|
usage() {
|
|
cat <<USAGE
|
|
Usage: ./scripts/ensure-nextcloud-service-auth.sh --compose-file <file> --env-file <file> [options]
|
|
|
|
Verify broker -> Nextcloud OCS service authentication works and optionally repair it.
|
|
Uses NEXTCLOUD_SERVICE_USER from env as-is (legacy sovereign_oidc_service or
|
|
nuqloud_oidc_service are both supported).
|
|
|
|
Options:
|
|
--compose-file <path> docker compose file to use (required)
|
|
--env-file <path> env file to use (required)
|
|
--app-service <name> Nextcloud app service name (default: app)
|
|
--broker-service <name> Broker service name (default: broker)
|
|
--repair Attempt automatic repair on auth failure
|
|
-h, --help Show this help
|
|
USAGE
|
|
}
|
|
|
|
read_kv() {
|
|
local key="$1"
|
|
local line
|
|
line="$(grep -m1 -E "^${key}=" "${env_file}" || true)"
|
|
if [[ -z "${line}" ]]; then
|
|
return 1
|
|
fi
|
|
echo "${line#*=}"
|
|
}
|
|
|
|
trim() {
|
|
local value="$1"
|
|
value="$(echo "${value}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
|
|
echo "${value}"
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--compose-file)
|
|
compose_file="${2:-}"
|
|
shift 2
|
|
;;
|
|
--compose-file=*)
|
|
compose_file="${1#*=}"
|
|
shift
|
|
;;
|
|
--env-file)
|
|
env_file="${2:-}"
|
|
shift 2
|
|
;;
|
|
--env-file=*)
|
|
env_file="${1#*=}"
|
|
shift
|
|
;;
|
|
--app-service)
|
|
app_service="${2:-}"
|
|
shift 2
|
|
;;
|
|
--app-service=*)
|
|
app_service="${1#*=}"
|
|
shift
|
|
;;
|
|
--broker-service)
|
|
broker_service="${2:-}"
|
|
shift 2
|
|
;;
|
|
--broker-service=*)
|
|
broker_service="${1#*=}"
|
|
shift
|
|
;;
|
|
--repair)
|
|
repair_mode="1"
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Unknown option: $1"
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ -z "${compose_file}" || -z "${env_file}" ]]; then
|
|
usage
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "${compose_file}" != /* ]]; then
|
|
compose_file="${repo_root}/${compose_file}"
|
|
fi
|
|
if [[ "${env_file}" != /* ]]; then
|
|
env_file="${repo_root}/${env_file}"
|
|
fi
|
|
|
|
if [[ ! -f "${compose_file}" ]]; then
|
|
echo "Missing compose file: ${compose_file}"
|
|
exit 1
|
|
fi
|
|
if [[ ! -f "${env_file}" ]]; then
|
|
echo "Missing env file: ${env_file}"
|
|
exit 1
|
|
fi
|
|
|
|
export_compose_project_name_from_env "${env_file}" || true
|
|
|
|
nextcloud_base_url="$(trim "$(read_kv "NEXTCLOUD_BASE_URL" || true)")"
|
|
service_user="$(trim "$(read_kv "NEXTCLOUD_SERVICE_USER" || true)")"
|
|
service_password="$(read_kv "NEXTCLOUD_SERVICE_PASSWORD" || true)"
|
|
admin_user="$(trim "$(read_kv "NEXTCLOUD_ADMIN_USER" || true)")"
|
|
|
|
if [[ -z "${nextcloud_base_url}" ]]; then
|
|
echo "NEXTCLOUD_BASE_URL is missing in ${env_file}"
|
|
exit 1
|
|
fi
|
|
if [[ -z "${service_user}" || -z "${service_password}" ]]; then
|
|
echo "NEXTCLOUD_SERVICE_USER/NEXTCLOUD_SERVICE_PASSWORD must be set in ${env_file}"
|
|
exit 1
|
|
fi
|
|
|
|
if ! docker compose -f "${compose_file}" --env-file "${env_file}" exec -T "${app_service}" php occ status >/dev/null 2>&1; then
|
|
echo "Nextcloud OCC is not ready in service '${app_service}'."
|
|
exit 1
|
|
fi
|
|
|
|
test_service_auth() {
|
|
local status
|
|
status="$(docker compose -f "${compose_file}" --env-file "${env_file}" exec -T "${broker_service}" node -e '
|
|
const base = (process.env.NEXTCLOUD_BASE_URL || "").replace(/\/+$/, "");
|
|
const user = process.env.NEXTCLOUD_SERVICE_USER || "";
|
|
const pass = process.env.NEXTCLOUD_SERVICE_PASSWORD || "";
|
|
const endpoint = `${base}/ocs/v1.php/cloud/users?search=${encodeURIComponent(user)}&format=json`;
|
|
const auth = Buffer.from(`${user}:${pass}`).toString("base64");
|
|
fetch(endpoint, {
|
|
method: "GET",
|
|
headers: {
|
|
Authorization: `Basic ${auth}`,
|
|
"OCS-APIRequest": "true",
|
|
Accept: "application/json"
|
|
}
|
|
}).then(async (res) => {
|
|
process.stdout.write(String(res.status));
|
|
}).catch(() => {
|
|
process.stdout.write("000");
|
|
});' 2>/dev/null || true)"
|
|
echo "${status}"
|
|
}
|
|
|
|
test_service_auth_with_retry() {
|
|
local attempts="${1:-18}"
|
|
local delay_seconds="${2:-2}"
|
|
local status=""
|
|
local i
|
|
for i in $(seq 1 "${attempts}"); do
|
|
status="$(test_service_auth)"
|
|
if [[ "${status}" == "200" ]]; then
|
|
echo "${status}"
|
|
return 0
|
|
fi
|
|
# A direct 401 usually means credentials mismatch, so fail fast.
|
|
if [[ "${status}" == "401" ]]; then
|
|
echo "${status}"
|
|
return 0
|
|
fi
|
|
if [[ "${i}" -lt "${attempts}" ]]; then
|
|
sleep "${delay_seconds}"
|
|
fi
|
|
done
|
|
echo "${status}"
|
|
}
|
|
|
|
auth_status="$(test_service_auth_with_retry 18 2)"
|
|
if [[ "${auth_status}" == "200" ]]; then
|
|
echo "Nextcloud broker service auth is healthy (HTTP 200)."
|
|
exit 0
|
|
fi
|
|
|
|
if [[ "${repair_mode}" != "1" ]]; then
|
|
echo "Nextcloud broker service auth check failed (HTTP ${auth_status:-unknown})."
|
|
echo "Run with --repair or reset NEXTCLOUD_SERVICE_* credentials."
|
|
exit 1
|
|
fi
|
|
|
|
echo "Attempting broker service auth repair (current HTTP ${auth_status:-unknown})..."
|
|
|
|
if [[ -n "${admin_user}" && "${service_user}" == "${admin_user}" ]]; then
|
|
echo "Refusing automatic password reset for admin account '${service_user}'."
|
|
echo "Set dedicated NEXTCLOUD_SERVICE_USER/NEXTCLOUD_SERVICE_PASSWORD in ${env_file}, then rerun."
|
|
exit 1
|
|
fi
|
|
|
|
if ! docker compose -f "${compose_file}" --env-file "${env_file}" exec -T "${app_service}" php occ user:info "${service_user}" >/dev/null 2>&1; then
|
|
echo "Creating missing service user '${service_user}'..."
|
|
docker compose -f "${compose_file}" --env-file "${env_file}" exec -T \
|
|
-e OC_PASS="${service_password}" \
|
|
"${app_service}" php occ user:add --password-from-env --display-name="${service_user}" "${service_user}" >/dev/null
|
|
fi
|
|
|
|
# Required for OCS user provisioning operations.
|
|
docker compose -f "${compose_file}" --env-file "${env_file}" exec -T "${app_service}" php occ group:adduser admin "${service_user}" >/dev/null 2>&1 || true
|
|
|
|
docker compose -f "${compose_file}" --env-file "${env_file}" exec -T \
|
|
-e OC_PASS="${service_password}" \
|
|
"${app_service}" php occ user:resetpassword --password-from-env "${service_user}" >/dev/null
|
|
|
|
auth_status_after="$(test_service_auth_with_retry 18 2)"
|
|
if [[ "${auth_status_after}" != "200" ]]; then
|
|
echo "Repair attempted but broker service auth still failing (HTTP ${auth_status_after:-unknown})."
|
|
exit 1
|
|
fi
|
|
|
|
echo "Repaired broker service auth (HTTP 200)."
|