restrict ops endpoints to admin-only access block operator and viewer keys from admin maintenance routes cover destructive pricing cleanup in smoke execution, not only preview extend orchestration without regressing existing smoke stages
167 lines
9.1 KiB
Bash
167 lines
9.1 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
TMP_DIR="$(mktemp -d)"
|
|
trap 'rm -rf "${TMP_DIR}"' EXIT
|
|
|
|
# shellcheck source=backend/scripts/smoke_common.sh
|
|
source "${SCRIPT_DIR}/smoke_common.sh"
|
|
|
|
ADMIN_API_KEY="${ADMIN_API_KEY:-admin-local-dev-key}"
|
|
OPERATOR_API_KEY="${OPERATOR_API_KEY:-operator-local-dev-key}"
|
|
VIEWER_API_KEY="${VIEWER_API_KEY:-viewer-local-dev-key}"
|
|
|
|
wait_for_health
|
|
|
|
create_fresh_scheme_from_upload "smoke-authz-admin-ops"
|
|
|
|
request "scheme_current" "GET" "${API_URL}/api/v1/schemes/${SCHEME_ID}/current" "200"
|
|
CURRENT_VERSION_ID="$(json_get "${TMP_DIR}/scheme_current.body" "scheme_version_id")"
|
|
echo "CURRENT_VERSION_ID=${CURRENT_VERSION_ID}"
|
|
|
|
request "ensure_draft" "POST" \
|
|
"${API_URL}/api/v1/schemes/${SCHEME_ID}/draft/ensure?expected_current_scheme_version_id=${CURRENT_VERSION_ID}" \
|
|
"200"
|
|
DRAFT_VERSION_ID="$(json_get "${TMP_DIR}/ensure_draft.body" "scheme_version_id")"
|
|
echo "DRAFT_VERSION_ID=${DRAFT_VERSION_ID}"
|
|
|
|
request "draft_structure" "GET" \
|
|
"${API_URL}/api/v1/schemes/${SCHEME_ID}/draft/structure?expected_scheme_version_id=${DRAFT_VERSION_ID}" \
|
|
"200"
|
|
|
|
TARGET_SEAT_ID="$(python3 - "${TMP_DIR}/draft_structure.body" <<'PY'
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
payload = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
|
|
seat = next((item for item in payload.get("seats", []) if item.get("seat_id")), None)
|
|
if seat is None:
|
|
raise SystemExit("No seat with seat_id found for authz admin ops smoke")
|
|
print(seat["seat_id"])
|
|
PY
|
|
)"
|
|
echo "TARGET_SEAT_ID=${TARGET_SEAT_ID}"
|
|
|
|
STAMP="$(date +%s)-$$"
|
|
CLEANUP_PREFIX="AUTHZ_ADMINOPS_${STAMP}_"
|
|
DELETE_CATEGORY_NAME="authz-adminops-delete-${STAMP}"
|
|
DELETE_CATEGORY_CODE="${CLEANUP_PREFIX}DELETE"
|
|
KEEP_CATEGORY_NAME="authz-adminops-keep-${STAMP}"
|
|
KEEP_CATEGORY_CODE="${CLEANUP_PREFIX}KEEP"
|
|
|
|
request "create_delete_category" "POST" \
|
|
"${API_URL}/api/v1/schemes/${SCHEME_ID}/pricing/categories?expected_scheme_version_id=${DRAFT_VERSION_ID}" \
|
|
"200" \
|
|
"{\"name\":\"${DELETE_CATEGORY_NAME}\",\"code\":\"${DELETE_CATEGORY_CODE}\"}"
|
|
DELETE_CATEGORY_ID="$(json_get "${TMP_DIR}/create_delete_category.body" "pricing_category_id")"
|
|
echo "DELETE_CATEGORY_ID=${DELETE_CATEGORY_ID}"
|
|
|
|
request "create_keep_category" "POST" \
|
|
"${API_URL}/api/v1/schemes/${SCHEME_ID}/pricing/categories?expected_scheme_version_id=${DRAFT_VERSION_ID}" \
|
|
"200" \
|
|
"{\"name\":\"${KEEP_CATEGORY_NAME}\",\"code\":\"${KEEP_CATEGORY_CODE}\"}"
|
|
KEEP_CATEGORY_ID="$(json_get "${TMP_DIR}/create_keep_category.body" "pricing_category_id")"
|
|
echo "KEEP_CATEGORY_ID=${KEEP_CATEGORY_ID}"
|
|
|
|
request "create_keep_category_rule" "POST" \
|
|
"${API_URL}/api/v1/schemes/${SCHEME_ID}/pricing/rules?expected_scheme_version_id=${DRAFT_VERSION_ID}" \
|
|
"200" \
|
|
"{\"pricing_category_id\":\"${KEEP_CATEGORY_ID}\",\"target_type\":\"seat\",\"target_ref\":\"${TARGET_SEAT_ID}\",\"amount\":\"666.00\",\"currency\":\"RUB\"}"
|
|
KEEP_RULE_ID="$(json_get "${TMP_DIR}/create_keep_category_rule.body" "price_rule_id")"
|
|
echo "KEEP_RULE_ID=${KEEP_RULE_ID}"
|
|
|
|
request "draft_pricing_snapshot" "POST" \
|
|
"${API_URL}/api/v1/schemes/${SCHEME_ID}/draft/pricing/snapshot?expected_scheme_version_id=${DRAFT_VERSION_ID}" \
|
|
"200"
|
|
request "publish_preview_refresh" "GET" \
|
|
"${API_URL}/api/v1/schemes/${SCHEME_ID}/draft/publish-preview?refresh=true&expected_scheme_version_id=${DRAFT_VERSION_ID}" \
|
|
"200"
|
|
|
|
request_with_api_key "${ADMIN_API_KEY}" "admin_publish_preview_audit" "GET" \
|
|
"${API_URL}/api/v1/admin/artifacts/publish-preview/audit" "200"
|
|
request_with_api_key "${OPERATOR_API_KEY}" "operator_publish_preview_audit" "GET" \
|
|
"${API_URL}/api/v1/admin/artifacts/publish-preview/audit" "403"
|
|
request_with_api_key "${VIEWER_API_KEY}" "viewer_publish_preview_audit" "GET" \
|
|
"${API_URL}/api/v1/admin/artifacts/publish-preview/audit" "403"
|
|
assert_file_contains "${TMP_DIR}/operator_publish_preview_audit.body" "Admin role required"
|
|
assert_file_contains "${TMP_DIR}/viewer_publish_preview_audit.body" "Admin role required"
|
|
|
|
request_with_api_key "${ADMIN_API_KEY}" "admin_publish_preview_cleanup_dry_run" "POST" \
|
|
"${API_URL}/api/v1/admin/artifacts/publish-preview/cleanup?dry_run=true" "200"
|
|
request_with_api_key "${OPERATOR_API_KEY}" "operator_publish_preview_cleanup_dry_run" "POST" \
|
|
"${API_URL}/api/v1/admin/artifacts/publish-preview/cleanup?dry_run=true" "403"
|
|
request_with_api_key "${VIEWER_API_KEY}" "viewer_publish_preview_cleanup_dry_run" "POST" \
|
|
"${API_URL}/api/v1/admin/artifacts/publish-preview/cleanup?dry_run=true" "403"
|
|
assert_file_contains "${TMP_DIR}/operator_publish_preview_cleanup_dry_run.body" "Admin role required"
|
|
assert_file_contains "${TMP_DIR}/viewer_publish_preview_cleanup_dry_run.body" "Admin role required"
|
|
|
|
request_with_api_key "${ADMIN_API_KEY}" "admin_pricing_cleanup_preview" "GET" \
|
|
"${API_URL}/api/v1/admin/schemes/${SCHEME_ID}/pricing/categories/cleanup-preview?code_prefix=${CLEANUP_PREFIX}" "200"
|
|
request_with_api_key "${OPERATOR_API_KEY}" "operator_pricing_cleanup_preview" "GET" \
|
|
"${API_URL}/api/v1/admin/schemes/${SCHEME_ID}/pricing/categories/cleanup-preview?code_prefix=${CLEANUP_PREFIX}" "403"
|
|
request_with_api_key "${VIEWER_API_KEY}" "viewer_pricing_cleanup_preview" "GET" \
|
|
"${API_URL}/api/v1/admin/schemes/${SCHEME_ID}/pricing/categories/cleanup-preview?code_prefix=${CLEANUP_PREFIX}" "403"
|
|
assert_file_contains "${TMP_DIR}/operator_pricing_cleanup_preview.body" "Admin role required"
|
|
assert_file_contains "${TMP_DIR}/viewer_pricing_cleanup_preview.body" "Admin role required"
|
|
|
|
request_with_api_key "${ADMIN_API_KEY}" "admin_pricing_cleanup_dry_run" "POST" \
|
|
"${API_URL}/api/v1/admin/schemes/${SCHEME_ID}/pricing/categories/cleanup" "200" \
|
|
"{\"code_prefixes\":[\"${CLEANUP_PREFIX}\"],\"name_prefixes\":[],\"pricing_category_ids\":[],\"delete_only_without_rules\":true,\"dry_run\":true}"
|
|
request_with_api_key "${OPERATOR_API_KEY}" "operator_pricing_cleanup_dry_run" "POST" \
|
|
"${API_URL}/api/v1/admin/schemes/${SCHEME_ID}/pricing/categories/cleanup" "403" \
|
|
"{\"code_prefixes\":[\"${CLEANUP_PREFIX}\"],\"name_prefixes\":[],\"pricing_category_ids\":[],\"delete_only_without_rules\":true,\"dry_run\":true}"
|
|
request_with_api_key "${VIEWER_API_KEY}" "viewer_pricing_cleanup_dry_run" "POST" \
|
|
"${API_URL}/api/v1/admin/schemes/${SCHEME_ID}/pricing/categories/cleanup" "403" \
|
|
"{\"code_prefixes\":[\"${CLEANUP_PREFIX}\"],\"name_prefixes\":[],\"pricing_category_ids\":[],\"delete_only_without_rules\":true,\"dry_run\":true}"
|
|
assert_file_contains "${TMP_DIR}/operator_pricing_cleanup_dry_run.body" "Admin role required"
|
|
assert_file_contains "${TMP_DIR}/viewer_pricing_cleanup_dry_run.body" "Admin role required"
|
|
|
|
request_with_api_key "${OPERATOR_API_KEY}" "operator_pricing_cleanup_execute" "POST" \
|
|
"${API_URL}/api/v1/admin/schemes/${SCHEME_ID}/pricing/categories/cleanup" "403" \
|
|
"{\"code_prefixes\":[\"${CLEANUP_PREFIX}\"],\"name_prefixes\":[],\"pricing_category_ids\":[],\"delete_only_without_rules\":true,\"dry_run\":false}"
|
|
request_with_api_key "${VIEWER_API_KEY}" "viewer_pricing_cleanup_execute" "POST" \
|
|
"${API_URL}/api/v1/admin/schemes/${SCHEME_ID}/pricing/categories/cleanup" "403" \
|
|
"{\"code_prefixes\":[\"${CLEANUP_PREFIX}\"],\"name_prefixes\":[],\"pricing_category_ids\":[],\"delete_only_without_rules\":true,\"dry_run\":false}"
|
|
assert_file_contains "${TMP_DIR}/operator_pricing_cleanup_execute.body" "Admin role required"
|
|
assert_file_contains "${TMP_DIR}/viewer_pricing_cleanup_execute.body" "Admin role required"
|
|
|
|
request_with_api_key "${ADMIN_API_KEY}" "admin_pricing_cleanup_execute" "POST" \
|
|
"${API_URL}/api/v1/admin/schemes/${SCHEME_ID}/pricing/categories/cleanup" "200" \
|
|
"{\"code_prefixes\":[\"${CLEANUP_PREFIX}\"],\"name_prefixes\":[],\"pricing_category_ids\":[],\"delete_only_without_rules\":true,\"dry_run\":false}"
|
|
assert_json_int_eq "${TMP_DIR}/admin_pricing_cleanup_execute.body" "deleted_count" "1"
|
|
assert_json_int_eq "${TMP_DIR}/admin_pricing_cleanup_execute.body" "skipped_count" "1"
|
|
|
|
request "pricing_bundle_after_admin_cleanup_execute" "GET" \
|
|
"${API_URL}/api/v1/schemes/${SCHEME_ID}/pricing" "200"
|
|
assert_json_len_eq "${TMP_DIR}/pricing_bundle_after_admin_cleanup_execute.body" "categories" "1"
|
|
assert_json_len_eq "${TMP_DIR}/pricing_bundle_after_admin_cleanup_execute.body" "rules" "1"
|
|
|
|
python3 - "${TMP_DIR}/pricing_bundle_after_admin_cleanup_execute.body" "${DELETE_CATEGORY_ID}" "${KEEP_CATEGORY_ID}" "${KEEP_RULE_ID}" <<'PY'
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
payload = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
|
|
delete_category_id = sys.argv[2]
|
|
keep_category_id = sys.argv[3]
|
|
keep_rule_id = sys.argv[4]
|
|
|
|
category_ids = {item["pricing_category_id"] for item in payload.get("categories", [])}
|
|
rule_ids = {item["price_rule_id"] for item in payload.get("rules", [])}
|
|
|
|
if delete_category_id in category_ids:
|
|
raise SystemExit("Authz cleanup execute left deletable category behind")
|
|
if keep_category_id not in category_ids:
|
|
raise SystemExit("Authz cleanup execute removed protected category")
|
|
if keep_rule_id not in rule_ids:
|
|
raise SystemExit("Authz cleanup execute removed protected rule")
|
|
PY
|
|
echo "[OK] admin cleanup execute remained destructive only for safe fixture category"
|
|
|
|
echo
|
|
echo "===== done ====="
|
|
echo "[OK] smoke authz admin ops completed successfully"
|
|
echo "FRESH_SCHEME_ID=${SCHEME_ID}"
|