From f4ffa99d0cd4732f57c1ee17a8e37ea8962a52c4 Mon Sep 17 00:00:00 2001 From: Ofer Bochan Date: Wed, 17 Jun 2026 09:43:45 +0300 Subject: [PATCH 1/4] OSAC-757: Add osac-admin client and align realm with fulfillment-service Add the osac-admin Keycloak client and service account user to the installer's realm.json, matching the pattern established in fulfillment-service PR #462. - Add osac-admin confidential client with serviceAccountsEnabled - Add service-account-osac-admin user linked to the new client - Add roles.client["osac-admin"] entry required by Keycloak - Set osac-controller directAccessGrantsEnabled to false (service accounts should only use client credentials grant) Related: https://redhat.atlassian.net/browse/OSAC-757 Signed-off-by: Ofer Bochan --- .../keycloak/service/files/realm.json | 81 ++++++++++++++++++- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/prerequisites/keycloak/service/files/realm.json b/prerequisites/keycloak/service/files/realm.json index 01edc275..05c2b525 100644 --- a/prerequisites/keycloak/service/files/realm.json +++ b/prerequisites/keycloak/service/files/realm.json @@ -430,7 +430,8 @@ "containerId": "23cad6d9-db0b-43b2-9d20-63e90b1b571e", "attributes": {} } - ] + ], + "osac-admin": [] } }, "groups": [ @@ -739,6 +740,24 @@ }, "notBefore": 0, "groups": [] + }, + { + "id": "c3e4f5a6-b7c8-4d9e-0f1a-2b3c4d5e6f7a", + "username": "service-account-osac-admin", + "enabled": true, + "totp": false, + "emailVerified": false, + "serviceAccountClientId": "osac-admin", + "createdTimestamp": 1757683950000, + "credentials": [], + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": [ + "default-roles-my realm" + ], + "clientRoles": {}, + "notBefore": 0, + "groups": [] } ], "scopeMappings": [ @@ -1028,7 +1047,65 @@ "consentRequired": false, "standardFlowEnabled": false, "implicitFlowEnabled": false, - "directAccessGrantsEnabled": true, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": true, + "publicClient": false, + "frontchannelLogout": true, + "protocol": "openid-connect", + "attributes": { + "realm_client": "false", + "oidc.ciba.grant.enabled": "false", + "client.secret.creation.time": "1757684016", + "backchannel.logout.session.required": "true", + "standard.token.exchange.enabled": "false", + "post.logout.redirect.uris": "+", + "oauth2.device.authorization.grant.enabled": "false", + "backchannel.logout.revoke.offline.tokens": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "basic", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "organization", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "a1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e", + "clientId": "osac-admin", + "name": "OSAC administrator", + "description": "Service account for the OSAC administrator", + "rootUrl": "", + "adminUrl": "", + "baseUrl": "", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "kP9xRmNvQw3hYjL8sT5uA2dF7gB0cE4i", + "redirectUris": [ + "/*" + ], + "webOrigins": [ + "/*" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, "serviceAccountsEnabled": true, "publicClient": false, "frontchannelLogout": true, From e3ffdf34dd00661a81699042588adb73856addfb Mon Sep 17 00:00:00 2001 From: Ofer Bochan Date: Wed, 17 Jun 2026 09:43:56 +0300 Subject: [PATCH 2/4] OSAC-757: Fix refresh script credential logic for multiple SA clients Hardcode osac-controller in the credential lookup instead of using a generic serviceAccountsEnabled select. The previous approach returns multiple results now that osac-admin is also a service account client, breaking the jq query. This matches setup.sh which already hardcodes osac-controller. Related: https://redhat.atlassian.net/browse/OSAC-757 Signed-off-by: Ofer Bochan --- scripts/refresh-after-snapshot.sh | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/refresh-after-snapshot.sh b/scripts/refresh-after-snapshot.sh index 8f095c52..1f4a59b0 100755 --- a/scripts/refresh-after-snapshot.sh +++ b/scripts/refresh-after-snapshot.sh @@ -144,15 +144,14 @@ keycloak_sync() { create_fulfillment_credentials() { echo "[2/9] Recreating fulfillment controller credentials..." - FC_CLIENT_ID=$(jq -er '.clients[] | select(.serviceAccountsEnabled == true) | .clientId' "${REALM_JSON}") - FC_CLIENT_SECRET=$(jq -er ".clients[] | select(.clientId == \"${FC_CLIENT_ID}\") | .secret // empty" "${REALM_JSON}") - [[ -n "${FC_CLIENT_SECRET}" ]] || { echo "ERROR: Could not resolve secret for ${FC_CLIENT_ID} in realm.json" >&2; exit 1; } + FC_CLIENT_SECRET=$(jq -er '.clients[] | select(.clientId == "osac-controller") | .secret // empty' "${REALM_JSON}") + [[ -n "${FC_CLIENT_SECRET}" ]] || { echo "ERROR: Could not resolve secret for osac-controller in realm.json" >&2; exit 1; } oc delete secret fulfillment-controller-credentials -n "${INSTALLER_NAMESPACE}" --ignore-not-found oc create secret generic fulfillment-controller-credentials \ - --from-literal=client-id="${FC_CLIENT_ID}" \ + --from-literal=client-id=osac-controller \ --from-literal=client-secret="${FC_CLIENT_SECRET}" \ -n "${INSTALLER_NAMESPACE}" - echo "[2/9] Credentials created for client: ${FC_CLIENT_ID}" + echo "[2/9] Credentials created for client: osac-controller" } keycloak_sync & From 0547602cb866b2624b24423314b7a96852ad78dc Mon Sep 17 00:00:00 2001 From: Ofer Bochan Date: Sat, 20 Jun 2026 15:58:32 +0300 Subject: [PATCH 3/4] OSAC-757: Use configurable FC_CLIENT for credential lookup Address review feedback: instead of hardcoding osac-controller in the function body, use an FC_CLIENT variable (set at the top, with a jq fallback to first serviceAccountsEnabled client). --- scripts/refresh-after-snapshot.sh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/refresh-after-snapshot.sh b/scripts/refresh-after-snapshot.sh index 1f4a59b0..1946695c 100755 --- a/scripts/refresh-after-snapshot.sh +++ b/scripts/refresh-after-snapshot.sh @@ -15,6 +15,7 @@ INSTALLER_VM_TEMPLATE=${INSTALLER_VM_TEMPLATE:-} CLUSTER_DOMAIN=$(oc get ingresses.config/cluster -o jsonpath='{.spec.domain}') KEYCLOAK_NS="keycloak" REALM_JSON="prerequisites/keycloak/service/files/realm.json" +FC_CLIENT="osac-controller" echo "=== Refreshing OSAC after snapshot boot ===" echo "Namespace: ${INSTALLER_NAMESPACE}" @@ -144,14 +145,15 @@ keycloak_sync() { create_fulfillment_credentials() { echo "[2/9] Recreating fulfillment controller credentials..." - FC_CLIENT_SECRET=$(jq -er '.clients[] | select(.clientId == "osac-controller") | .secret // empty' "${REALM_JSON}") - [[ -n "${FC_CLIENT_SECRET}" ]] || { echo "ERROR: Could not resolve secret for osac-controller in realm.json" >&2; exit 1; } + FC_CLIENT_ID=${FC_CLIENT:-$(jq -er 'first(.clients[] | select(.serviceAccountsEnabled==true)) | .clientId' "${REALM_JSON}")} + FC_CLIENT_SECRET=$(jq -er ".clients[] | select(.clientId == \"${FC_CLIENT_ID}\") | .secret // empty" "${REALM_JSON}") + [[ -n "${FC_CLIENT_SECRET}" ]] || { echo "ERROR: Could not resolve secret for ${FC_CLIENT_ID} in realm.json" >&2; exit 1; } oc delete secret fulfillment-controller-credentials -n "${INSTALLER_NAMESPACE}" --ignore-not-found oc create secret generic fulfillment-controller-credentials \ - --from-literal=client-id=osac-controller \ + --from-literal=client-id="${FC_CLIENT_ID}" \ --from-literal=client-secret="${FC_CLIENT_SECRET}" \ -n "${INSTALLER_NAMESPACE}" - echo "[2/9] Credentials created for client: osac-controller" + echo "[2/9] Credentials created for client: ${FC_CLIENT_ID}" } keycloak_sync & From bd4eccfe401e6598b7536a063aa93231fa55effe Mon Sep 17 00:00:00 2001 From: Ofer Bochan Date: Sun, 21 Jun 2026 09:00:25 +0300 Subject: [PATCH 4/4] OSAC-757: Make FC_CLIENT overridable from environment Use parameter-default assignment so an externally supplied FC_CLIENT is not unconditionally overwritten. --- scripts/refresh-after-snapshot.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/refresh-after-snapshot.sh b/scripts/refresh-after-snapshot.sh index 1946695c..fa771261 100755 --- a/scripts/refresh-after-snapshot.sh +++ b/scripts/refresh-after-snapshot.sh @@ -15,7 +15,7 @@ INSTALLER_VM_TEMPLATE=${INSTALLER_VM_TEMPLATE:-} CLUSTER_DOMAIN=$(oc get ingresses.config/cluster -o jsonpath='{.spec.domain}') KEYCLOAK_NS="keycloak" REALM_JSON="prerequisites/keycloak/service/files/realm.json" -FC_CLIENT="osac-controller" +FC_CLIENT=${FC_CLIENT:-"osac-controller"} echo "=== Refreshing OSAC after snapshot boot ===" echo "Namespace: ${INSTALLER_NAMESPACE}"