You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
…but no field for client roles on the OIDC client the operator provisions. A grep for ClientRole|CreateClientRole|client_roles across internal/ returns zero hits — the operator never talks to Keycloak's /admin/realms/{realm}/clients/{id}/roles endpoint.
In practice this forces deployers who need role-gated authz (e.g. spawner code that grants users access to a shared mount only if they hold a specific client role) to ship a separate post-install Job that runs the Keycloak Admin REST API directly. nebari-data-science-pack does this today via keycloak_rbac_bootstrap.py: the chart provisions an allow-group-directory-creation-role client role on the hub OIDC client and binds it to specific Keycloak groups. The bootstrap Job has its own bugs (separate issue on the chart side — see nebari-dev/data-science-pack#76), but the underlying gap is that the operator CRD can't express what the Job creates.
Proposed shape
Extend KeycloakClientConfig with a third sub-resource:
typeKeycloakClientConfigstruct {
Groups []KeycloakGroup`json:"groups,omitempty"`ProtocolMappers []KeycloakProtocolMapperConfig`json:"protocolMappers,omitempty"`// NEWRoles []KeycloakClientRole`json:"roles,omitempty"`
}
typeKeycloakClientRolestruct {
Namestring`json:"name"`// required, e.g. "allow-group-directory-creation-role"Descriptionstring`json:"description,omitempty"`// Optional attributes — Keycloak roles can carry key/value metadata// (the spawner needs role.attributes to distinguish "component=shared-directory"// from arbitrary same-named roles in other clients).Attributesmap[string][]string`json:"attributes,omitempty"`// Groups that should have this role granted on the client.// Additive-only (matches Groups sync semantics).Groups []string`json:"groups,omitempty"`
}
Roles in spec are ensured to exist on the client (created or updated).
Group→role assignments are added; existing assignments not in spec are NOT removed (matches KeycloakGroup.Members semantics).
Roles not in spec are NOT deleted (deployers retain manual roles).
Why now
The nebari-data-science-pack chart has a keycloak_rbac_bootstrap.py Job whose entire purpose could be replaced by this CRD field. Today the Job has to:
Wait for the operator to provision the client (so the client UUID exists)
Auth into Keycloak with admin-realm credentials supplied via a separate Secret
POST role + role-group bindings
A NebariApp-declarative path lets the operator do all of that as part of its existing reconciliation loop — same admin credentials, same realm, no separate Secret wiring, no separate Job, declarative drift detection.
Problem
AuthConfig.KeycloakConfig(api/v1/nebariapp_types.go:300) currently has:Groups []KeycloakGroup— declarative group + member syncProtocolMappers []KeycloakProtocolMapperConfig— declarative mapper sync…but no field for client roles on the OIDC client the operator provisions. A grep for
ClientRole|CreateClientRole|client_rolesacrossinternal/returns zero hits — the operator never talks to Keycloak's/admin/realms/{realm}/clients/{id}/rolesendpoint.In practice this forces deployers who need role-gated authz (e.g. spawner code that grants users access to a shared mount only if they hold a specific client role) to ship a separate post-install Job that runs the Keycloak Admin REST API directly.
nebari-data-science-packdoes this today viakeycloak_rbac_bootstrap.py: the chart provisions anallow-group-directory-creation-roleclient role on the hub OIDC client and binds it to specific Keycloak groups. The bootstrap Job has its own bugs (separate issue on the chart side — see nebari-dev/data-science-pack#76), but the underlying gap is that the operator CRD can't express what the Job creates.Proposed shape
Extend
KeycloakClientConfigwith a third sub-resource:User-facing YAML:
Reconciliation semantics
Same additive-only pattern as Groups/Mappers:
KeycloakGroup.Memberssemantics).Why now
The
nebari-data-science-packchart has akeycloak_rbac_bootstrap.pyJob whose entire purpose could be replaced by this CRD field. Today the Job has to:A NebariApp-declarative path lets the operator do all of that as part of its existing reconciliation loop — same admin credentials, same realm, no separate Secret wiring, no separate Job, declarative drift detection.
Scope check
api/v1/nebariapp_types.go(new types),internal/controller/reconcilers/auth/providers/keycloak.go(reconcile logic), CRD regen, tests.