Add domain settings and smpt

[ZITADOPER-1]
This commit is contained in:
Haim Kortovich
2024-05-16 15:37:01 -05:00
parent 974a4b4a9e
commit 85a88256b9
6 changed files with 287 additions and 7 deletions

View File

@@ -70,6 +70,22 @@ spec:
type: string
type: object
x-kubernetes-map-type: atomic
domainSettings:
properties:
smtpSenderAddressMatchesInstanceDomain:
default: true
type: boolean
userLoginMustBeDomain:
default: true
type: boolean
validateOrgDomains:
default: true
type: boolean
required:
- smtpSenderAddressMatchesInstanceDomain
- userLoginMustBeDomain
- validateOrgDomains
type: object
externalPort:
default: 443
format: int64
@@ -164,8 +180,55 @@ spec:
type: string
description: ServiceAnnotations to add to the service metadata.
type: object
smtpConfig:
properties:
host:
type: string
password:
properties:
secretRef:
description: SecretKeySelector selects a key of a Secret.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
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
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
x-kubernetes-map-type: atomic
required:
- secretRef
type: object
replyToAddress:
type: string
senderAddress:
type: string
senderName:
type: string
tls:
default: true
type: boolean
user:
type: string
required:
- host
- replyToAddress
- senderAddress
- senderName
- tls
type: object
required:
- crdbClusterRef
- domainSettings
- externalPort
- externalSecure
- firstOrgName
@@ -173,6 +236,7 @@ spec:
- image
- purpose
- resources
- smtpConfig
type: object
status:
description: ZitadelClusterStatus defines the observed state of ZitadelCluster

View File

@@ -30,13 +30,39 @@ type Image struct {
Tag string `json:"tag"`
}
type Password struct {
SecretKeyRef corev1.SecretKeySelector `json:"secretRef"`
}
type SMTPConfig struct {
SenderAddress string `json:"senderAddress"`
SenderName string `json:"senderName"`
// +kubebuilder:default=true
TLS bool `json:"tls"`
Host string `json:"host"`
User *string `json:"user,omitempty"`
Password *Password `json:"password,omitempty"`
ReplyToAddress string `json:"replyToAddress"`
}
type DomainSettings struct {
// +kubebuilder:default=true
UserLoginMustBeDomain bool `json:"userLoginMustBeDomain"`
// +kubebuilder:default=true
ValidateOrgDomains bool `json:"validateOrgDomains"`
// +kubebuilder:default=true
SMTPSenderAddressMatchesInstanceDomain bool `json:"smtpSenderAddressMatchesInstanceDomain"`
}
// ZitadelClusterSpec defines the desired state of ZitadelCluster
type ZitadelClusterSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
// +kubebuilder:default="DEFAULT"
FirstOrgName string `json:"firstOrgName"`
Host string `json:"host"`
FirstOrgName string `json:"firstOrgName"`
DomainSettings DomainSettings `json:"domainSettings"`
SMTPConfig SMTPConfig `json:"smtpConfig"`
Host string `json:"host"`
// +kubebuilder:default=443
ExternalPort int64 `json:"externalPort"`
// +kubebuilder:default=true

View File

@@ -139,6 +139,21 @@ func (in *CrdbClusterRef) DeepCopy() *CrdbClusterRef {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DomainSettings) DeepCopyInto(out *DomainSettings) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DomainSettings.
func (in *DomainSettings) DeepCopy() *DomainSettings {
if in == nil {
return nil
}
out := new(DomainSettings)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Image) DeepCopyInto(out *Image) {
*out = *in
@@ -491,6 +506,22 @@ func (in *OrganizationStatus) DeepCopy() *OrganizationStatus {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Password) DeepCopyInto(out *Password) {
*out = *in
in.SecretKeyRef.DeepCopyInto(&out.SecretKeyRef)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Password.
func (in *Password) DeepCopy() *Password {
if in == nil {
return nil
}
out := new(Password)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Project) DeepCopyInto(out *Project) {
*out = *in
@@ -624,6 +655,31 @@ func (in *Role) DeepCopy() *Role {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SMTPConfig) DeepCopyInto(out *SMTPConfig) {
*out = *in
if in.User != nil {
in, out := &in.User, &out.User
*out = new(string)
**out = **in
}
if in.Password != nil {
in, out := &in.Password, &out.Password
*out = new(Password)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SMTPConfig.
func (in *SMTPConfig) DeepCopy() *SMTPConfig {
if in == nil {
return nil
}
out := new(SMTPConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ZitadelCluster) DeepCopyInto(out *ZitadelCluster) {
*out = *in
@@ -702,6 +758,8 @@ func (in *ZitadelClusterRef) DeepCopy() *ZitadelClusterRef {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ZitadelClusterSpec) DeepCopyInto(out *ZitadelClusterSpec) {
*out = *in
out.DomainSettings = in.DomainSettings
in.SMTPConfig.DeepCopyInto(&out.SMTPConfig)
out.Image = in.Image
in.Resources.DeepCopyInto(&out.Resources)
out.CrdbClusterRef = in.CrdbClusterRef

View File

@@ -71,6 +71,22 @@ spec:
type: string
type: object
x-kubernetes-map-type: atomic
domainSettings:
properties:
smtpSenderAddressMatchesInstanceDomain:
default: true
type: boolean
userLoginMustBeDomain:
default: true
type: boolean
validateOrgDomains:
default: true
type: boolean
required:
- smtpSenderAddressMatchesInstanceDomain
- userLoginMustBeDomain
- validateOrgDomains
type: object
externalPort:
default: 443
format: int64
@@ -165,8 +181,55 @@ spec:
type: string
description: ServiceAnnotations to add to the service metadata.
type: object
smtpConfig:
properties:
host:
type: string
password:
properties:
secretRef:
description: SecretKeySelector selects a key of a Secret.
properties:
key:
description: The key of the secret to select from. Must
be a valid secret key.
type: string
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
optional:
description: Specify whether the Secret or its key must
be defined
type: boolean
required:
- key
type: object
x-kubernetes-map-type: atomic
required:
- secretRef
type: object
replyToAddress:
type: string
senderAddress:
type: string
senderName:
type: string
tls:
default: true
type: boolean
user:
type: string
required:
- host
- replyToAddress
- senderAddress
- senderName
- tls
type: object
required:
- crdbClusterRef
- domainSettings
- externalPort
- externalSecure
- firstOrgName
@@ -174,6 +237,7 @@ spec:
- image
- purpose
- resources
- smtpConfig
type: object
status:
description: ZitadelClusterStatus defines the observed state of ZitadelCluster

View File

@@ -42,6 +42,7 @@ import (
"github.com/hashicorp/go-multierror"
"github.com/zitadel/zitadel-go/v2/pkg/client/middleware"
"github.com/zitadel/zitadel-go/v2/pkg/client/system"
adm "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/admin"
authn "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/authn"
"github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/management"
pb "github.com/zitadel/zitadel-go/v2/pkg/client/zitadel/system"
@@ -152,6 +153,14 @@ func (r *ZitadelClusterReconciler) Reconcile(ctx context.Context, req ctrl.Reque
Name: "DefaultInstance",
Reconcile: r.reconcileDefaultInstance,
},
{
Name: "SMTPConfig",
Reconcile: r.reconcileSMTPConfig,
},
{
Name: "DomainPolicyConfig",
Reconcile: r.reconcileDomainPolicy,
},
{
Name: "InitialAdminSecret",
Reconcile: r.reconcileInitialAdminPassword,
@@ -448,6 +457,48 @@ func (r *ZitadelClusterReconciler) reconcileDefaultInstance(ctx context.Context,
return ctrl.Result{}, nil
}
func (r *ZitadelClusterReconciler) reconcileSMTPConfig(ctx context.Context, zitadel *zitadelv1alpha1.ZitadelCluster) (ctrl.Result, error) {
adminClient, err := zitadelClient.NewAdminClient(ctx, zitadel, *r.RefResolver)
if err != nil {
return ctrl.Result{}, err
}
adminRequest := &adm.AddSMTPConfigRequest{
SenderAddress: zitadel.Spec.SMTPConfig.SenderAddress,
SenderName: zitadel.Spec.SMTPConfig.SenderName,
Tls: zitadel.Spec.SMTPConfig.TLS,
Host: zitadel.Spec.SMTPConfig.Host,
ReplyToAddress: zitadel.Spec.SMTPConfig.ReplyToAddress,
}
if zitadel.Spec.SMTPConfig.User != nil && zitadel.Spec.SMTPConfig.Password != nil {
passwordSecret, err := r.RefResolver.SecretKeyRef(ctx, zitadel.Spec.SMTPConfig.Password.SecretKeyRef, zitadel.Namespace)
if err != nil {
return ctrl.Result{}, err
}
adminRequest.Password = passwordSecret
adminRequest.User = *zitadel.Spec.SMTPConfig.User
}
if _, err = adminClient.AddSMTPConfig(ctx, adminRequest); err != nil {
return ctrl.Result{}, fmt.Errorf("Could not add SMTP config: %v", err)
}
return ctrl.Result{}, nil
}
func (r *ZitadelClusterReconciler) reconcileDomainPolicy(ctx context.Context, zitadel *zitadelv1alpha1.ZitadelCluster) (ctrl.Result, error) {
adminClient, err := zitadelClient.NewAdminClient(ctx, zitadel, *r.RefResolver)
if err != nil {
return ctrl.Result{}, err
}
if _, err = adminClient.UpdateDomainPolicy(ctx, &adm.UpdateDomainPolicyRequest{
UserLoginMustBeDomain: zitadel.Spec.DomainSettings.UserLoginMustBeDomain,
ValidateOrgDomains: zitadel.Spec.DomainSettings.ValidateOrgDomains,
SmtpSenderAddressMatchesInstanceDomain: zitadel.Spec.DomainSettings.SMTPSenderAddressMatchesInstanceDomain,
}); err != nil {
return ctrl.Result{}, fmt.Errorf("Could not update domain policy config: %v", err)
}
return ctrl.Result{}, nil
}
func (r *ZitadelClusterReconciler) reconcileInitialAdminPassword(ctx context.Context, zitadel *zitadelv1alpha1.ZitadelCluster) (ctrl.Result, error) {
secretName := admin.AdminPasswordSecretName(zitadel)
key := types.NamespacedName{
@@ -540,6 +591,7 @@ func (r *ZitadelClusterReconciler) reconcileInitialHumanUser(ctx context.Context
zitadel.Status.InitialAdminId = userid
return ctrl.Result{}, r.Status().Patch(ctx, zitadel, patch)
}
func GetIssuer(zitadel *zitadelv1alpha1.ZitadelCluster) string {
scheme := "http"
if zitadel.Spec.ExternalSecure {

View File

@@ -1,22 +1,24 @@
package zitadel
import (
zitadelv1alpha1 "bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/api/v1alpha1"
"bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/deployment"
"bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/machinekey"
"context"
"encoding/json"
"fmt"
"github.com/gorilla/schema"
"google.golang.org/grpc"
"net/http"
"reflect"
"strings"
"time"
zitadelv1alpha1 "bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/api/v1alpha1"
"bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/deployment"
"bitbucket.org/topmanage-software-engineering/zitadel-k8s-operator/src/pkg/machinekey"
"github.com/gorilla/schema"
"google.golang.org/grpc"
"github.com/zitadel/oidc/pkg/client"
httphelper "github.com/zitadel/oidc/pkg/http"
"github.com/zitadel/oidc/pkg/oidc"
"github.com/zitadel/zitadel-go/v2/pkg/client/admin"
"github.com/zitadel/zitadel-go/v2/pkg/client/management"
"github.com/zitadel/zitadel-go/v2/pkg/client/zitadel"
"golang.org/x/oauth2"
@@ -46,6 +48,20 @@ func NewClient(ctx context.Context, zitadelCluster *zitadelv1alpha1.ZitadelClust
return api, nil
}
func NewAdminClient(ctx context.Context, zitadelCluster *zitadelv1alpha1.ZitadelCluster, refresolver zitadelv1alpha1.RefResolver) (*admin.Client, error) {
machineKeyData, err := refresolver.SecretKeyRef(ctx, corev1.SecretKeySelector{LocalObjectReference: corev1.LocalObjectReference{Name: machinekey.MachineKeySecretName(zitadelCluster)}, Key: machinekey.Key}, zitadelCluster.Namespace)
if err != nil {
return nil, err
}
api, err := admin.NewClient(GetIssuer(zitadelCluster), GetAPI(zitadelCluster), []string{oidc.ScopeOpenID, zitadel.ScopeZitadelAPI()}, zitadel.WithInsecure(), zitadel.WithJWTProfileTokenSource(Discover([]byte(machineKeyData), GetAPIUrl(zitadelCluster), GetAuthority(zitadelCluster), GetAPI(zitadelCluster))),
zitadel.WithDialOptions(grpc.WithAuthority(GetAuthority(zitadelCluster))),
)
if err != nil {
return nil, fmt.Errorf("ERROR CREATING CLIENT: %v", err)
}
return api, nil
}
func GetAuthority(zitadel *zitadelv1alpha1.ZitadelCluster) string {
return fmt.Sprintf("%s:%d", zitadel.Spec.Host, zitadel.Spec.ExternalPort)
}