crossplane-contrib / provider-keycloak

Apache License 2.0
25 stars 16 forks source link

Example of how to use ClientServiceAccountRole #71

Closed olav-st closed 6 months ago

olav-st commented 8 months ago

Hi. First of all, thanks for this project! :smiley:

It would be nice to have an example of how to use the ClientServiceAccountRole. I'm trying to use it to assign a role from the built-in realm-management client, but I haven't found a way to do that so far.

I have tried the examples in the examples-generated/ folder, but they fail with the following error message:

status:
  atProvider: {}
  conditions:
  - lastTransitionTime: "2024-03-02T13:07:02Z"
    message: 'observe failed: cannot run refresh: refresh failed: Reference to undeclared
      resource: A managed resource "keycloak_openid_client" "client2" has not been
      declared in the root module.'
    reason: ReconcileError
    status: "False"
    type: Synced

Even though client2 exists:

$ kubectl get client
NAME              READY   SYNCED   EXTERNAL-NAME                                  AGE
client1           True    True     fbe5e90f-7968-4ed4-9b9f-e9d7930fc164           3h34m
client2           True    True     9e981111-9f97-458f-b7fa-736fd5d94572           3h34m
Breee commented 8 months ago

Greetings, sorry for the late reply - i had some busy week and no time to answer I will try to look into that in the evening.

Breee commented 8 months ago

This is indeed a bug :) good catch

for example:

---
apiVersion: openidclient.keycloak.crossplane.io/v1alpha1
kind: ClientServiceAccountRole
metadata:
  name: client2-service-account-role
spec:
  forProvider:
    clientIdRef:
      name: client2
    realmIdRef:
      name: my-realm
    role: my-realm-role
    serviceAccountUserId: ${keycloak_openid_client.client2.service_account_user_id}
  providerConfigRef:
    name: "keycloak-provider-config"
serviceAccountUserId: ${keycloak_openid_client.client2.service_account_user_id}

is not correct, we need implement a reference to fetch the serviceAccountUserId from the created client:

apiVersion: openidclient.keycloak.crossplane.io/v1alpha1
kind: Client
metadata:
  annotations:
    crossplane.io/external-create-pending: "2024-03-11T18:44:29Z"
    crossplane.io/external-create-succeeded: "2024-03-11T18:44:29Z"
    crossplane.io/external-name: 537143de-658d-43b6-9083-7b5676a6d5f3
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"openidclient.keycloak.crossplane.io/v1alpha1","kind":"Client","metadata":{"annotations":{},"name":"client2"},"spec":{"forProvider":{"accessType":"CONFIDENTIAL","clientId":"client2","name":"client2","realmIdRef":{"name":"my-realm"},"serviceAccountsEnabled":true},"providerConfigRef":{"name":"keycloak-provider-config"}}}
    upjet.crossplane.io/provider-meta: "null"
  creationTimestamp: "2024-03-11T18:43:49Z"
  finalizers:
  - finalizer.managedresource.crossplane.io
  generation: 5
  name: client2
  resourceVersion: "1014942"
  uid: 20da717b-f166-4b20-99b7-3b70d1cd2548
spec:
  deletionPolicy: Delete
  forProvider:
    accessType: CONFIDENTIAL
    backchannelLogoutSessionRequired: true
    clientAuthenticatorType: client-secret
    clientId: client2
    enabled: true
    fullScopeAllowed: true
    name: client2
    realmId: my-realm
    realmIdRef:
      name: my-realm
    serviceAccountsEnabled: true
    useRefreshTokens: true
  initProvider: {}
  managementPolicies:
  - '*'
  providerConfigRef:
    name: keycloak-provider-config
status:
  atProvider:
    accessTokenLifespan: ""
    accessType: CONFIDENTIAL
    adminUrl: ""
    backchannelLogoutRevokeOfflineSessions: false
    backchannelLogoutSessionRequired: true
    backchannelLogoutUrl: ""
    baseUrl: ""
    clientAuthenticatorType: client-secret
    clientId: client2
    clientOfflineSessionIdleTimeout: ""
    clientOfflineSessionMaxLifespan: ""
    clientSessionIdleTimeout: ""
    clientSessionMaxLifespan: ""
    consentRequired: false
    consentScreenText: ""
    description: ""
    directAccessGrantsEnabled: false
    displayOnConsentScreen: false
    enabled: true
    frontchannelLogoutEnabled: false
    frontchannelLogoutUrl: ""
    fullScopeAllowed: true
    id: 537143de-658d-43b6-9083-7b5676a6d5f3
    implicitFlowEnabled: false
    import: false
    loginTheme: ""
    name: client2
    oauth2DeviceAuthorizationGrantEnabled: false
    oauth2DeviceCodeLifespan: ""
    oauth2DevicePollingInterval: ""
    realmId: my-realm
    rootUrl: ""
    serviceAccountUserId: 3e85339e-4b89-4aee-a0c6-2d364d3e793f
    serviceAccountsEnabled: true
    standardFlowEnabled: false
    useRefreshTokens: true
    useRefreshTokensClientCredentials: false

I'll look into that and try to fix it

Breee commented 7 months ago

Sorry for the radio silence, i visited kubecon and was on vacation.

I prepared a fix that enables us to do something like:

apiVersion: openidclient.keycloak.crossplane.io/v1alpha1
kind: ClientServiceAccountRole
metadata:
  name: test-service-account-role
spec:
  forProvider:
    clientIdRef:
      name: test
    realmId: dev
    role: create-realm
    # References the same Client as clientIdRef
    serviceAccountUserClientIDRef:
      name: test
  providerConfigRef:
    name: "keycloak-provider-config"

I'm no entirely happy with that right now as i would prefer to get rid of

    serviceAccountUserClientIdRef:
      name: test

entirely.

Problems:

I'll merge my changes soon tho, then you can test

Breee commented 7 months ago

@olav-st did you have a chance to test this?

olav-st commented 7 months ago

Hi. I just had time to test it :slightly_smiling_face:

I was able to make it work by making some changes to the example in examples-generated/openidclient/v1alpha1/clientserviceaccountrole.yaml. I had to change the role: field to my-client1-role and add accessType and clientId to the two clients.

After doing that it seems to work fine. Thanks for the fix!