- PoolParty Semantic Suite Documentation
- Administrator Guide
- PoolParty Administration
- Security Configuration
- Recommender Client in PoolParty Realm in Keycloak
Recommender Client in PoolParty Realm in Keycloak
When upgrading to PoolParty 2024 Release 1 it may happen that the recommender
client is not listed in the poolparty
realm in Keycloak making the Recommender feature unavailable to users.
We therefor recommend therefor to check whether this client has been correctly created in the poolparty
realm in Keyclock. To do so proceed as follows:
Open Keycloak.
Select the
poolparty
realm.Click Clients on the left navigation bar to display all clients registered for the
poolparty
realm.If the
recommender
client is shown on this list and the recommender feature is enabled, then you do not need to do anything.
If, however, you cannot find the recommender
client on this list you will have to run the following scripts to add this client to the the poolparty
realm in Keycloak.
Depending on the operating system these scripts vary.
Run the following code snippet to add the missing client to the the poolparty
realm in Keycloak on a system with Linux as the OS. The procedure to run this script is the same as for migrating database.
######################################################## #!/bin/bash # Default values SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) CONFIG_DIR=$(cd $SCRIPT_DIR/../config &>/dev/null && pwd) default_host=$(cat $CONFIG_DIR/keycloak/admin-keycloak.json | grep -o '"serverUrl":[^,]*' | sed 's/"serverUrl":\s*"//; s/"//; s/\/$//') default_user=$(cat $CONFIG_DIR/keycloak/admin-keycloak.json | grep -o '"username":[^,]*' | sed 's/"username":\s*"//; s/"//') default_realm=$(cat $CONFIG_DIR/keycloak/admin-keycloak.json | grep -o '"managedRealm":[^,]*' | sed 's/"managedRealm":\s*"//; s/"//; s/}$//') secret=$(cat $CONFIG_DIR/recommender/recommender.properties | grep secret | cut -d "=" -f2) host=$default_host user=$default_user realm=$default_realm password="" # Help message usage() { echo "Usage: $(basename "$0") [options]" echo echo "Options:" echo " -h, --host <host> Set the host (default is $default_host)" echo " -u, --user <user> Set the user (default is $default_user)" echo " -r, --realm <realm> Set the realm (default is $default_realm)" echo " -p, --password <password> Set the password (if not set, it will prompt for password)" echo " --help Display this help message" echo echo "Example:" echo " $(basename "$0") --host http://myhost.com --user myuser --realm myrealm --password mypassword" } # Parse command line arguments while [[ $# -gt 0 ]]; do key="$1" case $key in -h | --host) host="$2" shift # past argument name shift # past value ;; -u | --user) user="$2" shift # past argument name shift # past value ;; -r | --realm) realm="$2" shift # past argument name shift # past value ;; -p | --password) password="$2" shift # past argument name shift # past value ;; --help) usage exit 0 ;; *) echo "Unknown option: $1" usage exit 1 ;; esac done # Prompt for password if not provided as argument if [ -z "$password" ]; then read -s -p "Enter Password: " password echo fi # remove tailing slash if there is one: host="${host%/}" # Check if the endpoint is available status_code=$(curl -s -o /dev/null -w "%{http_code}" "$host") if [ "$status_code" -eq 200 ] || [ "$status_code" -eq 303 ]; then AUTHENTICATE=$(curl --silent --data "username="$user"&password="$password"&grant_type=password&client_id=admin-cli" $host/realms/master/protocol/openid-connect/token) AUTHENTICATION_TOKEN=$(echo $AUTHENTICATE | sed 's/.*access_token":"//g' | sed 's/".*//g') USER_PAYLOAD='{ "username": "service-account-recommender", "enabled": true, "totp": false, "emailVerified": true, "serviceAccountClientId": "recommender", "attributes": { "uiLanguage": [ "en" ] }, "realmRoles": [ "ApiUser" ], "notBefore": 0, "groups": [] }' CLIENT_TEMPLATE='{ "clientId": "recommender", "name": "recommender", "description": "", "rootUrl": "", "adminUrl": "", "baseUrl": "", "surrogateAuthRequired": false, "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", "secret": "%s", "redirectUris": [ "*" ], "webOrigins": [ "*" ], "notBefore": 0, "bearerOnly": false, "consentRequired": false, "standardFlowEnabled": true, "implicitFlowEnabled": false, "directAccessGrantsEnabled": true, "serviceAccountsEnabled": true, "publicClient": false, "frontchannelLogout": true, "protocol": "openid-connect", "attributes": { "post.logout.redirect.uris": "*", "oauth2.device.authorization.grant.enabled": "false", "backchannel.logout.revoke.offline.tokens": "false", "use.refresh.tokens": "true", "tls-client-certificate-bound-access-tokens": "false", "oidc.ciba.grant.enabled": "false", "backchannel.logout.session.required": "true", "client_credentials.use_refresh_token": "false", "require.pushed.authorization.requests": "false", "acr.loa.map": "{}", "display.on.consent.screen": "false", "token.response.type.bearer.lower-case": "false" }, "authenticationFlowBindingOverrides": {}, "fullScopeAllowed": true, "nodeReRegistrationTimeout": -1, "protocolMappers": [ { "name": "uiLanguage", "protocol": "openid-connect", "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { "userinfo.token.claim": "true", "user.attribute": "uiLanguage", "id.token.claim": "false", "access.token.claim": "true", "claim.name": "uiLanguage", "jsonType.label": "String" } }, { "name": "groups", "protocol": "openid-connect", "protocolMapper": "oidc-group-membership-mapper", "consentRequired": false, "config": { "full.path": "true", "id.token.claim": "false", "access.token.claim": "true", "claim.name": "groups", "userinfo.token.claim": "true" } } ], "defaultClientScopes": [ "web-origins", "acr", "roles", "profile", "email" ], "optionalClientScopes": [ "address", "phone", "offline_access", "microprofile-jwt" ], "access": { "view": true, "configure": true, "manage": true } }' CLIENT_PAYLOAD=$(printf "$CLIENT_TEMPLATE" "$secret") # add client to realm curl -X POST "$host/admin/realms/$realm/clients" \ -H "Authorization: Bearer $AUTHENTICATION_TOKEN" \ -H "Content-Type: application/json" \ -d "$CLIENT_PAYLOAD" # get client id response_get_client_id=$(curl -s -X GET "$host/admin/realms/$realm/clients?clientId=recommender" \ -H "Authorization: Bearer $AUTHENTICATION_TOKEN" \ -H "Content-Type: application/json") client_id=$(echo $response_get_client_id | grep -Po '"id":"\K[^"]+(?=","clientId")') # get service account id response_get_service_account_user=$(curl -s -X GET "$host/admin/realms/$realm/users?username=service-account-recommender" \ -H "Authorization: Bearer $AUTHENTICATION_TOKEN" \ -H "Content-Type: application/json") service_account_id=$(echo $response_get_service_account_user | sed -n 's/.*"id":"\([^"]*\)".*/\1/p') # manipulate service account according to user payload curl -X PUT "$host/admin/realms/$realm/users/$service_account_id" \ -H "Authorization: Bearer $AUTHENTICATION_TOKEN" \ -H "Content-Type: application/json" \ -d "$USER_PAYLOAD" # get ApiUser role representation response_get_role=$(curl -s -X GET "$host/admin/realms/$realm/roles/ApiUser" \ -H "Authorization: Bearer $AUTHENTICATION_TOKEN" \ -H "Content-Type: application/json") # assign ApiUse role to service account curl -X POST "$host/admin/realms/$realm/users/$service_account_id/role-mappings/realm" \ -H "Authorization: Bearer $AUTHENTICATION_TOKEN" \ -H "Content-Type: application/json" \ -d "[$response_get_role]" else echo -e "Error: The service is not up or url is incomplete. Received HTTP status code $status_code.\n" usage fi ######################################################################
Afterwards run the migragekeycloak.
script located in the poolparty_home/bin
directory.
If Keycloak is installed on a system running Windows use the following PowerShell script:
###################################################################### <#.SYNOPSIS Upgrade of the keycloak, recommender configuration/roles .DESCRIPTION This PowerShell migrates keycloak and adds needed configurations for recommender .PARAMETER keycloak_auth_server realm Specifies the keycloak authentication url and realm .EXAMPLE PS> ./migratekeycloak.ps1 '"http://myhost.com:myport/auth"' '"myrealm"' ... #Requires - Administrator Credentials to the Keycloak server #> param( [parameter(mandatory=$false)][string]$winhost, [parameter(mandatory=$false)][string]$realm, [parameter(mandatory=$false)][string]$help ) function Get-Usage { Write-Output "Usage: powershellscript [options]" Write-Output " " Write-Output "Options:" Write-Output " winhost <host> Set the winhost (default is default_host)" Write-Output " realm <realm> Set the realm (default is default_realm)" Write-Output " help Display this help message" Write-Output " " Write-Output "Example:" Write-Output "powershellscript" '"http://myhost.com:myport/auth"' '"myrealm"' } Set-Variable -Name "PoolPartyBinPath" -Value $PWD Set-Location -Path .. -PassThru Set-Variable -Name "PoolPartyHOME" -Value $PWD Set-Variable -Name "SCRIPT_DIR" -Value $PoolPartyBinPath Set-Variable -Name "CONFIG_DIR" -Value $PoolPartyHOME"\config" Set-Location $PoolPartyBinPath $JSON = Get-Content $CONFIG_DIR\keycloak\admin-keycloak.json | Out-String | ConvertFrom-JSON $RECOMMENDER_PROP = ConvertFrom-StringData (Get-Content $CONFIG_DIR/recommender/recommender.properties -raw) $secret = $RECOMMENDER_PROP."spring.security.oauth2.client.registration.recommender.client-secret" if ($PSBoundParameters.ContainsKey("winhost")) {$winhost = $PSBoundParameters["winhost"]} else {$winhost = $JSON.serverUrl} if ($PSBoundParameters.ContainsKey("realm")) {$realm = $PSBoundParameters["realm"]} else {$realm = $JSON.managedRealm} if ($PSBoundParameters.ContainsKey("help")) {Get-Usage} if ($winhost -like "*/") {$winhost = $winhost.TrimEnd("/")}Write-Output "Starting to migrate keyckloak in $PoolPartyHOME" $USER_PAYLOAD='{ "username": "service-account-recommender", "enabled": true, "totp": false, "emailVerified": true, "serviceAccountClientId": "recommender", "attributes": { "uiLanguage": [ "en" ] }, "realmRoles": [ "ApiUser" ], "notBefore": 0, "groups": [] }' $CLIENT_TEMPLATE='{ "clientId": "recommender", "name": "recommender", "description": "", "rootUrl": "", "adminUrl": "", "baseUrl": "", "surrogateAuthRequired": false, "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", "secret": "%s", "redirectUris": [ "*" ], "webOrigins": [ "*" ], "notBefore": 0, "bearerOnly": false, "consentRequired": false, "standardFlowEnabled": true, "implicitFlowEnabled": false, "directAccessGrantsEnabled": true, "serviceAccountsEnabled": true, "publicClient": false, "frontchannelLogout": true, "protocol": "openid-connect", "attributes": { "post.logout.redirect.uris": "*", "oauth2.device.authorization.grant.enabled": "false", "backchannel.logout.revoke.offline.tokens": "false", "use.refresh.tokens": "true", "tls-client-certificate-bound-access-tokens": "false", "oidc.ciba.grant.enabled": "false", "backchannel.logout.session.required": "true", "client_credentials.use_refresh_token": "false", "require.pushed.authorization.requests": "false", "acr.loa.map": "{}", "display.on.consent.screen": "false", "token.response.type.bearer.lower-case": "false" }, "authenticationFlowBindingOverrides": {}, "fullScopeAllowed": true, "nodeReRegistrationTimeout": -1, "protocolMappers": [ { "name": "uiLanguage", "protocol": "openid-connect", "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { "userinfo.token.claim": "true", "user.attribute": "uiLanguage", "id.token.claim": "false", "access.token.claim": "true", "claim.name": "uiLanguage", "jsonType.label": "String" } }, { "name": "groups", "protocol": "openid-connect", "protocolMapper": "oidc-group-membership-mapper", "consentRequired": false, "config": { "full.path": "true", "id.token.claim": "false", "access.token.claim": "true", "claim.name": "groups", "userinfo.token.claim": "true" } } ], "defaultClientScopes": [ "web-origins", "acr", "roles", "profile", "email" ], "optionalClientScopes": [ "address", "phone", "offline_access", "microprofile-jwt" ], "access": { "view": true, "configure": true, "manage": true } }' $CLIENT_TEMPLATE = $CLIENT_TEMPLATE.Replace('%s',"$secret") $Keycloak_Server = $winhost $credentials = Get-Credential -Message "Please enter your Keycloack Credentials" $Username = $credentials.UserName $Password = $credentials.GetNetworkCredential().Password $RequestURL = $Keycloak_Server + "/realms/master/protocol/openid-connect/token" $body = @{grant_type='password' username=$Username password=$Password client_id='admin-cli' } $result = Invoke-WebRequest -Method POST -Uri $RequestURL -ContentType "application/x-www-form-urlencoded" -Body $body -Credential $credentials $result_json = $result.Content | ConvertFrom-JSON $AUTHENTICATION_TOKEN = $result_json.access_token $headers = @{ 'Authorization' = "Bearer $AUTHENTICATION_TOKEN" 'Content-Type' = 'application/json' } ############# recommender client $result_post = Invoke-RestMethod -Uri "$Keycloak_Server/admin/realms/$realm/clients" -Method 'Post' -Headers $headers -Body $CLIENT_TEMPLATE ################################### ############# recommender client ID working part $response_get_client_id = Invoke-RestMethod -Uri "$Keycloak_Server/admin/realms/$realm/clients?clientId=recommender" -Method 'GET' -Headers $headers $client_id = $response_get_client_id.id ################################### ############# recommender service_account_id working part $response_get_service_account_user= Invoke-RestMethod -Uri "$Keycloak_Server/admin/realms/$realm/users?username=service-account-recommender" -Method 'GET' -Headers $headers $service_account_id = $response_get_service_account_user.id ################################### ############# service account <UNCOMMENT IN PROD> # manipulate service account according to user payload $result_post_update_user_acc = Invoke-RestMethod -Uri "$Keycloak_Server/admin/realms/$realm/users/$service_account_id" -Method 'PUT' -Headers $headers -Body $USER_PAYLOAD ################################### ############# get ApiUser role representation $response_get_role = Invoke-RestMethod -Uri "$Keycloak_Server/admin/realms/$realm/roles/ApiUser" -Method 'GET' -Headers $headers$user_get_role_payload = $response_get_role | ConvertTo-JSON $user_get_role_payload = "[" + $user_get_role_payload + "]" ################################### ############# assign ApiUse role to service account $result_postapi = Invoke-RestMethod -Uri "$Keycloak_Server/admin/realms/$realm/users/$service_account_id/role-mappings/realm" -Method 'Post' -Headers $headers -Body $user_get_role_payload ################################### Write-Output "Finished to migrate keyckloak in $PoolPartyHOME" ######################################################################
Afterwards run the migragekeycloak.ps1
script located in the poolparty_home/bin
directory.