new
This commit is contained in:
@@ -14,6 +14,10 @@ stringData:
|
|||||||
{
|
{
|
||||||
"clientId": "backstage",
|
"clientId": "backstage",
|
||||||
"name": "Backstage Developer Portal",
|
"name": "Backstage Developer Portal",
|
||||||
|
"serviceAccountsEnabled": true,
|
||||||
|
"serviceAccountRoles": {
|
||||||
|
"realm-management": ["view-users", "query-users", "view-groups", "query-groups"]
|
||||||
|
},
|
||||||
"redirectUris": ["https://backstage.forteapps.net/api/auth/oidc/handler/frame"],
|
"redirectUris": ["https://backstage.forteapps.net/api/auth/oidc/handler/frame"],
|
||||||
"webOrigins": ["https://backstage.forteapps.net"],
|
"webOrigins": ["https://backstage.forteapps.net"],
|
||||||
"defaultClientScopes": ["openid", "email", "profile"],
|
"defaultClientScopes": ["openid", "email", "profile"],
|
||||||
|
|||||||
@@ -997,6 +997,7 @@ ignore:
|
|||||||
- **Important**: `dangerouslyAllowSignInWithoutUserInCatalog` must be nested inside the resolver object, not at the provider level
|
- **Important**: `dangerouslyAllowSignInWithoutUserInCatalog` must be nested inside the resolver object, not at the provider level
|
||||||
|
|
||||||
**Keycloak User/Group Sync**:
|
**Keycloak User/Group Sync**:
|
||||||
|
- The `backstage` Keycloak client has `serviceAccountsEnabled: true` with `realm-management` roles (`view-users`, `query-users`, `view-groups`, `query-groups`) — assigned automatically by the registrar
|
||||||
- The `keycloakOrg` catalog provider auto-imports users and groups from the `forte` realm
|
- The `keycloakOrg` catalog provider auto-imports users and groups from the `forte` realm
|
||||||
- Requires the Keycloak dynamic plugin to be enabled (pre-installed but disabled by default in RHDH)
|
- Requires the Keycloak dynamic plugin to be enabled (pre-installed but disabled by default in RHDH)
|
||||||
- Syncs every 30 minutes with 15-second initial delay
|
- Syncs every 30 minutes with 15-second initial delay
|
||||||
@@ -1082,9 +1083,10 @@ upstream:
|
|||||||
2. For each config Secret, parses `client.json` and computes a config hash
|
2. For each config Secret, parses `client.json` and computes a config hash
|
||||||
3. Skips if hash matches annotation and credential Secret already exists
|
3. Skips if hash matches annotation and credential Secret already exists
|
||||||
4. Creates or updates the Keycloak client via Admin API
|
4. Creates or updates the Keycloak client via Admin API
|
||||||
5. Fetches the generated client secret
|
5. If `serviceAccountsEnabled: true` and `serviceAccountRoles` defined, assigns service account roles (e.g., `realm-management` → `view-users`)
|
||||||
6. Upserts credential Secret in target namespace + central `secrets` namespace
|
6. Fetches the generated client secret
|
||||||
7. Annotates config Secret with sync status, config hash, and timestamp
|
7. Upserts credential Secret in target namespace + central `secrets` namespace
|
||||||
|
8. Annotates config Secret with sync status, config hash, and timestamp
|
||||||
|
|
||||||
**Resources**:
|
**Resources**:
|
||||||
- `ServiceAccount`: `keycloak-client-registrar` (namespace: `keycloak`)
|
- `ServiceAccount`: `keycloak-client-registrar` (namespace: `keycloak`)
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ route:
|
|||||||
upstream:
|
upstream:
|
||||||
backstage:
|
backstage:
|
||||||
image:
|
image:
|
||||||
registry: quay.io
|
registry: ghcr.io
|
||||||
repository: rhdh-community/rhdh
|
repository: vfarcic/idp-full-backstage
|
||||||
tag: next
|
tag: 0.0.3
|
||||||
|
|
||||||
podSecurityContext:
|
podSecurityContext:
|
||||||
runAsUser: 1001
|
runAsUser: 1001
|
||||||
|
|||||||
@@ -371,7 +371,7 @@ extraDeploy:
|
|||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Build Keycloak client representation (strip our secret delivery config)
|
# Build Keycloak client representation (strip our secret/role delivery config)
|
||||||
KC_CLIENT=$(echo "$CLIENT_JSON" | jq '{
|
KC_CLIENT=$(echo "$CLIENT_JSON" | jq '{
|
||||||
clientId: .clientId,
|
clientId: .clientId,
|
||||||
name: .name,
|
name: .name,
|
||||||
@@ -379,6 +379,7 @@ extraDeploy:
|
|||||||
protocol: "openid-connect",
|
protocol: "openid-connect",
|
||||||
clientAuthenticatorType: "client-secret",
|
clientAuthenticatorType: "client-secret",
|
||||||
standardFlowEnabled: true,
|
standardFlowEnabled: true,
|
||||||
|
serviceAccountsEnabled: (.serviceAccountsEnabled // false),
|
||||||
directAccessGrantsEnabled: false,
|
directAccessGrantsEnabled: false,
|
||||||
publicClient: false,
|
publicClient: false,
|
||||||
redirectUris: .redirectUris,
|
redirectUris: .redirectUris,
|
||||||
@@ -426,6 +427,56 @@ extraDeploy:
|
|||||||
# Sync credentials to target namespace
|
# Sync credentials to target namespace
|
||||||
sync_credentials "$CLIENT_ID" "$CLIENT_UUID" "$CRED_NS" "$CRED_NAME" "$CRED_ID_KEY" "$CRED_SECRET_KEY"
|
sync_credentials "$CLIENT_ID" "$CLIENT_UUID" "$CRED_NS" "$CRED_NAME" "$CRED_ID_KEY" "$CRED_SECRET_KEY"
|
||||||
|
|
||||||
|
# Assign service account roles if serviceAccountsEnabled
|
||||||
|
SA_ENABLED=$(echo "$CLIENT_JSON" | jq -r '.serviceAccountsEnabled // false')
|
||||||
|
SA_ROLES_JSON=$(echo "$CLIENT_JSON" | jq -c '.serviceAccountRoles // empty')
|
||||||
|
if [ "$SA_ENABLED" = "true" ] && [ -n "$SA_ROLES_JSON" ]; then
|
||||||
|
echo " Assigning service account roles for '${CLIENT_ID}'"
|
||||||
|
# Get the service account user for this client
|
||||||
|
SA_USER_ID=$(curl -sf -H "Authorization: Bearer ${TOKEN}" \
|
||||||
|
"${KEYCLOAK_URL}/admin/realms/${REALM}/clients/${CLIENT_UUID}/service-account-user" \
|
||||||
|
| jq -r '.id // empty')
|
||||||
|
if [ -z "$SA_USER_ID" ]; then
|
||||||
|
echo " WARNING: Could not get service account user for '${CLIENT_ID}'"
|
||||||
|
else
|
||||||
|
# Iterate over each target client and its roles
|
||||||
|
echo "$SA_ROLES_JSON" | jq -r 'keys[]' | while read -r TARGET_CLIENT_ID; do
|
||||||
|
# Get the target client's UUID
|
||||||
|
TARGET_UUID=$(curl -sf -H "Authorization: Bearer ${TOKEN}" \
|
||||||
|
"${KEYCLOAK_URL}/admin/realms/${REALM}/clients?clientId=${TARGET_CLIENT_ID}" \
|
||||||
|
| jq -r '.[0].id // empty')
|
||||||
|
if [ -z "$TARGET_UUID" ]; then
|
||||||
|
echo " WARNING: Target client '${TARGET_CLIENT_ID}' not found"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
# Get available roles from the target client
|
||||||
|
AVAILABLE_ROLES=$(curl -sf -H "Authorization: Bearer ${TOKEN}" \
|
||||||
|
"${KEYCLOAK_URL}/admin/realms/${REALM}/clients/${TARGET_UUID}/roles")
|
||||||
|
# Build the role payload from requested role names
|
||||||
|
ROLE_PAYLOAD=$(echo "$SA_ROLES_JSON" | jq -c --arg tc "$TARGET_CLIENT_ID" --argjson avail "$AVAILABLE_ROLES" '
|
||||||
|
.[$tc] as $wanted |
|
||||||
|
[$avail[] | select(.name as $n | $wanted | index($n))]
|
||||||
|
')
|
||||||
|
ROLE_COUNT=$(echo "$ROLE_PAYLOAD" | jq 'length')
|
||||||
|
if [ "$ROLE_COUNT" = "0" ]; then
|
||||||
|
echo " WARNING: No matching roles found for '${TARGET_CLIENT_ID}'"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
# Assign the roles to the service account
|
||||||
|
HTTP_CODE=$(curl -sf -o /dev/null -w "%{http_code}" \
|
||||||
|
-H "Authorization: Bearer ${TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-X POST -d "$ROLE_PAYLOAD" \
|
||||||
|
"${KEYCLOAK_URL}/admin/realms/${REALM}/users/${SA_USER_ID}/role-mappings/clients/${TARGET_UUID}")
|
||||||
|
if [ "$HTTP_CODE" = "204" ] || [ "$HTTP_CODE" = "200" ]; then
|
||||||
|
echo " Assigned ${ROLE_COUNT} roles from '${TARGET_CLIENT_ID}'"
|
||||||
|
else
|
||||||
|
echo " WARNING: Failed to assign roles from '${TARGET_CLIENT_ID}' (HTTP ${HTTP_CODE})"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Annotate config Secret with hash and sync status
|
# Annotate config Secret with hash and sync status
|
||||||
annotate_secret "keycloak" "$CONFIG_NAME" "keycloak.forteapps.net/config-hash" "$CONFIG_HASH"
|
annotate_secret "keycloak" "$CONFIG_NAME" "keycloak.forteapps.net/config-hash" "$CONFIG_HASH"
|
||||||
annotate_secret "keycloak" "$CONFIG_NAME" "keycloak.forteapps.net/sync-status" "synced"
|
annotate_secret "keycloak" "$CONFIG_NAME" "keycloak.forteapps.net/sync-status" "synced"
|
||||||
|
|||||||
Reference in New Issue
Block a user