Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pkg/apis/keycloak/v1alpha1/keycloak_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,6 @@ func (i *Keycloak) UpdateStatusSecondaryResources(kind string, resourceName stri
type KeycloakRelatedImages struct {
// If set, operator will use it instead of the default Keycloak image
// +optional
Keycloak string `json:"keycloak,omitempty"`
Keycloak string `json:"keycloak,omitempty"`
ImagePullSecrets []string `json:"imagePullSecrets,omitempty"`
}
}
105 changes: 105 additions & 0 deletions pkg/apis/monkeypatch/monkeypatch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package monkeypatch

import "github.com/keycloak/keycloak-operator/pkg/apis/keycloak/v1alpha1"

type KeycloakAPIClient struct {
// Client ID. If not specified, automatically generated.
// +optional
ID string `json:"id,omitempty"`
// Client ID.
// +kubebuilder:validation:Required
ClientID string `json:"clientId"`
// Client name.
// +optional
Name string `json:"name,omitempty"`
// Surrogate Authentication Required option.
// +optional
SurrogateAuthRequired bool `json:"surrogateAuthRequired,omitempty"`
// Client enabled flag.
// +optional
Enabled bool `json:"enabled,omitempty"`
// What Client authentication type to use.
// +optional
ClientAuthenticatorType string `json:"clientAuthenticatorType,omitempty"`
// Client Secret. The Operator will automatically create a Secret based on this value.
// +optional
Secret string `json:"secret,omitempty"`
// Application base URL.
// +optional
BaseURL string `json:"baseUrl,omitempty"`
// Application Admin URL.
// +optional
AdminURL string `json:"adminUrl,omitempty"`
// Application root URL.
// +optional
RootURL string `json:"rootUrl,omitempty"`
// Client description.
// +optional
Description string `json:"description,omitempty"`
// Default Client roles.
// +optional
DefaultRoles []string `json:"defaultRoles,omitempty"`
// Default Client scopes.
// optional
DefaultClientScopes []string `json:"defaultClientScopes,omitempty"`
// A list of valid Redirection URLs.
// +optional
RedirectUris []string `json:"redirectUris"`
// A list of valid Web Origins.
// +optional
WebOrigins []string `json:"webOrigins,omitempty"`
// Not Before setting.
// +optional
NotBefore int `json:"notBefore,omitempty"`
// True if a client supports only Bearer Tokens.
// +optional
BearerOnly bool `json:"bearerOnly"`
// True if Consent Screen is required.
// +optional
ConsentRequired bool `json:"consentRequired"`
// True if Standard flow is enabled.
// +optional
StandardFlowEnabled bool `json:"standardFlowEnabled"`
// True if Implicit flow is enabled.
// +optional
ImplicitFlowEnabled bool `json:"implicitFlowEnabled"`
// True if Direct Grant is enabled.
// +optional
DirectAccessGrantsEnabled bool `json:"directAccessGrantsEnabled"`
// True if Service Accounts are enabled.
// +optional
ServiceAccountsEnabled bool `json:"serviceAccountsEnabled"`
// True if this is a public Client.
// +optional
PublicClient bool `json:"publicClient"`
// True if this client supports Front Channel logout.
// +optional
FrontchannelLogout bool `json:"frontchannelLogout,omitempty"`
// Protocol used for this Client.
// +optional
Protocol string `json:"protocol,omitempty"`
// Client Attributes.
// +optional
Attributes map[string]string `json:"attributes,omitempty"`
// True if Full Scope is allowed.
// +optional
FullScopeAllowed bool `json:"fullScopeAllowed,omitempty"`
// Node registration timeout.
// +optional
NodeReRegistrationTimeout int `json:"nodeReRegistrationTimeout,omitempty"`
// Protocol Mappers.
// +optional
ProtocolMappers []v1alpha1.KeycloakProtocolMapper `json:"protocolMappers,omitempty"`
// True to use a Template Config.
// +optional
UseTemplateConfig bool `json:"useTemplateConfig,omitempty"`
// True to use Template Scope.
// +optional
UseTemplateScope bool `json:"useTemplateScope,omitempty"`
// True to use Template Mappers.
// +optional
UseTemplateMappers bool `json:"useTemplateMappers,omitempty"`
// Access options.
// +optional
Access map[string]bool `json:"access,omitempty"`
}
48 changes: 46 additions & 2 deletions pkg/common/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"crypto/tls"
"encoding/json"
"fmt"
"github.com/keycloak/keycloak-operator/pkg/apis/monkeypatch"
"io"
"io/ioutil"
"net/http"
Expand Down Expand Up @@ -50,6 +51,9 @@ func (c *Client) create(obj T, resourcePath, resourceName string) error {
return nil
}

logrus.Infof("Calling POST %s", fmt.Sprintf("%s/auth/admin/%s", c.URL, resourcePath))
logrus.Debugf("Calling POST with body: %s", string(jsonValue))

req, err := http.NewRequest(
"POST",
fmt.Sprintf("%s/auth/admin/%s", c.URL, resourcePath),
Expand Down Expand Up @@ -87,7 +91,14 @@ func (c *Client) CreateRealm(realm *v1alpha1.KeycloakRealm) error {
}

func (c *Client) CreateClient(client *v1alpha1.KeycloakAPIClient, realmName string) error {
return c.create(client, fmt.Sprintf("realms/%s/clients", realmName), "client")
// Pace Keycloak 15 Workaround Fix
client.DefaultRoles = nil

if client.RedirectUris == nil {
client.RedirectUris = make([]string, 1)
}

return c.create(c.MonkeyPatchKeycloakAPIClient(client), fmt.Sprintf("realms/%s/clients", realmName), "client")
}

func (c *Client) CreateUser(user *v1alpha1.KeycloakAPIUser, realmName string) error {
Expand Down Expand Up @@ -273,6 +284,14 @@ func (c *Client) GetClient(clientID, realmName string) (*v1alpha1.KeycloakAPICli
result, err := c.get(fmt.Sprintf("realms/%s/clients/%s", realmName, clientID), "client", func(body []byte) (T, error) {
client := &v1alpha1.KeycloakAPIClient{}
err := json.Unmarshal(body, client)

// Pace Keycloak 15 Workaround Fix
client.DefaultRoles = nil

if client.RedirectUris == nil {
client.RedirectUris = make([]string, 0)
}

return client, err
})
if err != nil {
Expand Down Expand Up @@ -368,6 +387,8 @@ func (c *Client) update(obj T, resourcePath, resourceName string) error {
}

logrus.Infof("Calling PUT %s", fmt.Sprintf("%s/auth/admin/%s", c.URL, resourcePath))
logrus.Debugf("Calling PUT with body: %s", string(jsonValue))

req, err := http.NewRequest(
"PUT",
fmt.Sprintf("%s/auth/admin/%s", c.URL, resourcePath),
Expand Down Expand Up @@ -452,6 +473,12 @@ func (c *Client) updateClientScope(method, clientID, scopeID, realmName string)
return nil
}

// MonkeyPatchKeycloakAPIClient does monkey patching to adjust the tags.
func (c *Client) MonkeyPatchKeycloakAPIClient(specClient *v1alpha1.KeycloakAPIClient) (*monkeypatch.KeycloakAPIClient) {
var patchClient monkeypatch.KeycloakAPIClient = monkeypatch.KeycloakAPIClient(*specClient)
return &patchClient
}

func (c *Client) UpdateClientScopes(specClient *v1alpha1.KeycloakAPIClient, realmName string) error {
// First get the available scopes for the current realm
availableScopes, err := c.ListAllClientScopes(realmName)
Expand All @@ -468,7 +495,15 @@ func (c *Client) UpdateClientScopes(specClient *v1alpha1.KeycloakAPIClient, real
scopeNameToID[scope.Name] = scope.ID
}

// Pace Keycloak 15 Workaround Fix
specClient.DefaultRoles = nil

if specClient.RedirectUris == nil {
specClient.RedirectUris = make([]string, 0)
}

logrus.Infof("Syncing scopes for client with clientID %s/%s", realmName, specClient.ClientID)

for _, resourceScope := range specClient.DefaultClientScopes {
addScope := true
for _, scope := range currentClientScopes {
Expand All @@ -490,6 +525,7 @@ func (c *Client) UpdateClientScopes(specClient *v1alpha1.KeycloakAPIClient, real
return errors.Wrapf(err, "Could not add scope %s to client %s on realm %s", id, specClient.ID, realmName)
}
}

for _, scope := range currentClientScopes {
removeScope := true
for _, resourceScope := range specClient.DefaultClientScopes {
Expand All @@ -515,10 +551,18 @@ func (c *Client) UpdateRealm(realm *v1alpha1.KeycloakRealm) error {
}

func (c *Client) UpdateClient(specClient *v1alpha1.KeycloakAPIClient, realmName string) error {
err := c.update(specClient, fmt.Sprintf("realms/%s/clients/%s", realmName, specClient.ID), "client")
// Pace Keycloak 15 Workaround Fix
specClient.DefaultRoles = nil

if specClient.RedirectUris == nil {
specClient.RedirectUris = make([]string, 0)
}

err := c.update(c.MonkeyPatchKeycloakAPIClient(specClient), fmt.Sprintf("realms/%s/clients/%s", realmName, specClient.ID), "client")
if err != nil {
return err
}

// Updating (default)ClientScopes requires separate API calls.
return c.UpdateClientScopes(specClient, realmName)
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/common/client_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package common

import (
"context"
"github.com/sirupsen/logrus"

kc "github.com/keycloak/keycloak-operator/pkg/apis/keycloak/v1alpha1"
"github.com/keycloak/keycloak-operator/pkg/model"
Expand Down Expand Up @@ -35,6 +36,8 @@ func (i *ClientState) Read(context context.Context, cr *kc.KeycloakClient, realm
return nil
}

logrus.Infof("Reading Client-State: %+v", client)

clientSecret, err := realmClient.GetClientSecret(cr.Spec.Client.ID, i.Realm.Spec.Realm.Realm)
if err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion pkg/common/cluster_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ type ClusterState struct {
KeycloakDeployment *v12.StatefulSet
KeycloakAdminSecret *v1.Secret
KeycloakIngress *v1beta1.Ingress
KeycloakStartupConfigMap *v1.ConfigMap
KeycloakStartupConfigMap *v1.ConfigMap
KeycloakRoute *v13.Route
PostgresqlServiceEndpoints *v1.Endpoints
PodDisruptionBudget *v1beta12.PodDisruptionBudget
Expand Down
1 change: 0 additions & 1 deletion pkg/controller/keycloak/keycloak_migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ func needsMigration(cr *v1alpha1.Keycloak, currentState *common.ClusterState) bo
desiredImage = cr.Spec.ImageOverrides.Keycloak
}


if cr.Spec.Profile == common.RHSSOProfile {
desiredImage = model.RHSSOImage
}
Expand Down
4 changes: 1 addition & 3 deletions pkg/controller/keycloak/keycloak_reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,6 @@ func (i *KeycloakReconciler) getKeycloakDeploymentOrRHSSODesiredState(clusterSta
// deploymentReconciled = model.RHSSODeploymentReconciled(cr, clusterState.KeycloakDeployment, clusterState.DatabaseSecret)
//}


//return common.GenericUpdateAction{
// Ref: deploymentReconciled,
// Msg: "Update " + deploymentName + " Deployment (StatefulSet)",
Expand Down Expand Up @@ -381,7 +380,6 @@ func (i *KeycloakReconciler) getPodDisruptionBudgetDesiredState(clusterState *co
return nil
}


func (i *KeycloakReconciler) getKeycloakStartupScriptDesiredState(clusterState *common.ClusterState, cr *kc.Keycloak) common.ClusterAction {
if clusterState.KeycloakStartupConfigMap == nil {
return common.GenericCreateAction{
Expand All @@ -394,4 +392,4 @@ func (i *KeycloakReconciler) getKeycloakStartupScriptDesiredState(clusterState *
Ref: model.KeycloakConfigMapStartupReconiled(cr, clusterState.KeycloakStartupConfigMap),
Msg: "Update Keycloak Startup ConfigMap",
}
}
}
2 changes: 1 addition & 1 deletion pkg/controller/keycloak/keycloak_reconciler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,4 +387,4 @@ func TestKeycloakReconciler_Test_Should_Update_PDB(t *testing.T) {
assert.Equal(t, len(desiredState), 10)
assert.IsType(t, common.GenericUpdateAction{}, desiredState[9])
assert.IsType(t, model.PodDisruptionBudget(cr), desiredState[9].(common.GenericUpdateAction).Ref)
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

package keycloakclient

import (
Expand Down Expand Up @@ -210,4 +209,4 @@ func TestKeycloakClientReconciler_Test_Marshal_Client_directAccessGrantsEnabled(
// then
assert.Nil(t, err, "Client couldn't be marshalled")
assert.True(t, strings.Contains(s, "\"directAccessGrantsEnabled\":false"), "Element directAccessGrantsEnabled should not be omitted if false, as keycloaks default is true")
}
}
54 changes: 27 additions & 27 deletions pkg/model/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,40 @@ package model

// Constants for a community Keycloak installation
const (
ApplicationName = "keycloak"
MonitoringKey = "middleware"
DatabaseSecretName = ApplicationName + "-db-secret"
PostgresqlPersistentVolumeName = ApplicationName + "-postgresql-claim"
PostgresqlBackupPersistentVolumeName = ApplicationName + "-backup"
PostgresqlDeploymentName = ApplicationName + "-postgresql"
KeycloakProbesName = ApplicationName + "-probes"
PostgresqlDeploymentComponent = "database"
PostgresqlServiceName = ApplicationName + "-postgresql"
PostgresqlImage = "postgres:11.5"
KeycloakImage = "quay.io/keycloak/keycloak:8.0.2"
KeycloakInitContainerImage = "quay.io/keycloak/keycloak-init-container:master"
RHSSOImage = "registry.access.redhat.com/redhat-sso-7/sso73-openshift:1.0-15"
BackupImage = "quay.io/integreatly/backup-container:1.0.10"
KeycloakDiscoveryServiceName = ApplicationName + "-discovery"
KeycloakDeploymentName = ApplicationName
KeycloakDeploymentComponent = "keycloak"
PostgresqlBackupComponent = "database-backup"
PostgresqlDatabase = "root"
PostgresqlPersistentVolumeCapacity = "1Gi"
DatabaseSecretUsernameProperty = "POSTGRES_USERNAME" // nolint
DatabaseSecretPasswordProperty = "POSTGRES_PASSWORD" // nolint
ApplicationName = "keycloak"
MonitoringKey = "middleware"
DatabaseSecretName = ApplicationName + "-db-secret"
PostgresqlPersistentVolumeName = ApplicationName + "-postgresql-claim"
PostgresqlBackupPersistentVolumeName = ApplicationName + "-backup"
PostgresqlDeploymentName = ApplicationName + "-postgresql"
KeycloakProbesName = ApplicationName + "-probes"
PostgresqlDeploymentComponent = "database"
PostgresqlServiceName = ApplicationName + "-postgresql"
PostgresqlImage = "postgres:11.5"
KeycloakImage = "quay.io/keycloak/keycloak:8.0.2"
KeycloakInitContainerImage = "quay.io/keycloak/keycloak-init-container:master"
RHSSOImage = "registry.access.redhat.com/redhat-sso-7/sso73-openshift:1.0-15"
BackupImage = "quay.io/integreatly/backup-container:1.0.10"
KeycloakDiscoveryServiceName = ApplicationName + "-discovery"
KeycloakDeploymentName = ApplicationName
KeycloakDeploymentComponent = "keycloak"
PostgresqlBackupComponent = "database-backup"
PostgresqlDatabase = "root"
PostgresqlPersistentVolumeCapacity = "1Gi"
DatabaseSecretUsernameProperty = "POSTGRES_USERNAME" // nolint
DatabaseSecretPasswordProperty = "POSTGRES_PASSWORD" // nolint
// Required by the Integreately Backup Image
DatabaseSecretHostProperty = "POSTGRES_HOST" // nolint
DatabaseSecretHostProperty = "POSTGRES_HOST" // nolint
// Required by the Integreately Backup Image
DatabaseSecretDatabaseProperty = "POSTGRES_DATABASE" // nolint
DatabaseSecretDatabaseProperty = "POSTGRES_DATABASE" // nolint
// Required by the Integreately Backup Image
DatabaseSecretSuperuserProperty = "POSTGRES_SUPERUSER" // nolint
DatabaseSecretExternalAddressProperty = "POSTGRES_EXTERNAL_ADDRESS" // nolint
DatabaseSecretExternalPortProperty = "POSTGRES_EXTERNAL_PORT" // nolint
KeycloakServicePort = 8080
KeycloakServicePortSSL = 8443
KeycloakPodPort = 8080
KeycloakPodPortSSL = 8443
KeycloakServicePortSSL = 8443
KeycloakPodPort = 8080
KeycloakPodPortSSL = 8443
PostgresDefaultPort = 5432
AdminUsernameProperty = "ADMIN_USERNAME" // nolint
AdminPasswordProperty = "ADMIN_PASSWORD" // nolint
Expand Down
5 changes: 3 additions & 2 deletions pkg/model/keycloak_configmap_startup.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package model

import (
"strings"

kc "github.com/keycloak/keycloak-operator/pkg/apis/keycloak/v1alpha1"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"strings"
)

func GetStartupScriptConfigMapContent(cr *kc.Keycloak) map[string]string {
Expand Down
Loading