Add domain settings and smpt
[ZITADOPER-1]
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user