299 lines
9.6 KiB
Bash
Executable File
299 lines
9.6 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"
|
|
|
|
usage() {
|
|
cat <<USAGE
|
|
Usage: ./scripts/ensure-qortal-integration-runtime-config.sh --compose-file <file> --env-file <file> [options]
|
|
|
|
Sync qortal_integration app config from env-backed runtime values so recreate/setup
|
|
flows do not leave stale Qortal node or gateway URLs behind.
|
|
|
|
Synced keys:
|
|
- external_auth_base_url
|
|
- external_auth_app_id
|
|
- external_auth_app_secret
|
|
- external_auth_node_url
|
|
- external_auth_node_api_key
|
|
- external_auth_node_api_key_mode
|
|
- external_auth_node_api_key_paths
|
|
- qortal_node_url
|
|
- qortal_node_api_key
|
|
- qortal_gateway_url
|
|
- oidc_issuer_url
|
|
- oidc_client_id
|
|
- oidc_client_secret
|
|
- oidc_require_email_for_new_account
|
|
|
|
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)
|
|
-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#*=}"
|
|
}
|
|
|
|
read_kv_first() {
|
|
local value=""
|
|
local key
|
|
for key in "$@"; do
|
|
value="$(read_kv "${key}" || true)"
|
|
if [[ -n "${value}" ]]; then
|
|
echo "${value}"
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
trim() {
|
|
local value="${1:-}"
|
|
value="$(echo "${value}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
|
|
echo "${value}"
|
|
}
|
|
|
|
normalize_oidc_require_email() {
|
|
local value
|
|
value="$(trim "${1:-}")"
|
|
value="$(echo "${value}" | tr '[:upper:]' '[:lower:]')"
|
|
case "${value}" in
|
|
""|default|env|required|on|true|1|yes)
|
|
echo "required"
|
|
;;
|
|
off|false|0|no|optional)
|
|
echo "off"
|
|
;;
|
|
*)
|
|
echo "required"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
occ_app_get() {
|
|
local key="$1"
|
|
docker compose -f "${compose_file}" --env-file "${env_file}" exec -T --user www-data "${app_service}" \
|
|
php occ config:app:get qortal_integration "${key}" 2>/dev/null | tr -d '\r' | xargs || true
|
|
}
|
|
|
|
occ_app_set() {
|
|
local key="$1"
|
|
local value="$2"
|
|
docker compose -f "${compose_file}" --env-file "${env_file}" exec -T --user www-data "${app_service}" \
|
|
php occ config:app:set qortal_integration "${key}" --value="${value}" >/dev/null 2>&1
|
|
}
|
|
|
|
set_and_verify() {
|
|
local key="$1"
|
|
local value="$2"
|
|
local stored=""
|
|
occ_app_set "${key}" "${value}"
|
|
stored="$(occ_app_get "${key}")"
|
|
if [[ "${stored}" != "${value}" ]]; then
|
|
echo "Failed verifying qortal_integration ${key}: expected '${value}', got '${stored}'"
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
sync_oidc_require_email_with_broker() {
|
|
local required_value="$1"
|
|
local broker_internal_api_token="$2"
|
|
local expected_bool="false"
|
|
|
|
if [[ "${required_value}" == "required" ]]; then
|
|
expected_bool="true"
|
|
fi
|
|
|
|
docker compose -f "${compose_file}" --env-file "${env_file}" exec -T \
|
|
-e "BROKER_BASE_URL=http://broker:3000" \
|
|
-e "BROKER_INTERNAL_API_TOKEN=${broker_internal_api_token}" \
|
|
-e "OIDC_REQUIRE_EMAIL_FOR_NEW_ACCOUNT=${expected_bool}" \
|
|
--user www-data "${app_service}" \
|
|
php -r '
|
|
$baseUrl = rtrim((string)getenv("BROKER_BASE_URL"), "/");
|
|
$token = trim((string)getenv("BROKER_INTERNAL_API_TOKEN"));
|
|
$requireEmailRaw = strtolower(trim((string)getenv("OIDC_REQUIRE_EMAIL_FOR_NEW_ACCOUNT")));
|
|
if ($baseUrl === "" || $token === "") {
|
|
fwrite(STDERR, "Missing broker sync inputs\n");
|
|
exit(1);
|
|
}
|
|
$expected = in_array($requireEmailRaw, ["1", "true", "yes", "on", "required"], true);
|
|
$payload = json_encode(["requireEmailForNewAccount" => $expected], JSON_UNESCAPED_SLASHES);
|
|
if ($payload === false) {
|
|
fwrite(STDERR, "Failed to encode broker OIDC payload\n");
|
|
exit(1);
|
|
}
|
|
$context = stream_context_create([
|
|
"http" => [
|
|
"method" => "POST",
|
|
"header" => "Content-Type: application/json\r\nX-Broker-Internal-Token: " . $token . "\r\n",
|
|
"content" => $payload,
|
|
"timeout" => 30,
|
|
"ignore_errors" => true,
|
|
],
|
|
]);
|
|
$response = @file_get_contents($baseUrl . "/api/oidc/config", false, $context);
|
|
$statusLine = $http_response_header[0] ?? "";
|
|
if ($response === false || !preg_match("/^HTTP\/\S+\s+2\d\d\b/", $statusLine)) {
|
|
fwrite(STDERR, "Broker OIDC sync failed\n");
|
|
exit(1);
|
|
}
|
|
$decoded = json_decode($response, true);
|
|
if (!is_array($decoded) || !array_key_exists("requireEmailForNewAccount", $decoded) || (bool)$decoded["requireEmailForNewAccount"] !== $expected) {
|
|
fwrite(STDERR, "Broker OIDC sync did not persist the expected email requirement\n");
|
|
exit(1);
|
|
}
|
|
' >/dev/null 2>&1
|
|
}
|
|
|
|
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
|
|
;;
|
|
-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
|
|
|
|
if ! docker compose -f "${compose_file}" --env-file "${env_file}" exec -T --user www-data "${app_service}" php occ status >/dev/null 2>&1; then
|
|
echo "Nextcloud OCC is not ready in service '${app_service}'."
|
|
exit 1
|
|
fi
|
|
|
|
echo "Ensuring qortal_integration runtime config..."
|
|
echo " compose_file=${compose_file}"
|
|
echo " env_file=${env_file}"
|
|
|
|
desired_external_auth_base_url="$(trim "$(read_kv "QORTAL_EXTERNAL_AUTH_BASE_URL" || true)")"
|
|
desired_external_auth_app_id="$(trim "$(read_kv "QORTAL_EXTERNAL_AUTH_APP_ID" || true)")"
|
|
desired_external_auth_app_secret="$(read_kv "QORTAL_EXTERNAL_AUTH_APP_SECRET" || true)"
|
|
desired_external_auth_node_url="$(trim "$(read_kv_first "QORTAL_AUTH_NODE_URL" "QORTAL_NODE_URL" || true)")"
|
|
desired_qortal_node_url="$(trim "$(read_kv_first "QORTAL_NODE_URL" "QORTAL_AUTH_NODE_URL" || true)")"
|
|
desired_qortal_gateway_url="$(trim "$(read_kv_first "QORTAL_PUBLIC_GATEWAY_URL" "QORTAL_GATEWAY_PUBLIC_URL" "QORTAL_GATEWAY_URL" || true)")"
|
|
desired_oidc_issuer_url="$(trim "$(read_kv "OIDC_ISSUER" || true)")"
|
|
desired_oidc_client_id="$(trim "$(read_kv "OIDC_CLIENT_ID" || true)")"
|
|
desired_oidc_client_secret="$(read_kv "OIDC_CLIENT_SECRET" || true)"
|
|
desired_node_api_key="$(read_kv_first "QORTAL_NODE_API_KEY" "QORTAL_AUTH_NODE_API_KEY" || true)"
|
|
desired_node_api_key_mode="$(trim "$(read_kv "QORTAL_AUTH_NODE_API_KEY_MODE" || true)")"
|
|
desired_node_api_key_paths="$(trim "$(read_kv "QORTAL_AUTH_NODE_API_KEY_PATHS" || true)")"
|
|
desired_oidc_require_email_for_new_account="$(normalize_oidc_require_email "$(read_kv "OIDC_REQUIRE_EMAIL_FOR_NEW_ACCOUNT" || true)")"
|
|
desired_broker_internal_api_token="$(read_kv "BROKER_INTERNAL_API_TOKEN" || true)"
|
|
|
|
if [[ -z "${desired_external_auth_node_url}" ]]; then
|
|
desired_external_auth_node_url="http://qortal_node:12391"
|
|
fi
|
|
if [[ -z "${desired_qortal_node_url}" ]]; then
|
|
desired_qortal_node_url="${desired_external_auth_node_url}"
|
|
fi
|
|
if [[ -z "${desired_node_api_key_mode}" ]]; then
|
|
desired_node_api_key_mode="paths"
|
|
fi
|
|
if [[ -z "${desired_node_api_key_paths}" ]]; then
|
|
desired_node_api_key_paths="/"
|
|
fi
|
|
if [[ -z "${desired_oidc_client_id}" ]]; then
|
|
desired_oidc_client_id="nextcloud-local"
|
|
fi
|
|
|
|
status=0
|
|
set_and_verify "external_auth_base_url" "${desired_external_auth_base_url}" || status=1
|
|
set_and_verify "external_auth_app_id" "${desired_external_auth_app_id}" || status=1
|
|
set_and_verify "external_auth_app_secret" "${desired_external_auth_app_secret}" || status=1
|
|
set_and_verify "external_auth_node_url" "${desired_external_auth_node_url}" || status=1
|
|
set_and_verify "external_auth_node_api_key" "${desired_node_api_key}" || status=1
|
|
set_and_verify "external_auth_node_api_key_mode" "${desired_node_api_key_mode}" || status=1
|
|
set_and_verify "external_auth_node_api_key_paths" "${desired_node_api_key_paths}" || status=1
|
|
set_and_verify "qortal_node_url" "${desired_qortal_node_url}" || status=1
|
|
set_and_verify "qortal_node_api_key" "${desired_node_api_key}" || status=1
|
|
set_and_verify "qortal_gateway_url" "${desired_qortal_gateway_url}" || status=1
|
|
set_and_verify "oidc_issuer_url" "${desired_oidc_issuer_url}" || status=1
|
|
set_and_verify "oidc_client_id" "${desired_oidc_client_id}" || status=1
|
|
set_and_verify "oidc_client_secret" "${desired_oidc_client_secret}" || status=1
|
|
set_and_verify "oidc_require_email_for_new_account" "${desired_oidc_require_email_for_new_account}" || status=1
|
|
|
|
if ! sync_oidc_require_email_with_broker "${desired_oidc_require_email_for_new_account}" "${desired_broker_internal_api_token}"; then
|
|
status=1
|
|
fi
|
|
|
|
if [[ "${status}" -ne 0 ]]; then
|
|
echo "Qortal integration runtime config verification failed."
|
|
exit 1
|
|
fi
|
|
|
|
echo "Qortal integration runtime config verified."
|
|
echo " external_auth_node_url=${desired_external_auth_node_url}"
|
|
echo " qortal_node_url=${desired_qortal_node_url}"
|
|
echo " qortal_gateway_url=${desired_qortal_gateway_url:-<empty>}"
|
|
echo " oidc_issuer_url=${desired_oidc_issuer_url:-<empty>}"
|
|
echo " oidc_client_id=${desired_oidc_client_id}"
|
|
echo " oidc_require_email_for_new_account=${desired_oidc_require_email_for_new_account}"
|