Skip to content

Commit b45d95a

Browse files
authored
feat: operator can now configure smtp setting for created realms (#93)
* feat: operator can now configure smtp setting for created realms * fix: proper smtpConfig management
1 parent 121bb48 commit b45d95a

File tree

5 files changed

+63
-14
lines changed

5 files changed

+63
-14
lines changed

internal/config/config.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,19 @@ type Config struct {
1212
GroupClaim string `mapstructure:"group-claim" default:"groups"`
1313
UserClaim string `mapstructure:"user-claim" default:"email"`
1414
DomainCALookup bool `mapstructure:"domain-ca-lookup" default:"false"`
15+
IDP struct {
16+
// SMTP settings
17+
SMTPServer string `mapstructure:"idp-smtp-server"`
18+
SMTPPort int `mapstructure:"idp-smtp-port"`
19+
FromAddress string `mapstructure:"idp-from-address"`
20+
21+
// SSL settings
22+
SSL bool `mapstructure:"idp-smtp-ssl" default:"false"`
23+
StartTLS bool `mapstructure:"idp-smtp-starttls" default:"false"`
24+
25+
// Auth settings
26+
SMTPUser string `mapstructure:"idp-smtp-user"`
27+
SMTPPasswordSecretName string `mapstructure:"idp-smtp-password-secret-name"`
28+
SMTPPasswordSecretKey string `mapstructure:"idp-smtp-password-secret-key" default:"password"`
29+
} `mapstructure:",squash"`
1530
}

internal/controller/initializer_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func NewLogicalClusterReconciler(log *logger.Logger, restCfg *rest.Config, cl, o
2727
[]lifecyclesubroutine.Subroutine{
2828
subroutine.NewWorkspaceInitializer(cl, orgClient, restCfg, cfg),
2929
subroutine.NewWorkspaceAuthConfigurationSubroutine(orgClient, inClusterClient, cfg),
30-
subroutine.NewRealmSubroutine(inClusterClient, cfg.BaseDomain),
30+
subroutine.NewRealmSubroutine(inClusterClient, &cfg, cfg.BaseDomain),
3131
},
3232
"logicalcluster",
3333
"LogicalClusterReconciler",

internal/subroutine/manifests/organizationIdp/repository.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ spec:
77
interval: 5m
88
url: oci://ghcr.io/platform-mesh/helm-charts/organization-idp
99
ref:
10-
semver: "0.1.1"
10+
semver: "0.2.0"

internal/subroutine/realm.go

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
lifecyclesubroutine "github.com/platform-mesh/golang-commons/controller/lifecycle/subroutine"
1515
"github.com/platform-mesh/golang-commons/errors"
1616
"github.com/platform-mesh/golang-commons/logger"
17+
"github.com/platform-mesh/security-operator/internal/config"
1718
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
1819
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1920
ctrl "sigs.k8s.io/controller-runtime"
@@ -33,12 +34,14 @@ var (
3334
type realmSubroutine struct {
3435
k8s client.Client
3536
baseDomain string
37+
cfg *config.Config
3638
}
3739

38-
func NewRealmSubroutine(k8s client.Client, baseDomain string) *realmSubroutine {
40+
func NewRealmSubroutine(k8s client.Client, cfg *config.Config, baseDomain string) *realmSubroutine {
3941
return &realmSubroutine{
4042
k8s,
4143
baseDomain,
44+
cfg,
4245
}
4346
}
4447

@@ -86,30 +89,60 @@ func (r *realmSubroutine) Process(ctx context.Context, instance lifecycleruntime
8689
return ctrl.Result{}, errors.NewOperatorError(fmt.Errorf("failed to get workspace path"), true, false)
8790
}
8891

89-
patch := map[string]interface{}{
90-
"crossplane": map[string]interface{}{
91-
"realm": map[string]interface{}{
92+
patch := map[string]any{
93+
"crossplane": map[string]any{
94+
"realm": map[string]any{
9295
"name": workspaceName,
9396
"displayName": workspaceName,
9497
},
95-
"client": map[string]interface{}{
98+
"client": map[string]any{
9699
"name": workspaceName,
97100
"displayName": workspaceName,
98101
"validRedirectUris": []string{
99102
fmt.Sprintf("https://%s.%s/callback*", workspaceName, r.baseDomain),
100103
},
101104
},
105+
"organization": map[string]any{
106+
"domain": "example.com", // TODO: change
107+
},
102108
},
103-
"keycloakConfig": map[string]interface{}{
104-
"client": map[string]interface{}{
109+
"keycloakConfig": map[string]any{
110+
"client": map[string]any{
105111
"name": workspaceName,
106-
"targetSecret": map[string]interface{}{
112+
"targetSecret": map[string]any{
107113
"name": fmt.Sprintf("portal-client-secret-%s", workspaceName),
108114
},
109115
},
110116
},
111117
}
112118

119+
if r.cfg.IDP.SMTPServer != "" {
120+
121+
smtpConfig := map[string]any{
122+
"host": r.cfg.IDP.SMTPServer,
123+
"port": fmt.Sprintf("%d", r.cfg.IDP.SMTPPort),
124+
"from": r.cfg.IDP.FromAddress,
125+
"ssl": r.cfg.IDP.SSL,
126+
"starttls": r.cfg.IDP.StartTLS,
127+
}
128+
129+
if r.cfg.IDP.SMTPUser != "" {
130+
smtpConfig["auth"] = map[string]any{
131+
"username": r.cfg.IDP.SMTPUser,
132+
"passwordSecretRef": map[string]any{
133+
"namespace": "platform-mesh-system",
134+
"name": r.cfg.IDP.SMTPPasswordSecretName,
135+
"key": r.cfg.IDP.SMTPPasswordSecretKey,
136+
},
137+
}
138+
}
139+
140+
err := unstructured.SetNestedField(patch, []any{smtpConfig}, "crossplane", "realm", "smtpConfig")
141+
if err != nil {
142+
return ctrl.Result{}, errors.NewOperatorError(fmt.Errorf("failed to set SMTP server config: %w", err), true, true)
143+
}
144+
}
145+
113146
marshalledPatch, err := json.Marshal(patch)
114147
if err != nil {
115148
return ctrl.Result{}, errors.NewOperatorError(fmt.Errorf("failed to marshall patch map: %w", err), true, true)

internal/subroutine/realm_test.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
kcpv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/core/v1alpha1"
99
"github.com/platform-mesh/golang-commons/errors"
1010
"github.com/platform-mesh/golang-commons/logger"
11+
"github.com/platform-mesh/security-operator/internal/config"
1112
"github.com/platform-mesh/security-operator/internal/subroutine/mocks"
1213
"github.com/stretchr/testify/mock"
1314
"github.com/stretchr/testify/require"
@@ -175,7 +176,7 @@ func TestRealmSubroutine_ProcessAndFinalize(t *testing.T) {
175176

176177
repository, helmRelease = trim(repoYAML), trim(helmReleaseYAML)
177178

178-
rs := NewRealmSubroutine(clientMock, baseDomain)
179+
rs := NewRealmSubroutine(clientMock, &config.Config{}, baseDomain)
179180
lc := &kcpv1alpha1.LogicalCluster{}
180181
lc.Annotations = map[string]string{"kcp.io/path": "root:orgs:test"}
181182
res, opErr := rs.Process(context.Background(), lc)
@@ -191,7 +192,7 @@ func TestRealmSubroutine_ProcessAndFinalize(t *testing.T) {
191192
})
192193

193194
repository, helmRelease = trim(repoYAML), trim(helmReleaseYAML)
194-
rs := NewRealmSubroutine(clientMock, baseDomain)
195+
rs := NewRealmSubroutine(clientMock, &config.Config{}, baseDomain)
195196
lc := &kcpv1alpha1.LogicalCluster{}
196197
lc.Annotations = map[string]string{"kcp.io/path": "root:orgs:test"}
197198
res, opErr := rs.Process(context.Background(), lc)
@@ -202,7 +203,7 @@ func TestRealmSubroutine_ProcessAndFinalize(t *testing.T) {
202203
t.Run("missing workspace annotation", func(t *testing.T) {
203204
t.Parallel()
204205
clientMock := newClientMock(t, nil)
205-
rs := NewRealmSubroutine(clientMock, baseDomain)
206+
rs := NewRealmSubroutine(clientMock, &config.Config{}, baseDomain)
206207
lc := &kcpv1alpha1.LogicalCluster{}
207208
res, opErr := rs.Process(context.Background(), lc)
208209
require.NotNil(t, opErr)
@@ -247,7 +248,7 @@ func TestRealmSubroutine_ProcessAndFinalize(t *testing.T) {
247248
t.Run(tc.name, func(t *testing.T) {
248249
t.Parallel()
249250
clientMock := newClientMock(t, tc.setupMocks)
250-
rs := NewRealmSubroutine(clientMock, baseDomain)
251+
rs := NewRealmSubroutine(clientMock, &config.Config{}, baseDomain)
251252
lc := &kcpv1alpha1.LogicalCluster{}
252253
lc.Annotations = map[string]string{"kcp.io/path": "root:orgs:test"}
253254
res, opErr := rs.Finalize(context.Background(), lc)

0 commit comments

Comments
 (0)