feat(realm): configure smtp settings

This commit is contained in:
unteem 2024-04-26 13:22:28 +02:00
parent 3ea48d2908
commit bf053d6598
4 changed files with 80 additions and 0 deletions

View file

@ -19,6 +19,7 @@ package v1alpha1
import ( import (
"fmt" "fmt"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
lshmeta "libre.sh/api/meta/v1alpha1" lshmeta "libre.sh/api/meta/v1alpha1"
@ -83,6 +84,8 @@ type RealmSpec struct {
// Disable brute force // Disable brute force
// +kubebuilder:validation:Optional // +kubebuilder:validation:Optional
DisableBruteForce bool `json:"disableBruteForce,omitempty"` DisableBruteForce bool `json:"disableBruteForce,omitempty"`
//+kubebuilder:validation:Required
MailboxRef corev1.LocalObjectReference `json:"mailboxRef"`
} }
// RealmStatus defines the observed state of Realm // RealmStatus defines the observed state of Realm

View file

@ -110,6 +110,7 @@ func (in *RealmSpec) DeepCopyInto(out *RealmSpec) {
out.Spec = in.Spec out.Spec = in.Spec
out.Themes = in.Themes out.Themes = in.Themes
in.Locale.DeepCopyInto(&out.Locale) in.Locale.DeepCopyInto(&out.Locale)
out.MailboxRef = in.MailboxRef
} }
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RealmSpec. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RealmSpec.

View file

@ -79,6 +79,16 @@ spec:
default: fr default: fr
type: string type: string
type: object type: object
mailboxRef:
description: LocalObjectReference contains enough information to let
you locate the referenced object inside the same namespace.
properties:
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
type: object
x-kubernetes-map-type: atomic
metrics: metrics:
description: Are metrics enabled description: Are metrics enabled
type: boolean type: boolean
@ -105,6 +115,8 @@ spec:
description: Theme name for Login page description: Theme name for Login page
type: string type: string
type: object type: object
required:
- mailboxRef
type: object type: object
status: status:
description: RealmStatus defines the observed state of Realm description: RealmStatus defines the observed state of Realm

View file

@ -77,10 +77,23 @@ func (r *RealmReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl
// TODO what do we do with OIDCClient and SAMLCLient ? should we delete them when realm is deleted // TODO what do we do with OIDCClient and SAMLCLient ? should we delete them when realm is deleted
// TODO delete Realm in keycloak // TODO delete Realm in keycloak
return lshr.Finalize(ctx, r, patcher, &realm, func() error { return lshr.Finalize(ctx, r, patcher, &realm, func() error {
var mailbox lshcore.Bucket
mailbox.Name = realm.Spec.MailboxRef.Name
mailbox.Namespace = realm.Namespace
err := r.Get(ctx, client.ObjectKeyFromObject(&mailbox), &mailbox)
if err != nil {
return client.IgnoreNotFound(err)
}
err = lshr.UnsetControllerReference(ctx, r, &realm, &mailbox)
if err != nil {
return err
}
if realm.Annotations != nil && realm.Annotations["realm.keycloak.libre.sh/delete-protection"] == "true" { if realm.Annotations != nil && realm.Annotations["realm.keycloak.libre.sh/delete-protection"] == "true" {
r.log.Info("resource is protected") r.log.Info("resource is protected")
return nil return nil
} }
return nil return nil
}) })
} }
@ -120,6 +133,39 @@ func (r *RealmReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl
} }
MutateRealmRepresentation(realm, realmRepresentation) MutateRealmRepresentation(realm, realmRepresentation)
mailbox := &lshcore.Mailbox{}
mailbox.Namespace = realm.Namespace
mailbox.Name = realm.Spec.MailboxRef.Name
err = r.Get(ctx, client.ObjectKeyFromObject(mailbox), mailbox)
if err != nil {
return ctrl.Result{}, err
}
mailbox.ManagedFields = nil
controllerutil.SetOwnerReference(&realm, mailbox, r.Scheme())
opts := []client.PatchOption{
client.FieldOwner(r.Name()),
}
err = r.Patch(ctx, mailbox, client.Apply, opts...)
if err != nil {
return ctrl.Result{}, err
}
mailboxSecret := corev1.Secret{}
mailboxSecret.Name = mailbox.SecretName()
mailboxSecret.Namespace = mailbox.Namespace
err = r.Get(ctx, client.ObjectKeyFromObject(&mailboxSecret), &mailboxSecret)
if err != nil {
return ctrl.Result{}, err
}
r.SetRealmSMTPSettings(ctx, realmRepresentation, mailboxSecret)
err = keycloakClient.UpdateRealm(ctx, keycloakClient.AccessToken(), *realmRepresentation) err = keycloakClient.UpdateRealm(ctx, keycloakClient.AccessToken(), *realmRepresentation)
if err != nil { if err != nil {
return internal.HandleError(ctx, r, patcher, &realm, err) return internal.HandleError(ctx, r, patcher, &realm, err)
@ -180,6 +226,7 @@ func (r *RealmReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl
func (r *RealmReconciler) SetupWithManager(mgr ctrl.Manager) error { func (r *RealmReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr). return ctrl.NewControllerManagedBy(mgr).
For(&keycloakv1alpha1.Realm{}). For(&keycloakv1alpha1.Realm{}).
Owns(&lshcore.Mailbox{}).
Complete(r) Complete(r)
} }
@ -595,6 +642,23 @@ func contains(s []string, str string) bool {
return false return false
} }
func (r *RealmReconciler) SetRealmSMTPSettings(ctx context.Context, realmRepresentation *gocloak.RealmRepresentation, mailboxSecret corev1.Secret) {
smtp := map[string]string{
"auth": strconv.FormatBool(true),
"user": string(mailboxSecret.Data[lshmeta.SecretUsernameKey]),
"from": string(mailboxSecret.Data[lshmeta.SecretLocalPartsKey]),
"password": string(mailboxSecret.Data[lshmeta.SecretPasswordKey]),
"host": string(mailboxSecret.Data[lshmeta.SecretHostKey]),
"Host": string(mailboxSecret.Data[lshmeta.SecretHostKey]),
"port": string(mailboxSecret.Data[lshmeta.SecretPortKey]),
"Port": string(mailboxSecret.Data[lshmeta.SecretPortKey]),
// TODO should be in secret ?
"starttls": strconv.FormatBool(true),
}
realmRepresentation.SMTPServer = &smtp
}
func MutateRealmRepresentation(realm keycloakv1alpha1.Realm, realmRepresentation *gocloak.RealmRepresentation) { func MutateRealmRepresentation(realm keycloakv1alpha1.Realm, realmRepresentation *gocloak.RealmRepresentation) {
realmName := realm.GetRealmName() realmName := realm.GetRealmName()
realmRepresentation.Realm = &realmName realmRepresentation.Realm = &realmName