From fc6f1632da85ac913db4d86c85cdbee2054cf661 Mon Sep 17 00:00:00 2001
From: Paul <paul@zom.bi>
Date: Mon, 11 Jan 2021 04:21:29 +0100
Subject: [PATCH] Add new properties to CRDs

---
 api/v1alpha1/keycloakclient_types.go          |  94 ++++++++-
 api/v1alpha1/keycloakrealm_types.go           |  70 ++++++-
 api/v1alpha1/zz_generated.deepcopy.go         | 188 +++++++++++++++++-
 .../keycloak.bitmask.me_keycloakclients.yaml  | 107 +++++++++-
 .../keycloak.bitmask.me_keycloakrealms.yaml   |  61 +++++-
 config/rbac/role.yaml                         |  26 +++
 controllers/keycloakclient_controller.go      |   6 +-
 controllers/keycloakrealm_controller.go       |   5 +-
 main.go                                       |   9 +
 9 files changed, 538 insertions(+), 28 deletions(-)

diff --git a/api/v1alpha1/keycloakclient_types.go b/api/v1alpha1/keycloakclient_types.go
index e4b149d..517e74f 100644
--- a/api/v1alpha1/keycloakclient_types.go
+++ b/api/v1alpha1/keycloakclient_types.go
@@ -20,22 +20,98 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
-// EDIT THIS FILE!  THIS IS SCAFFOLDING FOR YOU TO OWN!
-// NOTE: json tags are required.  Any new fields you add must have json tags for the fields to be serialized.
-
 // KeycloakClientSpec defines the desired state of KeycloakClient
 type KeycloakClientSpec struct {
-	// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
-	// Important: Run "make" to regenerate code after modifying this file
+	// +kubebuilder:validation:Required
+	// Name of the Realm the client should be created in
+	RealmName string `json:"realmName"`
 
-	// Foo is an example field of KeycloakClient. Edit KeycloakClient_types.go to remove/update
-	Foo string `json:"foo,omitempty"`
+	// +kubebuilder:validation:Required
+	// ClientID is the alphanumeric identifier of the client in a realm.
+	ClientID string `json:"clientId"`
+
+	// If the client is enabled and active
+	Enabled *bool `json:"enabled,omitempty"`
+
+	// Displayed Name of the Client
+	Name *string `json:"name,omitempty"`
+
+	// Human Readable description
+	Description *string `json:"description,omitempty"`
+
+	// Protocol, either 'openid-connect' or 'saml'
+	Protocol *string `json:"protocol,omitempty"`
+
+	// How should Clients authenticate to the server? either 'client-secret' or 'client-jwt'.
+	ClientAuthenticatorType *string `json:"clientAuthenticatorType,omitempty"`
+
+	// Are direct access grants enabled for this client or not (OpenID connect).
+	DirectAccessGrantsEnabled *bool `json:"directAccessGrantsEnabled,omitempty"`
+
+	// Is the access type for this client public or not.
+	PublicClient *bool `json:"publicClient,omitempty"`
+
+	// Enable implicit flow for this client or not (OpenID connect).
+	ImplicitFlowEnabled *bool `json:"implicitFlowEnabled,omitempty"`
+
+	// Enable standard flow for this client or not (OpenID connect).
+	StandardFlowEnabled *bool `json:"standardFlowEnabled,omitempty"`
+
+	// Are service accounts enabled for this client or not (OpenID connect).
+	ServiceAccountsEnabled *bool `json:"serviceAccountsEnabled,omitempty"`
+
+	// Used for authentication when registering new clients
+	RegistrationAccessToken *string `json:"registrationAccessToken,omitempty"`
+
+	// Whether or not surrogate auth is required.
+	SurrogateAuthRequired *bool `json:"surrogateAuthRequired,omitempty"`
+
+	// The access type of this client is bearer-only.
+	BearerOnly *bool `json:"bearerOnly,omitempty"`
+
+	// If enabled, users have to consent to client access.
+	ConsentRequired *bool `json:"consentRequired,omitempty"`
+
+	// Which client scopes chould be granted by default, even without
+	// specifying them.
+	DefaultClientScopes *[]string `json:"defaultClientScopes,omitempty"`
+
+	// Which additional scopes can be specified by the client
+	OptionalClientScopes *[]string `json:"optionalClientScopes,omitempty"`
+
+	// Default URL to use when the auth server needs to redirect or link back to the client
+	BaseURL *string `json:"baseUrl,omitempty"`
+
+	// Root URL appended to relative URLs for this client
+	RootURL *string `json:"rootUrl,omitempty"`
+
+	// URL to the admin interface of the client
+	AdminURL *string `json:"adminUrl,omitempty"`
+
+	// URL to the admin interface of the client
+	RedirectURIs *[]string `json:"redirectUris,omitempty"`
+
+	// List of allowed CORS origins
+	WebOrigins *[]string `json:"webOrigins,omitempty"`
+
+	// +kubebuilder:validation:Optional
+	// A client Secret is not always required
+	Secret *KeycloakClientSecret `json:"secret,omitempty"`
+}
+
+// KeycloakClientSecret contains the Secret storing the Client Secret
+type KeycloakClientSecret struct {
+	// +kubebuilder:validation:Required
+	// Name of the Secret containing the client Secret.
+	Name string `json:"name"`
+	// +kubebuilder:default:=password
+	// Key of the attribute, that holds the value in the Secret.
+	Key string `json:"key,omitempty"`
 }
 
 // KeycloakClientStatus defines the observed state of KeycloakClient
 type KeycloakClientStatus struct {
-	// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
-	// Important: Run "make" to regenerate code after modifying this file
+	Available bool `json:"available"`
 }
 
 // +kubebuilder:object:root=true
diff --git a/api/v1alpha1/keycloakrealm_types.go b/api/v1alpha1/keycloakrealm_types.go
index caaa312..32e0a76 100644
--- a/api/v1alpha1/keycloakrealm_types.go
+++ b/api/v1alpha1/keycloakrealm_types.go
@@ -22,11 +22,14 @@ import (
 
 // KeycloakRealmSpec defines the desired state of KeycloakRealm
 type KeycloakRealmSpec struct {
+	// +kubebuilder:validation:Required
 	// RealmName is the name and public identifier of the Realm
 	RealmName string `json:"realmName"`
 
-	// Secret containing SMTP configuration
-	SMTPSecretName string `json:"smtpSecretName,omitempty"`
+	// If the realm is enabled and active
+	Enabled *bool `json:"enabled,omitempty"`
+
+	SMTP *KeycloakRealmSMTP `json:"smtp,omitempty"`
 
 	// name shown to the user
 	DisplayName *string `json:"displayName,omitempty"`
@@ -54,10 +57,69 @@ type KeycloakRealmSpec struct {
 	RememberMe *bool `json:"rememberMe,omitempty"`
 }
 
+// KeycloakRealmSMTP contains information about the SMTP server used to send
+// transactional mail (for registration and password reset).
+type KeycloakRealmSMTP struct {
+	// auth: "true"
+	// from: noreply@bitmask.me
+	// fromDisplayName: Bitmask Accounts
+	// host: email-smtp.eu-west-1.amazonaws.com
+	// password: '**********'
+	// port: "587"
+	// ssl: "false"
+	// starttls: "true"
+	// user: XXXXXXXXXXXXXXXXXXXX
+
+	// +kubebuilder:default:=true
+	// If authentication should be used
+	Auth bool `json:"auth,omitempty"`
+
+	// From which address the emails will be sent, takes precedence
+	// over the attribute defined in the secret.
+	From string `json:"from,omitempty"`
+
+	// From which NAME the email should originate.
+	FromDisplayName string `json:"fromDisplayName,omitempty"`
+
+	Secret *KeycloakRealmSMTPSecret `json:"secret,omitempty"`
+}
+
+// KeycloakRealmSMTPSecret contains Credentials for connecting to a SMTP
+// Server.
+type KeycloakRealmSMTPSecret struct {
+
+	// +kubebuilder:validation:Required
+	// Secret containing SMTP configuration
+	Name string `json:"name"`
+
+	// +kubebuilder:default:=host
+	// Key of the host attribute
+	HostKey string `json:"hostKey,omitempty"`
+
+	// +kubebuilder:default:=port
+	// Key of the port attribute
+	PortKey string `json:"portKey,omitempty"`
+
+	// +kubebuilder:default:=ssl
+	// Key of the ssl attribute
+	SSLKey string `json:"sslKey,omitempty"`
+
+	// +kubebuilder:default:=starttls
+	// Key of the starttls attribute
+	StartTLSKey string `json:"startTLSKey,omitempty"`
+
+	// +kubebuilder:default:=username
+	// Key of the username attribute
+	UsernameKey string `json:"usernameKey,omitempty"`
+
+	// +kubebuilder:default:=from
+	// Key of the from attribute, contains the mail address that email will be sent from.
+	FromKey string `json:"fromKey,omitempty"`
+}
+
 // KeycloakRealmStatus defines the observed state of KeycloakRealm
 type KeycloakRealmStatus struct {
-	Available bool   `json:"available"`
-	ID        string `json:"id"`
+	Available bool `json:"available"`
 }
 
 // +kubebuilder:object:root=true
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index 9401c90..cfc367c 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -29,7 +29,7 @@ func (in *KeycloakClient) DeepCopyInto(out *KeycloakClient) {
 	*out = *in
 	out.TypeMeta = in.TypeMeta
 	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
-	out.Spec = in.Spec
+	in.Spec.DeepCopyInto(&out.Spec)
 	out.Status = in.Status
 }
 
@@ -83,9 +83,150 @@ func (in *KeycloakClientList) DeepCopyObject() runtime.Object {
 	return nil
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *KeycloakClientSecret) DeepCopyInto(out *KeycloakClientSecret) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeycloakClientSecret.
+func (in *KeycloakClientSecret) DeepCopy() *KeycloakClientSecret {
+	if in == nil {
+		return nil
+	}
+	out := new(KeycloakClientSecret)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // 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.Enabled != nil {
+		in, out := &in.Enabled, &out.Enabled
+		*out = new(bool)
+		**out = **in
+	}
+	if in.Name != nil {
+		in, out := &in.Name, &out.Name
+		*out = new(string)
+		**out = **in
+	}
+	if in.Description != nil {
+		in, out := &in.Description, &out.Description
+		*out = new(string)
+		**out = **in
+	}
+	if in.Protocol != nil {
+		in, out := &in.Protocol, &out.Protocol
+		*out = new(string)
+		**out = **in
+	}
+	if in.ClientAuthenticatorType != nil {
+		in, out := &in.ClientAuthenticatorType, &out.ClientAuthenticatorType
+		*out = new(string)
+		**out = **in
+	}
+	if in.DirectAccessGrantsEnabled != nil {
+		in, out := &in.DirectAccessGrantsEnabled, &out.DirectAccessGrantsEnabled
+		*out = new(bool)
+		**out = **in
+	}
+	if in.PublicClient != nil {
+		in, out := &in.PublicClient, &out.PublicClient
+		*out = new(bool)
+		**out = **in
+	}
+	if in.ImplicitFlowEnabled != nil {
+		in, out := &in.ImplicitFlowEnabled, &out.ImplicitFlowEnabled
+		*out = new(bool)
+		**out = **in
+	}
+	if in.StandardFlowEnabled != nil {
+		in, out := &in.StandardFlowEnabled, &out.StandardFlowEnabled
+		*out = new(bool)
+		**out = **in
+	}
+	if in.ServiceAccountsEnabled != nil {
+		in, out := &in.ServiceAccountsEnabled, &out.ServiceAccountsEnabled
+		*out = new(bool)
+		**out = **in
+	}
+	if in.RegistrationAccessToken != nil {
+		in, out := &in.RegistrationAccessToken, &out.RegistrationAccessToken
+		*out = new(string)
+		**out = **in
+	}
+	if in.SurrogateAuthRequired != nil {
+		in, out := &in.SurrogateAuthRequired, &out.SurrogateAuthRequired
+		*out = new(bool)
+		**out = **in
+	}
+	if in.BearerOnly != nil {
+		in, out := &in.BearerOnly, &out.BearerOnly
+		*out = new(bool)
+		**out = **in
+	}
+	if in.ConsentRequired != nil {
+		in, out := &in.ConsentRequired, &out.ConsentRequired
+		*out = new(bool)
+		**out = **in
+	}
+	if in.DefaultClientScopes != nil {
+		in, out := &in.DefaultClientScopes, &out.DefaultClientScopes
+		*out = new([]string)
+		if **in != nil {
+			in, out := *in, *out
+			*out = make([]string, len(*in))
+			copy(*out, *in)
+		}
+	}
+	if in.OptionalClientScopes != nil {
+		in, out := &in.OptionalClientScopes, &out.OptionalClientScopes
+		*out = new([]string)
+		if **in != nil {
+			in, out := *in, *out
+			*out = make([]string, len(*in))
+			copy(*out, *in)
+		}
+	}
+	if in.BaseURL != nil {
+		in, out := &in.BaseURL, &out.BaseURL
+		*out = new(string)
+		**out = **in
+	}
+	if in.RootURL != nil {
+		in, out := &in.RootURL, &out.RootURL
+		*out = new(string)
+		**out = **in
+	}
+	if in.AdminURL != nil {
+		in, out := &in.AdminURL, &out.AdminURL
+		*out = new(string)
+		**out = **in
+	}
+	if in.RedirectURIs != nil {
+		in, out := &in.RedirectURIs, &out.RedirectURIs
+		*out = new([]string)
+		if **in != nil {
+			in, out := *in, *out
+			*out = make([]string, len(*in))
+			copy(*out, *in)
+		}
+	}
+	if in.WebOrigins != nil {
+		in, out := &in.WebOrigins, &out.WebOrigins
+		*out = new([]string)
+		if **in != nil {
+			in, out := *in, *out
+			*out = make([]string, len(*in))
+			copy(*out, *in)
+		}
+	}
+	if in.Secret != nil {
+		in, out := &in.Secret, &out.Secret
+		*out = new(KeycloakClientSecret)
+		**out = **in
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeycloakClientSpec.
@@ -172,9 +313,54 @@ func (in *KeycloakRealmList) DeepCopyObject() runtime.Object {
 	return nil
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *KeycloakRealmSMTP) DeepCopyInto(out *KeycloakRealmSMTP) {
+	*out = *in
+	if in.Secret != nil {
+		in, out := &in.Secret, &out.Secret
+		*out = new(KeycloakRealmSMTPSecret)
+		**out = **in
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeycloakRealmSMTP.
+func (in *KeycloakRealmSMTP) DeepCopy() *KeycloakRealmSMTP {
+	if in == nil {
+		return nil
+	}
+	out := new(KeycloakRealmSMTP)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *KeycloakRealmSMTPSecret) DeepCopyInto(out *KeycloakRealmSMTPSecret) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KeycloakRealmSMTPSecret.
+func (in *KeycloakRealmSMTPSecret) DeepCopy() *KeycloakRealmSMTPSecret {
+	if in == nil {
+		return nil
+	}
+	out := new(KeycloakRealmSMTPSecret)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *KeycloakRealmSpec) DeepCopyInto(out *KeycloakRealmSpec) {
 	*out = *in
+	if in.Enabled != nil {
+		in, out := &in.Enabled, &out.Enabled
+		*out = new(bool)
+		**out = **in
+	}
+	if in.SMTP != nil {
+		in, out := &in.SMTP, &out.SMTP
+		*out = new(KeycloakRealmSMTP)
+		(*in).DeepCopyInto(*out)
+	}
 	if in.DisplayName != nil {
 		in, out := &in.DisplayName, &out.DisplayName
 		*out = new(string)
diff --git a/config/crd/bases/keycloak.bitmask.me_keycloakclients.yaml b/config/crd/bases/keycloak.bitmask.me_keycloakclients.yaml
index 6d3ebc9..bbdb697 100644
--- a/config/crd/bases/keycloak.bitmask.me_keycloakclients.yaml
+++ b/config/crd/bases/keycloak.bitmask.me_keycloakclients.yaml
@@ -36,13 +36,114 @@ spec:
           spec:
             description: KeycloakClientSpec defines the desired state of KeycloakClient
             properties:
-              foo:
-                description: Foo is an example field of KeycloakClient. Edit KeycloakClient_types.go
-                  to remove/update
+              adminUrl:
+                description: URL to the admin interface of the client
                 type: string
+              baseUrl:
+                description: Default URL to use when the auth server needs to redirect
+                  or link back to the client
+                type: string
+              bearerOnly:
+                description: The access type of this client is bearer-only.
+                type: boolean
+              clientAuthenticatorType:
+                description: How should Clients authenticate to the server? either
+                  'client-secret' or 'client-jwt'.
+                type: string
+              clientId:
+                description: ClientID is the alphanumeric identifier of the client
+                  in a realm.
+                type: string
+              consentRequired:
+                description: If enabled, users have to consent to client access.
+                type: boolean
+              defaultClientScopes:
+                description: Which client scopes chould be granted by default, even
+                  without specifying them.
+                items:
+                  type: string
+                type: array
+              description:
+                description: Human Readable description
+                type: string
+              directAccessGrantsEnabled:
+                description: Are direct access grants enabled for this client or not
+                  (OpenID connect).
+                type: boolean
+              enabled:
+                description: If the client is enabled and active
+                type: boolean
+              implicitFlowEnabled:
+                description: Enable implicit flow for this client or not (OpenID connect).
+                type: boolean
+              name:
+                description: Displayed Name of the Client
+                type: string
+              optionalClientScopes:
+                description: Which additional scopes can be specified by the client
+                items:
+                  type: string
+                type: array
+              protocol:
+                description: Protocol, either 'openid-connect' or 'saml'
+                type: string
+              publicClient:
+                description: Is the access type for this client public or not.
+                type: boolean
+              realmName:
+                description: Name of the Realm the client should be created in
+                type: string
+              redirectUris:
+                description: URL to the admin interface of the client
+                items:
+                  type: string
+                type: array
+              registrationAccessToken:
+                description: Used for authentication when registering new clients
+                type: string
+              rootUrl:
+                description: Root URL appended to relative URLs for this client
+                type: string
+              secret:
+                description: A client Secret is not always required
+                properties:
+                  key:
+                    default: password
+                    description: Key of the attribute, that holds the value in the
+                      Secret.
+                    type: string
+                  name:
+                    description: Name of the Secret containing the client Secret.
+                    type: string
+                required:
+                - name
+                type: object
+              serviceAccountsEnabled:
+                description: Are service accounts enabled for this client or not (OpenID
+                  connect).
+                type: boolean
+              standardFlowEnabled:
+                description: Enable standard flow for this client or not (OpenID connect).
+                type: boolean
+              surrogateAuthRequired:
+                description: Whether or not surrogate auth is required.
+                type: boolean
+              webOrigins:
+                description: List of allowed CORS origins
+                items:
+                  type: string
+                type: array
+            required:
+            - clientId
+            - realmName
             type: object
           status:
             description: KeycloakClientStatus defines the observed state of KeycloakClient
+            properties:
+              available:
+                type: boolean
+            required:
+            - available
             type: object
         type: object
     served: true
diff --git a/config/crd/bases/keycloak.bitmask.me_keycloakrealms.yaml b/config/crd/bases/keycloak.bitmask.me_keycloakrealms.yaml
index b9065ae..8749f1e 100644
--- a/config/crd/bases/keycloak.bitmask.me_keycloakrealms.yaml
+++ b/config/crd/bases/keycloak.bitmask.me_keycloakrealms.yaml
@@ -49,6 +49,9 @@ spec:
                 description: if the user should be able to change their username after
                   account creation
                 type: boolean
+              enabled:
+                description: If the realm is enabled and active
+                type: boolean
               loginTheme:
                 description: the name of the Theme used for the login pages
                 type: string
@@ -74,9 +77,58 @@ spec:
               resetPasswordAllowed:
                 description: if the user is allowed to use the reset password flow
                 type: boolean
-              smtpSecretName:
-                description: Secret containing SMTP configuration
-                type: string
+              smtp:
+                description: KeycloakRealmSMTP contains information about the SMTP
+                  server used to send transactional mail (for registration and password
+                  reset).
+                properties:
+                  auth:
+                    default: true
+                    description: If authentication should be used
+                    type: boolean
+                  from:
+                    description: From which address the emails will be sent, takes
+                      precedence over the attribute defined in the secret.
+                    type: string
+                  fromDisplayName:
+                    description: From which NAME the email should originate.
+                    type: string
+                  secret:
+                    description: KeycloakRealmSMTPSecret contains Credentials for
+                      connecting to a SMTP Server.
+                    properties:
+                      fromKey:
+                        default: from
+                        description: Key of the from attribute, contains the mail
+                          address that email will be sent from.
+                        type: string
+                      hostKey:
+                        default: host
+                        description: Key of the host attribute
+                        type: string
+                      name:
+                        description: Secret containing SMTP configuration
+                        type: string
+                      portKey:
+                        default: port
+                        description: Key of the port attribute
+                        type: string
+                      sslKey:
+                        default: ssl
+                        description: Key of the ssl attribute
+                        type: string
+                      startTLSKey:
+                        default: starttls
+                        description: Key of the starttls attribute
+                        type: string
+                      usernameKey:
+                        default: username
+                        description: Key of the username attribute
+                        type: string
+                    required:
+                    - name
+                    type: object
+                type: object
               verifyEmail:
                 description: if emails should be verified before the user can log
                   into their account
@@ -89,11 +141,8 @@ spec:
             properties:
               available:
                 type: boolean
-              id:
-                type: string
             required:
             - available
-            - id
             type: object
         type: object
     served: true
diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml
index 4119a99..0022036 100644
--- a/config/rbac/role.yaml
+++ b/config/rbac/role.yaml
@@ -6,6 +6,32 @@ metadata:
   creationTimestamp: null
   name: manager-role
 rules:
+- apiGroups:
+  - keycloak.bitmask.me
+  resources:
+  - keycloakclients
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - keycloak.bitmask.me
+  resources:
+  - keycloakclients/finalizers
+  verbs:
+  - update
+- apiGroups:
+  - keycloak.bitmask.me
+  resources:
+  - keycloakclients/status
+  verbs:
+  - get
+  - patch
+  - update
 - apiGroups:
   - keycloak.bitmask.me
   resources:
diff --git a/controllers/keycloakclient_controller.go b/controllers/keycloakclient_controller.go
index 325a52f..ba8d356 100644
--- a/controllers/keycloakclient_controller.go
+++ b/controllers/keycloakclient_controller.go
@@ -25,13 +25,15 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 	keycloakv1alpha1 "git.zom.bi/images/keycloak-operator/api/v1alpha1"
+	"git.zom.bi/images/keycloak-operator/controllers/keycloak"
 )
 
 // KeycloakClientReconciler reconciles a KeycloakClient object
 type KeycloakClientReconciler struct {
 	client.Client
-	Log    logr.Logger
-	Scheme *runtime.Scheme
+	Keycloak *keycloak.Keycloak
+	Log      logr.Logger
+	Scheme   *runtime.Scheme
 }
 
 // +kubebuilder:rbac:groups=keycloak.bitmask.me,resources=keycloakclients,verbs=get;list;watch;create;update;patch;delete
diff --git a/controllers/keycloakrealm_controller.go b/controllers/keycloakrealm_controller.go
index 81f9faf..3214eeb 100644
--- a/controllers/keycloakrealm_controller.go
+++ b/controllers/keycloakrealm_controller.go
@@ -63,10 +63,10 @@ func (r *KeycloakRealmReconciler) Reconcile(ctx context.Context, req ctrl.Reques
 		}
 	}
 
-	if realm.Status.ID != "" {
+	if realm.Status.Available {
 		// try to get existing realm
 		log.Info("Would try to fetch the realm by its id.",
-			"id", realm.Status.ID)
+			"id", realm.Spec.RealmName)
 		// if found {
 		log.Info("will act like i found it, updating.")
 		// update()
@@ -76,7 +76,6 @@ func (r *KeycloakRealmReconciler) Reconcile(ctx context.Context, req ctrl.Reques
 
 	log.Info("Would now create the realm.")
 
-	realm.Status.ID = "dummy"
 	realm.Status.Available = true
 	r.Status().Update(ctx, &realm)
 
diff --git a/main.go b/main.go
index 21560bd..2f8cd0e 100644
--- a/main.go
+++ b/main.go
@@ -99,6 +99,15 @@ func main() {
 		setupLog.Error(err, "unable to create controller", "controller", "KeycloakRealm")
 		os.Exit(1)
 	}
+	if err = (&controllers.KeycloakClientReconciler{
+		Client:   mgr.GetClient(),
+		Keycloak: kc,
+		Log:      ctrl.Log.WithName("controllers").WithName("KeycloakClient"),
+		Scheme:   mgr.GetScheme(),
+	}).SetupWithManager(mgr); err != nil {
+		setupLog.Error(err, "unable to create controller", "controller", "KeycloakClient")
+		os.Exit(1)
+	}
 	// +kubebuilder:scaffold:builder
 
 	if err := mgr.AddHealthzCheck("health", healthz.Ping); err != nil {