diff --git a/helm/crds/provisioning.totalsoft.ro_keycloakclients.yaml b/helm/crds/provisioning.totalsoft.ro_keycloakclients.yaml index fe34e61..472fc54 100644 --- a/helm/crds/provisioning.totalsoft.ro_keycloakclients.yaml +++ b/helm/crds/provisioning.totalsoft.ro_keycloakclients.yaml @@ -47,6 +47,10 @@ spec: type: object spec: properties: + baseUrl: + description: BaseUrl is the default URL used when the auth server + needs to redirect or link back to the client. + type: string categoryOverrides: additionalProperties: x-kubernetes-preserve-unknown-fields: true @@ -157,6 +161,14 @@ spec: - domain type: object type: array + frontchannelLogout: + description: FrontchannelLogout enables front-channel logout. + type: boolean + frontchannelLogoutUrl: + description: FrontchannelLogoutUrl The URL Keycloak calls to perform + front-channel logout. It is only applicable when FrontchannelLogoutEnabled + is true. + type: string fullScopeAllowed: description: FullScopeAllowed allows full scope. type: boolean @@ -171,9 +183,18 @@ spec: organization: description: Associated tenant name. type: string + pkceCodeChallengeMethod: + description: PkceCodeChallengeMethod is the PKCE code challenge method. + type: string platformRef: description: Target platform (custom resource name). type: string + postLogoutRedirectUris: + description: PostLogoutRedirectUris is the list of valid redirect + URIs after logout. + items: + type: string + type: array protocol: description: Protocol e.g. "openid-connect" type: string @@ -209,6 +230,15 @@ spec: realm: description: Associated realm. type: string + redirectUris: + description: RedirectUris is the list of valid redirect URIs after + login. + items: + type: string + type: array + rootUrl: + description: RootUrl is the root URL appended to relative URLs. + type: string serviceAccountsEnabled: description: ServiceAccountsEnabled enables service accounts. type: boolean @@ -268,6 +298,11 @@ spec: Overrides for tenants. Dictionary with tenant name as key, spec override as value. The spec override has the same structure as Spec type: object + webOrigins: + description: WebOrigins is the list of allowed CORS origins. + items: + type: string + type: array required: - clientId - clientName diff --git a/internal/controllers/provisioning/provisioners/pulumi/keycloak_client.go b/internal/controllers/provisioning/provisioners/pulumi/keycloak_client.go index a5f3921..01ab79f 100644 --- a/internal/controllers/provisioning/provisioners/pulumi/keycloak_client.go +++ b/internal/controllers/provisioning/provisioners/pulumi/keycloak_client.go @@ -50,7 +50,7 @@ func deployKeycloakClient(target provisioning.ProvisioningTarget, func(platform *platformv1.Platform) string { return spec.ClientName }, ) - client, err := openid.NewClient(ctx, keycloakClient.Name, &openid.ClientArgs{ + clientArgs := &openid.ClientArgs{ RealmId: pulumi.String(realm.Id), ClientId: pulumi.String(clientId), Name: pulumi.String(clientName), @@ -61,8 +61,39 @@ func deployKeycloakClient(target provisioning.ProvisioningTarget, StandardFlowEnabled: pulumi.Bool(spec.StandardFlowEnabled), ImplicitFlowEnabled: pulumi.Bool(spec.ImplicitFlowEnabled), DirectAccessGrantsEnabled: pulumi.Bool(spec.DirectAccessGrantsEnabled), + FrontchannelLogoutEnabled: pulumi.Bool(spec.FrontchannelLogout), + FrontchannelLogoutUrl: pulumi.String(spec.FrontchannelLogoutUrl), FullScopeAllowed: pulumi.Bool(spec.FullScopeAllowed), - }, pulumi.DependsOn(dependencies)) + RootUrl: pulumi.String(spec.RootUrl), + BaseUrl: pulumi.String(spec.BaseUrl), + PkceCodeChallengeMethod: pulumi.String(spec.PkceCodeChallengeMethod), + } + + if len(spec.PostLogoutRedirectUris) > 0 { + redirectUris := make(pulumi.StringArray, len(spec.PostLogoutRedirectUris)) + for i, u := range spec.PostLogoutRedirectUris { + redirectUris[i] = pulumi.String(u) + } + clientArgs.ValidPostLogoutRedirectUris = redirectUris + } + + if len(spec.RedirectUris) > 0 { + redirectUris := make(pulumi.StringArray, len(spec.RedirectUris)) + for i, u := range spec.RedirectUris { + redirectUris[i] = pulumi.String(u) + } + clientArgs.ValidRedirectUris = redirectUris + } + + if len(spec.WebOrigins) > 0 { + webOrigins := make(pulumi.StringArray, len(spec.WebOrigins)) + for i, o := range spec.WebOrigins { + webOrigins[i] = pulumi.String(o) + } + clientArgs.WebOrigins = webOrigins + } + + client, err := openid.NewClient(ctx, keycloakClient.Name, clientArgs, pulumi.DependsOn(dependencies)) if err != nil { return nil, err } diff --git a/pkg/apis/provisioning/v1alpha1/keycloakClientTypes.go b/pkg/apis/provisioning/v1alpha1/keycloakClientTypes.go index 4dc7b72..3420994 100644 --- a/pkg/apis/provisioning/v1alpha1/keycloakClientTypes.go +++ b/pkg/apis/provisioning/v1alpha1/keycloakClientTypes.go @@ -31,6 +31,30 @@ type KeycloakClientSpec struct { // Associated tenant name. Organization string `json:"organization,omitempty"` + // RootUrl is the root URL appended to relative URLs. + // +optional + RootUrl string `json:"rootUrl,omitempty"` + + // BaseUrl is the default URL used when the auth server needs to redirect or link back to the client. + // +optional + BaseUrl string `json:"baseUrl,omitempty"` + + // RedirectUris is the list of valid redirect URIs after login. + // +optional + RedirectUris []string `json:"redirectUris,omitempty"` + + // PostLogoutRedirectUris is the list of valid redirect URIs after logout. + // +optional + PostLogoutRedirectUris []string `json:"postLogoutRedirectUris,omitempty"` + + // PkceCodeChallengeMethod is the PKCE code challenge method. + // +optional + PkceCodeChallengeMethod string `json:"pkceCodeChallengeMethod,omitempty"` + + // WebOrigins is the list of allowed CORS origins. + // +optional + WebOrigins []string `json:"webOrigins,omitempty"` + // Enabled indicates if the client is enabled. Enabled bool `json:"enabled"` @@ -52,6 +76,14 @@ type KeycloakClientSpec struct { // DirectAccessGrantsEnabled enables direct access grants (resource owner password). DirectAccessGrantsEnabled bool `json:"directAccessGrantsEnabled"` + // FrontchannelLogout enables front-channel logout. + // +optional + FrontchannelLogout bool `json:"frontchannelLogout,omitempty"` + + // FrontchannelLogoutUrl The URL Keycloak calls to perform front-channel logout. It is only applicable when FrontchannelLogoutEnabled is true. + // +optional + FrontchannelLogoutUrl string `json:"frontchannelLogoutUrl,omitempty"` + // Protocol e.g. "openid-connect" Protocol string `json:"protocol,omitempty"` diff --git a/pkg/apis/provisioning/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/provisioning/v1alpha1/zz_generated.deepcopy.go index ceece18..f8707a8 100644 --- a/pkg/apis/provisioning/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/provisioning/v1alpha1/zz_generated.deepcopy.go @@ -1095,6 +1095,21 @@ func (in *KeycloakClientList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KeycloakClientSpec) DeepCopyInto(out *KeycloakClientSpec) { *out = *in + if in.RedirectUris != nil { + in, out := &in.RedirectUris, &out.RedirectUris + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.PostLogoutRedirectUris != nil { + in, out := &in.PostLogoutRedirectUris, &out.PostLogoutRedirectUris + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.WebOrigins != nil { + in, out := &in.WebOrigins, &out.WebOrigins + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.ProtocolMappers != nil { in, out := &in.ProtocolMappers, &out.ProtocolMappers *out = make([]ProtocolMapper, len(*in)) diff --git a/pkg/generated/applyconfiguration/provisioning/v1alpha1/keycloakclientspec.go b/pkg/generated/applyconfiguration/provisioning/v1alpha1/keycloakclientspec.go index 30872ce..170cce6 100644 --- a/pkg/generated/applyconfiguration/provisioning/v1alpha1/keycloakclientspec.go +++ b/pkg/generated/applyconfiguration/provisioning/v1alpha1/keycloakclientspec.go @@ -29,6 +29,12 @@ type KeycloakClientSpecApplyConfiguration struct { ClientId *string `json:"clientId,omitempty"` Realm *string `json:"realm,omitempty"` Organization *string `json:"organization,omitempty"` + RootUrl *string `json:"rootUrl,omitempty"` + BaseUrl *string `json:"baseUrl,omitempty"` + RedirectUris []string `json:"redirectUris,omitempty"` + PostLogoutRedirectUris []string `json:"postLogoutRedirectUris,omitempty"` + PkceCodeChallengeMethod *string `json:"pkceCodeChallengeMethod,omitempty"` + WebOrigins []string `json:"webOrigins,omitempty"` Enabled *bool `json:"enabled,omitempty"` ConsentRequired *bool `json:"consentRequired,omitempty"` PublicClient *bool `json:"publicClient,omitempty"` @@ -36,6 +42,8 @@ type KeycloakClientSpecApplyConfiguration struct { StandardFlowEnabled *bool `json:"standardFlowEnabled,omitempty"` ImplicitFlowEnabled *bool `json:"implicitFlowEnabled,omitempty"` DirectAccessGrantsEnabled *bool `json:"directAccessGrantsEnabled,omitempty"` + FrontchannelLogout *bool `json:"frontchannelLogout,omitempty"` + FrontchannelLogoutUrl *string `json:"frontchannelLogoutUrl,omitempty"` Protocol *string `json:"protocol,omitempty"` FullScopeAllowed *bool `json:"fullScopeAllowed,omitempty"` ProtocolMappers []ProtocolMapperApplyConfiguration `json:"protocolMappers,omitempty"` @@ -83,6 +91,60 @@ func (b *KeycloakClientSpecApplyConfiguration) WithOrganization(value string) *K return b } +// WithRootUrl sets the RootUrl field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the RootUrl field is set to the value of the last call. +func (b *KeycloakClientSpecApplyConfiguration) WithRootUrl(value string) *KeycloakClientSpecApplyConfiguration { + b.RootUrl = &value + return b +} + +// WithBaseUrl sets the BaseUrl field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the BaseUrl field is set to the value of the last call. +func (b *KeycloakClientSpecApplyConfiguration) WithBaseUrl(value string) *KeycloakClientSpecApplyConfiguration { + b.BaseUrl = &value + return b +} + +// WithRedirectUris adds the given value to the RedirectUris field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the RedirectUris field. +func (b *KeycloakClientSpecApplyConfiguration) WithRedirectUris(values ...string) *KeycloakClientSpecApplyConfiguration { + for i := range values { + b.RedirectUris = append(b.RedirectUris, values[i]) + } + return b +} + +// WithPostLogoutRedirectUris adds the given value to the PostLogoutRedirectUris field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the PostLogoutRedirectUris field. +func (b *KeycloakClientSpecApplyConfiguration) WithPostLogoutRedirectUris(values ...string) *KeycloakClientSpecApplyConfiguration { + for i := range values { + b.PostLogoutRedirectUris = append(b.PostLogoutRedirectUris, values[i]) + } + return b +} + +// WithPkceCodeChallengeMethod sets the PkceCodeChallengeMethod field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PkceCodeChallengeMethod field is set to the value of the last call. +func (b *KeycloakClientSpecApplyConfiguration) WithPkceCodeChallengeMethod(value string) *KeycloakClientSpecApplyConfiguration { + b.PkceCodeChallengeMethod = &value + return b +} + +// WithWebOrigins adds the given value to the WebOrigins field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the WebOrigins field. +func (b *KeycloakClientSpecApplyConfiguration) WithWebOrigins(values ...string) *KeycloakClientSpecApplyConfiguration { + for i := range values { + b.WebOrigins = append(b.WebOrigins, values[i]) + } + return b +} + // WithEnabled sets the Enabled field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the Enabled field is set to the value of the last call. @@ -139,6 +201,22 @@ func (b *KeycloakClientSpecApplyConfiguration) WithDirectAccessGrantsEnabled(val return b } +// WithFrontchannelLogout sets the FrontchannelLogout field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the FrontchannelLogout field is set to the value of the last call. +func (b *KeycloakClientSpecApplyConfiguration) WithFrontchannelLogout(value bool) *KeycloakClientSpecApplyConfiguration { + b.FrontchannelLogout = &value + return b +} + +// WithFrontchannelLogoutUrl sets the FrontchannelLogoutUrl field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the FrontchannelLogoutUrl field is set to the value of the last call. +func (b *KeycloakClientSpecApplyConfiguration) WithFrontchannelLogoutUrl(value string) *KeycloakClientSpecApplyConfiguration { + b.FrontchannelLogoutUrl = &value + return b +} + // WithProtocol sets the Protocol field in the declarative configuration to the given value // and returns the receiver, so that objects can be built by chaining "With" function invocations. // If called multiple times, the Protocol field is set to the value of the last call.