Skip to content

Add Keycloak client-role support to NebariApp.auth.keycloakConfig #119

Description

@viniciusdc

Problem

AuthConfig.KeycloakConfig (api/v1/nebariapp_types.go:300) currently has:

  • Groups []KeycloakGroup — declarative group + member sync
  • ProtocolMappers []KeycloakProtocolMapperConfig — declarative mapper sync

…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:

type KeycloakClientConfig struct {
    Groups           []KeycloakGroup                  `json:"groups,omitempty"`
    ProtocolMappers  []KeycloakProtocolMapperConfig   `json:"protocolMappers,omitempty"`
    // NEW
    Roles            []KeycloakClientRole             `json:"roles,omitempty"`
}

type KeycloakClientRole struct {
    Name        string   `json:"name"`              // required, e.g. "allow-group-directory-creation-role"
    Description string   `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).
    Attributes  map[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"`
}

User-facing YAML:

spec:
  auth:
    enabled: true
    provider: keycloak
    keycloakConfig:
      roles:
        - name: allow-group-directory-creation-role
          attributes:
            component: [shared-directory]
            scopes:    [write:shared-mount]
          groups:
            - /developers
            - /admin

Reconciliation semantics

Same additive-only pattern as Groups/Mappers:

  • 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:

  1. Wait for the operator to provision the client (so the client UUID exists)
  2. Auth into Keycloak with admin-realm credentials supplied via a separate Secret
  3. 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.

Scope check

  • Belongs in Epic Epic: Nebari Operator - Keycloak & Auth #79 (Nebari Operator - Keycloak & Auth).
  • Touches: api/v1/nebariapp_types.go (new types), internal/controller/reconcilers/auth/providers/keycloak.go (reconcile logic), CRD regen, tests.
  • Likely needs a complementary chart change (drop the bootstrap Job once this lands).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Start date

    None yet

    Target date

    None yet

    Size

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions