Multi tenant approach - cleanup done
This commit is contained in:
@@ -13,10 +13,19 @@ INSTALLATION_ID=""
|
||||
CLOUD_BASE_URL=""
|
||||
TOKEN_URL=""
|
||||
CLIENT_DIR=""
|
||||
CLIENT_CONFIG="${CONFIG_DIR}/clients.yml"
|
||||
CLIENT_PRIVATE_KEY=""
|
||||
CLIENT_PUBLIC_KEY=""
|
||||
MACHINE_ASSERTION=""
|
||||
CLIENT_NAME=""
|
||||
DB_HOST_VALUE=""
|
||||
DB_PORT_VALUE=""
|
||||
DB_NAME_VALUE=""
|
||||
DB_USER_VALUE=""
|
||||
DB_PASSWORD_VALUE=""
|
||||
LICENSE_TYPE=""
|
||||
PACKAGE_CODE=""
|
||||
LICENSE_MONTHS=""
|
||||
PSQL_BIN=""
|
||||
SKIP_BUILD_CONFIGURED="${CYGNUS_SETUP_SKIP_BUILD+x}"
|
||||
SKIP_BUILD="${CYGNUS_SETUP_SKIP_BUILD:-false}"
|
||||
FORCE_ASSERTION="${CYGNUS_SETUP_FORCE_ASSERTION:-false}"
|
||||
@@ -77,6 +86,11 @@ validate_identifier() {
|
||||
"${label} must contain no spaces and use only letters, numbers, '-' or '_'."
|
||||
}
|
||||
|
||||
validate_client_slug() {
|
||||
[[ "$1" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]] || fail \
|
||||
"Customer identifier must use lowercase letters, numbers and single hyphens only."
|
||||
}
|
||||
|
||||
collect_client_inputs() {
|
||||
local default_client="${CYGNUS_SETUP_CLIENT_ID:-customer-a}"
|
||||
local default_installation="${CYGNUS_SETUP_INSTALLATION_ID:-site-01}"
|
||||
@@ -86,7 +100,11 @@ collect_client_inputs() {
|
||||
CLIENT_ID="$(prompt_value \
|
||||
"Customer identifier (no spaces; used in key and directory names)" \
|
||||
"${default_client}")"
|
||||
validate_identifier "Customer identifier" "${CLIENT_ID}"
|
||||
validate_client_slug "${CLIENT_ID}"
|
||||
CLIENT_NAME="$(prompt_value \
|
||||
"Customer display name" \
|
||||
"${CYGNUS_SETUP_CLIENT_NAME:-${CLIENT_ID}}")"
|
||||
[[ -n "${CLIENT_NAME}" ]] || fail "Customer display name is required."
|
||||
|
||||
INSTALLATION_ID="$(prompt_value \
|
||||
"Installation identifier (no spaces)" \
|
||||
@@ -111,11 +129,43 @@ collect_client_inputs() {
|
||||
CLIENT_PUBLIC_KEY="${CLIENT_DIR}/${CLIENT_ID}-signing-public.pem"
|
||||
MACHINE_ASSERTION="${CLIENT_DIR}/${CLIENT_ID}-${INSTALLATION_ID}-assertion.jwt"
|
||||
|
||||
DB_HOST_VALUE="$(prompt_value \
|
||||
"Cloud PostgreSQL host" "${CYGNUS_SETUP_DB_HOST:-${DB_HOST:-localhost}}")"
|
||||
DB_PORT_VALUE="$(prompt_value \
|
||||
"Cloud PostgreSQL port" "${CYGNUS_SETUP_DB_PORT:-${DB_PORT:-5432}}")"
|
||||
DB_NAME_VALUE="$(prompt_value \
|
||||
"Cloud PostgreSQL database" "${CYGNUS_SETUP_DB_NAME:-${DB_NAME:-matrix}}")"
|
||||
DB_USER_VALUE="$(prompt_value \
|
||||
"Cloud PostgreSQL user" "${CYGNUS_SETUP_DB_USER:-${DB_USER:-postgres}}")"
|
||||
DB_PASSWORD_VALUE="${CYGNUS_SETUP_DB_PASSWORD:-${DB_PASSWORD:-}}"
|
||||
if [[ -z "${DB_PASSWORD_VALUE}" ]] && [[ "${NON_INTERACTIVE}" != "true" ]] && [[ -t 0 ]]; then
|
||||
read -r -s -p "Cloud PostgreSQL password: " DB_PASSWORD_VALUE
|
||||
printf '\n'
|
||||
fi
|
||||
[[ -n "${DB_PASSWORD_VALUE}" ]] || fail \
|
||||
"Cloud PostgreSQL password is required through the prompt or CYGNUS_SETUP_DB_PASSWORD."
|
||||
|
||||
LICENSE_TYPE="$(prompt_value \
|
||||
"License type" "${CYGNUS_SETUP_LICENSE_TYPE:-ANNUAL}")"
|
||||
PACKAGE_CODE="$(prompt_value \
|
||||
"License package code" "${CYGNUS_SETUP_PACKAGE_CODE:-FULL}")"
|
||||
LICENSE_MONTHS="$(prompt_value \
|
||||
"License validity in months" "${CYGNUS_SETUP_LICENSE_MONTHS:-12}")"
|
||||
validate_identifier "License type" "${LICENSE_TYPE}"
|
||||
validate_identifier "Package code" "${PACKAGE_CODE}"
|
||||
[[ "${LICENSE_MONTHS}" =~ ^[1-9][0-9]*$ ]] || fail \
|
||||
"License validity must be a positive whole number of months."
|
||||
|
||||
printf '\nProvisioning summary:\n'
|
||||
printf ' Customer: %s\n' "${CLIENT_ID}"
|
||||
printf ' Customer name: %s\n' "${CLIENT_NAME}"
|
||||
printf ' Installation: %s\n' "${INSTALLATION_ID}"
|
||||
printf ' Cloud URL: %s\n' "${CLOUD_BASE_URL}"
|
||||
printf ' Token audience: %s\n' "${TOKEN_URL}"
|
||||
printf ' Database: %s@%s:%s/%s\n' \
|
||||
"${DB_USER_VALUE}" "${DB_HOST_VALUE}" "${DB_PORT_VALUE}" "${DB_NAME_VALUE}"
|
||||
printf ' License: %s / %s / %s month(s)\n' \
|
||||
"${LICENSE_TYPE}" "${PACKAGE_CODE}" "${LICENSE_MONTHS}"
|
||||
printf ' Verify build: %s\n' "$([[ "${SKIP_BUILD}" == "true" ]] && printf no || printf yes)"
|
||||
|
||||
if [[ "$(prompt_yes_no "Continue with these values" "true")" != "true" ]]; then
|
||||
@@ -160,44 +210,78 @@ configure_java_21() {
|
||||
"JDK 21 is required. Current java is: $(java -version 2>&1 | head -n 1)"
|
||||
}
|
||||
|
||||
write_client_configuration() {
|
||||
local public_key_uri="file:${CLIENT_PUBLIC_KEY}"
|
||||
local existing_installation=""
|
||||
register_client_in_database() {
|
||||
local client_id_b64 client_name_b64 installation_b64 public_key_b64
|
||||
local license_type_b64 package_code_b64
|
||||
|
||||
if [[ ! -f "${CLIENT_CONFIG}" ]]; then
|
||||
{
|
||||
printf '%s\n' "cygnus:"
|
||||
printf '%s\n' " security:"
|
||||
printf '%s\n' " clients:"
|
||||
} > "${CLIENT_CONFIG}"
|
||||
elif grep -Fq " ${CLIENT_ID}:" "${CLIENT_CONFIG}"; then
|
||||
existing_installation="$(awk \
|
||||
-v client=" ${CLIENT_ID}:" \
|
||||
'$0 == client { found = 1; next }
|
||||
found && /installation-id:/ {
|
||||
sub(/^.*installation-id:[[:space:]]*/, "");
|
||||
print;
|
||||
exit
|
||||
}
|
||||
found && /^ [^[:space:]]/ { exit }' \
|
||||
"${CLIENT_CONFIG}")"
|
||||
if [[ "${existing_installation}" != "${INSTALLATION_ID}" ]]; then
|
||||
fail "Client '${CLIENT_ID}' already uses installation '${existing_installation}' in ${CLIENT_CONFIG}; requested '${INSTALLATION_ID}'. Use the existing installation ID or provision a different customer identifier."
|
||||
fi
|
||||
printf 'Keeping existing configuration for client: %s\n' "${CLIENT_ID}"
|
||||
return
|
||||
fi
|
||||
client_id_b64="$(printf '%s' "${CLIENT_ID}" | openssl base64 -A)"
|
||||
client_name_b64="$(printf '%s' "${CLIENT_NAME}" | openssl base64 -A)"
|
||||
installation_b64="$(printf '%s' "${INSTALLATION_ID}" | openssl base64 -A)"
|
||||
public_key_b64="$(openssl base64 -A -in "${CLIENT_PUBLIC_KEY}")"
|
||||
license_type_b64="$(printf '%s' "${LICENSE_TYPE}" | openssl base64 -A)"
|
||||
package_code_b64="$(printf '%s' "${PACKAGE_CODE}" | openssl base64 -A)"
|
||||
|
||||
{
|
||||
printf ' %s:\n' "${CLIENT_ID}"
|
||||
printf '%s\n' " enabled: true"
|
||||
printf ' installation-id: %s\n' "${INSTALLATION_ID}"
|
||||
printf ' assertion-public-key: %s\n' "${public_key_uri}"
|
||||
printf '%s\n' " scopes:"
|
||||
printf '%s\n' " - identity.login"
|
||||
} >> "${CLIENT_CONFIG}"
|
||||
chmod 600 "${CLIENT_CONFIG}"
|
||||
printf 'Added client %s to: %s\n' "${CLIENT_ID}" "${CLIENT_CONFIG}"
|
||||
PGPASSWORD="${DB_PASSWORD_VALUE}" "${PSQL_BIN}" \
|
||||
-h "${DB_HOST_VALUE}" \
|
||||
-p "${DB_PORT_VALUE}" \
|
||||
-U "${DB_USER_VALUE}" \
|
||||
-d "${DB_NAME_VALUE}" \
|
||||
-X -v ON_ERROR_STOP=1 \
|
||||
-c "
|
||||
WITH account AS (
|
||||
INSERT INTO identity.client_account
|
||||
(tenant_id, client_slug, client_name, status)
|
||||
VALUES (
|
||||
gen_random_uuid(),
|
||||
convert_from(decode('${client_id_b64}', 'base64'), 'UTF8'),
|
||||
convert_from(decode('${client_name_b64}', 'base64'), 'UTF8'),
|
||||
'ACTIVE')
|
||||
ON CONFLICT (client_slug) DO UPDATE SET
|
||||
client_name = EXCLUDED.client_name,
|
||||
status = 'ACTIVE',
|
||||
updated_at = now()
|
||||
RETURNING tenant_id
|
||||
), installation AS (
|
||||
INSERT INTO identity.client_installation
|
||||
(installation_id, tenant_id, client_id, installation_code,
|
||||
assertion_public_key, allowed_scopes, enabled)
|
||||
SELECT
|
||||
gen_random_uuid(),
|
||||
tenant_id,
|
||||
convert_from(decode('${client_id_b64}', 'base64'), 'UTF8'),
|
||||
convert_from(decode('${installation_b64}', 'base64'), 'UTF8'),
|
||||
convert_from(decode('${public_key_b64}', 'base64'), 'UTF8'),
|
||||
ARRAY['identity.login']::text[],
|
||||
true
|
||||
FROM account
|
||||
ON CONFLICT (client_id, installation_code) DO UPDATE SET
|
||||
tenant_id = EXCLUDED.tenant_id,
|
||||
assertion_public_key = EXCLUDED.assertion_public_key,
|
||||
allowed_scopes = EXCLUDED.allowed_scopes,
|
||||
enabled = true,
|
||||
security_version = identity.client_installation.security_version + 1,
|
||||
updated_at = now()
|
||||
RETURNING tenant_id
|
||||
)
|
||||
INSERT INTO identity.client_license
|
||||
(license_id, tenant_id, license_type, package_code,
|
||||
valid_from, valid_until, status)
|
||||
SELECT
|
||||
gen_random_uuid(),
|
||||
tenant_id,
|
||||
convert_from(decode('${license_type_b64}', 'base64'), 'UTF8'),
|
||||
convert_from(decode('${package_code_b64}', 'base64'), 'UTF8'),
|
||||
now(),
|
||||
now() + make_interval(months => ${LICENSE_MONTHS}),
|
||||
'ACTIVE'
|
||||
FROM installation
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM identity.client_license existing
|
||||
WHERE existing.tenant_id = installation.tenant_id
|
||||
AND existing.status = 'ACTIVE'
|
||||
AND existing.valid_until > now()
|
||||
);"
|
||||
}
|
||||
|
||||
generate_machine_assertion() {
|
||||
@@ -236,6 +320,13 @@ main() {
|
||||
require_command awk
|
||||
require_command openssl
|
||||
require_command mvn
|
||||
if command -v psql >/dev/null 2>&1; then
|
||||
PSQL_BIN="$(command -v psql)"
|
||||
elif [[ -x /Library/PostgreSQL/17/bin/psql ]]; then
|
||||
PSQL_BIN="/Library/PostgreSQL/17/bin/psql"
|
||||
else
|
||||
fail "PostgreSQL psql was not found in PATH or /Library/PostgreSQL/17/bin."
|
||||
fi
|
||||
configure_java_21
|
||||
java -version
|
||||
mvn -version
|
||||
@@ -277,8 +368,8 @@ main() {
|
||||
"${CLIENT_PRIVATE_KEY}" \
|
||||
"${CLIENT_PUBLIC_KEY}"
|
||||
|
||||
log "Writing cloud machine-client configuration"
|
||||
write_client_configuration
|
||||
log "Registering tenant, installation, and license in the cloud database"
|
||||
register_client_in_database
|
||||
|
||||
log "Generating encrypted machine assertion"
|
||||
generate_machine_assertion
|
||||
@@ -288,7 +379,7 @@ main() {
|
||||
"Client ID: ${CLIENT_ID}" \
|
||||
"Installation ID: ${INSTALLATION_ID}" \
|
||||
"Token audience: ${TOKEN_URL}" \
|
||||
"Client config: ${CLIENT_CONFIG}" \
|
||||
"Registration: PostgreSQL identity schema" \
|
||||
"Assertion file: ${MACHINE_ASSERTION}" \
|
||||
"Login public key: ${KEY_DIR}/login-public.pem" \
|
||||
"" \
|
||||
|
||||
Reference in New Issue
Block a user