feat: auth hardening — scope enforcement, tenant isolation, and docs
Add @PreAuthorize annotations to all API controllers (14 endpoints
across 6 controllers) enforcing OAuth2 scopes: apps:manage, apps:deploy,
billing:manage, observe:read, platform:admin.
Enforce tenant isolation: TenantResolutionFilter now rejects cross-tenant
access on /api/tenants/{id}/* paths. New TenantOwnershipValidator checks
environment/app ownership for paths without tenantId. Platform admins
bypass both layers.
Fix frontend: OrgResolver split into two useEffect hooks so scopes
refresh on org switch. Scopes now served from /api/config (single source
of truth). Bootstrap cleaned — standalone org permissions removed.
Update docs/architecture.md, docs/user-manual.md, and CLAUDE.md to
reflect all auth hardening changes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -201,12 +201,12 @@ create_scope() {
|
||||
local desc="$2"
|
||||
local existing_id=$(echo "$EXISTING_SCOPES" | jq -r ".[] | select(.name == \"$name\") | .id")
|
||||
if [ -n "$existing_id" ]; then
|
||||
log " Scope '$name' exists: $existing_id"
|
||||
log " Scope '$name' exists: $existing_id" >&2
|
||||
echo "$existing_id"
|
||||
else
|
||||
local resp=$(api_post "/api/resources/${API_RESOURCE_ID}/scopes" "{\"name\": \"$name\", \"description\": \"$desc\"}")
|
||||
local new_id=$(echo "$resp" | jq -r '.id')
|
||||
log " Created scope '$name': $new_id"
|
||||
log " Created scope '$name': $new_id" >&2
|
||||
echo "$new_id"
|
||||
fi
|
||||
}
|
||||
@@ -328,40 +328,11 @@ if [ -z "$ORG_MEMBER_ROLE_ID" ]; then
|
||||
log "Created org member role: $ORG_MEMBER_ROLE_ID"
|
||||
fi
|
||||
|
||||
# --- Organization permissions (scopes) ---
|
||||
log "Creating organization permissions..."
|
||||
EXISTING_ORG_SCOPES=$(api_get "/api/organization-scopes")
|
||||
|
||||
create_org_scope() {
|
||||
local name="$1"
|
||||
local desc="$2"
|
||||
local existing_id=$(echo "$EXISTING_ORG_SCOPES" | jq -r ".[] | select(.name == \"$name\") | .id")
|
||||
if [ -n "$existing_id" ]; then
|
||||
echo "$existing_id"
|
||||
else
|
||||
local resp=$(api_post "/api/organization-scopes" "{\"name\": \"$name\", \"description\": \"$desc\"}")
|
||||
echo "$(echo "$resp" | jq -r '.id')"
|
||||
fi
|
||||
}
|
||||
|
||||
ORG_SCOPE_TENANT_MANAGE=$(create_org_scope "tenant:manage" "Manage tenant settings")
|
||||
ORG_SCOPE_BILLING_MANAGE=$(create_org_scope "billing:manage" "Manage billing")
|
||||
ORG_SCOPE_TEAM_MANAGE=$(create_org_scope "team:manage" "Manage team members")
|
||||
ORG_SCOPE_APPS_MANAGE=$(create_org_scope "apps:manage" "Create and delete apps")
|
||||
ORG_SCOPE_APPS_DEPLOY=$(create_org_scope "apps:deploy" "Deploy apps")
|
||||
ORG_SCOPE_SECRETS_MANAGE=$(create_org_scope "secrets:manage" "Manage secrets")
|
||||
ORG_SCOPE_OBSERVE_READ=$(create_org_scope "observe:read" "View observability data")
|
||||
ORG_SCOPE_OBSERVE_DEBUG=$(create_org_scope "observe:debug" "Debug and replay operations")
|
||||
ORG_SCOPE_SETTINGS_MANAGE=$(create_org_scope "settings:manage" "Manage settings")
|
||||
|
||||
ALL_ORG_SCOPE_IDS="\"$ORG_SCOPE_TENANT_MANAGE\",\"$ORG_SCOPE_BILLING_MANAGE\",\"$ORG_SCOPE_TEAM_MANAGE\",\"$ORG_SCOPE_APPS_MANAGE\",\"$ORG_SCOPE_APPS_DEPLOY\",\"$ORG_SCOPE_SECRETS_MANAGE\",\"$ORG_SCOPE_OBSERVE_READ\",\"$ORG_SCOPE_OBSERVE_DEBUG\",\"$ORG_SCOPE_SETTINGS_MANAGE\""
|
||||
MEMBER_ORG_SCOPE_IDS="\"$ORG_SCOPE_APPS_DEPLOY\",\"$ORG_SCOPE_OBSERVE_READ\",\"$ORG_SCOPE_OBSERVE_DEBUG\""
|
||||
|
||||
# Assign organization scopes to org roles
|
||||
log "Assigning organization scopes to roles..."
|
||||
api_put "/api/organization-roles/${ORG_ADMIN_ROLE_ID}/scopes" "{\"organizationScopeIds\": [$ALL_ORG_SCOPE_IDS]}" >/dev/null 2>&1
|
||||
api_put "/api/organization-roles/${ORG_MEMBER_ROLE_ID}/scopes" "{\"organizationScopeIds\": [$MEMBER_ORG_SCOPE_IDS]}" >/dev/null 2>&1
|
||||
log "Organization scopes assigned."
|
||||
# Assign API resource scopes to org roles (these appear in org-scoped resource tokens)
|
||||
log "Assigning API resource scopes to organization roles..."
|
||||
api_put "/api/organization-roles/${ORG_ADMIN_ROLE_ID}/resource-scopes" "{\"scopeIds\": [$ALL_TENANT_SCOPE_IDS]}" >/dev/null 2>&1
|
||||
api_put "/api/organization-roles/${ORG_MEMBER_ROLE_ID}/resource-scopes" "{\"scopeIds\": [$MEMBER_SCOPE_IDS]}" >/dev/null 2>&1
|
||||
log "API resource scopes assigned to organization roles."
|
||||
|
||||
# ============================================================
|
||||
# PHASE 5: Create users
|
||||
@@ -427,10 +398,10 @@ fi
|
||||
|
||||
# Add users to organization
|
||||
if [ -n "$TENANT_USER_ID" ] && [ "$TENANT_USER_ID" != "null" ]; then
|
||||
log "Adding tenant admin to organization..."
|
||||
log "Adding tenant user to organization..."
|
||||
api_post "/api/organizations/$ORG_ID/users" "{\"userIds\": [\"$TENANT_USER_ID\"]}" >/dev/null 2>&1
|
||||
api_put "/api/organizations/$ORG_ID/users/$TENANT_USER_ID/roles" "{\"organizationRoleIds\": [\"$ORG_ADMIN_ROLE_ID\"]}" >/dev/null 2>&1
|
||||
log "Tenant admin added to org with admin role."
|
||||
api_put "/api/organizations/$ORG_ID/users/$TENANT_USER_ID/roles" "{\"organizationRoleIds\": [\"$ORG_MEMBER_ROLE_ID\"]}" >/dev/null 2>&1
|
||||
log "Tenant user added to org with member role."
|
||||
fi
|
||||
|
||||
if [ -n "$ADMIN_USER_ID" ] && [ "$ADMIN_USER_ID" != "null" ]; then
|
||||
|
||||
Reference in New Issue
Block a user