diff --git a/cmd/admin/start/start.go b/cmd/admin/start/start.go
index 7623ec6d4a..fb06b80234 100644
--- a/cmd/admin/start/start.go
+++ b/cmd/admin/start/start.go
@@ -142,6 +142,9 @@ func startZitadel(config *startConfig) error {
if err != nil {
return fmt.Errorf("cannot start eventstore for queries: %w", err)
}
+ smtpPasswordCrypto, err := crypto.NewAESCrypto(config.SystemDefaults.SMTPPasswordVerificationKey)
+ logging.Log("MAIN-en9ew").OnError(err).Fatal("cannot create smtp crypto")
+
queries, err := query.StartQueries(ctx, eventstoreClient, dbClient, config.Projections.Config, config.SystemDefaults, config.Projections.KeyConfig, keyChan, config.InternalAuthZ.RolePermissionMappings)
if err != nil {
return fmt.Errorf("cannot start queries: %w", err)
@@ -156,12 +159,12 @@ func startZitadel(config *startConfig) error {
Origin: http_util.BuildHTTP(config.ExternalDomain, config.ExternalPort, config.ExternalSecure),
DisplayName: "ZITADEL",
}
- commands, err := command.StartCommands(eventstoreClient, config.SystemDefaults, config.InternalAuthZ, storage, authZRepo, config.OIDC.KeyConfig, webAuthNConfig)
+ commands, err := command.StartCommands(eventstoreClient, config.SystemDefaults, config.InternalAuthZ, storage, authZRepo, config.OIDC.KeyConfig, smtpPasswordCrypto, webAuthNConfig)
if err != nil {
return fmt.Errorf("cannot start commands: %w", err)
}
- notification.Start(config.Notification, config.SystemDefaults, commands, queries, dbClient, assets.HandlerPrefix)
+ notification.Start(config.Notification, config.SystemDefaults, commands, queries, dbClient, assets.HandlerPrefix, smtpPasswordCrypto)
router := mux.NewRouter()
err = startAPIs(ctx, router, commands, queries, eventstoreClient, dbClient, keyChan, config, storage, authZRepo)
@@ -181,9 +184,12 @@ func startAPIs(ctx context.Context, router *mux.Router, commands *command.Comman
}
verifier := internal_authz.Start(repo)
- apis := api.New(config.Port, router, &repo, config.InternalAuthZ, config.SystemDefaults, config.ExternalSecure)
-
- authRepo, err := auth_es.Start(config.Auth, config.SystemDefaults, commands, queries, dbClient, config.OIDC.KeyConfig, assets.HandlerPrefix)
+ apis := api.New(config.Port, router, &repo, config.InternalAuthZ, config.ExternalSecure)
+ userEncryptionAlgorithm, err := crypto.NewAESCrypto(config.SystemDefaults.UserVerificationKey)
+ if err != nil {
+ return nil
+ }
+ authRepo, err := auth_es.Start(config.Auth, config.SystemDefaults, commands, queries, dbClient, config.OIDC.KeyConfig, assets.HandlerPrefix, userEncryptionAlgorithm)
if err != nil {
return fmt.Errorf("error starting auth repo: %w", err)
}
@@ -191,13 +197,13 @@ func startAPIs(ctx context.Context, router *mux.Router, commands *command.Comman
if err != nil {
return fmt.Errorf("error starting admin repo: %w", err)
}
- if err := apis.RegisterServer(ctx, admin.CreateServer(commands, queries, adminRepo, config.SystemDefaults.Domain, assets.HandlerPrefix)); err != nil {
+ if err := apis.RegisterServer(ctx, admin.CreateServer(commands, queries, adminRepo, config.SystemDefaults.Domain, assets.HandlerPrefix, userEncryptionAlgorithm)); err != nil {
return err
}
- if err := apis.RegisterServer(ctx, management.CreateServer(commands, queries, config.SystemDefaults, assets.HandlerPrefix)); err != nil {
+ if err := apis.RegisterServer(ctx, management.CreateServer(commands, queries, config.SystemDefaults, assets.HandlerPrefix, userEncryptionAlgorithm)); err != nil {
return err
}
- if err := apis.RegisterServer(ctx, auth.CreateServer(commands, queries, authRepo, config.SystemDefaults, assets.HandlerPrefix)); err != nil {
+ if err := apis.RegisterServer(ctx, auth.CreateServer(commands, queries, authRepo, config.SystemDefaults, assets.HandlerPrefix, userEncryptionAlgorithm)); err != nil {
return err
}
@@ -231,7 +237,7 @@ func startAPIs(ctx context.Context, router *mux.Router, commands *command.Comman
}
apis.RegisterHandler(console.HandlerPrefix, c)
- l, err := login.CreateLogin(config.Login, commands, queries, authRepo, store, config.SystemDefaults, console.HandlerPrefix, config.ExternalDomain, oidc.AuthCallback, config.ExternalSecure, userAgentInterceptor)
+ l, err := login.CreateLogin(config.Login, commands, queries, authRepo, store, config.SystemDefaults, console.HandlerPrefix, config.ExternalDomain, oidc.AuthCallback, config.ExternalSecure, userAgentInterceptor, userEncryptionAlgorithm)
if err != nil {
return fmt.Errorf("unable to start login: %w", err)
}
diff --git a/cmd/defaults.yaml b/cmd/defaults.yaml
index 322bf220b4..be85496a74 100644
--- a/cmd/defaults.yaml
+++ b/cmd/defaults.yaml
@@ -141,6 +141,8 @@ SystemDefaults:
EncryptionKeyID: $ZITADEL_USER_VERIFICATION_KEY
IDPConfigVerificationKey:
EncryptionKeyID: $ZITADEL_IDP_CONFIG_VERIFICATION_KEY
+ SMTPPasswordVerificationKey:
+ EncryptionKeyID: $ZITADEL_SMTP_PASSWORD_VERIFICATION_KEY
SecretGenerators:
PasswordSaltCost: 14
ClientSecretGenerator:
@@ -197,7 +199,6 @@ SystemDefaults:
MFAInitSkip: 720h #30d
SecondFactorCheck: 18h
MultiFactorCheck: 12h
- IamID: 'IAM'
DomainVerification:
VerificationKey:
EncryptionKeyID: $ZITADEL_DOMAIN_VERIFICATION_KEY
@@ -240,42 +241,6 @@ SystemDefaults:
Url: $CHAT_URL
# Compact: $CHAT_COMPACT
SplitCount: 4000
- TemplateData:
- InitCode:
- Title: 'InitCode.Title'
- PreHeader: 'InitCode.PreHeader'
- Subject: 'InitCode.Subject'
- Greeting: 'InitCode.Greeting'
- Text: 'InitCode.Text'
- ButtonText: 'InitCode.ButtonText'
- PasswordReset:
- Title: 'PasswordReset.Title'
- PreHeader: 'PasswordReset.PreHeader'
- Subject: 'PasswordReset.Subject'
- Greeting: 'PasswordReset.Greeting'
- Text: 'PasswordReset.Text'
- ButtonText: 'PasswordReset.ButtonText'
- VerifyEmail:
- Title: 'VerifyEmail.Title'
- PreHeader: 'VerifyEmail.PreHeader'
- Subject: 'VerifyEmail.Subject'
- Greeting: 'VerifyEmail.Greeting'
- Text: 'VerifyEmail.Text'
- ButtonText: 'VerifyEmail.ButtonText'
- VerifyPhone:
- Title: 'VerifyPhone.Title'
- PreHeader: 'VerifyPhone.PreHeader'
- Subject: 'VerifyPhone.Subject'
- Greeting: 'VerifyPhone.Greeting'
- Text: 'VerifyPhone.Text'
- ButtonText: 'VerifyPhone.ButtonText'
- DomainClaimed:
- Title: 'DomainClaimed.Title'
- PreHeader: 'DomainClaimed.PreHeader'
- Subject: 'DomainClaimed.Subject'
- Greeting: 'DomainClaimed.Greeting'
- Text: 'DomainClaimed.Text'
- ButtonText: 'DomainClaimed.ButtonText'
KeyConfig:
Size: 2048
PrivateKeyLifetime: 6h
diff --git a/docs/docs/apis/proto/admin.md b/docs/docs/apis/proto/admin.md
index 2c95815ed1..1b1c1e12cb 100644
--- a/docs/docs/apis/proto/admin.md
+++ b/docs/docs/apis/proto/admin.md
@@ -32,6 +32,102 @@ Returns the default languages
GET: /languages
+### SetDefaultLanguage
+
+> **rpc** SetDefaultLanguage([SetDefaultLanguageRequest](#setdefaultlanguagerequest))
+[SetDefaultLanguageResponse](#setdefaultlanguageresponse)
+
+Set the default language
+
+
+
+ PUT: /languages/default/{language}
+
+
+### GetDefaultLanguage
+
+> **rpc** GetDefaultLanguage([GetDefaultLanguageRequest](#getdefaultlanguagerequest))
+[GetDefaultLanguageResponse](#getdefaultlanguageresponse)
+
+Set the default language
+
+
+
+ GET: /languages/default
+
+
+### ListSecretGenerators
+
+> **rpc** ListSecretGenerators([ListSecretGeneratorsRequest](#listsecretgeneratorsrequest))
+[ListSecretGeneratorsResponse](#listsecretgeneratorsresponse)
+
+Set the default language
+
+
+
+ POST: /secretgenerators/_search
+
+
+### GetSecretGenerator
+
+> **rpc** GetSecretGenerator([GetSecretGeneratorRequest](#getsecretgeneratorrequest))
+[GetSecretGeneratorResponse](#getsecretgeneratorresponse)
+
+Get Secret Generator by type (e.g PasswordResetCode)
+
+
+
+ GET: /secretgenerators/{generator_type}
+
+
+### UpdateSecretGenerator
+
+> **rpc** UpdateSecretGenerator([UpdateSecretGeneratorRequest](#updatesecretgeneratorrequest))
+[UpdateSecretGeneratorResponse](#updatesecretgeneratorresponse)
+
+Update secret generator configuration
+
+
+
+ PUT: /secretgenerators/{generator_type}
+
+
+### GetSMTPConfig
+
+> **rpc** GetSMTPConfig([GetSMTPConfigRequest](#getsmtpconfigrequest))
+[GetSMTPConfigResponse](#getsmtpconfigresponse)
+
+Get system smtp configuration
+
+
+
+ GET: /smtp
+
+
+### UpdateSMTPConfig
+
+> **rpc** UpdateSMTPConfig([UpdateSMTPConfigRequest](#updatesmtpconfigrequest))
+[UpdateSMTPConfigResponse](#updatesmtpconfigresponse)
+
+Update system smtp configuration
+
+
+
+ PUT: /smtp
+
+
+### UpdateSMTPConfigPassword
+
+> **rpc** UpdateSMTPConfigPassword([UpdateSMTPConfigPasswordRequest](#updatesmtpconfigpasswordrequest))
+[UpdateSMTPConfigPasswordResponse](#updatesmtpconfigpasswordresponse)
+
+Update system smtp configuration password for host
+
+
+
+ PUT: /smtp/password
+
+
### GetOrgByID
> **rpc** GetOrgByID([GetOrgByIDRequest](#getorgbyidrequest))
@@ -1665,6 +1761,23 @@ This is an empty response
+### GetDefaultLanguageRequest
+This is an empty request
+
+
+
+
+### GetDefaultLanguageResponse
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| language | string | - | |
+
+
+
+
### GetDefaultLoginTextsRequest
@@ -1977,6 +2090,45 @@ This is an empty request
+### GetSMTPConfigRequest
+This is an empty request
+
+
+
+
+### GetSMTPConfigResponse
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| smtp_config | zitadel.settings.v1.SMTPConfig | - | |
+
+
+
+
+### GetSecretGeneratorRequest
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| generator_type | zitadel.settings.v1.SecretGeneratorType | - | enum.defined_only: true
enum.not_in: [0]
|
+
+
+
+
+### GetSecretGeneratorResponse
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| secret_generator | zitadel.settings.v1.SecretGenerator | - | |
+
+
+
+
### GetSupportedLanguagesRequest
This is an empty request
@@ -2211,6 +2363,30 @@ This is an empty request
+### ListSecretGeneratorsRequest
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| query | zitadel.v1.ListQuery | list limitations and ordering | |
+| queries | repeated zitadel.settings.v1.SecretGeneratorQuery | criterias the client is looking for | |
+
+
+
+
+### ListSecretGeneratorsResponse
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| details | zitadel.v1.ListDetails | - | |
+| result | repeated zitadel.settings.v1.SecretGenerator | - | |
+
+
+
+
### ListViewsRequest
This is an empty request
@@ -2820,6 +2996,28 @@ This is an empty request
+### SetDefaultLanguageRequest
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| language | string | - | string.min_len: 1
string.max_len: 10
|
+
+
+
+
+### SetDefaultLanguageResponse
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| details | zitadel.v1.ObjectDetails | - | |
+
+
+
+
### SetDefaultPasswordResetMessageTextRequest
@@ -3374,6 +3572,82 @@ This is an empty request
+### UpdateSMTPConfigPasswordRequest
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| password | string | - | |
+
+
+
+
+### UpdateSMTPConfigPasswordResponse
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| details | zitadel.v1.ObjectDetails | - | |
+
+
+
+
+### UpdateSMTPConfigRequest
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| sender_address | string | - | string.min_len: 1
string.max_len: 200
|
+| sender_name | string | - | string.min_len: 1
string.max_len: 200
|
+| tls | bool | - | |
+| host | string | - | string.min_len: 1
string.max_len: 500
|
+| user | string | - | |
+
+
+
+
+### UpdateSMTPConfigResponse
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| details | zitadel.v1.ObjectDetails | - | |
+
+
+
+
+### UpdateSecretGeneratorRequest
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| generator_type | zitadel.settings.v1.SecretGeneratorType | - | enum.defined_only: true
enum.not_in: [0]
|
+| length | uint32 | - | |
+| expiry | google.protobuf.Duration | - | |
+| include_lower_letters | bool | - | |
+| include_upper_letters | bool | - | |
+| include_digits | bool | - | |
+| include_symbols | bool | - | |
+
+
+
+
+### UpdateSecretGeneratorResponse
+
+
+
+| Field | Type | Description | Validation |
+| ----- | ---- | ----------- | ----------- |
+| details | zitadel.v1.ObjectDetails | - | |
+
+
+
+
### View
diff --git a/go.mod b/go.mod
index 9615d7a70f..40b4e7d838 100644
--- a/go.mod
+++ b/go.mod
@@ -44,7 +44,6 @@ require (
github.com/pquerna/otp v1.3.0
github.com/rakyll/statik v0.1.7
github.com/rs/cors v1.8.0
- github.com/sirupsen/logrus v1.8.1
github.com/sony/sonyflake v1.0.0
github.com/spf13/cobra v1.3.0
github.com/spf13/viper v1.10.1
@@ -140,6 +139,7 @@ require (
github.com/prometheus/procfs v0.6.0 // indirect
github.com/rs/xid v1.2.1 // indirect
github.com/satori/go.uuid v1.2.0 // indirect
+ github.com/sirupsen/logrus v1.8.1 // indirect
github.com/spf13/afero v1.8.1 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
diff --git a/go.sum b/go.sum
index a3b0b63b54..13c1281d47 100644
--- a/go.sum
+++ b/go.sum
@@ -124,8 +124,6 @@ github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBW
github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/caos/logging v0.0.2/go.mod h1:9LKiDE2ChuGv6CHYif/kiugrfEXu9AwDiFWSreX7Wp0=
-github.com/caos/logging v0.3.0 h1:PMKd47Aqr98FjAPcAE8v3SCe8eqTEclytruND/zc7QU=
-github.com/caos/logging v0.3.0/go.mod h1:B8QNS0WDmR2Keac52Fw+XN4ZJkzLDGrcRIPB2Ux4uRo=
github.com/caos/logging v0.3.1 h1:892AMeHs09D0e3ZcGB+QDRsZ5+2xtPAsAhOy8eKfztc=
github.com/caos/logging v0.3.1/go.mod h1:B8QNS0WDmR2Keac52Fw+XN4ZJkzLDGrcRIPB2Ux4uRo=
github.com/caos/oidc v1.0.1 h1:8UHAPynCObwaqortppDtJFktjqLDLYSLidkNy0Num4o=
diff --git a/internal/api/api.go b/internal/api/api.go
index 98e2e6c1a4..ed5978488f 100644
--- a/internal/api/api.go
+++ b/internal/api/api.go
@@ -15,7 +15,6 @@ import (
"github.com/caos/zitadel/internal/api/grpc/server"
http_util "github.com/caos/zitadel/internal/api/http"
"github.com/caos/zitadel/internal/authz/repository"
- "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/query"
@@ -44,7 +43,6 @@ func New(
*query.Queries
},
authZ internal_authz.Config,
- sd systemdefaults.SystemDefaults,
externalSecure bool,
) *API {
verifier := internal_authz.Start(repo)
@@ -55,7 +53,7 @@ func New(
router: router,
externalSecure: externalSecure,
}
- api.grpcServer = server.CreateServer(api.verifier, authZ, sd.DefaultLanguage)
+ api.grpcServer = server.CreateServer(api.verifier, authZ, repo.Queries)
api.routeGRPC()
api.RegisterHandler("/debug", api.healthHandler())
diff --git a/internal/api/grpc/admin/iam_settings.go b/internal/api/grpc/admin/iam_settings.go
new file mode 100644
index 0000000000..c64d38839b
--- /dev/null
+++ b/internal/api/grpc/admin/iam_settings.go
@@ -0,0 +1,82 @@
+package admin
+
+import (
+ "context"
+
+ "github.com/caos/zitadel/internal/api/grpc/object"
+ "github.com/caos/zitadel/internal/domain"
+ admin_pb "github.com/caos/zitadel/pkg/grpc/admin"
+)
+
+func (s *Server) ListSecretGenerators(ctx context.Context, req *admin_pb.ListSecretGeneratorsRequest) (*admin_pb.ListSecretGeneratorsResponse, error) {
+ queries, err := listSecretGeneratorToModel(req)
+ if err != nil {
+ return nil, err
+ }
+ result, err := s.query.SearchSecretGenerators(ctx, queries)
+ if err != nil {
+ return nil, err
+ }
+ return &admin_pb.ListSecretGeneratorsResponse{
+ Details: object.ToListDetails(result.Count, result.Sequence, result.Timestamp),
+ }, nil
+}
+
+func (s *Server) GetSecretGenerator(ctx context.Context, req *admin_pb.GetSecretGeneratorRequest) (*admin_pb.GetSecretGeneratorResponse, error) {
+ generator, err := s.query.SecretGeneratorByType(ctx, SecretGeneratorTypeToDomain(req.GeneratorType))
+ if err != nil {
+ return nil, err
+ }
+ return &admin_pb.GetSecretGeneratorResponse{
+ SecretGenerator: SecretGeneratorToPb(generator),
+ }, nil
+}
+
+func (s *Server) UpdateSecretGenerator(ctx context.Context, req *admin_pb.UpdateSecretGeneratorRequest) (*admin_pb.UpdateSecretGeneratorResponse, error) {
+ details, err := s.command.ChangeSecretGeneratorConfig(ctx, SecretGeneratorTypeToDomain(req.GeneratorType), UpdateSecretGeneratorToConfig(req))
+ if err != nil {
+ return nil, err
+ }
+ return &admin_pb.UpdateSecretGeneratorResponse{
+ Details: object.ChangeToDetailsPb(
+ details.Sequence,
+ details.EventDate,
+ details.ResourceOwner),
+ }, nil
+}
+
+func (s *Server) GetSMTPConfig(ctx context.Context, req *admin_pb.GetSMTPConfigRequest) (*admin_pb.GetSMTPConfigResponse, error) {
+ smtp, err := s.query.SMTPConfigByAggregateID(ctx, domain.IAMID)
+ if err != nil {
+ return nil, err
+ }
+ return &admin_pb.GetSMTPConfigResponse{
+ SmtpConfig: SMTPConfigToPb(smtp),
+ }, nil
+}
+
+func (s *Server) UpdateSMTPConfig(ctx context.Context, req *admin_pb.UpdateSMTPConfigRequest) (*admin_pb.UpdateSMTPConfigResponse, error) {
+ details, err := s.command.ChangeSMTPConfig(ctx, UpdateSMTPToConfig(req))
+ if err != nil {
+ return nil, err
+ }
+ return &admin_pb.UpdateSMTPConfigResponse{
+ Details: object.ChangeToDetailsPb(
+ details.Sequence,
+ details.EventDate,
+ details.ResourceOwner),
+ }, nil
+}
+
+func (s *Server) UpdateSMTPConfigPassword(ctx context.Context, req *admin_pb.UpdateSMTPConfigPasswordRequest) (*admin_pb.UpdateSMTPConfigPasswordResponse, error) {
+ details, err := s.command.ChangeSMTPConfigPassword(ctx, req.Password)
+ if err != nil {
+ return nil, err
+ }
+ return &admin_pb.UpdateSMTPConfigPasswordResponse{
+ Details: object.ChangeToDetailsPb(
+ details.Sequence,
+ details.EventDate,
+ details.ResourceOwner),
+ }, nil
+}
diff --git a/internal/api/grpc/admin/iam_settings_converter.go b/internal/api/grpc/admin/iam_settings_converter.go
new file mode 100644
index 0000000000..69f2581b84
--- /dev/null
+++ b/internal/api/grpc/admin/iam_settings_converter.go
@@ -0,0 +1,138 @@
+package admin
+
+import (
+ "github.com/caos/zitadel/internal/domain"
+ "google.golang.org/protobuf/types/known/durationpb"
+
+ "github.com/caos/zitadel/internal/api/grpc/object"
+ obj_grpc "github.com/caos/zitadel/internal/api/grpc/object"
+ "github.com/caos/zitadel/internal/crypto"
+ "github.com/caos/zitadel/internal/errors"
+ "github.com/caos/zitadel/internal/notification/channels/smtp"
+ "github.com/caos/zitadel/internal/query"
+ admin_pb "github.com/caos/zitadel/pkg/grpc/admin"
+ settings_pb "github.com/caos/zitadel/pkg/grpc/settings"
+)
+
+func listSecretGeneratorToModel(req *admin_pb.ListSecretGeneratorsRequest) (*query.SecretGeneratorSearchQueries, error) {
+ offset, limit, asc := object.ListQueryToModel(req.Query)
+ queries, err := SecretGeneratorQueriesToModel(req.Queries)
+ if err != nil {
+ return nil, err
+ }
+ return &query.SecretGeneratorSearchQueries{
+ SearchRequest: query.SearchRequest{
+ Offset: offset,
+ Limit: limit,
+ Asc: asc,
+ },
+ Queries: queries,
+ }, nil
+}
+
+func SecretGeneratorQueriesToModel(queries []*settings_pb.SecretGeneratorQuery) (_ []query.SearchQuery, err error) {
+ q := make([]query.SearchQuery, len(queries))
+ for i, query := range queries {
+ q[i], err = SecretGeneratorQueryToModel(query)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return q, nil
+}
+
+func SecretGeneratorQueryToModel(apiQuery *settings_pb.SecretGeneratorQuery) (query.SearchQuery, error) {
+ switch q := apiQuery.Query.(type) {
+ case *settings_pb.SecretGeneratorQuery_TypeQuery:
+ domainType := SecretGeneratorTypeToDomain(q.TypeQuery.GeneratorType)
+ return query.NewSecretGeneratorTypeSearchQuery(int32(domainType))
+ default:
+ return nil, errors.ThrowInvalidArgument(nil, "ORG-fm9es", "List.Query.Invalid")
+ }
+}
+
+func UpdateSecretGeneratorToConfig(req *admin_pb.UpdateSecretGeneratorRequest) *crypto.GeneratorConfig {
+ return &crypto.GeneratorConfig{
+ Length: uint(req.Length),
+ Expiry: req.Expiry.AsDuration(),
+ IncludeUpperLetters: req.IncludeUpperLetters,
+ IncludeLowerLetters: req.IncludeLowerLetters,
+ IncludeDigits: req.IncludeDigits,
+ IncludeSymbols: req.IncludeSymbols,
+ }
+}
+
+func SecretGeneratorToPb(generator *query.SecretGenerator) *settings_pb.SecretGenerator {
+ mapped := &settings_pb.SecretGenerator{
+ Length: uint32(generator.Length),
+ Expiry: durationpb.New(generator.Expiry),
+ IncludeUpperLetters: generator.IncludeUpperLetters,
+ IncludeLowerLetters: generator.IncludeLowerLetters,
+ IncludeDigits: generator.IncludeDigits,
+ IncludeSymbols: generator.IncludeSymbols,
+ Details: obj_grpc.ToViewDetailsPb(generator.Sequence, generator.CreationDate, generator.ChangeDate, generator.AggregateID),
+ }
+ return mapped
+}
+
+func SecretGeneratorTypeToPb(generatorType domain.SecretGeneratorType) settings_pb.SecretGeneratorType {
+ switch generatorType {
+ case domain.SecretGeneratorTypeInitCode:
+ return settings_pb.SecretGeneratorType_SECRET_GENERATOR_TYPE_INIT_CODE
+ case domain.SecretGeneratorTypeVerifyEmailCode:
+ return settings_pb.SecretGeneratorType_SECRET_GENERATOR_TYPE_VERIFY_EMAIL_CODE
+ case domain.SecretGeneratorTypeVerifyPhoneCode:
+ return settings_pb.SecretGeneratorType_SECRET_GENERATOR_TYPE_VERIFY_PHONE_CODE
+ case domain.SecretGeneratorTypePasswordResetCode:
+ return settings_pb.SecretGeneratorType_SECRET_GENERATOR_TYPE_PASSWORD_RESET_CODE
+ case domain.SecretGeneratorTypePasswordlessInitCode:
+ return settings_pb.SecretGeneratorType_SECRET_GENERATOR_TYPE_PASSWORDLESS_INIT_CODE
+ case domain.SecretGeneratorTypeAppSecret:
+ return settings_pb.SecretGeneratorType_SECRET_GENERATOR_TYPE_APP_SECRET
+ default:
+ return settings_pb.SecretGeneratorType_SECRET_GENERATOR_TYPE_UNSPECIFIED
+ }
+}
+
+func SecretGeneratorTypeToDomain(generatorType settings_pb.SecretGeneratorType) domain.SecretGeneratorType {
+ switch generatorType {
+ case settings_pb.SecretGeneratorType_SECRET_GENERATOR_TYPE_INIT_CODE:
+ return domain.SecretGeneratorTypeInitCode
+ case settings_pb.SecretGeneratorType_SECRET_GENERATOR_TYPE_VERIFY_EMAIL_CODE:
+ return domain.SecretGeneratorTypeVerifyEmailCode
+ case settings_pb.SecretGeneratorType_SECRET_GENERATOR_TYPE_VERIFY_PHONE_CODE:
+ return domain.SecretGeneratorTypeVerifyPhoneCode
+ case settings_pb.SecretGeneratorType_SECRET_GENERATOR_TYPE_PASSWORD_RESET_CODE:
+ return domain.SecretGeneratorTypePasswordResetCode
+ case settings_pb.SecretGeneratorType_SECRET_GENERATOR_TYPE_PASSWORDLESS_INIT_CODE:
+ return domain.SecretGeneratorTypePasswordlessInitCode
+ case settings_pb.SecretGeneratorType_SECRET_GENERATOR_TYPE_APP_SECRET:
+ return domain.SecretGeneratorTypeAppSecret
+ default:
+ return domain.SecretGeneratorTypeUnspecified
+ }
+}
+
+func UpdateSMTPToConfig(req *admin_pb.UpdateSMTPConfigRequest) *smtp.EmailConfig {
+ return &smtp.EmailConfig{
+ Tls: req.Tls,
+ From: req.SenderAddress,
+ FromName: req.SenderName,
+ SMTP: smtp.SMTP{
+ Host: req.Host,
+ User: req.User,
+ },
+ }
+}
+
+func SMTPConfigToPb(smtp *query.SMTPConfig) *settings_pb.SMTPConfig {
+ mapped := &settings_pb.SMTPConfig{
+ Tls: smtp.TLS,
+ SenderAddress: smtp.SenderAddress,
+ SenderName: smtp.SenderName,
+ Host: smtp.Host,
+ User: smtp.User,
+ Details: obj_grpc.ToViewDetailsPb(smtp.Sequence, smtp.CreationDate, smtp.ChangeDate, smtp.AggregateID),
+ }
+ return mapped
+}
diff --git a/internal/api/grpc/admin/language.go b/internal/api/grpc/admin/language.go
index 67fa950d79..fff5b43381 100644
--- a/internal/api/grpc/admin/language.go
+++ b/internal/api/grpc/admin/language.go
@@ -3,8 +3,12 @@ package admin
import (
"context"
+ "github.com/caos/zitadel/internal/api/grpc/object"
"github.com/caos/zitadel/internal/api/grpc/text"
+ "github.com/caos/zitadel/internal/domain"
+ caos_errors "github.com/caos/zitadel/internal/errors"
admin_pb "github.com/caos/zitadel/pkg/grpc/admin"
+ "golang.org/x/text/language"
)
func (s *Server) GetSupportedLanguages(ctx context.Context, req *admin_pb.GetSupportedLanguagesRequest) (*admin_pb.GetSupportedLanguagesResponse, error) {
@@ -14,3 +18,25 @@ func (s *Server) GetSupportedLanguages(ctx context.Context, req *admin_pb.GetSup
}
return &admin_pb.GetSupportedLanguagesResponse{Languages: text.LanguageTagsToStrings(langs)}, nil
}
+
+func (s *Server) SetDefaultLanguage(ctx context.Context, req *admin_pb.SetDefaultLanguageRequest) (*admin_pb.SetDefaultLanguageResponse, error) {
+ lang, err := language.Parse(req.Language)
+ if err != nil {
+ return nil, caos_errors.ThrowInvalidArgument(err, "API-39nnf", "Errors.Language.Parse")
+ }
+ details, err := s.command.SetDefaultLanguage(ctx, lang)
+ if err != nil {
+ return nil, err
+ }
+ return &admin_pb.SetDefaultLanguageResponse{
+ Details: object.DomainToChangeDetailsPb(details),
+ }, nil
+}
+
+func (s *Server) GetDefaultLanguage(ctx context.Context, req *admin_pb.GetDefaultLanguageRequest) (*admin_pb.GetDefaultLanguageResponse, error) {
+ iam, err := s.query.IAMByID(ctx, domain.IAMID)
+ if err != nil {
+ return nil, err
+ }
+ return &admin_pb.GetDefaultLanguageResponse{Language: iam.DefaultLanguage.String()}, nil
+}
diff --git a/internal/api/grpc/admin/org.go b/internal/api/grpc/admin/org.go
index 9a9daa889c..a660d2aa58 100644
--- a/internal/api/grpc/admin/org.go
+++ b/internal/api/grpc/admin/org.go
@@ -53,7 +53,15 @@ func (s *Server) SetUpOrg(ctx context.Context, req *admin_pb.SetUpOrgRequest) (*
human := setUpOrgHumanToDomain(req.User.(*admin_pb.SetUpOrgRequest_Human_).Human) //TODO: handle machine
org := setUpOrgOrgToDomain(req.Org)
- objectDetails, err := s.command.SetUpOrg(ctx, org, human, userIDs, false)
+ initCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeInitCode, s.UserCodeAlg)
+ if err != nil {
+ return nil, err
+ }
+ phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.UserCodeAlg)
+ if err != nil {
+ return nil, err
+ }
+ objectDetails, err := s.command.SetUpOrg(ctx, org, human, initCodeGenerator, phoneCodeGenerator, userIDs, false)
if err != nil {
return nil, err
}
diff --git a/internal/api/grpc/admin/server.go b/internal/api/grpc/admin/server.go
index 18818f1401..dfcdc23e6d 100644
--- a/internal/api/grpc/admin/server.go
+++ b/internal/api/grpc/admin/server.go
@@ -1,6 +1,7 @@
package admin
import (
+ "github.com/caos/zitadel/internal/crypto"
"google.golang.org/grpc"
"github.com/caos/zitadel/internal/admin/repository"
@@ -25,19 +26,22 @@ type Server struct {
administrator repository.AdministratorRepository
iamDomain string
assetsAPIDomain string
+
+ UserCodeAlg crypto.EncryptionAlgorithm
}
type Config struct {
Repository eventsourcing.Config
}
-func CreateServer(command *command.Commands, query *query.Queries, repo repository.Repository, iamDomain, assetsAPIDomain string) *Server {
+func CreateServer(command *command.Commands, query *query.Queries, repo repository.Repository, iamDomain, assetsAPIDomain string, userCrypto *crypto.AESCrypto) *Server {
return &Server{
command: command,
query: query,
administrator: repo,
iamDomain: iamDomain,
assetsAPIDomain: assetsAPIDomain,
+ UserCodeAlg: userCrypto,
}
}
diff --git a/internal/api/grpc/auth/email.go b/internal/api/grpc/auth/email.go
index 6636c64850..465f14ac29 100644
--- a/internal/api/grpc/auth/email.go
+++ b/internal/api/grpc/auth/email.go
@@ -6,6 +6,7 @@ import (
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/api/grpc/object"
"github.com/caos/zitadel/internal/api/grpc/user"
+ "github.com/caos/zitadel/internal/domain"
auth_pb "github.com/caos/zitadel/pkg/grpc/auth"
)
@@ -26,7 +27,11 @@ func (s *Server) GetMyEmail(ctx context.Context, _ *auth_pb.GetMyEmailRequest) (
}
func (s *Server) SetMyEmail(ctx context.Context, req *auth_pb.SetMyEmailRequest) (*auth_pb.SetMyEmailResponse, error) {
- email, err := s.command.ChangeHumanEmail(ctx, UpdateMyEmailToDomain(ctx, req))
+ emailCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyEmailCode, s.UserCodeAlg)
+ if err != nil {
+ return nil, err
+ }
+ email, err := s.command.ChangeHumanEmail(ctx, UpdateMyEmailToDomain(ctx, req), emailCodeGenerator)
if err != nil {
return nil, err
}
@@ -40,8 +45,12 @@ func (s *Server) SetMyEmail(ctx context.Context, req *auth_pb.SetMyEmailRequest)
}
func (s *Server) VerifyMyEmail(ctx context.Context, req *auth_pb.VerifyMyEmailRequest) (*auth_pb.VerifyMyEmailResponse, error) {
+ emailCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyEmailCode, s.UserCodeAlg)
+ if err != nil {
+ return nil, err
+ }
ctxData := authz.GetCtxData(ctx)
- objectDetails, err := s.command.VerifyHumanEmail(ctx, ctxData.UserID, req.Code, ctxData.ResourceOwner)
+ objectDetails, err := s.command.VerifyHumanEmail(ctx, ctxData.UserID, req.Code, ctxData.ResourceOwner, emailCodeGenerator)
if err != nil {
return nil, err
}
@@ -52,7 +61,11 @@ func (s *Server) VerifyMyEmail(ctx context.Context, req *auth_pb.VerifyMyEmailRe
func (s *Server) ResendMyEmailVerification(ctx context.Context, _ *auth_pb.ResendMyEmailVerificationRequest) (*auth_pb.ResendMyEmailVerificationResponse, error) {
ctxData := authz.GetCtxData(ctx)
- objectDetails, err := s.command.CreateHumanEmailVerificationCode(ctx, ctxData.UserID, ctxData.ResourceOwner)
+ emailCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyEmailCode, s.UserCodeAlg)
+ if err != nil {
+ return nil, err
+ }
+ objectDetails, err := s.command.CreateHumanEmailVerificationCode(ctx, ctxData.UserID, ctxData.ResourceOwner, emailCodeGenerator)
if err != nil {
return nil, err
}
diff --git a/internal/api/grpc/auth/passwordless.go b/internal/api/grpc/auth/passwordless.go
index 1d1453c974..fee5c0c7e6 100644
--- a/internal/api/grpc/auth/passwordless.go
+++ b/internal/api/grpc/auth/passwordless.go
@@ -57,7 +57,11 @@ func (s *Server) AddMyPasswordless(ctx context.Context, _ *auth_pb.AddMyPassword
func (s *Server) AddMyPasswordlessLink(ctx context.Context, _ *auth_pb.AddMyPasswordlessLinkRequest) (*auth_pb.AddMyPasswordlessLinkResponse, error) {
ctxData := authz.GetCtxData(ctx)
- initCode, err := s.command.HumanAddPasswordlessInitCode(ctx, ctxData.UserID, ctxData.ResourceOwner)
+ passwordlessInitCode, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypePasswordlessInitCode, s.UserCodeAlg)
+ if err != nil {
+ return nil, err
+ }
+ initCode, err := s.command.HumanAddPasswordlessInitCode(ctx, ctxData.UserID, ctxData.ResourceOwner, passwordlessInitCode)
if err != nil {
return nil, err
}
@@ -70,7 +74,11 @@ func (s *Server) AddMyPasswordlessLink(ctx context.Context, _ *auth_pb.AddMyPass
func (s *Server) SendMyPasswordlessLink(ctx context.Context, _ *auth_pb.SendMyPasswordlessLinkRequest) (*auth_pb.SendMyPasswordlessLinkResponse, error) {
ctxData := authz.GetCtxData(ctx)
- initCode, err := s.command.HumanSendPasswordlessInitCode(ctx, ctxData.UserID, ctxData.ResourceOwner)
+ passwordlessInitCode, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypePasswordlessInitCode, s.UserCodeAlg)
+ if err != nil {
+ return nil, err
+ }
+ initCode, err := s.command.HumanSendPasswordlessInitCode(ctx, ctxData.UserID, ctxData.ResourceOwner, passwordlessInitCode)
if err != nil {
return nil, err
}
diff --git a/internal/api/grpc/auth/phone.go b/internal/api/grpc/auth/phone.go
index dc1b1fa23a..60efca999d 100644
--- a/internal/api/grpc/auth/phone.go
+++ b/internal/api/grpc/auth/phone.go
@@ -6,6 +6,7 @@ import (
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/api/grpc/object"
"github.com/caos/zitadel/internal/api/grpc/user"
+ "github.com/caos/zitadel/internal/domain"
auth_pb "github.com/caos/zitadel/pkg/grpc/auth"
)
@@ -26,7 +27,11 @@ func (s *Server) GetMyPhone(ctx context.Context, _ *auth_pb.GetMyPhoneRequest) (
}
func (s *Server) SetMyPhone(ctx context.Context, req *auth_pb.SetMyPhoneRequest) (*auth_pb.SetMyPhoneResponse, error) {
- phone, err := s.command.ChangeHumanPhone(ctx, UpdateMyPhoneToDomain(ctx, req), authz.GetCtxData(ctx).ResourceOwner)
+ phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.UserCodeAlg)
+ if err != nil {
+ return nil, err
+ }
+ phone, err := s.command.ChangeHumanPhone(ctx, UpdateMyPhoneToDomain(ctx, req), authz.GetCtxData(ctx).ResourceOwner, phoneCodeGenerator)
if err != nil {
return nil, err
}
@@ -41,7 +46,11 @@ func (s *Server) SetMyPhone(ctx context.Context, req *auth_pb.SetMyPhoneRequest)
func (s *Server) VerifyMyPhone(ctx context.Context, req *auth_pb.VerifyMyPhoneRequest) (*auth_pb.VerifyMyPhoneResponse, error) {
ctxData := authz.GetCtxData(ctx)
- objectDetails, err := s.command.VerifyHumanPhone(ctx, ctxData.UserID, req.Code, ctxData.ResourceOwner)
+ phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.UserCodeAlg)
+ if err != nil {
+ return nil, err
+ }
+ objectDetails, err := s.command.VerifyHumanPhone(ctx, ctxData.UserID, req.Code, ctxData.ResourceOwner, phoneCodeGenerator)
if err != nil {
return nil, err
}
@@ -53,7 +62,11 @@ func (s *Server) VerifyMyPhone(ctx context.Context, req *auth_pb.VerifyMyPhoneRe
func (s *Server) ResendMyPhoneVerification(ctx context.Context, _ *auth_pb.ResendMyPhoneVerificationRequest) (*auth_pb.ResendMyPhoneVerificationResponse, error) {
ctxData := authz.GetCtxData(ctx)
- objectDetails, err := s.command.CreateHumanPhoneVerificationCode(ctx, ctxData.UserID, ctxData.ResourceOwner)
+ phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.UserCodeAlg)
+ if err != nil {
+ return nil, err
+ }
+ objectDetails, err := s.command.CreateHumanPhoneVerificationCode(ctx, ctxData.UserID, ctxData.ResourceOwner, phoneCodeGenerator)
if err != nil {
return nil, err
}
diff --git a/internal/api/grpc/auth/server.go b/internal/api/grpc/auth/server.go
index b027ef797f..c40295ac8b 100644
--- a/internal/api/grpc/auth/server.go
+++ b/internal/api/grpc/auth/server.go
@@ -1,6 +1,7 @@
package auth
import (
+ "github.com/caos/zitadel/internal/crypto"
"google.golang.org/grpc"
"github.com/caos/zitadel/internal/api/authz"
@@ -26,19 +27,21 @@ type Server struct {
repo repository.Repository
defaults systemdefaults.SystemDefaults
assetsAPIDomain string
+ UserCodeAlg crypto.EncryptionAlgorithm
}
type Config struct {
Repository eventsourcing.Config
}
-func CreateServer(command *command.Commands, query *query.Queries, authRepo repository.Repository, defaults systemdefaults.SystemDefaults, assetsAPIDomain string) *Server {
+func CreateServer(command *command.Commands, query *query.Queries, authRepo repository.Repository, defaults systemdefaults.SystemDefaults, assetsAPIDomain string, userCrypto *crypto.AESCrypto) *Server {
return &Server{
command: command,
query: query,
repo: authRepo,
defaults: defaults,
assetsAPIDomain: assetsAPIDomain,
+ UserCodeAlg: userCrypto,
}
}
diff --git a/internal/api/grpc/management/iam.go b/internal/api/grpc/management/iam.go
index 006ada73c4..03aa70a91a 100644
--- a/internal/api/grpc/management/iam.go
+++ b/internal/api/grpc/management/iam.go
@@ -3,11 +3,12 @@ package management
import (
"context"
+ "github.com/caos/zitadel/internal/domain"
mgmt_pb "github.com/caos/zitadel/pkg/grpc/management"
)
func (s *Server) GetIAM(ctx context.Context, req *mgmt_pb.GetIAMRequest) (*mgmt_pb.GetIAMResponse, error) {
- iam, err := s.query.IAMByID(ctx, s.systemDefaults.IamID)
+ iam, err := s.query.IAMByID(ctx, domain.IAMID)
if err != nil {
return nil, err
}
diff --git a/internal/api/grpc/management/project_application.go b/internal/api/grpc/management/project_application.go
index f4cbd6e87d..11886acbf0 100644
--- a/internal/api/grpc/management/project_application.go
+++ b/internal/api/grpc/management/project_application.go
@@ -8,6 +8,7 @@ import (
change_grpc "github.com/caos/zitadel/internal/api/grpc/change"
object_grpc "github.com/caos/zitadel/internal/api/grpc/object"
project_grpc "github.com/caos/zitadel/internal/api/grpc/project"
+ "github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/query"
mgmt_pb "github.com/caos/zitadel/pkg/grpc/management"
)
@@ -57,7 +58,11 @@ func (s *Server) ListAppChanges(ctx context.Context, req *mgmt_pb.ListAppChanges
}
func (s *Server) AddOIDCApp(ctx context.Context, req *mgmt_pb.AddOIDCAppRequest) (*mgmt_pb.AddOIDCAppResponse, error) {
- app, err := s.command.AddOIDCApplication(ctx, AddOIDCAppRequestToDomain(req), authz.GetCtxData(ctx).OrgID)
+ appSecretGenerator, err := s.query.InitHashGenerator(ctx, domain.SecretGeneratorTypeAppSecret, s.PasswordHashAlg)
+ if err != nil {
+ return nil, err
+ }
+ app, err := s.command.AddOIDCApplication(ctx, AddOIDCAppRequestToDomain(req), authz.GetCtxData(ctx).OrgID, appSecretGenerator)
if err != nil {
return nil, err
}
@@ -72,7 +77,11 @@ func (s *Server) AddOIDCApp(ctx context.Context, req *mgmt_pb.AddOIDCAppRequest)
}
func (s *Server) AddAPIApp(ctx context.Context, req *mgmt_pb.AddAPIAppRequest) (*mgmt_pb.AddAPIAppResponse, error) {
- app, err := s.command.AddAPIApplication(ctx, AddAPIAppRequestToDomain(req), authz.GetCtxData(ctx).OrgID)
+ appSecretGenerator, err := s.query.InitHashGenerator(ctx, domain.SecretGeneratorTypeAppSecret, s.PasswordHashAlg)
+ if err != nil {
+ return nil, err
+ }
+ app, err := s.command.AddAPIApplication(ctx, AddAPIAppRequestToDomain(req), authz.GetCtxData(ctx).OrgID, appSecretGenerator)
if err != nil {
return nil, err
}
@@ -153,7 +162,11 @@ func (s *Server) RemoveApp(ctx context.Context, req *mgmt_pb.RemoveAppRequest) (
}
func (s *Server) RegenerateOIDCClientSecret(ctx context.Context, req *mgmt_pb.RegenerateOIDCClientSecretRequest) (*mgmt_pb.RegenerateOIDCClientSecretResponse, error) {
- config, err := s.command.ChangeOIDCApplicationSecret(ctx, req.ProjectId, req.AppId, authz.GetCtxData(ctx).OrgID)
+ appSecretGenerator, err := s.query.InitHashGenerator(ctx, domain.SecretGeneratorTypeAppSecret, s.PasswordHashAlg)
+ if err != nil {
+ return nil, err
+ }
+ config, err := s.command.ChangeOIDCApplicationSecret(ctx, req.ProjectId, req.AppId, authz.GetCtxData(ctx).OrgID, appSecretGenerator)
if err != nil {
return nil, err
}
@@ -168,7 +181,11 @@ func (s *Server) RegenerateOIDCClientSecret(ctx context.Context, req *mgmt_pb.Re
}
func (s *Server) RegenerateAPIClientSecret(ctx context.Context, req *mgmt_pb.RegenerateAPIClientSecretRequest) (*mgmt_pb.RegenerateAPIClientSecretResponse, error) {
- config, err := s.command.ChangeAPIApplicationSecret(ctx, req.ProjectId, req.AppId, authz.GetCtxData(ctx).OrgID)
+ appSecretGenerator, err := s.query.InitHashGenerator(ctx, domain.SecretGeneratorTypeAppSecret, s.PasswordHashAlg)
+ if err != nil {
+ return nil, err
+ }
+ config, err := s.command.ChangeAPIApplicationSecret(ctx, req.ProjectId, req.AppId, authz.GetCtxData(ctx).OrgID, appSecretGenerator)
if err != nil {
return nil, err
}
diff --git a/internal/api/grpc/management/server.go b/internal/api/grpc/management/server.go
index 67baa1e510..56778acb74 100644
--- a/internal/api/grpc/management/server.go
+++ b/internal/api/grpc/management/server.go
@@ -1,6 +1,7 @@
package management
import (
+ "github.com/caos/zitadel/internal/crypto"
"google.golang.org/grpc"
"github.com/caos/zitadel/internal/api/authz"
@@ -19,18 +20,22 @@ var _ management.ManagementServiceServer = (*Server)(nil)
type Server struct {
management.UnimplementedManagementServiceServer
- command *command.Commands
- query *query.Queries
- systemDefaults systemdefaults.SystemDefaults
- assetAPIPrefix string
+ command *command.Commands
+ query *query.Queries
+ systemDefaults systemdefaults.SystemDefaults
+ assetAPIPrefix string
+ PasswordHashAlg crypto.HashAlgorithm
+ UserCodeAlg crypto.EncryptionAlgorithm
}
-func CreateServer(command *command.Commands, query *query.Queries, sd systemdefaults.SystemDefaults, assetAPIPrefix string) *Server {
+func CreateServer(command *command.Commands, query *query.Queries, sd systemdefaults.SystemDefaults, assetAPIPrefix string, userCrypto *crypto.AESCrypto) *Server {
return &Server{
- command: command,
- query: query,
- systemDefaults: sd,
- assetAPIPrefix: assetAPIPrefix,
+ command: command,
+ query: query,
+ systemDefaults: sd,
+ assetAPIPrefix: assetAPIPrefix,
+ PasswordHashAlg: crypto.NewBCrypt(sd.SecretGenerators.PasswordSaltCost),
+ UserCodeAlg: userCrypto,
}
}
diff --git a/internal/api/grpc/management/user.go b/internal/api/grpc/management/user.go
index 3a829a86f6..a9cd0161ee 100644
--- a/internal/api/grpc/management/user.go
+++ b/internal/api/grpc/management/user.go
@@ -192,7 +192,15 @@ func (s *Server) BulkRemoveUserMetadata(ctx context.Context, req *mgmt_pb.BulkRe
}
func (s *Server) AddHumanUser(ctx context.Context, req *mgmt_pb.AddHumanUserRequest) (*mgmt_pb.AddHumanUserResponse, error) {
- human, err := s.command.AddHuman(ctx, authz.GetCtxData(ctx).OrgID, AddHumanUserRequestToDomain(req))
+ initCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeInitCode, s.UserCodeAlg)
+ if err != nil {
+ return nil, err
+ }
+ phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.UserCodeAlg)
+ if err != nil {
+ return nil, err
+ }
+ human, err := s.command.AddHuman(ctx, authz.GetCtxData(ctx).OrgID, AddHumanUserRequestToDomain(req), initCodeGenerator, phoneCodeGenerator)
if err != nil {
return nil, err
}
@@ -208,7 +216,19 @@ func (s *Server) AddHumanUser(ctx context.Context, req *mgmt_pb.AddHumanUserRequ
func (s *Server) ImportHumanUser(ctx context.Context, req *mgmt_pb.ImportHumanUserRequest) (*mgmt_pb.ImportHumanUserResponse, error) {
human, passwordless := ImportHumanUserRequestToDomain(req)
- addedHuman, code, err := s.command.ImportHuman(ctx, authz.GetCtxData(ctx).OrgID, human, passwordless)
+ initCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeInitCode, s.UserCodeAlg)
+ if err != nil {
+ return nil, err
+ }
+ phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.UserCodeAlg)
+ if err != nil {
+ return nil, err
+ }
+ passwordlessInitCode, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypePasswordlessInitCode, s.UserCodeAlg)
+ if err != nil {
+ return nil, err
+ }
+ addedHuman, code, err := s.command.ImportHuman(ctx, authz.GetCtxData(ctx).OrgID, human, passwordless, initCodeGenerator, phoneCodeGenerator, passwordlessInitCode)
if err != nil {
return nil, err
}
@@ -388,7 +408,11 @@ func (s *Server) GetHumanEmail(ctx context.Context, req *mgmt_pb.GetHumanEmailRe
}
func (s *Server) UpdateHumanEmail(ctx context.Context, req *mgmt_pb.UpdateHumanEmailRequest) (*mgmt_pb.UpdateHumanEmailResponse, error) {
- email, err := s.command.ChangeHumanEmail(ctx, UpdateHumanEmailRequestToDomain(ctx, req))
+ emailCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyEmailCode, s.UserCodeAlg)
+ if err != nil {
+ return nil, err
+ }
+ email, err := s.command.ChangeHumanEmail(ctx, UpdateHumanEmailRequestToDomain(ctx, req), emailCodeGenerator)
if err != nil {
return nil, err
}
@@ -402,7 +426,11 @@ func (s *Server) UpdateHumanEmail(ctx context.Context, req *mgmt_pb.UpdateHumanE
}
func (s *Server) ResendHumanInitialization(ctx context.Context, req *mgmt_pb.ResendHumanInitializationRequest) (*mgmt_pb.ResendHumanInitializationResponse, error) {
- details, err := s.command.ResendInitialMail(ctx, req.UserId, req.Email, authz.GetCtxData(ctx).OrgID)
+ initCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeInitCode, s.UserCodeAlg)
+ if err != nil {
+ return nil, err
+ }
+ details, err := s.command.ResendInitialMail(ctx, req.UserId, req.Email, authz.GetCtxData(ctx).OrgID, initCodeGenerator)
if err != nil {
return nil, err
}
@@ -412,7 +440,11 @@ func (s *Server) ResendHumanInitialization(ctx context.Context, req *mgmt_pb.Res
}
func (s *Server) ResendHumanEmailVerification(ctx context.Context, req *mgmt_pb.ResendHumanEmailVerificationRequest) (*mgmt_pb.ResendHumanEmailVerificationResponse, error) {
- objectDetails, err := s.command.CreateHumanEmailVerificationCode(ctx, req.UserId, authz.GetCtxData(ctx).OrgID)
+ emailCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyEmailCode, s.UserCodeAlg)
+ if err != nil {
+ return nil, err
+ }
+ objectDetails, err := s.command.CreateHumanEmailVerificationCode(ctx, req.UserId, authz.GetCtxData(ctx).OrgID, emailCodeGenerator)
if err != nil {
return nil, err
}
@@ -442,7 +474,11 @@ func (s *Server) GetHumanPhone(ctx context.Context, req *mgmt_pb.GetHumanPhoneRe
}
func (s *Server) UpdateHumanPhone(ctx context.Context, req *mgmt_pb.UpdateHumanPhoneRequest) (*mgmt_pb.UpdateHumanPhoneResponse, error) {
- phone, err := s.command.ChangeHumanPhone(ctx, UpdateHumanPhoneRequestToDomain(req), authz.GetCtxData(ctx).OrgID)
+ phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.UserCodeAlg)
+ if err != nil {
+ return nil, err
+ }
+ phone, err := s.command.ChangeHumanPhone(ctx, UpdateHumanPhoneRequestToDomain(req), authz.GetCtxData(ctx).OrgID, phoneCodeGenerator)
if err != nil {
return nil, err
}
@@ -466,7 +502,11 @@ func (s *Server) RemoveHumanPhone(ctx context.Context, req *mgmt_pb.RemoveHumanP
}
func (s *Server) ResendHumanPhoneVerification(ctx context.Context, req *mgmt_pb.ResendHumanPhoneVerificationRequest) (*mgmt_pb.ResendHumanPhoneVerificationResponse, error) {
- objectDetails, err := s.command.CreateHumanPhoneVerificationCode(ctx, req.UserId, authz.GetCtxData(ctx).OrgID)
+ phoneCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, s.UserCodeAlg)
+ if err != nil {
+ return nil, err
+ }
+ objectDetails, err := s.command.CreateHumanPhoneVerificationCode(ctx, req.UserId, authz.GetCtxData(ctx).OrgID, phoneCodeGenerator)
if err != nil {
return nil, err
}
@@ -507,7 +547,11 @@ func (s *Server) SetHumanPassword(ctx context.Context, req *mgmt_pb.SetHumanPass
}
func (s *Server) SendHumanResetPasswordNotification(ctx context.Context, req *mgmt_pb.SendHumanResetPasswordNotificationRequest) (*mgmt_pb.SendHumanResetPasswordNotificationResponse, error) {
- objectDetails, err := s.command.RequestSetPassword(ctx, req.UserId, authz.GetCtxData(ctx).OrgID, notifyTypeToDomain(req.Type))
+ passwordCodeGenerator, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypePasswordResetCode, s.UserCodeAlg)
+ if err != nil {
+ return nil, err
+ }
+ objectDetails, err := s.command.RequestSetPassword(ctx, req.UserId, authz.GetCtxData(ctx).OrgID, notifyTypeToDomain(req.Type), passwordCodeGenerator)
if err != nil {
return nil, err
}
@@ -584,7 +628,11 @@ func (s *Server) ListHumanPasswordless(ctx context.Context, req *mgmt_pb.ListHum
func (s *Server) AddPasswordlessRegistration(ctx context.Context, req *mgmt_pb.AddPasswordlessRegistrationRequest) (*mgmt_pb.AddPasswordlessRegistrationResponse, error) {
ctxData := authz.GetCtxData(ctx)
- initCode, err := s.command.HumanAddPasswordlessInitCode(ctx, req.UserId, ctxData.OrgID)
+ passwordlessInitCode, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypePasswordlessInitCode, s.UserCodeAlg)
+ if err != nil {
+ return nil, err
+ }
+ initCode, err := s.command.HumanAddPasswordlessInitCode(ctx, req.UserId, ctxData.OrgID, passwordlessInitCode)
if err != nil {
return nil, err
}
@@ -597,7 +645,11 @@ func (s *Server) AddPasswordlessRegistration(ctx context.Context, req *mgmt_pb.A
func (s *Server) SendPasswordlessRegistration(ctx context.Context, req *mgmt_pb.SendPasswordlessRegistrationRequest) (*mgmt_pb.SendPasswordlessRegistrationResponse, error) {
ctxData := authz.GetCtxData(ctx)
- initCode, err := s.command.HumanSendPasswordlessInitCode(ctx, req.UserId, ctxData.OrgID)
+ passwordlessInitCode, err := s.query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypePasswordlessInitCode, s.UserCodeAlg)
+ if err != nil {
+ return nil, err
+ }
+ initCode, err := s.command.HumanSendPasswordlessInitCode(ctx, req.UserId, ctxData.OrgID, passwordlessInitCode)
if err != nil {
return nil, err
}
diff --git a/internal/api/grpc/server/middleware/translation_interceptor.go b/internal/api/grpc/server/middleware/translation_interceptor.go
index ed760c53f7..09a2f37e18 100644
--- a/internal/api/grpc/server/middleware/translation_interceptor.go
+++ b/internal/api/grpc/server/middleware/translation_interceptor.go
@@ -3,15 +3,14 @@ package middleware
import (
"context"
- "golang.org/x/text/language"
-
+ "github.com/caos/zitadel/internal/query"
"google.golang.org/grpc"
_ "github.com/caos/zitadel/internal/statik"
)
-func TranslationHandler(defaultLanguage language.Tag) func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
- translator := newZitadelTranslator(defaultLanguage)
+func TranslationHandler(query *query.Queries) func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
+ translator := newZitadelTranslator(query.GetDefaultLanguage(context.Background()))
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
resp, err := handler(ctx, req)
diff --git a/internal/api/grpc/server/server.go b/internal/api/grpc/server/server.go
index 419b47f6bc..2776831607 100644
--- a/internal/api/grpc/server/server.go
+++ b/internal/api/grpc/server/server.go
@@ -2,10 +2,10 @@ package server
import (
grpc_api "github.com/caos/zitadel/internal/api/grpc"
+ "github.com/caos/zitadel/internal/query"
"github.com/caos/zitadel/internal/telemetry/metrics"
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
- "golang.org/x/text/language"
"google.golang.org/grpc"
"github.com/caos/zitadel/internal/api/authz"
@@ -20,7 +20,7 @@ type Server interface {
AuthMethods() authz.MethodMapping
}
-func CreateServer(verifier *authz.TokenVerifier, authConfig authz.Config, lang language.Tag) *grpc.Server {
+func CreateServer(verifier *authz.TokenVerifier, authConfig authz.Config, queries *query.Queries) *grpc.Server {
metricTypes := []metrics.MetricType{metrics.MetricTypeTotalCount, metrics.MetricTypeRequestCount, metrics.MetricTypeStatusCode}
return grpc.NewServer(
grpc.UnaryInterceptor(
@@ -31,7 +31,7 @@ func CreateServer(verifier *authz.TokenVerifier, authConfig authz.Config, lang l
middleware.NoCacheInterceptor(),
middleware.ErrorHandler(),
middleware.AuthorizationInterceptor(verifier, authConfig),
- middleware.TranslationHandler(lang),
+ middleware.TranslationHandler(queries),
middleware.ValidationHandler(),
middleware.ServiceHandler(),
),
diff --git a/internal/api/ui/login/external_register_handler.go b/internal/api/ui/login/external_register_handler.go
index 668692a6bc..dd4a844839 100644
--- a/internal/api/ui/login/external_register_handler.go
+++ b/internal/api/ui/login/external_register_handler.go
@@ -145,7 +145,17 @@ func (l *Login) registerExternalUser(w http.ResponseWriter, r *http.Request, aut
memberRoles = nil
resourceOwner = authReq.RequestedOrgID
}
- _, err := l.command.RegisterHuman(setContext(r.Context(), resourceOwner), resourceOwner, user, externalIDP, memberRoles)
+ initCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeInitCode, l.UserCodeAlg)
+ if err != nil {
+ l.renderRegisterOption(w, r, authReq, err)
+ return
+ }
+ phoneCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeVerifyPhoneCode, l.UserCodeAlg)
+ if err != nil {
+ l.renderRegisterOption(w, r, authReq, err)
+ return
+ }
+ _, err = l.command.RegisterHuman(setContext(r.Context(), resourceOwner), resourceOwner, user, externalIDP, memberRoles, initCodeGenerator, phoneCodeGenerator)
if err != nil {
l.renderRegisterOption(w, r, authReq, err)
return
@@ -216,7 +226,17 @@ func (l *Login) handleExternalRegisterCheck(w http.ResponseWriter, r *http.Reque
l.renderRegisterOption(w, r, authReq, err)
return
}
- _, err = l.command.RegisterHuman(setContext(r.Context(), resourceOwner), resourceOwner, user, externalIDP, memberRoles)
+ initCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeInitCode, l.UserCodeAlg)
+ if err != nil {
+ l.renderRegisterOption(w, r, authReq, err)
+ return
+ }
+ phoneCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeVerifyPhoneCode, l.UserCodeAlg)
+ if err != nil {
+ l.renderRegisterOption(w, r, authReq, err)
+ return
+ }
+ _, err = l.command.RegisterHuman(setContext(r.Context(), resourceOwner), resourceOwner, user, externalIDP, memberRoles, initCodeGenerator, phoneCodeGenerator)
if err != nil {
l.renderRegisterOption(w, r, authReq, err)
return
diff --git a/internal/api/ui/login/init_password_handler.go b/internal/api/ui/login/init_password_handler.go
index fcec051e70..238bf46431 100644
--- a/internal/api/ui/login/init_password_handler.go
+++ b/internal/api/ui/login/init_password_handler.go
@@ -70,7 +70,12 @@ func (l *Login) checkPWCode(w http.ResponseWriter, r *http.Request, authReq *dom
userOrg = authReq.UserOrgID
}
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
- err = l.command.SetPasswordWithVerifyCode(setContext(r.Context(), userOrg), userOrg, data.UserID, data.Code, data.Password, userAgentID)
+ passwordCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypePasswordResetCode, l.UserCodeAlg)
+ if err != nil {
+ l.renderInitPassword(w, r, authReq, data.UserID, "", err)
+ return
+ }
+ err = l.command.SetPasswordWithVerifyCode(setContext(r.Context(), userOrg), userOrg, data.UserID, data.Code, data.Password, userAgentID, passwordCodeGenerator)
if err != nil {
l.renderInitPassword(w, r, authReq, data.UserID, "", err)
return
@@ -92,12 +97,17 @@ func (l *Login) resendPasswordSet(w http.ResponseWriter, r *http.Request, authRe
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
return
}
+ passwordCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypePasswordResetCode, l.UserCodeAlg)
+ if err != nil {
+ l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
+ return
+ }
user, err := l.query.GetUser(setContext(r.Context(), userOrg), loginName)
if err != nil {
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
return
}
- _, err = l.command.RequestSetPassword(setContext(r.Context(), userOrg), user.ID, user.ResourceOwner, domain.NotificationTypeEmail)
+ _, err = l.command.RequestSetPassword(setContext(r.Context(), userOrg), user.ID, user.ResourceOwner, domain.NotificationTypeEmail, passwordCodeGenerator)
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
}
diff --git a/internal/api/ui/login/init_user_handler.go b/internal/api/ui/login/init_user_handler.go
index c3dd7fb91d..31fd70eac3 100644
--- a/internal/api/ui/login/init_user_handler.go
+++ b/internal/api/ui/login/init_user_handler.go
@@ -73,7 +73,12 @@ func (l *Login) checkUserInitCode(w http.ResponseWriter, r *http.Request, authRe
if authReq != nil {
userOrgID = authReq.UserOrgID
}
- err = l.command.HumanVerifyInitCode(setContext(r.Context(), userOrgID), data.UserID, userOrgID, data.Code, data.Password)
+ initCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeInitCode, l.UserCodeAlg)
+ if err != nil {
+ l.renderInitUser(w, r, authReq, data.UserID, "", data.PasswordSet, err)
+ return
+ }
+ err = l.command.HumanVerifyInitCode(setContext(r.Context(), userOrgID), data.UserID, userOrgID, data.Code, data.Password, initCodeGenerator)
if err != nil {
l.renderInitUser(w, r, authReq, data.UserID, "", data.PasswordSet, err)
return
@@ -86,7 +91,12 @@ func (l *Login) resendUserInit(w http.ResponseWriter, r *http.Request, authReq *
if authReq != nil {
userOrgID = authReq.UserOrgID
}
- _, err := l.command.ResendInitialMail(setContext(r.Context(), userOrgID), userID, "", userOrgID)
+ initCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeInitCode, l.UserCodeAlg)
+ if err != nil {
+ l.renderInitUser(w, r, authReq, userID, "", showPassword, err)
+ return
+ }
+ _, err = l.command.ResendInitialMail(setContext(r.Context(), userOrgID), userID, "", userOrgID, initCodeGenerator)
l.renderInitUser(w, r, authReq, userID, "", showPassword, err)
}
diff --git a/internal/api/ui/login/login.go b/internal/api/ui/login/login.go
index e184d1077c..057f3bed2d 100644
--- a/internal/api/ui/login/login.go
+++ b/internal/api/ui/login/login.go
@@ -38,6 +38,7 @@ type Login struct {
zitadelURL string
oidcAuthCallbackURL string
IDPConfigAesCrypto crypto.EncryptionAlgorithm
+ UserCodeAlg crypto.EncryptionAlgorithm
iamDomain string
}
@@ -59,7 +60,7 @@ const (
DefaultLoggedOutPath = HandlerPrefix + EndpointLogoutDone
)
-func CreateLogin(config Config, command *command.Commands, query *query.Queries, authRepo *eventsourcing.EsRepository, staticStorage static.Storage, systemDefaults systemdefaults.SystemDefaults, zitadelURL, domain, oidcAuthCallbackURL string, externalSecure bool, userAgentCookie mux.MiddlewareFunc) (*Login, error) {
+func CreateLogin(config Config, command *command.Commands, query *query.Queries, authRepo *eventsourcing.EsRepository, staticStorage static.Storage, systemDefaults systemdefaults.SystemDefaults, zitadelURL, domain, oidcAuthCallbackURL string, externalSecure bool, userAgentCookie mux.MiddlewareFunc, userCrypto *crypto.AESCrypto) (*Login, error) {
aesCrypto, err := crypto.NewAESCrypto(systemDefaults.IDPConfigVerificationKey)
if err != nil {
return nil, fmt.Errorf("error create new aes crypto: %w", err)
@@ -74,6 +75,7 @@ func CreateLogin(config Config, command *command.Commands, query *query.Queries,
authRepo: authRepo,
IDPConfigAesCrypto: aesCrypto,
iamDomain: domain,
+ UserCodeAlg: userCrypto,
}
//TODO: enable when storage is implemented again
//login.staticCache, err = config.StaticCache.Config.NewCache()
diff --git a/internal/api/ui/login/mail_verify_handler.go b/internal/api/ui/login/mail_verify_handler.go
index 6cd4cde2c4..fdfb80e69a 100644
--- a/internal/api/ui/login/mail_verify_handler.go
+++ b/internal/api/ui/login/mail_verify_handler.go
@@ -51,7 +51,12 @@ func (l *Login) handleMailVerificationCheck(w http.ResponseWriter, r *http.Reque
if authReq != nil {
userOrg = authReq.UserOrgID
}
- _, err = l.command.CreateHumanEmailVerificationCode(setContext(r.Context(), userOrg), data.UserID, userOrg)
+ emailCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeVerifyEmailCode, l.UserCodeAlg)
+ if err != nil {
+ l.checkMailCode(w, r, authReq, data.UserID, data.Code)
+ return
+ }
+ _, err = l.command.CreateHumanEmailVerificationCode(setContext(r.Context(), userOrg), data.UserID, userOrg, emailCodeGenerator)
l.renderMailVerification(w, r, authReq, data.UserID, err)
}
@@ -61,7 +66,12 @@ func (l *Login) checkMailCode(w http.ResponseWriter, r *http.Request, authReq *d
userID = authReq.UserID
userOrg = authReq.UserOrgID
}
- _, err := l.command.VerifyHumanEmail(setContext(r.Context(), userOrg), userID, code, userOrg)
+ emailCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeVerifyEmailCode, l.UserCodeAlg)
+ if err != nil {
+ l.renderMailVerification(w, r, authReq, userID, err)
+ return
+ }
+ _, err = l.command.VerifyHumanEmail(setContext(r.Context(), userOrg), userID, code, userOrg, emailCodeGenerator)
if err != nil {
l.renderMailVerification(w, r, authReq, userID, err)
return
diff --git a/internal/api/ui/login/password_reset_handler.go b/internal/api/ui/login/password_reset_handler.go
index 2741841c7e..8f28ff914e 100644
--- a/internal/api/ui/login/password_reset_handler.go
+++ b/internal/api/ui/login/password_reset_handler.go
@@ -27,7 +27,12 @@ func (l *Login) handlePasswordReset(w http.ResponseWriter, r *http.Request) {
l.renderPasswordResetDone(w, r, authReq, err)
return
}
- _, err = l.command.RequestSetPassword(setContext(r.Context(), authReq.UserOrgID), user.ID, authReq.UserOrgID, domain.NotificationTypeEmail)
+ passwordCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypePasswordResetCode, l.UserCodeAlg)
+ if err != nil {
+ l.renderPasswordResetDone(w, r, authReq, err)
+ return
+ }
+ _, err = l.command.RequestSetPassword(setContext(r.Context(), authReq.UserOrgID), user.ID, authReq.UserOrgID, domain.NotificationTypeEmail, passwordCodeGenerator)
l.renderPasswordResetDone(w, r, authReq, err)
}
diff --git a/internal/api/ui/login/register_handler.go b/internal/api/ui/login/register_handler.go
index 754e72714d..546663378a 100644
--- a/internal/api/ui/login/register_handler.go
+++ b/internal/api/ui/login/register_handler.go
@@ -74,7 +74,17 @@ func (l *Login) handleRegisterCheck(w http.ResponseWriter, r *http.Request) {
memberRoles = nil
resourceOwner = authRequest.RequestedOrgID
}
- user, err := l.command.RegisterHuman(setContext(r.Context(), resourceOwner), resourceOwner, data.toHumanDomain(), nil, memberRoles)
+ initCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeInitCode, l.UserCodeAlg)
+ if err != nil {
+ l.renderRegister(w, r, authRequest, data, err)
+ return
+ }
+ phoneCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeVerifyPhoneCode, l.UserCodeAlg)
+ if err != nil {
+ l.renderRegister(w, r, authRequest, data, err)
+ return
+ }
+ user, err := l.command.RegisterHuman(setContext(r.Context(), resourceOwner), resourceOwner, data.toHumanDomain(), nil, memberRoles, initCodeGenerator, phoneCodeGenerator)
if err != nil {
l.renderRegister(w, r, authRequest, data, err)
return
diff --git a/internal/api/ui/login/register_org_handler.go b/internal/api/ui/login/register_org_handler.go
index 2970aa39f4..2e0854f7c6 100644
--- a/internal/api/ui/login/register_org_handler.go
+++ b/internal/api/ui/login/register_org_handler.go
@@ -65,7 +65,17 @@ func (l *Login) handleRegisterOrgCheck(w http.ResponseWriter, r *http.Request) {
l.renderRegisterOrg(w, r, authRequest, data, err)
return
}
- _, err = l.command.SetUpOrg(ctx, data.toOrgDomain(), data.toUserDomain(), userIDs, true)
+ initCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypePasswordlessInitCode, l.UserCodeAlg)
+ if err != nil {
+ l.renderRegisterOrg(w, r, authRequest, data, err)
+ return
+ }
+ phoneCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeVerifyPhoneCode, l.UserCodeAlg)
+ if err != nil {
+ l.renderRegisterOrg(w, r, authRequest, data, err)
+ return
+ }
+ _, err = l.command.SetUpOrg(ctx, data.toOrgDomain(), data.toUserDomain(), initCodeGenerator, phoneCodeGenerator, userIDs, true)
if err != nil {
l.renderRegisterOrg(w, r, authRequest, data, err)
return
diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go
index 92b2a5c1dd..0aedc1f26f 100644
--- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go
+++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go
@@ -5,6 +5,7 @@ import (
"time"
"github.com/caos/logging"
+ "github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
@@ -32,6 +33,7 @@ type AuthRequestRepo struct {
AuthRequests cache.AuthRequestCache
View *view.View
Eventstore v1.Eventstore
+ UserCodeAlg crypto.EncryptionAlgorithm
LabelPolicyProvider labelPolicyProvider
UserSessionViewProvider userSessionViewProvider
@@ -54,8 +56,6 @@ type AuthRequestRepo struct {
MFAInitSkippedLifeTime time.Duration
SecondFactorCheckLifeTime time.Duration
MultiFactorCheckLifeTime time.Duration
-
- IAMID string
}
type labelPolicyProvider interface {
@@ -381,13 +381,21 @@ func (repo *AuthRequestRepo) VerifyPasswordlessSetup(ctx context.Context, userID
func (repo *AuthRequestRepo) BeginPasswordlessInitCodeSetup(ctx context.Context, userID, resourceOwner, codeID, verificationCode string, preferredPlatformType domain.AuthenticatorAttachment) (login *domain.WebAuthNToken, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
- return repo.Command.HumanAddPasswordlessSetupInitCode(ctx, userID, resourceOwner, codeID, verificationCode, preferredPlatformType)
+ passwordlessInitCode, err := repo.Query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypePasswordlessInitCode, repo.UserCodeAlg)
+ if err != nil {
+ return nil, err
+ }
+ return repo.Command.HumanAddPasswordlessSetupInitCode(ctx, userID, resourceOwner, codeID, verificationCode, preferredPlatformType, passwordlessInitCode)
}
func (repo *AuthRequestRepo) VerifyPasswordlessInitCodeSetup(ctx context.Context, userID, resourceOwner, userAgentID, tokenName, codeID, verificationCode string, credentialData []byte) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
- _, err = repo.Command.HumanPasswordlessSetupInitCode(ctx, userID, resourceOwner, tokenName, userAgentID, codeID, verificationCode, credentialData)
+ passwordlessInitCode, err := repo.Query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypePasswordlessInitCode, repo.UserCodeAlg)
+ if err != nil {
+ return err
+ }
+ _, err = repo.Command.HumanPasswordlessSetupInitCode(ctx, userID, resourceOwner, tokenName, userAgentID, codeID, verificationCode, credentialData, passwordlessInitCode)
return err
}
@@ -447,7 +455,15 @@ func (repo *AuthRequestRepo) AutoRegisterExternalUser(ctx context.Context, regis
if err != nil {
return err
}
- human, err := repo.Command.RegisterHuman(ctx, resourceOwner, registerUser, externalIDP, orgMemberRoles)
+ initCodeGenerator, err := repo.Query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeInitCode, repo.UserCodeAlg)
+ if err != nil {
+ return err
+ }
+ phoneCodeGenerator, err := repo.Query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, repo.UserCodeAlg)
+ if err != nil {
+ return err
+ }
+ human, err := repo.Command.RegisterHuman(ctx, resourceOwner, registerUser, externalIDP, orgMemberRoles, initCodeGenerator, phoneCodeGenerator)
if err != nil {
return err
}
@@ -519,7 +535,7 @@ func (repo *AuthRequestRepo) getLoginPolicyAndIDPProviders(ctx context.Context,
if !policy.AllowExternalIDPs {
return policy, nil, nil
}
- idpProviders, err := getLoginPolicyIDPProviders(repo.IDPProviderViewProvider, repo.IAMID, orgID, policy.IsDefault)
+ idpProviders, err := getLoginPolicyIDPProviders(repo.IDPProviderViewProvider, domain.IAMID, orgID, policy.IsDefault)
if err != nil {
return nil, nil, err
}
@@ -534,7 +550,7 @@ func (repo *AuthRequestRepo) fillPolicies(ctx context.Context, request *domain.A
orgID = request.UserOrgID
}
if orgID == "" {
- orgID = repo.IAMID
+ orgID = domain.IAMID
}
loginPolicy, idpProviders, err := repo.getLoginPolicyAndIDPProviders(ctx, orgID)
diff --git a/internal/auth/repository/eventsourcing/eventstore/org.go b/internal/auth/repository/eventsourcing/eventstore/org.go
index 678ee3be8e..613536b929 100644
--- a/internal/auth/repository/eventsourcing/eventstore/org.go
+++ b/internal/auth/repository/eventsourcing/eventstore/org.go
@@ -53,5 +53,5 @@ func (repo *OrgRepository) GetLoginText(ctx context.Context, orgID string) ([]*d
}
func (p *OrgRepository) getIAMEvents(ctx context.Context, sequence uint64) ([]*models.Event, error) {
- return p.Eventstore.FilterEvents(ctx, models.NewSearchQuery().AggregateIDFilter(p.SystemDefaults.IamID).AggregateTypeFilter(iam.AggregateType))
+ return p.Eventstore.FilterEvents(ctx, models.NewSearchQuery().AggregateIDFilter(domain.IAMID).AggregateTypeFilter(iam.AggregateType))
}
diff --git a/internal/auth/repository/eventsourcing/handler/handler.go b/internal/auth/repository/eventsourcing/handler/handler.go
index 0afab0affe..26abb93e6d 100644
--- a/internal/auth/repository/eventsourcing/handler/handler.go
+++ b/internal/auth/repository/eventsourcing/handler/handler.go
@@ -32,8 +32,7 @@ func (h *handler) Eventstore() v1.Eventstore {
func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es v1.Eventstore, systemDefaults sd.SystemDefaults, queries *query2.Queries) []query.Handler {
return []query.Handler{
newUser(
- handler{view, bulkLimit, configs.cycleDuration("User"), errorCount, es},
- systemDefaults.IamID, queries),
+ handler{view, bulkLimit, configs.cycleDuration("User"), errorCount, es}, queries),
newUserSession(
handler{view, bulkLimit, configs.cycleDuration("UserSession"), errorCount, es}),
newToken(
diff --git a/internal/auth/repository/eventsourcing/handler/idp_providers.go b/internal/auth/repository/eventsourcing/handler/idp_providers.go
index 2465db6639..734e8dcec5 100644
--- a/internal/auth/repository/eventsourcing/handler/idp_providers.go
+++ b/internal/auth/repository/eventsourcing/handler/idp_providers.go
@@ -111,7 +111,7 @@ func (i *IDPProvider) processIdpProvider(event *es_models.Event) (err error) {
case model.IDPConfigChanged, org_es_model.IDPConfigChanged:
esConfig := new(iam_view_model.IDPConfigView)
providerType := iam_model.IDPProviderTypeSystem
- if event.AggregateID != i.systemDefaults.IamID {
+ if event.AggregateID != domain.IAMID {
providerType = iam_model.IDPProviderTypeOrg
}
esConfig.AppendEvent(providerType, event)
@@ -120,7 +120,7 @@ func (i *IDPProvider) processIdpProvider(event *es_models.Event) (err error) {
return err
}
config := new(query2.IDP)
- if event.AggregateID == i.systemDefaults.IamID {
+ if event.AggregateID == domain.IAMID {
config, err = i.getDefaultIDPConfig(context.TODO(), esConfig.IDPConfigID)
} else {
config, err = i.getOrgIDPConfig(context.TODO(), event.AggregateID, esConfig.IDPConfigID)
diff --git a/internal/auth/repository/eventsourcing/handler/user.go b/internal/auth/repository/eventsourcing/handler/user.go
index bd058058ce..8463777200 100644
--- a/internal/auth/repository/eventsourcing/handler/user.go
+++ b/internal/auth/repository/eventsourcing/handler/user.go
@@ -25,19 +25,16 @@ const (
type User struct {
handler
- iamID string
subscription *v1.Subscription
queries *query2.Queries
}
func newUser(
handler handler,
- iamID string,
queries *query2.Queries,
) *User {
h := &User{
handler: handler,
- iamID: iamID,
queries: queries,
}
diff --git a/internal/auth/repository/eventsourcing/repository.go b/internal/auth/repository/eventsourcing/repository.go
index 003d0fca03..d3c635d9b3 100644
--- a/internal/auth/repository/eventsourcing/repository.go
+++ b/internal/auth/repository/eventsourcing/repository.go
@@ -33,7 +33,7 @@ type EsRepository struct {
eventstore.OrgRepository
}
-func Start(conf Config, systemDefaults sd.SystemDefaults, command *command.Commands, queries *query.Queries, dbClient *sql.DB, keyConfig *crypto.KeyConfig, assetsPrefix string) (*EsRepository, error) {
+func Start(conf Config, systemDefaults sd.SystemDefaults, command *command.Commands, queries *query.Queries, dbClient *sql.DB, keyConfig *crypto.KeyConfig, assetsPrefix string, userCrypto *crypto.AESCrypto) (*EsRepository, error) {
es, err := v1.Start(dbClient)
if err != nil {
return nil, err
@@ -80,6 +80,7 @@ func Start(conf Config, systemDefaults sd.SystemDefaults, command *command.Comma
AuthRequests: authReq,
View: view,
Eventstore: es,
+ UserCodeAlg: userCrypto,
UserSessionViewProvider: view,
UserViewProvider: view,
UserCommandProvider: command,
@@ -96,7 +97,6 @@ func Start(conf Config, systemDefaults sd.SystemDefaults, command *command.Comma
MFAInitSkippedLifeTime: systemDefaults.VerificationLifetimes.MFAInitSkip,
SecondFactorCheckLifeTime: systemDefaults.VerificationLifetimes.SecondFactorCheck,
MultiFactorCheckLifeTime: systemDefaults.VerificationLifetimes.MultiFactorCheck,
- IAMID: systemDefaults.IamID,
},
eventstore.TokenRepo{
View: view,
diff --git a/internal/authz/repository/eventsourcing/handler/handler.go b/internal/authz/repository/eventsourcing/handler/handler.go
index c90af02098..8fd9014e25 100644
--- a/internal/authz/repository/eventsourcing/handler/handler.go
+++ b/internal/authz/repository/eventsourcing/handler/handler.go
@@ -31,8 +31,7 @@ func (h *handler) Eventstore() v1.Eventstore {
func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es v1.Eventstore, systemDefaults sd.SystemDefaults) []query.Handler {
return []query.Handler{
newUserGrant(
- handler{view, bulkLimit, configs.cycleDuration("UserGrants"), errorCount, es},
- systemDefaults.IamID),
+ handler{view, bulkLimit, configs.cycleDuration("UserGrants"), errorCount, es}),
newUserMembership(
handler{view, bulkLimit, configs.cycleDuration("UserMemberships"), errorCount, es}),
}
diff --git a/internal/authz/repository/eventsourcing/handler/user_grant.go b/internal/authz/repository/eventsourcing/handler/user_grant.go
index ec47e331cd..e0dd933739 100644
--- a/internal/authz/repository/eventsourcing/handler/user_grant.go
+++ b/internal/authz/repository/eventsourcing/handler/user_grant.go
@@ -29,18 +29,15 @@ const (
type UserGrant struct {
handler
- iamID string
iamProjectID string
subscription *v1.Subscription
}
func newUserGrant(
handler handler,
- iamID string,
) *UserGrant {
h := &UserGrant{
handler: handler,
- iamID: iamID,
}
h.subscribe()
@@ -151,15 +148,15 @@ func (u *UserGrant) processIAMMember(event *es_models.Event, rolePrefix string,
case iam_es_model.IAMMemberAdded, iam_es_model.IAMMemberChanged:
member.SetData(event)
- grant, err := u.view.UserGrantByIDs(u.iamID, u.iamProjectID, member.UserID)
+ grant, err := u.view.UserGrantByIDs(domain.IAMID, u.iamProjectID, member.UserID)
if err != nil && !errors.IsNotFound(err) {
return err
}
if errors.IsNotFound(err) {
grant = &view_model.UserGrantView{
ID: u.iamProjectID + member.UserID,
- ResourceOwner: u.iamID,
- OrgName: u.iamID,
+ ResourceOwner: domain.IAMID,
+ OrgName: domain.IAMID,
ProjectID: u.iamProjectID,
UserID: member.UserID,
RoleKeys: member.Roles,
@@ -182,7 +179,7 @@ func (u *UserGrant) processIAMMember(event *es_models.Event, rolePrefix string,
case iam_es_model.IAMMemberRemoved,
iam_es_model.IAMMemberCascadeRemoved:
member.SetData(event)
- grant, err := u.view.UserGrantByIDs(u.iamID, u.iamProjectID, member.UserID)
+ grant, err := u.view.UserGrantByIDs(domain.IAMID, u.iamProjectID, member.UserID)
if err != nil {
return err
}
diff --git a/internal/command/command.go b/internal/command/command.go
index 30ccd80621..e8f222e989 100644
--- a/internal/command/command.go
+++ b/internal/command/command.go
@@ -20,7 +20,6 @@ import (
usr_repo "github.com/caos/zitadel/internal/repository/user"
usr_grant_repo "github.com/caos/zitadel/internal/repository/usergrant"
"github.com/caos/zitadel/internal/static"
- "github.com/caos/zitadel/internal/telemetry/tracing"
webauthn_helper "github.com/caos/zitadel/internal/webauthn"
)
@@ -32,17 +31,11 @@ type Commands struct {
zitadelRoles []authz.RoleMapping
idpConfigSecretCrypto crypto.EncryptionAlgorithm
+ smtpPasswordCrypto crypto.EncryptionAlgorithm
userPasswordAlg crypto.HashAlgorithm
- initializeUserCode crypto.Generator
- emailVerificationCode crypto.Generator
- phoneVerificationCode crypto.Generator
- passwordVerificationCode crypto.Generator
- passwordlessInitCode crypto.Generator
- machineKeyAlg crypto.EncryptionAlgorithm
machineKeySize int
applicationKeySize int
- applicationSecretGenerator crypto.Generator
domainVerificationAlg crypto.EncryptionAlgorithm
domainVerificationGenerator crypto.Generator
domainVerificationValidator func(domain, token, verifier string, checkType http.CheckType) error
@@ -60,7 +53,16 @@ type orgFeatureChecker interface {
CheckOrgFeatures(ctx context.Context, orgID string, requiredFeatures ...string) error
}
-func StartCommands(es *eventstore.Eventstore, defaults sd.SystemDefaults, authZConfig authz.Config, staticStore static.Storage, authZRepo authz_repo.Repository, keyConfig *crypto.KeyConfig, webAuthN webauthn_helper.Config) (repo *Commands, err error) {
+func StartCommands(
+ es *eventstore.Eventstore,
+ defaults sd.SystemDefaults,
+ authZConfig authz.Config,
+ staticStore static.Storage,
+ authZRepo authz_repo.Repository,
+ keyConfig *crypto.KeyConfig,
+ smtpPasswordEncAlg crypto.EncryptionAlgorithm,
+ webAuthN webauthn_helper.Config,
+) (repo *Commands, err error) {
repo = &Commands{
eventstore: es,
static: staticStore,
@@ -70,6 +72,7 @@ func StartCommands(es *eventstore.Eventstore, defaults sd.SystemDefaults, authZC
keySize: defaults.KeyConfig.Size,
privateKeyLifetime: defaults.KeyConfig.PrivateKeyLifetime,
publicKeyLifetime: defaults.KeyConfig.PublicKeyLifetime,
+ smtpPasswordCrypto: smtpPasswordEncAlg,
}
iam_repo.RegisterEventMappers(repo.eventstore)
org.RegisterEventMappers(repo.eventstore)
@@ -83,17 +86,8 @@ func StartCommands(es *eventstore.Eventstore, defaults sd.SystemDefaults, authZC
if err != nil {
return nil, err
}
- userEncryptionAlgorithm, err := crypto.NewAESCrypto(defaults.UserVerificationKey)
- if err != nil {
- return nil, err
- }
- repo.initializeUserCode = crypto.NewEncryptionGenerator(defaults.SecretGenerators.InitializeUserCode, userEncryptionAlgorithm)
- repo.emailVerificationCode = crypto.NewEncryptionGenerator(defaults.SecretGenerators.EmailVerificationCode, userEncryptionAlgorithm)
- repo.phoneVerificationCode = crypto.NewEncryptionGenerator(defaults.SecretGenerators.PhoneVerificationCode, userEncryptionAlgorithm)
- repo.passwordVerificationCode = crypto.NewEncryptionGenerator(defaults.SecretGenerators.PasswordVerificationCode, userEncryptionAlgorithm)
- repo.passwordlessInitCode = crypto.NewEncryptionGenerator(defaults.SecretGenerators.PasswordlessInitCode, userEncryptionAlgorithm)
+
repo.userPasswordAlg = crypto.NewBCrypt(defaults.SecretGenerators.PasswordSaltCost)
- repo.machineKeyAlg = userEncryptionAlgorithm
repo.machineKeySize = int(defaults.SecretGenerators.MachineKeySize)
repo.applicationKeySize = int(defaults.SecretGenerators.ApplicationKeySize)
@@ -107,8 +101,6 @@ func StartCommands(es *eventstore.Eventstore, defaults sd.SystemDefaults, authZC
Issuer: defaults.Multifactors.OTP.Issuer,
},
}
- passwordAlg := crypto.NewBCrypt(defaults.SecretGenerators.PasswordSaltCost)
- repo.applicationSecretGenerator = crypto.NewHashGenerator(defaults.SecretGenerators.ClientSecretGenerator, passwordAlg)
repo.domainVerificationAlg, err = crypto.NewAESCrypto(defaults.DomainVerification.VerificationKey)
if err != nil {
@@ -132,19 +124,6 @@ func StartCommands(es *eventstore.Eventstore, defaults sd.SystemDefaults, authZC
return repo, nil
}
-func (c *Commands) getIAMWriteModel(ctx context.Context) (_ *IAMWriteModel, err error) {
- ctx, span := tracing.NewSpan(ctx)
- defer func() { span.EndWithError(err) }()
-
- writeModel := NewIAMWriteModel()
- err = c.eventstore.FilterToQueryReducer(ctx, writeModel)
- if err != nil {
- return nil, err
- }
-
- return writeModel, nil
-}
-
func AppendAndReduce(object interface {
AppendEvents(...eventstore.Event)
Reduce() error
diff --git a/internal/command/iam.go b/internal/command/iam.go
index a234647b12..4847dd6f52 100644
--- a/internal/command/iam.go
+++ b/internal/command/iam.go
@@ -7,6 +7,8 @@ import (
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/repository/iam"
+ "github.com/caos/zitadel/internal/telemetry/tracing"
+ "golang.org/x/text/language"
)
//TODO: private as soon as setup uses query
@@ -40,3 +42,33 @@ func (c *Commands) setIAMProject(ctx context.Context, iamAgg *eventstore.Aggrega
}
return iam.NewIAMProjectSetEvent(ctx, iamAgg, projectID), nil
}
+
+func (c *Commands) SetDefaultLanguage(ctx context.Context, language language.Tag) (*domain.ObjectDetails, error) {
+ iamWriteModel, err := c.getIAMWriteModel(ctx)
+ if err != nil {
+ return nil, err
+ }
+ iamAgg := IAMAggregateFromWriteModel(&iamWriteModel.WriteModel)
+ pushedEvents, err := c.eventstore.Push(ctx, iam.NewDefaultLanguageSetEvent(ctx, iamAgg, language))
+ if err != nil {
+ return nil, err
+ }
+ err = AppendAndReduce(iamWriteModel, pushedEvents...)
+ if err != nil {
+ return nil, err
+ }
+ return writeModelToObjectDetails(&iamWriteModel.WriteModel), nil
+}
+
+func (c *Commands) getIAMWriteModel(ctx context.Context) (_ *IAMWriteModel, err error) {
+ ctx, span := tracing.NewSpan(ctx)
+ defer func() { span.EndWithError(err) }()
+
+ writeModel := NewIAMWriteModel()
+ err = c.eventstore.FilterToQueryReducer(ctx, writeModel)
+ if err != nil {
+ return nil, err
+ }
+
+ return writeModel, nil
+}
diff --git a/internal/command/iam_idp_config.go b/internal/command/iam_idp_config.go
index 5fd35d5ad4..3feb5537f5 100644
--- a/internal/command/iam_idp_config.go
+++ b/internal/command/iam_idp_config.go
@@ -15,9 +15,8 @@ import (
func (c *Commands) AddDefaultIDPConfig(ctx context.Context, config *domain.IDPConfig) (*domain.IDPConfig, error) {
if config.OIDCConfig == nil && config.JWTConfig == nil {
- return nil, errors.ThrowInvalidArgument(nil, "IAM-eUpQU", "Errors.idp.config.notset")
+ return nil, caos_errs.ThrowInvalidArgument(nil, "IDP-s8nn3", "Errors.IDPConfig.Invalid")
}
-
idpConfigID, err := c.idGenerator.Next()
if err != nil {
return nil, err
diff --git a/internal/command/iam_model.go b/internal/command/iam_model.go
index 855fd74e4d..9d13156a67 100644
--- a/internal/command/iam_model.go
+++ b/internal/command/iam_model.go
@@ -4,6 +4,7 @@ import (
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/repository/iam"
+ "golang.org/x/text/language"
)
type IAMWriteModel struct {
@@ -12,8 +13,9 @@ type IAMWriteModel struct {
SetUpStarted domain.Step
SetUpDone domain.Step
- GlobalOrgID string
- ProjectID string
+ GlobalOrgID string
+ ProjectID string
+ DefaultLanguage language.Tag
}
func NewIAMWriteModel() *IAMWriteModel {
@@ -32,6 +34,8 @@ func (wm *IAMWriteModel) Reduce() error {
wm.ProjectID = e.ProjectID
case *iam.GlobalOrgSetEvent:
wm.GlobalOrgID = e.OrgID
+ case *iam.DefaultLanguageSetEvent:
+ wm.DefaultLanguage = e.Language
case *iam.SetupStepEvent:
if e.Done {
wm.SetUpDone = e.Step
@@ -52,6 +56,7 @@ func (wm *IAMWriteModel) Query() *eventstore.SearchQueryBuilder {
EventTypes(
iam.ProjectSetEventType,
iam.GlobalOrgSetEventType,
+ iam.DefaultLanguageSetEventType,
iam.SetupStartedEventType,
iam.SetupDoneEventType).
Builder()
diff --git a/internal/command/iam_secret_generator_model.go b/internal/command/iam_secret_generator_model.go
new file mode 100644
index 0000000000..f9caf7555d
--- /dev/null
+++ b/internal/command/iam_secret_generator_model.go
@@ -0,0 +1,140 @@
+package command
+
+import (
+ "context"
+ "time"
+
+ "github.com/caos/zitadel/internal/domain"
+ "github.com/caos/zitadel/internal/eventstore"
+ "github.com/caos/zitadel/internal/repository/iam"
+)
+
+type IAMSecretGeneratorConfigWriteModel struct {
+ eventstore.WriteModel
+
+ GeneratorType domain.SecretGeneratorType
+ Length uint
+ Expiry time.Duration
+ IncludeLowerLetters bool
+ IncludeUpperLetters bool
+ IncludeDigits bool
+ IncludeSymbols bool
+ State domain.SecretGeneratorState
+}
+
+func NewIAMSecretGeneratorConfigWriteModel(GeneratorType domain.SecretGeneratorType) *IAMSecretGeneratorConfigWriteModel {
+ return &IAMSecretGeneratorConfigWriteModel{
+ WriteModel: eventstore.WriteModel{
+ AggregateID: domain.IAMID,
+ ResourceOwner: domain.IAMID,
+ },
+ GeneratorType: GeneratorType,
+ }
+}
+
+func (wm *IAMSecretGeneratorConfigWriteModel) Reduce() error {
+ for _, event := range wm.Events {
+ switch e := event.(type) {
+ case *iam.SecretGeneratorAddedEvent:
+ if wm.GeneratorType != e.GeneratorType {
+ continue
+ }
+ wm.Length = e.Length
+ wm.Expiry = e.Expiry
+ wm.IncludeLowerLetters = e.IncludeLowerLetters
+ wm.IncludeUpperLetters = e.IncludeUpperLetters
+ wm.IncludeDigits = e.IncludeDigits
+ wm.IncludeSymbols = e.IncludeDigits
+ wm.State = domain.SecretGeneratorStateActive
+ case *iam.SecretGeneratorChangedEvent:
+ if wm.GeneratorType != e.GeneratorType {
+ continue
+ }
+ if e.Length != nil {
+ wm.Length = *e.Length
+ }
+ if e.Expiry != nil {
+ wm.Expiry = *e.Expiry
+ }
+ if e.IncludeUpperLetters != nil {
+ wm.IncludeUpperLetters = *e.IncludeUpperLetters
+ }
+ if e.IncludeLowerLetters != nil {
+ wm.IncludeLowerLetters = *e.IncludeLowerLetters
+ }
+ if e.IncludeDigits != nil {
+ wm.IncludeDigits = *e.IncludeDigits
+ }
+ if e.IncludeSymbols != nil {
+ wm.IncludeSymbols = *e.IncludeSymbols
+ }
+ case *iam.SecretGeneratorRemovedEvent:
+ if wm.GeneratorType != e.GeneratorType {
+ continue
+ }
+ wm.State = domain.SecretGeneratorStateRemoved
+ wm.Length = 0
+ wm.Expiry = 0
+ wm.IncludeLowerLetters = false
+ wm.IncludeUpperLetters = false
+ wm.IncludeDigits = false
+ wm.IncludeSymbols = false
+ }
+ }
+ return wm.WriteModel.Reduce()
+}
+
+func (wm *IAMSecretGeneratorConfigWriteModel) Query() *eventstore.SearchQueryBuilder {
+ return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
+ ResourceOwner(wm.ResourceOwner).
+ AddQuery().
+ AggregateTypes(iam.AggregateType).
+ AggregateIDs(wm.AggregateID).
+ EventTypes(
+ iam.SecretGeneratorAddedEventType,
+ iam.SecretGeneratorChangedEventType,
+ iam.SecretGeneratorRemovedEventType).
+ Builder()
+}
+
+func (wm *IAMSecretGeneratorConfigWriteModel) NewChangedEvent(
+ ctx context.Context,
+ aggregate *eventstore.Aggregate,
+ generatorType domain.SecretGeneratorType,
+ length uint,
+ expiry time.Duration,
+ includeLowerLetters,
+ includeUpperLetters,
+ includeDigits,
+ includeSymbols bool,
+) (*iam.SecretGeneratorChangedEvent, bool, error) {
+ changes := make([]iam.SecretGeneratorChanges, 0)
+ var err error
+
+ if wm.Length != length {
+ changes = append(changes, iam.ChangeSecretGeneratorLength(length))
+ }
+ if wm.Expiry != expiry {
+ changes = append(changes, iam.ChangeSecretGeneratorExpiry(expiry))
+ }
+ if wm.IncludeLowerLetters != includeLowerLetters {
+ changes = append(changes, iam.ChangeSecretGeneratorIncludeLowerLetters(includeLowerLetters))
+ }
+ if wm.IncludeUpperLetters != includeUpperLetters {
+ changes = append(changes, iam.ChangeSecretGeneratorIncludeUpperLetters(includeUpperLetters))
+ }
+ if wm.IncludeDigits != includeDigits {
+ changes = append(changes, iam.ChangeSecretGeneratorIncludeDigits(includeDigits))
+ }
+ if wm.IncludeSymbols != includeSymbols {
+ changes = append(changes, iam.ChangeSecretGeneratorIncludeSymbols(includeSymbols))
+ }
+ if len(changes) == 0 {
+ return nil, false, nil
+ }
+ changeEvent, err := iam.NewSecretGeneratorChangeEvent(ctx, aggregate, generatorType, changes)
+ if err != nil {
+ return nil, false, err
+ }
+ return changeEvent, true, nil
+}
diff --git a/internal/command/iam_settings.go b/internal/command/iam_settings.go
new file mode 100644
index 0000000000..8a73718995
--- /dev/null
+++ b/internal/command/iam_settings.go
@@ -0,0 +1,118 @@
+package command
+
+import (
+ "context"
+
+ "github.com/caos/zitadel/internal/crypto"
+ "github.com/caos/zitadel/internal/domain"
+ caos_errs "github.com/caos/zitadel/internal/errors"
+ "github.com/caos/zitadel/internal/repository/iam"
+)
+
+func (c *Commands) AddSecretGeneratorConfig(ctx context.Context, generatorType domain.SecretGeneratorType, config *crypto.GeneratorConfig) (*domain.ObjectDetails, error) {
+ if generatorType == domain.SecretGeneratorTypeUnspecified {
+ return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-0pkwf", "Errors.SecretGenerator.TypeMissing")
+ }
+
+ generatorWriteModel, err := c.getSecretConfig(ctx, generatorType)
+ if err != nil {
+ return nil, err
+ }
+ if generatorWriteModel.State == domain.SecretGeneratorStateActive {
+ return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-3n9ls", "Errors.SecretGenerator.AlreadyExists")
+ }
+ iamAgg := IAMAggregateFromWriteModel(&generatorWriteModel.WriteModel)
+ pushedEvents, err := c.eventstore.Push(ctx, iam.NewSecretGeneratorAddedEvent(
+ ctx,
+ iamAgg,
+ generatorType,
+ config.Length,
+ config.Expiry,
+ config.IncludeLowerLetters,
+ config.IncludeUpperLetters,
+ config.IncludeDigits,
+ config.IncludeSymbols))
+ if err != nil {
+ return nil, err
+ }
+ err = AppendAndReduce(generatorWriteModel, pushedEvents...)
+ if err != nil {
+ return nil, err
+ }
+ return writeModelToObjectDetails(&generatorWriteModel.WriteModel), nil
+}
+
+func (c *Commands) ChangeSecretGeneratorConfig(ctx context.Context, generatorType domain.SecretGeneratorType, config *crypto.GeneratorConfig) (*domain.ObjectDetails, error) {
+ if generatorType == domain.SecretGeneratorTypeUnspecified {
+ return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-33k9f", "Errors.SecretGenerator.TypeMissing")
+ }
+
+ generatorWriteModel, err := c.getSecretConfig(ctx, generatorType)
+ if err != nil {
+ return nil, err
+ }
+ if generatorWriteModel.State == domain.SecretGeneratorStateUnspecified || generatorWriteModel.State == domain.SecretGeneratorStateRemoved {
+ return nil, caos_errs.ThrowNotFound(nil, "COMMAND-3n9ls", "Errors.SecretGenerator.NotFound")
+ }
+ iamAgg := IAMAggregateFromWriteModel(&generatorWriteModel.WriteModel)
+
+ changedEvent, hasChanged, err := generatorWriteModel.NewChangedEvent(
+ ctx,
+ iamAgg,
+ generatorType,
+ config.Length,
+ config.Expiry,
+ config.IncludeLowerLetters,
+ config.IncludeUpperLetters,
+ config.IncludeDigits,
+ config.IncludeSymbols)
+ if err != nil {
+ return nil, err
+ }
+ if !hasChanged {
+ return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-m0o3f", "Errors.NoChangesFound")
+ }
+ pushedEvents, err := c.eventstore.Push(ctx, changedEvent)
+ if err != nil {
+ return nil, err
+ }
+ err = AppendAndReduce(generatorWriteModel, pushedEvents...)
+ if err != nil {
+ return nil, err
+ }
+ return writeModelToObjectDetails(&generatorWriteModel.WriteModel), nil
+}
+
+func (c *Commands) RemoveSecretGeneratorConfig(ctx context.Context, generatorType domain.SecretGeneratorType) (*domain.ObjectDetails, error) {
+ if generatorType == domain.SecretGeneratorTypeUnspecified {
+ return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-2j9lw", "Errors.SecretGenerator.TypeMissing")
+ }
+
+ generatorWriteModel, err := c.getSecretConfig(ctx, generatorType)
+ if err != nil {
+ return nil, err
+ }
+ if generatorWriteModel.State == domain.SecretGeneratorStateUnspecified || generatorWriteModel.State == domain.SecretGeneratorStateRemoved {
+ return nil, caos_errs.ThrowNotFound(nil, "COMMAND-b8les", "Errors.SecretGenerator.NotFound")
+ }
+ iamAgg := IAMAggregateFromWriteModel(&generatorWriteModel.WriteModel)
+ pushedEvents, err := c.eventstore.Push(ctx, iam.NewSecretGeneratorRemovedEvent(ctx, iamAgg, generatorType))
+ if err != nil {
+ return nil, err
+ }
+ err = AppendAndReduce(generatorWriteModel, pushedEvents...)
+ if err != nil {
+ return nil, err
+ }
+ return writeModelToObjectDetails(&generatorWriteModel.WriteModel), nil
+}
+
+func (c *Commands) getSecretConfig(ctx context.Context, generatorType domain.SecretGeneratorType) (_ *IAMSecretGeneratorConfigWriteModel, err error) {
+ writeModel := NewIAMSecretGeneratorConfigWriteModel(generatorType)
+ err = c.eventstore.FilterToQueryReducer(ctx, writeModel)
+ if err != nil {
+ return nil, err
+ }
+
+ return writeModel, nil
+}
diff --git a/internal/command/iam_settings_test.go b/internal/command/iam_settings_test.go
new file mode 100644
index 0000000000..db25f5d6d8
--- /dev/null
+++ b/internal/command/iam_settings_test.go
@@ -0,0 +1,512 @@
+package command
+
+import (
+ "context"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+
+ "github.com/caos/zitadel/internal/crypto"
+ "github.com/caos/zitadel/internal/domain"
+ caos_errs "github.com/caos/zitadel/internal/errors"
+ "github.com/caos/zitadel/internal/eventstore"
+ "github.com/caos/zitadel/internal/eventstore/repository"
+ "github.com/caos/zitadel/internal/repository/iam"
+)
+
+func TestCommandSide_AddSecretGenerator(t *testing.T) {
+ type fields struct {
+ eventstore *eventstore.Eventstore
+ }
+ type args struct {
+ ctx context.Context
+ generator *crypto.GeneratorConfig
+ generatorType domain.SecretGeneratorType
+ }
+ type res struct {
+ want *domain.ObjectDetails
+ err func(error) bool
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ res res
+ }{
+ {
+ name: "invalid empty type, error",
+ fields: fields{
+ eventstore: eventstoreExpect(
+ t,
+ ),
+ },
+ args: args{
+ ctx: context.Background(),
+ generator: &crypto.GeneratorConfig{},
+ generatorType: domain.SecretGeneratorTypeUnspecified,
+ },
+ res: res{
+ err: caos_errs.IsErrorInvalidArgument,
+ },
+ },
+ {
+ name: "secret generator config, error already exists",
+ fields: fields{
+ eventstore: eventstoreExpect(
+ t,
+ expectFilter(
+ eventFromEventPusher(
+ iam.NewSecretGeneratorAddedEvent(context.Background(),
+ &iam.NewAggregate().Aggregate,
+ domain.SecretGeneratorTypeInitCode,
+ 4,
+ time.Hour*1,
+ true,
+ true,
+ true,
+ true,
+ ),
+ ),
+ ),
+ ),
+ },
+ args: args{
+ ctx: context.Background(),
+ generator: &crypto.GeneratorConfig{
+ Length: 4,
+ Expiry: 1 * time.Hour,
+ IncludeLowerLetters: true,
+ IncludeUpperLetters: true,
+ IncludeDigits: true,
+ IncludeSymbols: true,
+ },
+ generatorType: domain.SecretGeneratorTypeInitCode,
+ },
+ res: res{
+ err: caos_errs.IsErrorAlreadyExists,
+ },
+ },
+ {
+ name: "add secret generator, ok",
+ fields: fields{
+ eventstore: eventstoreExpect(
+ t,
+ expectFilter(),
+ expectPush(
+ []*repository.Event{
+ eventFromEventPusher(iam.NewSecretGeneratorAddedEvent(
+ context.Background(),
+ &iam.NewAggregate().Aggregate,
+ domain.SecretGeneratorTypeInitCode,
+ 4,
+ time.Hour*1,
+ true,
+ true,
+ true,
+ true,
+ ),
+ ),
+ },
+ uniqueConstraintsFromEventConstraint(iam.NewAddSecretGeneratorTypeUniqueConstraint(domain.SecretGeneratorTypeInitCode)),
+ ),
+ ),
+ },
+ args: args{
+ ctx: context.Background(),
+ generator: &crypto.GeneratorConfig{
+ Length: 4,
+ Expiry: 1 * time.Hour,
+ IncludeLowerLetters: true,
+ IncludeUpperLetters: true,
+ IncludeDigits: true,
+ IncludeSymbols: true,
+ },
+ generatorType: domain.SecretGeneratorTypeInitCode,
+ },
+ res: res{
+ want: &domain.ObjectDetails{
+ ResourceOwner: "IAM",
+ },
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ r := &Commands{
+ eventstore: tt.fields.eventstore,
+ }
+ got, err := r.AddSecretGeneratorConfig(tt.args.ctx, tt.args.generatorType, tt.args.generator)
+ if tt.res.err == nil {
+ assert.NoError(t, err)
+ }
+ if tt.res.err != nil && !tt.res.err(err) {
+ t.Errorf("got wrong err: %v ", err)
+ }
+ if tt.res.err == nil {
+ assert.Equal(t, tt.res.want, got)
+ }
+ })
+ }
+}
+
+func TestCommandSide_ChangeSecretGenerator(t *testing.T) {
+ type fields struct {
+ eventstore *eventstore.Eventstore
+ }
+ type args struct {
+ ctx context.Context
+ generator *crypto.GeneratorConfig
+ generatorType domain.SecretGeneratorType
+ }
+ type res struct {
+ want *domain.ObjectDetails
+ err func(error) bool
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ res res
+ }{
+ {
+ name: "empty generatortype, invalid error",
+ fields: fields{
+ eventstore: eventstoreExpect(
+ t,
+ ),
+ },
+ args: args{
+ ctx: context.Background(),
+ generator: &crypto.GeneratorConfig{},
+ generatorType: domain.SecretGeneratorTypeUnspecified,
+ },
+ res: res{
+ err: caos_errs.IsErrorInvalidArgument,
+ },
+ },
+ {
+ name: "generator not existing, not found error",
+ fields: fields{
+ eventstore: eventstoreExpect(
+ t,
+ expectFilter(),
+ ),
+ },
+ args: args{
+ ctx: context.Background(),
+ generatorType: domain.SecretGeneratorTypeInitCode,
+ },
+ res: res{
+ err: caos_errs.IsNotFound,
+ },
+ },
+ {
+ name: "generator removed, not found error",
+ fields: fields{
+ eventstore: eventstoreExpect(
+ t,
+ expectFilter(
+ eventFromEventPusher(
+ iam.NewSecretGeneratorAddedEvent(
+ context.Background(),
+ &iam.NewAggregate().Aggregate,
+ domain.SecretGeneratorTypeInitCode,
+ 4,
+ time.Hour*1,
+ true,
+ true,
+ true,
+ true,
+ ),
+ ),
+ eventFromEventPusher(
+ iam.NewSecretGeneratorRemovedEvent(context.Background(),
+ &iam.NewAggregate().Aggregate,
+ domain.SecretGeneratorTypeInitCode),
+ ),
+ ),
+ ),
+ },
+ args: args{
+ ctx: context.Background(),
+ generatorType: domain.SecretGeneratorTypeInitCode,
+ },
+ res: res{
+ err: caos_errs.IsNotFound,
+ },
+ },
+ {
+ name: "no changes, precondition error",
+ fields: fields{
+ eventstore: eventstoreExpect(
+ t,
+ expectFilter(
+ eventFromEventPusher(
+ iam.NewSecretGeneratorAddedEvent(
+ context.Background(),
+ &iam.NewAggregate().Aggregate,
+ domain.SecretGeneratorTypeInitCode,
+ 4,
+ time.Hour*1,
+ true,
+ true,
+ true,
+ true,
+ ),
+ ),
+ ),
+ ),
+ },
+ args: args{
+ ctx: context.Background(),
+ generator: &crypto.GeneratorConfig{
+ Length: 4,
+ Expiry: 1 * time.Hour,
+ IncludeLowerLetters: true,
+ IncludeUpperLetters: true,
+ IncludeDigits: true,
+ IncludeSymbols: true,
+ },
+ generatorType: domain.SecretGeneratorTypeInitCode,
+ },
+ res: res{
+ err: caos_errs.IsPreconditionFailed,
+ },
+ },
+ {
+ name: "secret generator change, ok",
+ fields: fields{
+ eventstore: eventstoreExpect(
+ t,
+ expectFilter(
+ eventFromEventPusher(
+ iam.NewSecretGeneratorAddedEvent(
+ context.Background(),
+ &iam.NewAggregate().Aggregate,
+ domain.SecretGeneratorTypeInitCode,
+ 4,
+ time.Hour*1,
+ true,
+ true,
+ true,
+ true,
+ ),
+ ),
+ ),
+ expectPush(
+ []*repository.Event{
+ eventFromEventPusher(
+ newSecretGeneratorChangedEvent(context.Background(),
+ domain.SecretGeneratorTypeInitCode,
+ 8,
+ time.Hour*2,
+ false,
+ false,
+ false,
+ false),
+ ),
+ },
+ ),
+ ),
+ },
+ args: args{
+ ctx: context.Background(),
+ generator: &crypto.GeneratorConfig{
+ Length: 8,
+ Expiry: 2 * time.Hour,
+ IncludeLowerLetters: false,
+ IncludeUpperLetters: false,
+ IncludeDigits: false,
+ IncludeSymbols: false,
+ },
+ generatorType: domain.SecretGeneratorTypeInitCode,
+ },
+ res: res{
+ want: &domain.ObjectDetails{
+ ResourceOwner: "IAM",
+ },
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ r := &Commands{
+ eventstore: tt.fields.eventstore,
+ }
+ got, err := r.ChangeSecretGeneratorConfig(tt.args.ctx, tt.args.generatorType, tt.args.generator)
+ if tt.res.err == nil {
+ assert.NoError(t, err)
+ }
+ if tt.res.err != nil && !tt.res.err(err) {
+ t.Errorf("got wrong err: %v ", err)
+ }
+ if tt.res.err == nil {
+ assert.Equal(t, tt.res.want, got)
+ }
+ })
+ }
+}
+
+func TestCommandSide_RemoveSecretGenerator(t *testing.T) {
+ type fields struct {
+ eventstore *eventstore.Eventstore
+ }
+ type args struct {
+ ctx context.Context
+ generatorType domain.SecretGeneratorType
+ }
+ type res struct {
+ want *domain.ObjectDetails
+ err func(error) bool
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ res res
+ }{
+ {
+ name: "empty type, invalid error",
+ fields: fields{
+ eventstore: eventstoreExpect(
+ t,
+ ),
+ },
+ args: args{
+ ctx: context.Background(),
+ generatorType: domain.SecretGeneratorTypeUnspecified,
+ },
+ res: res{
+ err: caos_errs.IsErrorInvalidArgument,
+ },
+ },
+ {
+ name: "generator not existing, not found error",
+ fields: fields{
+ eventstore: eventstoreExpect(
+ t,
+ expectFilter(),
+ ),
+ },
+ args: args{
+ ctx: context.Background(),
+ generatorType: domain.SecretGeneratorTypeInitCode,
+ },
+ res: res{
+ err: caos_errs.IsNotFound,
+ },
+ },
+ {
+ name: "generator removed, not found error",
+ fields: fields{
+ eventstore: eventstoreExpect(
+ t,
+ expectFilter(
+ eventFromEventPusher(
+ iam.NewSecretGeneratorAddedEvent(
+ context.Background(),
+ &iam.NewAggregate().Aggregate,
+ domain.SecretGeneratorTypeInitCode,
+ 4,
+ time.Hour*1,
+ true,
+ true,
+ true,
+ true,
+ ),
+ ),
+ eventFromEventPusher(
+ iam.NewSecretGeneratorRemovedEvent(context.Background(),
+ &iam.NewAggregate().Aggregate,
+ domain.SecretGeneratorTypeInitCode),
+ ),
+ ),
+ ),
+ },
+ args: args{
+ ctx: context.Background(),
+ generatorType: domain.SecretGeneratorTypeInitCode,
+ },
+ res: res{
+ err: caos_errs.IsNotFound,
+ },
+ },
+ {
+ name: "generator config remove, ok",
+ fields: fields{
+ eventstore: eventstoreExpect(
+ t,
+ expectFilter(
+ eventFromEventPusher(
+ iam.NewSecretGeneratorAddedEvent(
+ context.Background(),
+ &iam.NewAggregate().Aggregate,
+ domain.SecretGeneratorTypeInitCode,
+ 4,
+ time.Hour*1,
+ true,
+ true,
+ true,
+ true,
+ ),
+ ),
+ ),
+ expectPush(
+ []*repository.Event{
+ eventFromEventPusher(
+ iam.NewSecretGeneratorRemovedEvent(context.Background(),
+ &iam.NewAggregate().Aggregate,
+ domain.SecretGeneratorTypeInitCode),
+ ),
+ },
+ uniqueConstraintsFromEventConstraint(iam.NewRemoveSecretGeneratorTypeUniqueConstraint(domain.SecretGeneratorTypeInitCode)),
+ ),
+ ),
+ },
+ args: args{
+ ctx: context.Background(),
+ generatorType: domain.SecretGeneratorTypeInitCode,
+ },
+ res: res{
+ want: &domain.ObjectDetails{
+ ResourceOwner: "IAM",
+ },
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ r := &Commands{
+ eventstore: tt.fields.eventstore,
+ }
+ got, err := r.RemoveSecretGeneratorConfig(tt.args.ctx, tt.args.generatorType)
+ if tt.res.err == nil {
+ assert.NoError(t, err)
+ }
+ if tt.res.err != nil && !tt.res.err(err) {
+ t.Errorf("got wrong err: %v ", err)
+ }
+ if tt.res.err == nil {
+ assert.Equal(t, tt.res.want, got)
+ }
+ })
+ }
+}
+
+func newSecretGeneratorChangedEvent(ctx context.Context, generatorType domain.SecretGeneratorType, length uint, expiry time.Duration, lowerCase, upperCase, digits, symbols bool) *iam.SecretGeneratorChangedEvent {
+ changes := []iam.SecretGeneratorChanges{
+ iam.ChangeSecretGeneratorLength(length),
+ iam.ChangeSecretGeneratorExpiry(expiry),
+ iam.ChangeSecretGeneratorIncludeLowerLetters(lowerCase),
+ iam.ChangeSecretGeneratorIncludeUpperLetters(upperCase),
+ iam.ChangeSecretGeneratorIncludeDigits(digits),
+ iam.ChangeSecretGeneratorIncludeSymbols(symbols),
+ }
+ event, _ := iam.NewSecretGeneratorChangeEvent(ctx,
+ &iam.NewAggregate().Aggregate,
+ generatorType,
+ changes,
+ )
+ return event
+}
diff --git a/internal/command/iam_smtp_config_model.go b/internal/command/iam_smtp_config_model.go
new file mode 100644
index 0000000000..fc778ac5ad
--- /dev/null
+++ b/internal/command/iam_smtp_config_model.go
@@ -0,0 +1,106 @@
+package command
+
+import (
+ "context"
+
+ "github.com/caos/zitadel/internal/crypto"
+ "github.com/caos/zitadel/internal/domain"
+ "github.com/caos/zitadel/internal/eventstore"
+ "github.com/caos/zitadel/internal/repository/iam"
+)
+
+type IAMSMTPConfigWriteModel struct {
+ eventstore.WriteModel
+
+ SenderAddress string
+ SenderName string
+ TLS bool
+ Host string
+ User string
+ Password *crypto.CryptoValue
+ State domain.SMTPConfigState
+}
+
+func NewIAMSMTPConfigWriteModel() *IAMSMTPConfigWriteModel {
+ return &IAMSMTPConfigWriteModel{
+ WriteModel: eventstore.WriteModel{
+ AggregateID: domain.IAMID,
+ ResourceOwner: domain.IAMID,
+ },
+ }
+}
+
+func (wm *IAMSMTPConfigWriteModel) Reduce() error {
+ for _, event := range wm.Events {
+ switch e := event.(type) {
+ case *iam.SMTPConfigAddedEvent:
+ wm.TLS = e.TLS
+ wm.SenderAddress = e.SenderAddress
+ wm.SenderName = e.SenderName
+ wm.Host = e.Host
+ wm.User = e.User
+ wm.Password = e.Password
+ wm.State = domain.SMTPConfigStateActive
+ case *iam.SMTPConfigChangedEvent:
+ if e.TLS != nil {
+ wm.TLS = *e.TLS
+ }
+ if e.FromAddress != nil {
+ wm.SenderAddress = *e.FromAddress
+ }
+ if e.FromName != nil {
+ wm.SenderName = *e.FromName
+ }
+ if e.Host != nil {
+ wm.Host = *e.Host
+ }
+ if e.User != nil {
+ wm.User = *e.User
+ }
+ }
+ }
+ return wm.WriteModel.Reduce()
+}
+
+func (wm *IAMSMTPConfigWriteModel) Query() *eventstore.SearchQueryBuilder {
+ return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
+ ResourceOwner(wm.ResourceOwner).
+ AddQuery().
+ AggregateTypes(iam.AggregateType).
+ AggregateIDs(wm.AggregateID).
+ EventTypes(
+ iam.SMTPConfigAddedEventType,
+ iam.SMTPConfigChangedEventType,
+ iam.SMTPConfigPasswordChangedEventType).
+ Builder()
+}
+
+func (wm *IAMSMTPConfigWriteModel) NewChangedEvent(ctx context.Context, aggregate *eventstore.Aggregate, tls bool, fromAddress, fromName, smtpHost, smtpUser string) (*iam.SMTPConfigChangedEvent, bool, error) {
+ changes := make([]iam.SMTPConfigChanges, 0)
+ var err error
+
+ if wm.TLS != tls {
+ changes = append(changes, iam.ChangeSMTPConfigTLS(tls))
+ }
+ if wm.SenderAddress != fromAddress {
+ changes = append(changes, iam.ChangeSMTPConfigFromAddress(fromAddress))
+ }
+ if wm.SenderName != fromName {
+ changes = append(changes, iam.ChangeSMTPConfigFromName(fromName))
+ }
+ if wm.Host != smtpHost {
+ changes = append(changes, iam.ChangeSMTPConfigSMTPHost(smtpHost))
+ }
+ if wm.User != smtpUser {
+ changes = append(changes, iam.ChangeSMTPConfigSMTPUser(smtpUser))
+ }
+
+ if len(changes) == 0 {
+ return nil, false, nil
+ }
+ changeEvent, err := iam.NewSMTPConfigChangeEvent(ctx, aggregate, changes)
+ if err != nil {
+ return nil, false, err
+ }
+ return changeEvent, true, nil
+}
diff --git a/internal/command/org.go b/internal/command/org.go
index 288f804a6c..0f8fe083f3 100644
--- a/internal/command/org.go
+++ b/internal/command/org.go
@@ -3,6 +3,7 @@ package command
import (
"context"
+ "github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
@@ -31,7 +32,7 @@ func (c *Commands) checkOrgExists(ctx context.Context, orgID string) error {
return nil
}
-func (c *Commands) SetUpOrg(ctx context.Context, organisation *domain.Org, admin *domain.Human, claimedUserIDs []string, selfregistered bool) (*domain.ObjectDetails, error) {
+func (c *Commands) SetUpOrg(ctx context.Context, organisation *domain.Org, admin *domain.Human, initCodeGenerator crypto.Generator, phoneCodeGenerator crypto.Generator, claimedUserIDs []string, selfregistered bool) (*domain.ObjectDetails, error) {
orgIAMPolicy, err := c.getDefaultOrgIAMPolicy(ctx)
if err != nil {
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-33M9f", "Errors.IAM.OrgIAMPolicy.NotFound")
@@ -40,7 +41,7 @@ func (c *Commands) SetUpOrg(ctx context.Context, organisation *domain.Org, admin
if err != nil {
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-M5Fsd", "Errors.IAM.PasswordComplexity.NotFound")
}
- _, orgWriteModel, _, _, events, err := c.setUpOrg(ctx, organisation, admin, orgIAMPolicy, pwPolicy, claimedUserIDs, selfregistered)
+ _, orgWriteModel, _, _, events, err := c.setUpOrg(ctx, organisation, admin, orgIAMPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator, claimedUserIDs, selfregistered)
if err != nil {
return nil, err
}
@@ -168,6 +169,8 @@ func (c *Commands) setUpOrg(
admin *domain.Human,
loginPolicy *domain.OrgIAMPolicy,
pwPolicy *domain.PasswordComplexityPolicy,
+ initCodeGenerator crypto.Generator,
+ phoneCodeGenerator crypto.Generator,
claimedUserIDs []string,
selfregistered bool,
) (orgAgg *eventstore.Aggregate, org *OrgWriteModel, human *HumanWriteModel, orgMember *OrgMemberWriteModel, events []eventstore.Command, err error) {
@@ -178,9 +181,9 @@ func (c *Commands) setUpOrg(
var userEvents []eventstore.Command
if selfregistered {
- userEvents, human, err = c.registerHuman(ctx, orgAgg.ID, admin, nil, loginPolicy, pwPolicy)
+ userEvents, human, err = c.registerHuman(ctx, orgAgg.ID, admin, nil, loginPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator)
} else {
- userEvents, human, err = c.addHuman(ctx, orgAgg.ID, admin, loginPolicy, pwPolicy)
+ userEvents, human, err = c.addHuman(ctx, orgAgg.ID, admin, loginPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator)
}
if err != nil {
return nil, nil, nil, nil, nil, err
diff --git a/internal/command/project_application_api.go b/internal/command/project_application_api.go
index 4006f1bce7..86857793da 100644
--- a/internal/command/project_application_api.go
+++ b/internal/command/project_application_api.go
@@ -13,7 +13,7 @@ import (
"github.com/caos/zitadel/internal/telemetry/tracing"
)
-func (c *Commands) AddAPIApplication(ctx context.Context, application *domain.APIApp, resourceOwner string) (_ *domain.APIApp, err error) {
+func (c *Commands) AddAPIApplication(ctx context.Context, application *domain.APIApp, resourceOwner string, appSecretGenerator crypto.Generator) (_ *domain.APIApp, err error) {
if application == nil || application.AggregateID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-5m9E", "Errors.Application.Invalid")
}
@@ -23,7 +23,7 @@ func (c *Commands) AddAPIApplication(ctx context.Context, application *domain.AP
}
addedApplication := NewAPIApplicationWriteModel(application.AggregateID, resourceOwner)
projectAgg := ProjectAggregateFromWriteModel(&addedApplication.WriteModel)
- events, stringPw, err := c.addAPIApplication(ctx, projectAgg, project, application, resourceOwner)
+ events, stringPw, err := c.addAPIApplication(ctx, projectAgg, project, application, resourceOwner, appSecretGenerator)
if err != nil {
return nil, err
}
@@ -41,7 +41,7 @@ func (c *Commands) AddAPIApplication(ctx context.Context, application *domain.AP
return result, nil
}
-func (c *Commands) addAPIApplication(ctx context.Context, projectAgg *eventstore.Aggregate, proj *domain.Project, apiAppApp *domain.APIApp, resourceOwner string) (events []eventstore.Command, stringPW string, err error) {
+func (c *Commands) addAPIApplication(ctx context.Context, projectAgg *eventstore.Aggregate, proj *domain.Project, apiAppApp *domain.APIApp, resourceOwner string, appSecretGenerator crypto.Generator) (events []eventstore.Command, stringPW string, err error) {
if !apiAppApp.IsValid() {
return nil, "", caos_errs.ThrowInvalidArgument(nil, "PROJECT-Bff2g", "Errors.Application.Invalid")
}
@@ -59,7 +59,7 @@ func (c *Commands) addAPIApplication(ctx context.Context, projectAgg *eventstore
if err != nil {
return nil, "", err
}
- stringPw, err = domain.SetNewClientSecretIfNeeded(apiAppApp, c.applicationSecretGenerator)
+ stringPw, err = domain.SetNewClientSecretIfNeeded(apiAppApp, appSecretGenerator)
if err != nil {
return nil, "", err
}
@@ -113,7 +113,7 @@ func (c *Commands) ChangeAPIApplication(ctx context.Context, apiApp *domain.APIA
return apiWriteModelToAPIConfig(existingAPI), nil
}
-func (c *Commands) ChangeAPIApplicationSecret(ctx context.Context, projectID, appID, resourceOwner string) (*domain.APIApp, error) {
+func (c *Commands) ChangeAPIApplicationSecret(ctx context.Context, projectID, appID, resourceOwner string, appSecretGenerator crypto.Generator) (*domain.APIApp, error) {
if projectID == "" || appID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-99i83", "Errors.IDMissing")
}
@@ -128,7 +128,7 @@ func (c *Commands) ChangeAPIApplicationSecret(ctx context.Context, projectID, ap
if !existingAPI.IsAPI() {
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-aeH4", "Errors.Project.App.IsNotAPI")
}
- cryptoSecret, stringPW, err := domain.NewClientSecret(c.applicationSecretGenerator)
+ cryptoSecret, stringPW, err := domain.NewClientSecret(appSecretGenerator)
if err != nil {
return nil, err
}
diff --git a/internal/command/project_application_api_test.go b/internal/command/project_application_api_test.go
index 6de1636741..507ac3512b 100644
--- a/internal/command/project_application_api_test.go
+++ b/internal/command/project_application_api_test.go
@@ -2,6 +2,8 @@ package command
import (
"context"
+ "testing"
+
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors"
@@ -12,19 +14,18 @@ import (
id_mock "github.com/caos/zitadel/internal/id/mock"
"github.com/caos/zitadel/internal/repository/project"
"github.com/stretchr/testify/assert"
- "testing"
)
func TestCommandSide_AddAPIApplication(t *testing.T) {
type fields struct {
- eventstore *eventstore.Eventstore
- idGenerator id.Generator
- secretGenerator crypto.Generator
+ eventstore *eventstore.Eventstore
+ idGenerator id.Generator
}
type args struct {
- ctx context.Context
- apiApp *domain.APIApp
- resourceOwner string
+ ctx context.Context
+ apiApp *domain.APIApp
+ resourceOwner string
+ secretGenerator crypto.Generator
}
type res struct {
want *domain.APIApp
@@ -144,8 +145,7 @@ func TestCommandSide_AddAPIApplication(t *testing.T) {
uniqueConstraintsFromEventConstraint(project.NewAddApplicationUniqueConstraint("app", "project1")),
),
),
- idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "app1", "client1"),
- secretGenerator: GetMockSecretGenerator(t),
+ idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "app1", "client1"),
},
args: args{
ctx: context.Background(),
@@ -156,7 +156,8 @@ func TestCommandSide_AddAPIApplication(t *testing.T) {
AppName: "app",
AuthMethodType: domain.APIAuthMethodTypeBasic,
},
- resourceOwner: "org1",
+ resourceOwner: "org1",
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.APIApp{
@@ -238,11 +239,10 @@ func TestCommandSide_AddAPIApplication(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
- eventstore: tt.fields.eventstore,
- idGenerator: tt.fields.idGenerator,
- applicationSecretGenerator: tt.fields.secretGenerator,
+ eventstore: tt.fields.eventstore,
+ idGenerator: tt.fields.idGenerator,
}
- got, err := r.AddAPIApplication(tt.args.ctx, tt.args.apiApp, tt.args.resourceOwner)
+ got, err := r.AddAPIApplication(tt.args.ctx, tt.args.apiApp, tt.args.resourceOwner, tt.args.secretGenerator)
if tt.res.err == nil {
assert.NoError(t, err)
}
@@ -472,14 +472,14 @@ func TestCommandSide_ChangeAPIApplication(t *testing.T) {
func TestCommandSide_ChangeAPIApplicationSecret(t *testing.T) {
type fields struct {
- eventstore *eventstore.Eventstore
- secretGenerator crypto.Generator
+ eventstore *eventstore.Eventstore
}
type args struct {
- ctx context.Context
- appID string
- projectID string
- resourceOwner string
+ ctx context.Context
+ appID string
+ projectID string
+ resourceOwner string
+ secretGenerator crypto.Generator
}
type res struct {
want *domain.APIApp
@@ -585,13 +585,13 @@ func TestCommandSide_ChangeAPIApplicationSecret(t *testing.T) {
},
),
),
- secretGenerator: GetMockSecretGenerator(t),
},
args: args{
- ctx: context.Background(),
- projectID: "project1",
- appID: "app1",
- resourceOwner: "org1",
+ ctx: context.Background(),
+ projectID: "project1",
+ appID: "app1",
+ resourceOwner: "org1",
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.APIApp{
@@ -612,10 +612,9 @@ func TestCommandSide_ChangeAPIApplicationSecret(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
- eventstore: tt.fields.eventstore,
- applicationSecretGenerator: tt.fields.secretGenerator,
+ eventstore: tt.fields.eventstore,
}
- got, err := r.ChangeAPIApplicationSecret(tt.args.ctx, tt.args.projectID, tt.args.appID, tt.args.resourceOwner)
+ got, err := r.ChangeAPIApplicationSecret(tt.args.ctx, tt.args.projectID, tt.args.appID, tt.args.resourceOwner, tt.args.secretGenerator)
if tt.res.err == nil {
assert.NoError(t, err)
}
diff --git a/internal/command/project_application_key_test.go b/internal/command/project_application_key_test.go
index cc4ead3889..3643e1ebfd 100644
--- a/internal/command/project_application_key_test.go
+++ b/internal/command/project_application_key_test.go
@@ -2,6 +2,8 @@ package command
import (
"context"
+ "testing"
+
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors"
@@ -11,15 +13,13 @@ import (
id_mock "github.com/caos/zitadel/internal/id/mock"
"github.com/caos/zitadel/internal/repository/project"
"github.com/stretchr/testify/assert"
- "testing"
)
func TestCommandSide_AddAPIApplicationKey(t *testing.T) {
type fields struct {
- eventstore *eventstore.Eventstore
- idGenerator id.Generator
- secretGenerator crypto.Generator
- keySize int
+ eventstore *eventstore.Eventstore
+ idGenerator id.Generator
+ keySize int
}
type args struct {
ctx context.Context
@@ -126,8 +126,7 @@ func TestCommandSide_AddAPIApplicationKey(t *testing.T) {
),
),
),
- idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "key1"),
- secretGenerator: GetMockSecretGenerator(t),
+ idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "key1"),
},
args: args{
ctx: context.Background(),
@@ -173,9 +172,8 @@ func TestCommandSide_AddAPIApplicationKey(t *testing.T) {
),
),
),
- idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "key1"),
- secretGenerator: GetMockSecretGenerator(t),
- keySize: 10,
+ idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "key1"),
+ keySize: 10,
},
args: args{
ctx: context.Background(),
@@ -195,10 +193,9 @@ func TestCommandSide_AddAPIApplicationKey(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
- eventstore: tt.fields.eventstore,
- idGenerator: tt.fields.idGenerator,
- applicationSecretGenerator: tt.fields.secretGenerator,
- applicationKeySize: tt.fields.keySize,
+ eventstore: tt.fields.eventstore,
+ idGenerator: tt.fields.idGenerator,
+ applicationKeySize: tt.fields.keySize,
}
got, err := r.AddApplicationKey(tt.args.ctx, tt.args.key, tt.args.resourceOwner)
if tt.res.err == nil {
diff --git a/internal/command/project_application_oidc.go b/internal/command/project_application_oidc.go
index e26cd7d7df..1b07eae68f 100644
--- a/internal/command/project_application_oidc.go
+++ b/internal/command/project_application_oidc.go
@@ -13,7 +13,7 @@ import (
"github.com/caos/zitadel/internal/telemetry/tracing"
)
-func (c *Commands) AddOIDCApplication(ctx context.Context, application *domain.OIDCApp, resourceOwner string) (_ *domain.OIDCApp, err error) {
+func (c *Commands) AddOIDCApplication(ctx context.Context, application *domain.OIDCApp, resourceOwner string, appSecretGenerator crypto.Generator) (_ *domain.OIDCApp, err error) {
if application == nil || application.AggregateID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "PROJECT-34Fm0", "Errors.Application.Invalid")
}
@@ -23,7 +23,7 @@ func (c *Commands) AddOIDCApplication(ctx context.Context, application *domain.O
}
addedApplication := NewOIDCApplicationWriteModel(application.AggregateID, resourceOwner)
projectAgg := ProjectAggregateFromWriteModel(&addedApplication.WriteModel)
- events, stringPw, err := c.addOIDCApplication(ctx, projectAgg, project, application, resourceOwner)
+ events, stringPw, err := c.addOIDCApplication(ctx, projectAgg, project, application, resourceOwner, appSecretGenerator)
if err != nil {
return nil, err
}
@@ -42,7 +42,7 @@ func (c *Commands) AddOIDCApplication(ctx context.Context, application *domain.O
return result, nil
}
-func (c *Commands) addOIDCApplication(ctx context.Context, projectAgg *eventstore.Aggregate, proj *domain.Project, oidcApp *domain.OIDCApp, resourceOwner string) (events []eventstore.Command, stringPW string, err error) {
+func (c *Commands) addOIDCApplication(ctx context.Context, projectAgg *eventstore.Aggregate, proj *domain.Project, oidcApp *domain.OIDCApp, resourceOwner string, appSecretGenerator crypto.Generator) (events []eventstore.Command, stringPW string, err error) {
if oidcApp.AppName == "" || !oidcApp.IsValid() {
return nil, "", caos_errs.ThrowInvalidArgument(nil, "PROJECT-1n8df", "Errors.Application.Invalid")
}
@@ -60,7 +60,7 @@ func (c *Commands) addOIDCApplication(ctx context.Context, projectAgg *eventstor
if err != nil {
return nil, "", err
}
- stringPw, err = domain.SetNewClientSecretIfNeeded(oidcApp, c.applicationSecretGenerator)
+ stringPw, err = domain.SetNewClientSecretIfNeeded(oidcApp, appSecretGenerator)
if err != nil {
return nil, "", err
}
@@ -142,7 +142,7 @@ func (c *Commands) ChangeOIDCApplication(ctx context.Context, oidc *domain.OIDCA
return result, nil
}
-func (c *Commands) ChangeOIDCApplicationSecret(ctx context.Context, projectID, appID, resourceOwner string) (*domain.OIDCApp, error) {
+func (c *Commands) ChangeOIDCApplicationSecret(ctx context.Context, projectID, appID, resourceOwner string, appSecretGenerator crypto.Generator) (*domain.OIDCApp, error) {
if projectID == "" || appID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-99i83", "Errors.IDMissing")
}
@@ -157,7 +157,7 @@ func (c *Commands) ChangeOIDCApplicationSecret(ctx context.Context, projectID, a
if !existingOIDC.IsOIDC() {
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-Ghrh3", "Errors.Project.App.IsNotOIDC")
}
- cryptoSecret, stringPW, err := domain.NewClientSecret(c.applicationSecretGenerator)
+ cryptoSecret, stringPW, err := domain.NewClientSecret(appSecretGenerator)
if err != nil {
return nil, err
}
diff --git a/internal/command/project_application_oidc_test.go b/internal/command/project_application_oidc_test.go
index d690bbe8da..7a34d0645e 100644
--- a/internal/command/project_application_oidc_test.go
+++ b/internal/command/project_application_oidc_test.go
@@ -20,14 +20,14 @@ import (
func TestCommandSide_AddOIDCApplication(t *testing.T) {
type fields struct {
- eventstore *eventstore.Eventstore
- idGenerator id.Generator
- secretGenerator crypto.Generator
+ eventstore *eventstore.Eventstore
+ idGenerator id.Generator
}
type args struct {
- ctx context.Context
- oidcApp *domain.OIDCApp
- resourceOwner string
+ ctx context.Context
+ oidcApp *domain.OIDCApp
+ resourceOwner string
+ secretGenerator crypto.Generator
}
type res struct {
want *domain.OIDCApp
@@ -160,8 +160,7 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) {
uniqueConstraintsFromEventConstraint(project.NewAddApplicationUniqueConstraint("app", "project1")),
),
),
- idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "app1", "client1"),
- secretGenerator: GetMockSecretGenerator(t),
+ idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "app1", "client1"),
},
args: args{
ctx: context.Background(),
@@ -185,7 +184,8 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) {
ClockSkew: time.Second * 1,
AdditionalOrigins: []string{"https://sub.test.ch"},
},
- resourceOwner: "org1",
+ resourceOwner: "org1",
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.OIDCApp{
@@ -220,11 +220,10 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
- eventstore: tt.fields.eventstore,
- idGenerator: tt.fields.idGenerator,
- applicationSecretGenerator: tt.fields.secretGenerator,
+ eventstore: tt.fields.eventstore,
+ idGenerator: tt.fields.idGenerator,
}
- got, err := r.AddOIDCApplication(tt.args.ctx, tt.args.oidcApp, tt.args.resourceOwner)
+ got, err := r.AddOIDCApplication(tt.args.ctx, tt.args.oidcApp, tt.args.resourceOwner, tt.args.secretGenerator)
if tt.res.err == nil {
assert.NoError(t, err)
}
@@ -549,14 +548,14 @@ func TestCommandSide_ChangeOIDCApplication(t *testing.T) {
func TestCommandSide_ChangeOIDCApplicationSecret(t *testing.T) {
type fields struct {
- eventstore *eventstore.Eventstore
- secretGenerator crypto.Generator
+ eventstore *eventstore.Eventstore
}
type args struct {
- ctx context.Context
- appID string
- projectID string
- resourceOwner string
+ ctx context.Context
+ appID string
+ projectID string
+ resourceOwner string
+ secretGenerator crypto.Generator
}
type res struct {
want *domain.OIDCApp
@@ -675,13 +674,13 @@ func TestCommandSide_ChangeOIDCApplicationSecret(t *testing.T) {
},
),
),
- secretGenerator: GetMockSecretGenerator(t),
},
args: args{
- ctx: context.Background(),
- projectID: "project1",
- appID: "app1",
- resourceOwner: "org1",
+ ctx: context.Background(),
+ projectID: "project1",
+ appID: "app1",
+ resourceOwner: "org1",
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.OIDCApp{
@@ -715,10 +714,9 @@ func TestCommandSide_ChangeOIDCApplicationSecret(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
- eventstore: tt.fields.eventstore,
- applicationSecretGenerator: tt.fields.secretGenerator,
+ eventstore: tt.fields.eventstore,
}
- got, err := r.ChangeOIDCApplicationSecret(tt.args.ctx, tt.args.projectID, tt.args.appID, tt.args.resourceOwner)
+ got, err := r.ChangeOIDCApplicationSecret(tt.args.ctx, tt.args.projectID, tt.args.appID, tt.args.resourceOwner, tt.args.secretGenerator)
if tt.res.err == nil {
assert.NoError(t, err)
}
diff --git a/internal/command/setup_step1.go b/internal/command/setup_step1.go
index 78bb0a041e..e05db49f94 100644
--- a/internal/command/setup_step1.go
+++ b/internal/command/setup_step1.go
@@ -3,6 +3,7 @@ package command
import (
"context"
+ "github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/v1/models"
@@ -134,7 +135,10 @@ func (c *Commands) SetupStep1(ctx context.Context, step1 *Step1) error {
EmailAddress: organisation.Owner.Email,
IsEmailVerified: true,
},
- }, orgIAMPolicy, pwPolicy, nil, false)
+ }, orgIAMPolicy, pwPolicy,
+ nil, //TODO: Code Generator missing! Should be setuped in step1 create iam
+ nil, //TODO: Code Generator missing! Should be setuped in step1 create iam
+ nil, false)
if err != nil {
return err
}
@@ -180,14 +184,16 @@ func (c *Commands) SetupStep1(ctx context.Context, step1 *Step1) error {
}
//create applications
for _, app := range proj.OIDCApps {
- applicationEvents, err := setUpOIDCApplication(ctx, c, projectWriteModel, project, app, orgAgg.ID)
+ //TODO: Add Secret Generator
+ applicationEvents, err := setUpOIDCApplication(ctx, c, projectWriteModel, project, app, orgAgg.ID, nil)
if err != nil {
return err
}
events = append(events, applicationEvents...)
}
for _, app := range proj.APIs {
- applicationEvents, err := setUpAPI(ctx, c, projectWriteModel, project, app, orgAgg.ID)
+ //TODO: Add Secret Generator
+ applicationEvents, err := setUpAPI(ctx, c, projectWriteModel, project, app, orgAgg.ID, nil)
if err != nil {
return err
}
@@ -205,7 +211,7 @@ func (c *Commands) SetupStep1(ctx context.Context, step1 *Step1) error {
return nil
}
-func setUpOIDCApplication(ctx context.Context, r *Commands, projectWriteModel *ProjectWriteModel, project *domain.Project, oidcApp OIDCApp, resourceOwner string) ([]eventstore.Command, error) {
+func setUpOIDCApplication(ctx context.Context, r *Commands, projectWriteModel *ProjectWriteModel, project *domain.Project, oidcApp OIDCApp, resourceOwner string, appSecretGenerator crypto.Generator) ([]eventstore.Command, error) {
app := &domain.OIDCApp{
ObjectRoot: models.ObjectRoot{
AggregateID: projectWriteModel.AggregateID,
@@ -220,7 +226,7 @@ func setUpOIDCApplication(ctx context.Context, r *Commands, projectWriteModel *P
}
projectAgg := ProjectAggregateFromWriteModel(&projectWriteModel.WriteModel)
- events, _, err := r.addOIDCApplication(ctx, projectAgg, project, app, resourceOwner)
+ events, _, err := r.addOIDCApplication(ctx, projectAgg, project, app, resourceOwner, appSecretGenerator)
if err != nil {
return nil, err
}
@@ -228,7 +234,7 @@ func setUpOIDCApplication(ctx context.Context, r *Commands, projectWriteModel *P
return events, nil
}
-func setUpAPI(ctx context.Context, r *Commands, projectWriteModel *ProjectWriteModel, project *domain.Project, apiApp API, resourceOwner string) ([]eventstore.Command, error) {
+func setUpAPI(ctx context.Context, r *Commands, projectWriteModel *ProjectWriteModel, project *domain.Project, apiApp API, resourceOwner string, appSecretGenerator crypto.Generator) ([]eventstore.Command, error) {
app := &domain.APIApp{
ObjectRoot: models.ObjectRoot{
AggregateID: projectWriteModel.AggregateID,
@@ -238,7 +244,7 @@ func setUpAPI(ctx context.Context, r *Commands, projectWriteModel *ProjectWriteM
}
projectAgg := ProjectAggregateFromWriteModel(&projectWriteModel.WriteModel)
- events, _, err := r.addAPIApplication(ctx, projectAgg, project, app, resourceOwner)
+ events, _, err := r.addAPIApplication(ctx, projectAgg, project, app, resourceOwner, appSecretGenerator)
if err != nil {
return nil, err
}
diff --git a/internal/command/smtp.go b/internal/command/smtp.go
new file mode 100644
index 0000000000..173197e19f
--- /dev/null
+++ b/internal/command/smtp.go
@@ -0,0 +1,119 @@
+package command
+
+import (
+ "context"
+
+ "github.com/caos/zitadel/internal/crypto"
+ "github.com/caos/zitadel/internal/domain"
+ caos_errs "github.com/caos/zitadel/internal/errors"
+ "github.com/caos/zitadel/internal/notification/channels/smtp"
+ "github.com/caos/zitadel/internal/repository/iam"
+)
+
+func (c *Commands) AddSMTPConfig(ctx context.Context, config *smtp.EmailConfig) (*domain.ObjectDetails, error) {
+ smtpConfigWriteModel, err := c.getSMTPConfig(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if smtpConfigWriteModel.State == domain.SMTPConfigStateActive {
+ return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-en9lw", "Errors.SMTPConfig.AlreadyExists")
+ }
+ var smtpPassword *crypto.CryptoValue
+ if config.SMTP.Password != "" {
+ smtpPassword, err = crypto.Encrypt([]byte(config.SMTP.Password), c.smtpPasswordCrypto)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ iamAgg := IAMAggregateFromWriteModel(&smtpConfigWriteModel.WriteModel)
+ pushedEvents, err := c.eventstore.Push(ctx, iam.NewSMTPConfigAddedEvent(
+ ctx,
+ iamAgg,
+ config.Tls,
+ config.From,
+ config.FromName,
+ config.SMTP.Host,
+ config.SMTP.User,
+ smtpPassword))
+ if err != nil {
+ return nil, err
+ }
+ err = AppendAndReduce(smtpConfigWriteModel, pushedEvents...)
+ if err != nil {
+ return nil, err
+ }
+ return writeModelToObjectDetails(&smtpConfigWriteModel.WriteModel), nil
+}
+
+func (c *Commands) ChangeSMTPConfig(ctx context.Context, config *smtp.EmailConfig) (*domain.ObjectDetails, error) {
+ smtpConfigWriteModel, err := c.getSMTPConfig(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if smtpConfigWriteModel.State == domain.SMTPConfigStateUnspecified {
+ return nil, caos_errs.ThrowNotFound(nil, "COMMAND-3n9ls", "Errors.SMTPConfig.NotFound")
+ }
+ iamAgg := IAMAggregateFromWriteModel(&smtpConfigWriteModel.WriteModel)
+
+ changedEvent, hasChanged, err := smtpConfigWriteModel.NewChangedEvent(
+ ctx,
+ iamAgg,
+ config.Tls,
+ config.From,
+ config.FromName,
+ config.SMTP.Host,
+ config.SMTP.User)
+ if err != nil {
+ return nil, err
+ }
+ if !hasChanged {
+ return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-m0o3f", "Errors.NoChangesFound")
+ }
+ pushedEvents, err := c.eventstore.Push(ctx, changedEvent)
+ if err != nil {
+ return nil, err
+ }
+ err = AppendAndReduce(smtpConfigWriteModel, pushedEvents...)
+ if err != nil {
+ return nil, err
+ }
+ return writeModelToObjectDetails(&smtpConfigWriteModel.WriteModel), nil
+}
+
+func (c *Commands) ChangeSMTPConfigPassword(ctx context.Context, password string) (*domain.ObjectDetails, error) {
+ smtpConfigWriteModel, err := c.getSMTPConfig(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if smtpConfigWriteModel.State == domain.SMTPConfigStateUnspecified {
+ return nil, caos_errs.ThrowNotFound(nil, "COMMAND-3n9ls", "Errors.SMTPConfig.NotFound")
+ }
+ iamAgg := IAMAggregateFromWriteModel(&smtpConfigWriteModel.WriteModel)
+ newPW, err := crypto.Encrypt([]byte(password), c.smtpPasswordCrypto)
+ if err != nil {
+ return nil, err
+ }
+ pushedEvents, err := c.eventstore.Push(ctx, iam.NewSMTPConfigPasswordChangedEvent(
+ ctx,
+ iamAgg,
+ newPW))
+ if err != nil {
+ return nil, err
+ }
+ err = AppendAndReduce(smtpConfigWriteModel, pushedEvents...)
+ if err != nil {
+ return nil, err
+ }
+ return writeModelToObjectDetails(&smtpConfigWriteModel.WriteModel), nil
+}
+
+func (c *Commands) getSMTPConfig(ctx context.Context) (_ *IAMSMTPConfigWriteModel, err error) {
+ writeModel := NewIAMSMTPConfigWriteModel()
+ err = c.eventstore.FilterToQueryReducer(ctx, writeModel)
+ if err != nil {
+ return nil, err
+ }
+
+ return writeModel, nil
+}
diff --git a/internal/command/smtp_test.go b/internal/command/smtp_test.go
new file mode 100644
index 0000000000..c297c831e5
--- /dev/null
+++ b/internal/command/smtp_test.go
@@ -0,0 +1,398 @@
+package command
+
+import (
+ "context"
+ "testing"
+
+ "github.com/caos/zitadel/internal/crypto"
+ "github.com/caos/zitadel/internal/notification/channels/smtp"
+ "github.com/golang/mock/gomock"
+ "github.com/stretchr/testify/assert"
+
+ "github.com/caos/zitadel/internal/domain"
+ caos_errs "github.com/caos/zitadel/internal/errors"
+ "github.com/caos/zitadel/internal/eventstore"
+ "github.com/caos/zitadel/internal/eventstore/repository"
+ "github.com/caos/zitadel/internal/repository/iam"
+)
+
+func TestCommandSide_AddSMTPConfig(t *testing.T) {
+ type fields struct {
+ eventstore *eventstore.Eventstore
+ alg crypto.EncryptionAlgorithm
+ }
+ type args struct {
+ ctx context.Context
+ smtp *smtp.EmailConfig
+ }
+ type res struct {
+ want *domain.ObjectDetails
+ err func(error) bool
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ res res
+ }{
+ {
+ name: "smtp config, error already exists",
+ fields: fields{
+ eventstore: eventstoreExpect(
+ t,
+ expectFilter(
+ eventFromEventPusher(
+ iam.NewSMTPConfigAddedEvent(context.Background(),
+ &iam.NewAggregate().Aggregate,
+ true,
+ "from",
+ "name",
+ "host",
+ "user",
+ &crypto.CryptoValue{},
+ ),
+ ),
+ ),
+ ),
+ },
+ args: args{
+ ctx: context.Background(),
+ smtp: &smtp.EmailConfig{
+ Tls: true,
+ },
+ },
+ res: res{
+ err: caos_errs.IsErrorAlreadyExists,
+ },
+ },
+ {
+ name: "add smtp config, ok",
+ fields: fields{
+ eventstore: eventstoreExpect(
+ t,
+ expectFilter(),
+ expectPush(
+ []*repository.Event{
+ eventFromEventPusher(iam.NewSMTPConfigAddedEvent(
+ context.Background(),
+ &iam.NewAggregate().Aggregate,
+ true,
+ "from",
+ "name",
+ "host",
+ "user",
+ &crypto.CryptoValue{
+ CryptoType: crypto.TypeEncryption,
+ Algorithm: "enc",
+ KeyID: "id",
+ Crypted: []byte("password"),
+ },
+ ),
+ ),
+ },
+ ),
+ ),
+ alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
+ },
+ args: args{
+ ctx: context.Background(),
+ smtp: &smtp.EmailConfig{
+ Tls: true,
+ From: "from",
+ FromName: "name",
+ SMTP: smtp.SMTP{
+ Host: "host",
+ User: "user",
+ Password: "password",
+ },
+ },
+ },
+ res: res{
+ want: &domain.ObjectDetails{
+ ResourceOwner: "IAM",
+ },
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ r := &Commands{
+ eventstore: tt.fields.eventstore,
+ smtpPasswordCrypto: tt.fields.alg,
+ }
+ got, err := r.AddSMTPConfig(tt.args.ctx, tt.args.smtp)
+ if tt.res.err == nil {
+ assert.NoError(t, err)
+ }
+ if tt.res.err != nil && !tt.res.err(err) {
+ t.Errorf("got wrong err: %v ", err)
+ }
+ if tt.res.err == nil {
+ assert.Equal(t, tt.res.want, got)
+ }
+ })
+ }
+}
+
+func TestCommandSide_ChangeSMTPConfig(t *testing.T) {
+ type fields struct {
+ eventstore *eventstore.Eventstore
+ }
+ type args struct {
+ ctx context.Context
+ smtp *smtp.EmailConfig
+ }
+ type res struct {
+ want *domain.ObjectDetails
+ err func(error) bool
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ res res
+ }{
+ {
+ name: "smtp not existing, not found error",
+ fields: fields{
+ eventstore: eventstoreExpect(
+ t,
+ expectFilter(),
+ ),
+ },
+ args: args{
+ ctx: context.Background(),
+ smtp: &smtp.EmailConfig{},
+ },
+ res: res{
+ err: caos_errs.IsNotFound,
+ },
+ },
+
+ {
+ name: "no changes, precondition error",
+ fields: fields{
+ eventstore: eventstoreExpect(
+ t,
+ expectFilter(
+ eventFromEventPusher(
+ iam.NewSMTPConfigAddedEvent(
+ context.Background(),
+ &iam.NewAggregate().Aggregate,
+ true,
+ "from",
+ "name",
+ "host",
+ "user",
+ &crypto.CryptoValue{},
+ ),
+ ),
+ ),
+ ),
+ },
+ args: args{
+ ctx: context.Background(),
+ smtp: &smtp.EmailConfig{
+ Tls: true,
+ From: "from",
+ FromName: "name",
+ SMTP: smtp.SMTP{
+ Host: "host",
+ User: "user",
+ },
+ },
+ },
+ res: res{
+ err: caos_errs.IsPreconditionFailed,
+ },
+ },
+ {
+ name: "smtp config change, ok",
+ fields: fields{
+ eventstore: eventstoreExpect(
+ t,
+ expectFilter(
+ eventFromEventPusher(
+ iam.NewSMTPConfigAddedEvent(
+ context.Background(),
+ &iam.NewAggregate().Aggregate,
+ true,
+ "from",
+ "name",
+ "host",
+ "user",
+ &crypto.CryptoValue{},
+ ),
+ ),
+ ),
+ expectPush(
+ []*repository.Event{
+ eventFromEventPusher(
+ newSMTPConfigChangedEvent(
+ context.Background(),
+ false,
+ "from2",
+ "name2",
+ "host2",
+ "user2",
+ ),
+ ),
+ },
+ ),
+ ),
+ },
+ args: args{
+ ctx: context.Background(),
+ smtp: &smtp.EmailConfig{
+ Tls: false,
+ From: "from2",
+ FromName: "name2",
+ SMTP: smtp.SMTP{
+ Host: "host2",
+ User: "user2",
+ },
+ },
+ },
+ res: res{
+ want: &domain.ObjectDetails{
+ ResourceOwner: "IAM",
+ },
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ r := &Commands{
+ eventstore: tt.fields.eventstore,
+ }
+ got, err := r.ChangeSMTPConfig(tt.args.ctx, tt.args.smtp)
+ if tt.res.err == nil {
+ assert.NoError(t, err)
+ }
+ if tt.res.err != nil && !tt.res.err(err) {
+ t.Errorf("got wrong err: %v ", err)
+ }
+ if tt.res.err == nil {
+ assert.Equal(t, tt.res.want, got)
+ }
+ })
+ }
+}
+
+func TestCommandSide_ChangeSMTPConfigPassword(t *testing.T) {
+ type fields struct {
+ eventstore *eventstore.Eventstore
+ alg crypto.EncryptionAlgorithm
+ }
+ type args struct {
+ ctx context.Context
+ password string
+ }
+ type res struct {
+ want *domain.ObjectDetails
+ err func(error) bool
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ res res
+ }{
+ {
+ name: "smtp config, error not found",
+ fields: fields{
+ eventstore: eventstoreExpect(
+ t,
+ expectFilter(),
+ ),
+ },
+ args: args{
+ ctx: context.Background(),
+ password: "",
+ },
+ res: res{
+ err: caos_errs.IsNotFound,
+ },
+ },
+ {
+ name: "change smtp config password, ok",
+ fields: fields{
+ eventstore: eventstoreExpect(
+ t,
+ expectFilter(
+ eventFromEventPusher(
+ iam.NewSMTPConfigAddedEvent(
+ context.Background(),
+ &iam.NewAggregate().Aggregate,
+ true,
+ "from",
+ "name",
+ "host",
+ "user",
+ &crypto.CryptoValue{},
+ ),
+ ),
+ ),
+ expectPush(
+ []*repository.Event{
+ eventFromEventPusher(iam.NewSMTPConfigPasswordChangedEvent(
+ context.Background(),
+ &iam.NewAggregate().Aggregate,
+ &crypto.CryptoValue{
+ CryptoType: crypto.TypeEncryption,
+ Algorithm: "enc",
+ KeyID: "id",
+ Crypted: []byte("password"),
+ },
+ ),
+ ),
+ },
+ ),
+ ),
+ alg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
+ },
+ args: args{
+ ctx: context.Background(),
+ password: "password",
+ },
+ res: res{
+ want: &domain.ObjectDetails{
+ ResourceOwner: "IAM",
+ },
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ r := &Commands{
+ eventstore: tt.fields.eventstore,
+ smtpPasswordCrypto: tt.fields.alg,
+ }
+ got, err := r.ChangeSMTPConfigPassword(tt.args.ctx, tt.args.password)
+ if tt.res.err == nil {
+ assert.NoError(t, err)
+ }
+ if tt.res.err != nil && !tt.res.err(err) {
+ t.Errorf("got wrong err: %v ", err)
+ }
+ if tt.res.err == nil {
+ assert.Equal(t, tt.res.want, got)
+ }
+ })
+ }
+}
+
+func newSMTPConfigChangedEvent(ctx context.Context, tls bool, fromAddress, fromName, host, user string) *iam.SMTPConfigChangedEvent {
+ changes := []iam.SMTPConfigChanges{
+ iam.ChangeSMTPConfigTLS(tls),
+ iam.ChangeSMTPConfigFromAddress(fromAddress),
+ iam.ChangeSMTPConfigFromName(fromName),
+ iam.ChangeSMTPConfigSMTPHost(host),
+ iam.ChangeSMTPConfigSMTPUser(user),
+ }
+ event, _ := iam.NewSMTPConfigChangeEvent(ctx,
+ &iam.NewAggregate().Aggregate,
+ changes,
+ )
+ return event
+}
diff --git a/internal/command/user_human.go b/internal/command/user_human.go
index c65d3a95c4..e93fe3183b 100644
--- a/internal/command/user_human.go
+++ b/internal/command/user_human.go
@@ -4,6 +4,7 @@ import (
"context"
"strings"
+ "github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/domain"
@@ -23,7 +24,7 @@ func (c *Commands) getHuman(ctx context.Context, userID, resourceowner string) (
return writeModelToHuman(human), nil
}
-func (c *Commands) AddHuman(ctx context.Context, orgID string, human *domain.Human) (*domain.Human, error) {
+func (c *Commands) AddHuman(ctx context.Context, orgID string, human *domain.Human, initCodeGenerator crypto.Generator, phoneCodeGenerator crypto.Generator) (*domain.Human, error) {
if orgID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-XYFk9", "Errors.ResourceOwnerMissing")
}
@@ -35,7 +36,7 @@ func (c *Commands) AddHuman(ctx context.Context, orgID string, human *domain.Hum
if err != nil {
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-M5Fsd", "Errors.Org.PasswordComplexity.NotFound")
}
- events, addedHuman, err := c.addHuman(ctx, orgID, human, orgIAMPolicy, pwPolicy)
+ events, addedHuman, err := c.addHuman(ctx, orgID, human, orgIAMPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator)
if err != nil {
return nil, err
}
@@ -52,7 +53,7 @@ func (c *Commands) AddHuman(ctx context.Context, orgID string, human *domain.Hum
return writeModelToHuman(addedHuman), nil
}
-func (c *Commands) ImportHuman(ctx context.Context, orgID string, human *domain.Human, passwordless bool) (_ *domain.Human, passwordlessCode *domain.PasswordlessInitCode, err error) {
+func (c *Commands) ImportHuman(ctx context.Context, orgID string, human *domain.Human, passwordless bool, initCodeGenerator crypto.Generator, phoneCodeGenerator crypto.Generator, passwordlessCodeGenerator crypto.Generator) (_ *domain.Human, passwordlessCode *domain.PasswordlessInitCode, err error) {
if orgID == "" {
return nil, nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-5N8fs", "Errors.ResourceOwnerMissing")
}
@@ -64,7 +65,7 @@ func (c *Commands) ImportHuman(ctx context.Context, orgID string, human *domain.
if err != nil {
return nil, nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-4N8gs", "Errors.Org.PasswordComplexity.NotFound")
}
- events, addedHuman, addedCode, code, err := c.importHuman(ctx, orgID, human, passwordless, orgIAMPolicy, pwPolicy)
+ events, addedHuman, addedCode, code, err := c.importHuman(ctx, orgID, human, passwordless, orgIAMPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator, passwordlessCodeGenerator)
if err != nil {
return nil, nil, err
}
@@ -88,27 +89,27 @@ func (c *Commands) ImportHuman(ctx context.Context, orgID string, human *domain.
return writeModelToHuman(addedHuman), passwordlessCode, nil
}
-func (c *Commands) addHuman(ctx context.Context, orgID string, human *domain.Human, orgIAMPolicy *domain.OrgIAMPolicy, pwPolicy *domain.PasswordComplexityPolicy) ([]eventstore.Command, *HumanWriteModel, error) {
+func (c *Commands) addHuman(ctx context.Context, orgID string, human *domain.Human, orgIAMPolicy *domain.OrgIAMPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator crypto.Generator, phoneCodeGenerator crypto.Generator) ([]eventstore.Command, *HumanWriteModel, error) {
if orgID == "" || !human.IsValid() {
return nil, nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-67Ms8", "Errors.User.Invalid")
}
if human.Password != nil && human.SecretString != "" {
human.ChangeRequired = true
}
- return c.createHuman(ctx, orgID, human, nil, false, false, orgIAMPolicy, pwPolicy)
+ return c.createHuman(ctx, orgID, human, nil, false, false, orgIAMPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator)
}
-func (c *Commands) importHuman(ctx context.Context, orgID string, human *domain.Human, passwordless bool, orgIAMPolicy *domain.OrgIAMPolicy, pwPolicy *domain.PasswordComplexityPolicy) (events []eventstore.Command, humanWriteModel *HumanWriteModel, passwordlessCodeWriteModel *HumanPasswordlessInitCodeWriteModel, code string, err error) {
+func (c *Commands) importHuman(ctx context.Context, orgID string, human *domain.Human, passwordless bool, orgIAMPolicy *domain.OrgIAMPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator crypto.Generator, phoneCodeGenerator crypto.Generator, passwordlessCodeGenerator crypto.Generator) (events []eventstore.Command, humanWriteModel *HumanWriteModel, passwordlessCodeWriteModel *HumanPasswordlessInitCodeWriteModel, code string, err error) {
if orgID == "" || !human.IsValid() {
return nil, nil, nil, "", caos_errs.ThrowInvalidArgument(nil, "COMMAND-00p2b", "Errors.User.Invalid")
}
- events, humanWriteModel, err = c.createHuman(ctx, orgID, human, nil, false, passwordless, orgIAMPolicy, pwPolicy)
+ events, humanWriteModel, err = c.createHuman(ctx, orgID, human, nil, false, passwordless, orgIAMPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator)
if err != nil {
return nil, nil, nil, "", err
}
if passwordless {
var codeEvent eventstore.Command
- codeEvent, passwordlessCodeWriteModel, code, err = c.humanAddPasswordlessInitCode(ctx, human.AggregateID, orgID, true)
+ codeEvent, passwordlessCodeWriteModel, code, err = c.humanAddPasswordlessInitCode(ctx, human.AggregateID, orgID, true, passwordlessCodeGenerator)
if err != nil {
return nil, nil, nil, "", err
}
@@ -117,7 +118,7 @@ func (c *Commands) importHuman(ctx context.Context, orgID string, human *domain.
return events, humanWriteModel, passwordlessCodeWriteModel, code, nil
}
-func (c *Commands) RegisterHuman(ctx context.Context, orgID string, human *domain.Human, link *domain.UserIDPLink, orgMemberRoles []string) (*domain.Human, error) {
+func (c *Commands) RegisterHuman(ctx context.Context, orgID string, human *domain.Human, link *domain.UserIDPLink, orgMemberRoles []string, initCodeGenerator crypto.Generator, phoneCodeGenerator crypto.Generator) (*domain.Human, error) {
if orgID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-GEdf2", "Errors.ResourceOwnerMissing")
}
@@ -136,7 +137,7 @@ func (c *Commands) RegisterHuman(ctx context.Context, orgID string, human *domai
if !loginPolicy.AllowRegister {
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-SAbr3", "Errors.Org.LoginPolicy.RegistrationNotAllowed")
}
- userEvents, registeredHuman, err := c.registerHuman(ctx, orgID, human, link, orgIAMPolicy, pwPolicy)
+ userEvents, registeredHuman, err := c.registerHuman(ctx, orgID, human, link, orgIAMPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator)
if err != nil {
return nil, err
}
@@ -170,7 +171,7 @@ func (c *Commands) RegisterHuman(ctx context.Context, orgID string, human *domai
return writeModelToHuman(registeredHuman), nil
}
-func (c *Commands) registerHuman(ctx context.Context, orgID string, human *domain.Human, link *domain.UserIDPLink, orgIAMPolicy *domain.OrgIAMPolicy, pwPolicy *domain.PasswordComplexityPolicy) ([]eventstore.Command, *HumanWriteModel, error) {
+func (c *Commands) registerHuman(ctx context.Context, orgID string, human *domain.Human, link *domain.UserIDPLink, orgIAMPolicy *domain.OrgIAMPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator crypto.Generator, phoneCodeGenerator crypto.Generator) ([]eventstore.Command, *HumanWriteModel, error) {
if human != nil && human.Username == "" {
human.Username = human.EmailAddress
}
@@ -180,10 +181,10 @@ func (c *Commands) registerHuman(ctx context.Context, orgID string, human *domai
if human.Password != nil && human.SecretString != "" {
human.ChangeRequired = false
}
- return c.createHuman(ctx, orgID, human, link, true, false, orgIAMPolicy, pwPolicy)
+ return c.createHuman(ctx, orgID, human, link, true, false, orgIAMPolicy, pwPolicy, initCodeGenerator, phoneCodeGenerator)
}
-func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain.Human, link *domain.UserIDPLink, selfregister, passwordless bool, orgIAMPolicy *domain.OrgIAMPolicy, pwPolicy *domain.PasswordComplexityPolicy) ([]eventstore.Command, *HumanWriteModel, error) {
+func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain.Human, link *domain.UserIDPLink, selfregister, passwordless bool, orgIAMPolicy *domain.OrgIAMPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator crypto.Generator, phoneCodeGenerator crypto.Generator) ([]eventstore.Command, *HumanWriteModel, error) {
if err := human.CheckOrgIAMPolicy(orgIAMPolicy); err != nil {
return nil, nil, err
}
@@ -232,7 +233,7 @@ func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain.
}
if human.IsInitialState(passwordless, link != nil) {
- initCode, err := domain.NewInitUserCode(c.initializeUserCode)
+ initCode, err := domain.NewInitUserCode(initCodeGenerator)
if err != nil {
return nil, nil, err
}
@@ -244,7 +245,7 @@ func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain.
}
if human.Phone != nil && human.PhoneNumber != "" && !human.IsPhoneVerified {
- phoneCode, err := domain.NewPhoneCode(c.phoneVerificationCode)
+ phoneCode, err := domain.NewPhoneCode(phoneCodeGenerator)
if err != nil {
return nil, nil, err
}
diff --git a/internal/command/user_human_email.go b/internal/command/user_human_email.go
index 5a4863ac0b..a786310627 100644
--- a/internal/command/user_human_email.go
+++ b/internal/command/user_human_email.go
@@ -12,7 +12,7 @@ import (
"github.com/caos/zitadel/internal/telemetry/tracing"
)
-func (c *Commands) ChangeHumanEmail(ctx context.Context, email *domain.Email) (*domain.Email, error) {
+func (c *Commands) ChangeHumanEmail(ctx context.Context, email *domain.Email, emailCodeGenerator crypto.Generator) (*domain.Email, error) {
if !email.IsValid() || email.AggregateID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4M9sf", "Errors.Email.Invalid")
}
@@ -38,7 +38,7 @@ func (c *Commands) ChangeHumanEmail(ctx context.Context, email *domain.Email) (*
if email.IsEmailVerified {
events = append(events, user.NewHumanEmailVerifiedEvent(ctx, userAgg))
} else {
- emailCode, err := domain.NewEmailCode(c.emailVerificationCode)
+ emailCode, err := domain.NewEmailCode(emailCodeGenerator)
if err != nil {
return nil, err
}
@@ -56,7 +56,7 @@ func (c *Commands) ChangeHumanEmail(ctx context.Context, email *domain.Email) (*
return writeModelToEmail(existingEmail), nil
}
-func (c *Commands) VerifyHumanEmail(ctx context.Context, userID, code, resourceowner string) (*domain.ObjectDetails, error) {
+func (c *Commands) VerifyHumanEmail(ctx context.Context, userID, code, resourceowner string, emailCodeGenerator crypto.Generator) (*domain.ObjectDetails, error) {
if userID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4M0ds", "Errors.User.UserIDMissing")
}
@@ -73,7 +73,7 @@ func (c *Commands) VerifyHumanEmail(ctx context.Context, userID, code, resourceo
}
userAgg := UserAggregateFromWriteModel(&existingCode.WriteModel)
- err = crypto.VerifyCode(existingCode.CodeCreationDate, existingCode.CodeExpiry, existingCode.Code, code, c.emailVerificationCode)
+ err = crypto.VerifyCode(existingCode.CodeCreationDate, existingCode.CodeExpiry, existingCode.Code, code, emailCodeGenerator)
if err == nil {
pushedEvents, err := c.eventstore.Push(ctx, user.NewHumanEmailVerifiedEvent(ctx, userAgg))
if err != nil {
@@ -91,7 +91,7 @@ func (c *Commands) VerifyHumanEmail(ctx context.Context, userID, code, resourceo
return nil, caos_errs.ThrowInvalidArgument(err, "COMMAND-Gdsgs", "Errors.User.Code.Invalid")
}
-func (c *Commands) CreateHumanEmailVerificationCode(ctx context.Context, userID, resourceOwner string) (*domain.ObjectDetails, error) {
+func (c *Commands) CreateHumanEmailVerificationCode(ctx context.Context, userID, resourceOwner string, emailCodeGenerator crypto.Generator) (*domain.ObjectDetails, error) {
if userID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4M0ds", "Errors.User.UserIDMissing")
}
@@ -110,7 +110,7 @@ func (c *Commands) CreateHumanEmailVerificationCode(ctx context.Context, userID,
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3M9ds", "Errors.User.Email.AlreadyVerified")
}
userAgg := UserAggregateFromWriteModel(&existingEmail.WriteModel)
- emailCode, err := domain.NewEmailCode(c.emailVerificationCode)
+ emailCode, err := domain.NewEmailCode(emailCodeGenerator)
if err != nil {
return nil, err
}
diff --git a/internal/command/user_human_email_test.go b/internal/command/user_human_email_test.go
index eadb20d040..7d9cac2d02 100644
--- a/internal/command/user_human_email_test.go
+++ b/internal/command/user_human_email_test.go
@@ -19,13 +19,13 @@ import (
func TestCommandSide_ChangeHumanEmail(t *testing.T) {
type fields struct {
- eventstore *eventstore.Eventstore
- secretGenerator crypto.Generator
+ eventstore *eventstore.Eventstore
}
type args struct {
- ctx context.Context
- email *domain.Email
- resourceOwner string
+ ctx context.Context
+ email *domain.Email
+ resourceOwner string
+ secretGenerator crypto.Generator
}
type res struct {
want *domain.Email
@@ -263,7 +263,6 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
},
),
),
- secretGenerator: GetMockSecretGenerator(t),
},
args: args{
ctx: context.Background(),
@@ -273,7 +272,8 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
},
EmailAddress: "email-changed@test.ch",
},
- resourceOwner: "org1",
+ resourceOwner: "org1",
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.Email{
@@ -289,10 +289,9 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
- eventstore: tt.fields.eventstore,
- emailVerificationCode: tt.fields.secretGenerator,
+ eventstore: tt.fields.eventstore,
}
- got, err := r.ChangeHumanEmail(tt.args.ctx, tt.args.email)
+ got, err := r.ChangeHumanEmail(tt.args.ctx, tt.args.email, tt.args.secretGenerator)
if tt.res.err == nil {
assert.NoError(t, err)
}
@@ -308,14 +307,14 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
func TestCommandSide_VerifyHumanEmail(t *testing.T) {
type fields struct {
- eventstore *eventstore.Eventstore
- secretGenerator crypto.Generator
+ eventstore *eventstore.Eventstore
}
type args struct {
- ctx context.Context
- userID string
- code string
- resourceOwner string
+ ctx context.Context
+ userID string
+ code string
+ resourceOwner string
+ secretGenerator crypto.Generator
}
type res struct {
want *domain.ObjectDetails
@@ -453,13 +452,13 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
},
),
),
- secretGenerator: GetMockSecretGenerator(t),
},
args: args{
- ctx: context.Background(),
- userID: "user1",
- code: "test",
- resourceOwner: "org1",
+ ctx: context.Background(),
+ userID: "user1",
+ code: "test",
+ resourceOwner: "org1",
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
err: caos_errs.IsErrorInvalidArgument,
@@ -508,13 +507,13 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
},
),
),
- secretGenerator: GetMockSecretGenerator(t),
},
args: args{
- ctx: context.Background(),
- userID: "user1",
- code: "a",
- resourceOwner: "org1",
+ ctx: context.Background(),
+ userID: "user1",
+ code: "a",
+ resourceOwner: "org1",
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.ObjectDetails{
@@ -526,10 +525,9 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
- eventstore: tt.fields.eventstore,
- emailVerificationCode: tt.fields.secretGenerator,
+ eventstore: tt.fields.eventstore,
}
- got, err := r.VerifyHumanEmail(tt.args.ctx, tt.args.userID, tt.args.code, tt.args.resourceOwner)
+ got, err := r.VerifyHumanEmail(tt.args.ctx, tt.args.userID, tt.args.code, tt.args.resourceOwner, tt.args.secretGenerator)
if tt.res.err == nil {
assert.NoError(t, err)
}
@@ -545,13 +543,13 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
type fields struct {
- eventstore *eventstore.Eventstore
- secretGenerator crypto.Generator
+ eventstore *eventstore.Eventstore
}
type args struct {
- ctx context.Context
- userID string
- resourceOwner string
+ ctx context.Context
+ userID string
+ resourceOwner string
+ secretGenerator crypto.Generator
}
type res struct {
want *domain.ObjectDetails
@@ -719,12 +717,12 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
},
),
),
- secretGenerator: GetMockSecretGenerator(t),
},
args: args{
- ctx: context.Background(),
- userID: "user1",
- resourceOwner: "org1",
+ ctx: context.Background(),
+ userID: "user1",
+ resourceOwner: "org1",
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.ObjectDetails{
@@ -736,10 +734,9 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
- eventstore: tt.fields.eventstore,
- emailVerificationCode: tt.fields.secretGenerator,
+ eventstore: tt.fields.eventstore,
}
- got, err := r.CreateHumanEmailVerificationCode(tt.args.ctx, tt.args.userID, tt.args.resourceOwner)
+ got, err := r.CreateHumanEmailVerificationCode(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.secretGenerator)
if tt.res.err == nil {
assert.NoError(t, err)
}
diff --git a/internal/command/user_human_init.go b/internal/command/user_human_init.go
index f4669fde5d..8a72b5c7d4 100644
--- a/internal/command/user_human_init.go
+++ b/internal/command/user_human_init.go
@@ -12,7 +12,7 @@ import (
)
//ResendInitialMail resend inital mail and changes email if provided
-func (c *Commands) ResendInitialMail(ctx context.Context, userID, email, resourceOwner string) (objectDetails *domain.ObjectDetails, err error) {
+func (c *Commands) ResendInitialMail(ctx context.Context, userID, email, resourceOwner string, initCodeGenerator crypto.Generator) (objectDetails *domain.ObjectDetails, err error) {
if userID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-2n8vs", "Errors.User.UserIDMissing")
}
@@ -33,7 +33,7 @@ func (c *Commands) ResendInitialMail(ctx context.Context, userID, email, resourc
changedEvent, _ := existingCode.NewChangedEvent(ctx, userAgg, email)
events = append(events, changedEvent)
}
- initCode, err := domain.NewInitUserCode(c.initializeUserCode)
+ initCode, err := domain.NewInitUserCode(initCodeGenerator)
if err != nil {
return nil, err
}
@@ -49,7 +49,7 @@ func (c *Commands) ResendInitialMail(ctx context.Context, userID, email, resourc
return writeModelToObjectDetails(&existingCode.WriteModel), nil
}
-func (c *Commands) HumanVerifyInitCode(ctx context.Context, userID, resourceOwner, code, passwordString string) error {
+func (c *Commands) HumanVerifyInitCode(ctx context.Context, userID, resourceOwner, code, passwordString string, initCodeGenerator crypto.Generator) error {
if userID == "" {
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-mkM9f", "Errors.User.UserIDMissing")
}
@@ -66,7 +66,7 @@ func (c *Commands) HumanVerifyInitCode(ctx context.Context, userID, resourceOwne
}
userAgg := UserAggregateFromWriteModel(&existingCode.WriteModel)
- err = crypto.VerifyCode(existingCode.CodeCreationDate, existingCode.CodeExpiry, existingCode.Code, code, c.initializeUserCode)
+ err = crypto.VerifyCode(existingCode.CodeCreationDate, existingCode.CodeExpiry, existingCode.Code, code, initCodeGenerator)
if err != nil {
_, err = c.eventstore.Push(ctx, user.NewHumanInitializedCheckFailedEvent(ctx, userAgg))
logging.LogWithFields("COMMAND-Dg2z5", "userID", userAgg.ID).OnError(err).Error("NewHumanInitializedCheckFailedEvent push failed")
diff --git a/internal/command/user_human_init_test.go b/internal/command/user_human_init_test.go
index f0089fbf5a..bcac84c32c 100644
--- a/internal/command/user_human_init_test.go
+++ b/internal/command/user_human_init_test.go
@@ -20,14 +20,14 @@ import (
func TestCommandSide_ResendInitialMail(t *testing.T) {
type fields struct {
- eventstore *eventstore.Eventstore
- secretGenerator crypto.Generator
+ eventstore *eventstore.Eventstore
}
type args struct {
- ctx context.Context
- userID string
- email string
- resourceOwner string
+ ctx context.Context
+ userID string
+ email string
+ resourceOwner string
+ secretGenerator crypto.Generator
}
type res struct {
want *domain.ObjectDetails
@@ -150,13 +150,13 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
},
),
),
- secretGenerator: GetMockSecretGenerator(t),
},
args: args{
- ctx: context.Background(),
- userID: "user1",
- resourceOwner: "org1",
- email: "email@test.ch",
+ ctx: context.Background(),
+ userID: "user1",
+ resourceOwner: "org1",
+ email: "email@test.ch",
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.ObjectDetails{
@@ -208,12 +208,12 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
},
),
),
- secretGenerator: GetMockSecretGenerator(t),
},
args: args{
- ctx: context.Background(),
- userID: "user1",
- resourceOwner: "org1",
+ ctx: context.Background(),
+ userID: "user1",
+ resourceOwner: "org1",
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.ObjectDetails{
@@ -269,13 +269,13 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
},
),
),
- secretGenerator: GetMockSecretGenerator(t),
},
args: args{
- ctx: context.Background(),
- userID: "user1",
- resourceOwner: "org1",
- email: "email2@test.ch",
+ ctx: context.Background(),
+ userID: "user1",
+ resourceOwner: "org1",
+ email: "email2@test.ch",
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.ObjectDetails{
@@ -287,10 +287,9 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
- eventstore: tt.fields.eventstore,
- initializeUserCode: tt.fields.secretGenerator,
+ eventstore: tt.fields.eventstore,
}
- got, err := r.ResendInitialMail(tt.args.ctx, tt.args.userID, tt.args.email, tt.args.resourceOwner)
+ got, err := r.ResendInitialMail(tt.args.ctx, tt.args.userID, tt.args.email, tt.args.resourceOwner, tt.args.secretGenerator)
if tt.res.err == nil {
assert.NoError(t, err)
}
@@ -307,15 +306,15 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
func TestCommandSide_VerifyInitCode(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
- secretGenerator crypto.Generator
userPasswordAlg crypto.HashAlgorithm
}
type args struct {
- ctx context.Context
- userID string
- code string
- resourceOwner string
- password string
+ ctx context.Context
+ userID string
+ code string
+ resourceOwner string
+ password string
+ secretGenerator crypto.Generator
}
type res struct {
want *domain.ObjectDetails
@@ -453,13 +452,13 @@ func TestCommandSide_VerifyInitCode(t *testing.T) {
},
),
),
- secretGenerator: GetMockSecretGenerator(t),
},
args: args{
- ctx: context.Background(),
- userID: "user1",
- code: "test",
- resourceOwner: "org1",
+ ctx: context.Background(),
+ userID: "user1",
+ code: "test",
+ resourceOwner: "org1",
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
err: caos_errs.IsErrorInvalidArgument,
@@ -513,13 +512,13 @@ func TestCommandSide_VerifyInitCode(t *testing.T) {
},
),
),
- secretGenerator: GetMockSecretGenerator(t),
},
args: args{
- ctx: context.Background(),
- userID: "user1",
- code: "a",
- resourceOwner: "org1",
+ ctx: context.Background(),
+ userID: "user1",
+ code: "a",
+ resourceOwner: "org1",
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.ObjectDetails{
@@ -596,15 +595,15 @@ func TestCommandSide_VerifyInitCode(t *testing.T) {
},
),
),
- secretGenerator: GetMockSecretGenerator(t),
userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
},
args: args{
- ctx: context.Background(),
- userID: "user1",
- code: "a",
- resourceOwner: "org1",
- password: "password",
+ ctx: context.Background(),
+ userID: "user1",
+ code: "a",
+ resourceOwner: "org1",
+ password: "password",
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.ObjectDetails{
@@ -616,11 +615,10 @@ func TestCommandSide_VerifyInitCode(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
- eventstore: tt.fields.eventstore,
- initializeUserCode: tt.fields.secretGenerator,
- userPasswordAlg: tt.fields.userPasswordAlg,
+ eventstore: tt.fields.eventstore,
+ userPasswordAlg: tt.fields.userPasswordAlg,
}
- err := r.HumanVerifyInitCode(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.code, tt.args.password)
+ err := r.HumanVerifyInitCode(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.code, tt.args.password, tt.args.secretGenerator)
if tt.res.err == nil {
assert.NoError(t, err)
}
diff --git a/internal/command/user_human_password.go b/internal/command/user_human_password.go
index 5a12113f65..0d07eae6dd 100644
--- a/internal/command/user_human_password.go
+++ b/internal/command/user_human_password.go
@@ -46,7 +46,7 @@ func (c *Commands) SetPassword(ctx context.Context, orgID, userID, passwordStrin
return writeModelToObjectDetails(&existingPassword.WriteModel), nil
}
-func (c *Commands) SetPasswordWithVerifyCode(ctx context.Context, orgID, userID, code, passwordString, userAgentID string) (err error) {
+func (c *Commands) SetPasswordWithVerifyCode(ctx context.Context, orgID, userID, code, passwordString, userAgentID string, passwordVerificationCode crypto.Generator) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
@@ -65,7 +65,7 @@ func (c *Commands) SetPasswordWithVerifyCode(ctx context.Context, orgID, userID,
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.Code.NotFound")
}
- err = crypto.VerifyCode(existingCode.CodeCreationDate, existingCode.CodeExpiry, existingCode.Code, code, c.passwordVerificationCode)
+ err = crypto.VerifyCode(existingCode.CodeCreationDate, existingCode.CodeExpiry, existingCode.Code, code, passwordVerificationCode)
if err != nil {
return err
}
@@ -148,7 +148,7 @@ func (c *Commands) changePassword(ctx context.Context, userAgentID string, passw
return user.NewHumanPasswordChangedEvent(ctx, userAgg, password.SecretCrypto, password.ChangeRequired, userAgentID), nil
}
-func (c *Commands) RequestSetPassword(ctx context.Context, userID, resourceOwner string, notifyType domain.NotificationType) (objectDetails *domain.ObjectDetails, err error) {
+func (c *Commands) RequestSetPassword(ctx context.Context, userID, resourceOwner string, notifyType domain.NotificationType, passwordVerificationCode crypto.Generator) (objectDetails *domain.ObjectDetails, err error) {
if userID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-M00oL", "Errors.User.UserIDMissing")
}
@@ -164,7 +164,7 @@ func (c *Commands) RequestSetPassword(ctx context.Context, userID, resourceOwner
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9sd", "Errors.User.NotInitialised")
}
userAgg := UserAggregateFromWriteModel(&existingHuman.WriteModel)
- passwordCode, err := domain.NewPasswordCode(c.passwordVerificationCode)
+ passwordCode, err := domain.NewPasswordCode(passwordVerificationCode)
if err != nil {
return nil, err
}
diff --git a/internal/command/user_human_password_test.go b/internal/command/user_human_password_test.go
index a33ee1a2c8..fa95a9c3ec 100644
--- a/internal/command/user_human_password_test.go
+++ b/internal/command/user_human_password_test.go
@@ -239,15 +239,15 @@ func TestCommandSide_SetPassword(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
userPasswordAlg crypto.HashAlgorithm
- secretGenerator crypto.Generator
}
type args struct {
- ctx context.Context
- userID string
- code string
- resourceOwner string
- password string
- agentID string
+ ctx context.Context
+ userID string
+ code string
+ resourceOwner string
+ password string
+ agentID string
+ secretGenerator crypto.Generator
}
type res struct {
want *domain.ObjectDetails
@@ -377,14 +377,14 @@ func TestCommandSide_SetPassword(t *testing.T) {
),
),
),
- secretGenerator: GetMockSecretGenerator(t),
},
args: args{
- ctx: context.Background(),
- userID: "user1",
- code: "test",
- resourceOwner: "org1",
- password: "password",
+ ctx: context.Background(),
+ userID: "user1",
+ code: "test",
+ resourceOwner: "org1",
+ password: "password",
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
err: caos_errs.IsPreconditionFailed,
@@ -459,15 +459,15 @@ func TestCommandSide_SetPassword(t *testing.T) {
},
),
),
- secretGenerator: GetMockSecretGenerator(t),
userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
},
args: args{
- ctx: context.Background(),
- userID: "user1",
- resourceOwner: "org1",
- password: "password",
- code: "a",
+ ctx: context.Background(),
+ userID: "user1",
+ resourceOwner: "org1",
+ password: "password",
+ code: "a",
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.ObjectDetails{
@@ -479,11 +479,10 @@ func TestCommandSide_SetPassword(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
- eventstore: tt.fields.eventstore,
- userPasswordAlg: tt.fields.userPasswordAlg,
- passwordVerificationCode: tt.fields.secretGenerator,
+ eventstore: tt.fields.eventstore,
+ userPasswordAlg: tt.fields.userPasswordAlg,
}
- err := r.SetPasswordWithVerifyCode(tt.args.ctx, tt.args.resourceOwner, tt.args.userID, tt.args.code, tt.args.password, tt.args.agentID)
+ err := r.SetPasswordWithVerifyCode(tt.args.ctx, tt.args.resourceOwner, tt.args.userID, tt.args.code, tt.args.password, tt.args.agentID, tt.args.secretGenerator)
if tt.res.err == nil {
assert.NoError(t, err)
}
@@ -778,14 +777,14 @@ func TestCommandSide_ChangePassword(t *testing.T) {
func TestCommandSide_RequestSetPassword(t *testing.T) {
type fields struct {
- eventstore *eventstore.Eventstore
- secretGenerator crypto.Generator
+ eventstore *eventstore.Eventstore
}
type args struct {
- ctx context.Context
- userID string
- resourceOwner string
- notifyType domain.NotificationType
+ ctx context.Context
+ userID string
+ resourceOwner string
+ notifyType domain.NotificationType
+ secretGenerator crypto.Generator
}
type res struct {
want *domain.ObjectDetails
@@ -925,12 +924,12 @@ func TestCommandSide_RequestSetPassword(t *testing.T) {
},
),
),
- secretGenerator: GetMockSecretGenerator(t),
},
args: args{
- ctx: context.Background(),
- userID: "user1",
- resourceOwner: "org1",
+ ctx: context.Background(),
+ userID: "user1",
+ resourceOwner: "org1",
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.ObjectDetails{
@@ -942,10 +941,9 @@ func TestCommandSide_RequestSetPassword(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
- eventstore: tt.fields.eventstore,
- passwordVerificationCode: tt.fields.secretGenerator,
+ eventstore: tt.fields.eventstore,
}
- got, err := r.RequestSetPassword(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.notifyType)
+ got, err := r.RequestSetPassword(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.notifyType, tt.args.secretGenerator)
if tt.res.err == nil {
assert.NoError(t, err)
}
diff --git a/internal/command/user_human_phone.go b/internal/command/user_human_phone.go
index abaa03742f..42c02c2de1 100644
--- a/internal/command/user_human_phone.go
+++ b/internal/command/user_human_phone.go
@@ -13,7 +13,7 @@ import (
"github.com/caos/zitadel/internal/telemetry/tracing"
)
-func (c *Commands) ChangeHumanPhone(ctx context.Context, phone *domain.Phone, resourceOwner string) (*domain.Phone, error) {
+func (c *Commands) ChangeHumanPhone(ctx context.Context, phone *domain.Phone, resourceOwner string, phoneCodeGenerator crypto.Generator) (*domain.Phone, error) {
if !phone.IsValid() {
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-6M0ds", "Errors.Phone.Invalid")
}
@@ -36,7 +36,7 @@ func (c *Commands) ChangeHumanPhone(ctx context.Context, phone *domain.Phone, re
if phone.IsPhoneVerified {
events = append(events, user.NewHumanPhoneVerifiedEvent(ctx, userAgg))
} else {
- phoneCode, err := domain.NewPhoneCode(c.phoneVerificationCode)
+ phoneCode, err := domain.NewPhoneCode(phoneCodeGenerator)
if err != nil {
return nil, err
}
@@ -55,7 +55,7 @@ func (c *Commands) ChangeHumanPhone(ctx context.Context, phone *domain.Phone, re
return writeModelToPhone(existingPhone), nil
}
-func (c *Commands) VerifyHumanPhone(ctx context.Context, userID, code, resourceowner string) (*domain.ObjectDetails, error) {
+func (c *Commands) VerifyHumanPhone(ctx context.Context, userID, code, resourceowner string, phoneCodeGenerator crypto.Generator) (*domain.ObjectDetails, error) {
if userID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-Km9ds", "Errors.User.UserIDMissing")
}
@@ -75,7 +75,7 @@ func (c *Commands) VerifyHumanPhone(ctx context.Context, userID, code, resourceo
}
userAgg := UserAggregateFromWriteModel(&existingCode.WriteModel)
- err = crypto.VerifyCode(existingCode.CodeCreationDate, existingCode.CodeExpiry, existingCode.Code, code, c.phoneVerificationCode)
+ err = crypto.VerifyCode(existingCode.CodeCreationDate, existingCode.CodeExpiry, existingCode.Code, code, phoneCodeGenerator)
if err == nil {
pushedEvents, err := c.eventstore.Push(ctx, user.NewHumanPhoneVerifiedEvent(ctx, userAgg))
if err != nil {
@@ -92,7 +92,7 @@ func (c *Commands) VerifyHumanPhone(ctx context.Context, userID, code, resourceo
return nil, caos_errs.ThrowInvalidArgument(err, "COMMAND-sM0cs", "Errors.User.Code.Invalid")
}
-func (c *Commands) CreateHumanPhoneVerificationCode(ctx context.Context, userID, resourceowner string) (*domain.ObjectDetails, error) {
+func (c *Commands) CreateHumanPhoneVerificationCode(ctx context.Context, userID, resourceowner string, phoneCodeGenerator crypto.Generator) (*domain.ObjectDetails, error) {
if userID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4M0ds", "Errors.User.UserIDMissing")
}
@@ -112,7 +112,7 @@ func (c *Commands) CreateHumanPhoneVerificationCode(ctx context.Context, userID,
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9sf", "Errors.User.Phone.AlreadyVerified")
}
- phoneCode, err := domain.NewPhoneCode(c.phoneVerificationCode)
+ phoneCode, err := domain.NewPhoneCode(phoneCodeGenerator)
if err != nil {
return nil, err
}
diff --git a/internal/command/user_human_phone_test.go b/internal/command/user_human_phone_test.go
index 60eaa86330..b5d33cabc2 100644
--- a/internal/command/user_human_phone_test.go
+++ b/internal/command/user_human_phone_test.go
@@ -19,13 +19,13 @@ import (
func TestCommandSide_ChangeHumanPhone(t *testing.T) {
type fields struct {
- eventstore *eventstore.Eventstore
- secretGenerator crypto.Generator
+ eventstore *eventstore.Eventstore
}
type args struct {
- ctx context.Context
- email *domain.Phone
- resourceOwner string
+ ctx context.Context
+ email *domain.Phone
+ resourceOwner string
+ secretGenerator crypto.Generator
}
type res struct {
want *domain.Phone
@@ -232,7 +232,6 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
},
),
),
- secretGenerator: GetMockSecretGenerator(t),
},
args: args{
ctx: context.Background(),
@@ -242,7 +241,8 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
},
PhoneNumber: "0711234567",
},
- resourceOwner: "org1",
+ resourceOwner: "org1",
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.Phone{
@@ -258,10 +258,9 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
- eventstore: tt.fields.eventstore,
- phoneVerificationCode: tt.fields.secretGenerator,
+ eventstore: tt.fields.eventstore,
}
- got, err := r.ChangeHumanPhone(tt.args.ctx, tt.args.email, tt.args.resourceOwner)
+ got, err := r.ChangeHumanPhone(tt.args.ctx, tt.args.email, tt.args.resourceOwner, tt.args.secretGenerator)
if tt.res.err == nil {
assert.NoError(t, err)
}
@@ -277,14 +276,14 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
func TestCommandSide_VerifyHumanPhone(t *testing.T) {
type fields struct {
- eventstore *eventstore.Eventstore
- secretGenerator crypto.Generator
+ eventstore *eventstore.Eventstore
}
type args struct {
- ctx context.Context
- userID string
- code string
- resourceOwner string
+ ctx context.Context
+ userID string
+ code string
+ resourceOwner string
+ secretGenerator crypto.Generator
}
type res struct {
want *domain.ObjectDetails
@@ -428,13 +427,13 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
},
),
),
- secretGenerator: GetMockSecretGenerator(t),
},
args: args{
- ctx: context.Background(),
- userID: "user1",
- code: "test",
- resourceOwner: "org1",
+ ctx: context.Background(),
+ userID: "user1",
+ code: "test",
+ resourceOwner: "org1",
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
err: caos_errs.IsErrorInvalidArgument,
@@ -489,13 +488,13 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
},
),
),
- secretGenerator: GetMockSecretGenerator(t),
},
args: args{
- ctx: context.Background(),
- userID: "user1",
- code: "a",
- resourceOwner: "org1",
+ ctx: context.Background(),
+ userID: "user1",
+ code: "a",
+ resourceOwner: "org1",
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.ObjectDetails{
@@ -507,10 +506,9 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
- eventstore: tt.fields.eventstore,
- phoneVerificationCode: tt.fields.secretGenerator,
+ eventstore: tt.fields.eventstore,
}
- got, err := r.VerifyHumanPhone(tt.args.ctx, tt.args.userID, tt.args.code, tt.args.resourceOwner)
+ got, err := r.VerifyHumanPhone(tt.args.ctx, tt.args.userID, tt.args.code, tt.args.resourceOwner, tt.args.secretGenerator)
if tt.res.err == nil {
assert.NoError(t, err)
}
@@ -526,13 +524,13 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) {
type fields struct {
- eventstore *eventstore.Eventstore
- secretGenerator crypto.Generator
+ eventstore *eventstore.Eventstore
}
type args struct {
- ctx context.Context
- userID string
- resourceOwner string
+ ctx context.Context
+ userID string
+ resourceOwner string
+ secretGenerator crypto.Generator
}
type res struct {
want *domain.ObjectDetails
@@ -663,12 +661,12 @@ func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) {
},
),
),
- secretGenerator: GetMockSecretGenerator(t),
},
args: args{
- ctx: context.Background(),
- userID: "user1",
- resourceOwner: "org1",
+ ctx: context.Background(),
+ userID: "user1",
+ resourceOwner: "org1",
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.ObjectDetails{
@@ -680,10 +678,9 @@ func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
- eventstore: tt.fields.eventstore,
- phoneVerificationCode: tt.fields.secretGenerator,
+ eventstore: tt.fields.eventstore,
}
- got, err := r.CreateHumanPhoneVerificationCode(tt.args.ctx, tt.args.userID, tt.args.resourceOwner)
+ got, err := r.CreateHumanPhoneVerificationCode(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.secretGenerator)
if tt.res.err == nil {
assert.NoError(t, err)
}
diff --git a/internal/command/user_human_test.go b/internal/command/user_human_test.go
index f17df3ec25..f84a0c8a3a 100644
--- a/internal/command/user_human_test.go
+++ b/internal/command/user_human_test.go
@@ -25,13 +25,13 @@ func TestCommandSide_AddHuman(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
idGenerator id.Generator
- secretGenerator crypto.Generator
userPasswordAlg crypto.HashAlgorithm
}
type args struct {
- ctx context.Context
- orgID string
- human *domain.Human
+ ctx context.Context
+ orgID string
+ human *domain.Human
+ secretGenerator crypto.Generator
}
type res struct {
want *domain.Human
@@ -228,8 +228,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)),
),
),
- idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
- secretGenerator: GetMockSecretGenerator(t),
+ idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
},
args: args{
ctx: context.Background(),
@@ -244,6 +243,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
EmailAddress: "email@test.ch",
},
},
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.Human{
@@ -312,7 +312,6 @@ func TestCommandSide_AddHuman(t *testing.T) {
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
- secretGenerator: GetMockSecretGenerator(t),
userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
},
args: args{
@@ -331,6 +330,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
EmailAddress: "email@test.ch",
},
},
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.Human{
@@ -391,7 +391,6 @@ func TestCommandSide_AddHuman(t *testing.T) {
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
- secretGenerator: GetMockSecretGenerator(t),
userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
},
args: args{
@@ -411,6 +410,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
IsEmailVerified: true,
},
},
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.Human{
@@ -489,8 +489,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)),
),
),
- idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
- secretGenerator: GetMockSecretGenerator(t),
+ idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
},
args: args{
ctx: context.Background(),
@@ -508,6 +507,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
PhoneNumber: "+41711234567",
},
},
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.Human{
@@ -582,8 +582,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)),
),
),
- idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
- secretGenerator: GetMockSecretGenerator(t),
+ idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
},
args: args{
ctx: context.Background(),
@@ -602,6 +601,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
IsPhoneVerified: true,
},
},
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.Human{
@@ -630,13 +630,11 @@ func TestCommandSide_AddHuman(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
- eventstore: tt.fields.eventstore,
- idGenerator: tt.fields.idGenerator,
- initializeUserCode: tt.fields.secretGenerator,
- phoneVerificationCode: tt.fields.secretGenerator,
- userPasswordAlg: tt.fields.userPasswordAlg,
+ eventstore: tt.fields.eventstore,
+ idGenerator: tt.fields.idGenerator,
+ userPasswordAlg: tt.fields.userPasswordAlg,
}
- got, err := r.AddHuman(tt.args.ctx, tt.args.orgID, tt.args.human)
+ got, err := r.AddHuman(tt.args.ctx, tt.args.orgID, tt.args.human, tt.args.secretGenerator, tt.args.secretGenerator)
if tt.res.err == nil {
assert.NoError(t, err)
}
@@ -652,17 +650,17 @@ func TestCommandSide_AddHuman(t *testing.T) {
func TestCommandSide_ImportHuman(t *testing.T) {
type fields struct {
- eventstore *eventstore.Eventstore
- idGenerator id.Generator
- secretGenerator crypto.Generator
- userPasswordAlg crypto.HashAlgorithm
- passwordlessInitCode crypto.Generator
+ eventstore *eventstore.Eventstore
+ idGenerator id.Generator
+ userPasswordAlg crypto.HashAlgorithm
}
type args struct {
- ctx context.Context
- orgID string
- human *domain.Human
- passwordless bool
+ ctx context.Context
+ orgID string
+ human *domain.Human
+ passwordless bool
+ secretGenerator crypto.Generator
+ passwordlessInitCode crypto.Generator
}
type res struct {
wantHuman *domain.Human
@@ -850,7 +848,6 @@ func TestCommandSide_ImportHuman(t *testing.T) {
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
- secretGenerator: GetMockSecretGenerator(t),
userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
},
args: args{
@@ -870,6 +867,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
EmailAddress: "email@test.ch",
},
},
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
wantHuman: &domain.Human{
@@ -930,7 +928,6 @@ func TestCommandSide_ImportHuman(t *testing.T) {
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
- secretGenerator: GetMockSecretGenerator(t),
userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
},
args: args{
@@ -951,6 +948,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
IsEmailVerified: true,
},
},
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
wantHuman: &domain.Human{
@@ -1025,10 +1023,8 @@ func TestCommandSide_ImportHuman(t *testing.T) {
uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)),
),
),
- idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1", "code1"),
- secretGenerator: GetMockSecretGenerator(t),
- userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
- passwordlessInitCode: GetMockSecretGenerator(t),
+ idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1", "code1"),
+ userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
},
args: args{
ctx: context.Background(),
@@ -1044,7 +1040,9 @@ func TestCommandSide_ImportHuman(t *testing.T) {
IsEmailVerified: true,
},
},
- passwordless: true,
+ passwordless: true,
+ secretGenerator: GetMockSecretGenerator(t),
+ passwordlessInitCode: GetMockSecretGenerator(t),
},
res: res{
wantHuman: &domain.Human{
@@ -1129,10 +1127,8 @@ func TestCommandSide_ImportHuman(t *testing.T) {
uniqueConstraintsFromEventConstraint(user.NewAddUsernameUniqueConstraint("username", "org1", true)),
),
),
- idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1", "code1"),
- secretGenerator: GetMockSecretGenerator(t),
- userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
- passwordlessInitCode: GetMockSecretGenerator(t),
+ idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1", "code1"),
+ userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
},
args: args{
ctx: context.Background(),
@@ -1152,7 +1148,9 @@ func TestCommandSide_ImportHuman(t *testing.T) {
IsEmailVerified: true,
},
},
- passwordless: true,
+ passwordless: true,
+ secretGenerator: GetMockSecretGenerator(t),
+ passwordlessInitCode: GetMockSecretGenerator(t),
},
res: res{
wantHuman: &domain.Human{
@@ -1242,7 +1240,6 @@ func TestCommandSide_ImportHuman(t *testing.T) {
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
- secretGenerator: GetMockSecretGenerator(t),
userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
},
args: args{
@@ -1265,6 +1262,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
PhoneNumber: "+41711234567",
},
},
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
wantHuman: &domain.Human{
@@ -1340,7 +1338,6 @@ func TestCommandSide_ImportHuman(t *testing.T) {
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
- secretGenerator: GetMockSecretGenerator(t),
userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
},
args: args{
@@ -1364,6 +1361,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
IsPhoneVerified: true,
},
},
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
wantHuman: &domain.Human{
@@ -1392,14 +1390,11 @@ func TestCommandSide_ImportHuman(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
- eventstore: tt.fields.eventstore,
- idGenerator: tt.fields.idGenerator,
- initializeUserCode: tt.fields.secretGenerator,
- phoneVerificationCode: tt.fields.secretGenerator,
- userPasswordAlg: tt.fields.userPasswordAlg,
- passwordlessInitCode: tt.fields.passwordlessInitCode,
+ eventstore: tt.fields.eventstore,
+ idGenerator: tt.fields.idGenerator,
+ userPasswordAlg: tt.fields.userPasswordAlg,
}
- gotHuman, gotCode, err := r.ImportHuman(tt.args.ctx, tt.args.orgID, tt.args.human, tt.args.passwordless)
+ gotHuman, gotCode, err := r.ImportHuman(tt.args.ctx, tt.args.orgID, tt.args.human, tt.args.passwordless, tt.args.secretGenerator, tt.args.secretGenerator, tt.args.secretGenerator)
if tt.res.err == nil {
assert.NoError(t, err)
}
@@ -1418,15 +1413,15 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
idGenerator id.Generator
- secretGenerator crypto.Generator
userPasswordAlg crypto.HashAlgorithm
}
type args struct {
- ctx context.Context
- orgID string
- human *domain.Human
- link *domain.UserIDPLink
- orgMemberRoles []string
+ ctx context.Context
+ orgID string
+ human *domain.Human
+ link *domain.UserIDPLink
+ orgMemberRoles []string
+ secretGenerator crypto.Generator
}
type res struct {
want *domain.Human
@@ -1858,7 +1853,6 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
- secretGenerator: GetMockSecretGenerator(t),
userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
},
args: args{
@@ -1876,6 +1870,7 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
EmailAddress: "email@test.ch",
},
},
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.Human{
@@ -1957,7 +1952,6 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
- secretGenerator: GetMockSecretGenerator(t),
userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
},
args: args{
@@ -1976,6 +1970,7 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
EmailAddress: "email@test.ch",
},
},
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.Human{
@@ -2049,7 +2044,6 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
- secretGenerator: GetMockSecretGenerator(t),
userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
},
args: args{
@@ -2069,6 +2063,7 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
IsEmailVerified: true,
},
},
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.Human{
@@ -2161,7 +2156,6 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
- secretGenerator: GetMockSecretGenerator(t),
userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
},
args: args{
@@ -2183,6 +2177,7 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
SecretString: "password",
},
},
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.Human{
@@ -2271,7 +2266,6 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
- secretGenerator: GetMockSecretGenerator(t),
userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
},
args: args{
@@ -2294,6 +2288,7 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
SecretString: "password",
},
},
+ secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.Human{
@@ -2322,13 +2317,11 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
- eventstore: tt.fields.eventstore,
- idGenerator: tt.fields.idGenerator,
- initializeUserCode: tt.fields.secretGenerator,
- phoneVerificationCode: tt.fields.secretGenerator,
- userPasswordAlg: tt.fields.userPasswordAlg,
+ eventstore: tt.fields.eventstore,
+ idGenerator: tt.fields.idGenerator,
+ userPasswordAlg: tt.fields.userPasswordAlg,
}
- got, err := r.RegisterHuman(tt.args.ctx, tt.args.orgID, tt.args.human, tt.args.link, tt.args.orgMemberRoles)
+ got, err := r.RegisterHuman(tt.args.ctx, tt.args.orgID, tt.args.human, tt.args.link, tt.args.orgMemberRoles, tt.args.secretGenerator, tt.args.secretGenerator)
if tt.res.err == nil {
assert.NoError(t, err)
}
diff --git a/internal/command/user_human_webauthn.go b/internal/command/user_human_webauthn.go
index d2d4110224..a12a5cbdd8 100644
--- a/internal/command/user_human_webauthn.go
+++ b/internal/command/user_human_webauthn.go
@@ -129,8 +129,8 @@ func (c *Commands) HumanAddPasswordlessSetup(ctx context.Context, userID, resour
return createdWebAuthN, nil
}
-func (c *Commands) HumanAddPasswordlessSetupInitCode(ctx context.Context, userID, resourceowner, codeID, verificationCode string, preferredPlatformType domain.AuthenticatorAttachment) (*domain.WebAuthNToken, error) {
- err := c.humanVerifyPasswordlessInitCode(ctx, userID, resourceowner, codeID, verificationCode)
+func (c *Commands) HumanAddPasswordlessSetupInitCode(ctx context.Context, userID, resourceowner, codeID, verificationCode string, preferredPlatformType domain.AuthenticatorAttachment, passwordlessCodeGenerator crypto.Generator) (*domain.WebAuthNToken, error) {
+ err := c.humanVerifyPasswordlessInitCode(ctx, userID, resourceowner, codeID, verificationCode, passwordlessCodeGenerator)
if err != nil {
return nil, err
}
@@ -208,8 +208,8 @@ func (c *Commands) HumanVerifyU2FSetup(ctx context.Context, userID, resourceowne
return writeModelToObjectDetails(&verifyWebAuthN.WriteModel), nil
}
-func (c *Commands) HumanPasswordlessSetupInitCode(ctx context.Context, userID, resourceowner, tokenName, userAgentID, codeID, verificationCode string, credentialData []byte) (*domain.ObjectDetails, error) {
- err := c.humanVerifyPasswordlessInitCode(ctx, userID, resourceowner, codeID, verificationCode)
+func (c *Commands) HumanPasswordlessSetupInitCode(ctx context.Context, userID, resourceowner, tokenName, userAgentID, codeID, verificationCode string, credentialData []byte, passwordlessCodeGenerator crypto.Generator) (*domain.ObjectDetails, error) {
+ err := c.humanVerifyPasswordlessInitCode(ctx, userID, resourceowner, codeID, verificationCode, passwordlessCodeGenerator)
if err != nil {
return nil, err
}
@@ -483,8 +483,8 @@ func (c *Commands) HumanRemovePasswordless(ctx context.Context, userID, webAuthN
return c.removeHumanWebAuthN(ctx, userID, webAuthNID, resourceOwner, event)
}
-func (c *Commands) HumanAddPasswordlessInitCode(ctx context.Context, userID, resourceOwner string) (*domain.PasswordlessInitCode, error) {
- codeEvent, initCode, code, err := c.humanAddPasswordlessInitCode(ctx, userID, resourceOwner, true)
+func (c *Commands) HumanAddPasswordlessInitCode(ctx context.Context, userID, resourceOwner string, passwordlessCodeGenerator crypto.Generator) (*domain.PasswordlessInitCode, error) {
+ codeEvent, initCode, code, err := c.humanAddPasswordlessInitCode(ctx, userID, resourceOwner, true, passwordlessCodeGenerator)
pushedEvents, err := c.eventstore.Push(ctx, codeEvent)
if err != nil {
return nil, err
@@ -496,8 +496,8 @@ func (c *Commands) HumanAddPasswordlessInitCode(ctx context.Context, userID, res
return writeModelToPasswordlessInitCode(initCode, code), nil
}
-func (c *Commands) HumanSendPasswordlessInitCode(ctx context.Context, userID, resourceOwner string) (*domain.PasswordlessInitCode, error) {
- codeEvent, initCode, code, err := c.humanAddPasswordlessInitCode(ctx, userID, resourceOwner, false)
+func (c *Commands) HumanSendPasswordlessInitCode(ctx context.Context, userID, resourceOwner string, passwordlessCodeGenerator crypto.Generator) (*domain.PasswordlessInitCode, error) {
+ codeEvent, initCode, code, err := c.humanAddPasswordlessInitCode(ctx, userID, resourceOwner, false, passwordlessCodeGenerator)
if err != nil {
return nil, err
}
@@ -512,7 +512,7 @@ func (c *Commands) HumanSendPasswordlessInitCode(ctx context.Context, userID, re
return writeModelToPasswordlessInitCode(initCode, code), nil
}
-func (c *Commands) humanAddPasswordlessInitCode(ctx context.Context, userID, resourceOwner string, direct bool) (eventstore.Command, *HumanPasswordlessInitCodeWriteModel, string, error) {
+func (c *Commands) humanAddPasswordlessInitCode(ctx context.Context, userID, resourceOwner string, direct bool, passwordlessCodeGenerator crypto.Generator) (eventstore.Command, *HumanPasswordlessInitCodeWriteModel, string, error) {
if userID == "" {
return nil, nil, "", caos_errs.ThrowPreconditionFailed(nil, "COMMAND-GVfg3", "Errors.IDMissing")
}
@@ -527,7 +527,7 @@ func (c *Commands) humanAddPasswordlessInitCode(ctx context.Context, userID, res
return nil, nil, "", err
}
- cryptoCode, code, err := crypto.NewCode(c.passwordlessInitCode)
+ cryptoCode, code, err := crypto.NewCode(passwordlessCodeGenerator)
if err != nil {
return nil, nil, "", err
}
@@ -539,7 +539,7 @@ func (c *Commands) humanAddPasswordlessInitCode(ctx context.Context, userID, res
return usr_repo.NewHumanPasswordlessInitCodeRequestedEvent(ctx, agg, id, cryptoCode, exp)
}
}
- codeEvent := codeEventCreator(ctx, UserAggregateFromWriteModel(&initCode.WriteModel), codeID, cryptoCode, c.passwordlessInitCode.Expiry())
+ codeEvent := codeEventCreator(ctx, UserAggregateFromWriteModel(&initCode.WriteModel), codeID, cryptoCode, passwordlessCodeGenerator.Expiry())
return codeEvent, initCode, code, nil
}
@@ -563,7 +563,7 @@ func (c *Commands) HumanPasswordlessInitCodeSent(ctx context.Context, userID, re
return err
}
-func (c *Commands) humanVerifyPasswordlessInitCode(ctx context.Context, userID, resourceOwner, codeID, verificationCode string) error {
+func (c *Commands) humanVerifyPasswordlessInitCode(ctx context.Context, userID, resourceOwner, codeID, verificationCode string, passwordlessCodeGenerator crypto.Generator) error {
if userID == "" || codeID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-GVfg3", "Errors.IDMissing")
}
@@ -572,7 +572,7 @@ func (c *Commands) humanVerifyPasswordlessInitCode(ctx context.Context, userID,
if err != nil {
return err
}
- err = crypto.VerifyCode(initCode.ChangeDate, initCode.Expiration, initCode.CryptoCode, verificationCode, c.passwordlessInitCode)
+ err = crypto.VerifyCode(initCode.ChangeDate, initCode.Expiration, initCode.CryptoCode, verificationCode, passwordlessCodeGenerator)
if err != nil || initCode.State != domain.PasswordlessInitCodeStateActive {
userAgg := UserAggregateFromWriteModel(&initCode.WriteModel)
_, err = c.eventstore.Push(ctx, usr_repo.NewHumanPasswordlessInitCodeCheckFailedEvent(ctx, userAgg, codeID))
diff --git a/internal/config/systemdefaults/system_defaults.go b/internal/config/systemdefaults/system_defaults.go
index 5125a64ca5..6f2f1ba535 100644
--- a/internal/config/systemdefaults/system_defaults.go
+++ b/internal/config/systemdefaults/system_defaults.go
@@ -9,24 +9,23 @@ import (
"github.com/caos/zitadel/internal/notification/channels/chat"
"github.com/caos/zitadel/internal/notification/channels/fs"
"github.com/caos/zitadel/internal/notification/channels/log"
- "github.com/caos/zitadel/internal/notification/channels/smtp"
"github.com/caos/zitadel/internal/notification/channels/twilio"
"github.com/caos/zitadel/internal/notification/templates"
)
type SystemDefaults struct {
- DefaultLanguage language.Tag
- Domain string
- ZitadelDocs ZitadelDocs
- SecretGenerators SecretGenerators
- UserVerificationKey *crypto.KeyConfig
- IDPConfigVerificationKey *crypto.KeyConfig
- Multifactors MultifactorConfig
- VerificationLifetimes VerificationLifetimes
- DomainVerification DomainVerification
- IamID string
- Notifications Notifications
- KeyConfig KeyConfig
+ DefaultLanguage language.Tag
+ Domain string
+ ZitadelDocs ZitadelDocs
+ SecretGenerators SecretGenerators
+ UserVerificationKey *crypto.KeyConfig
+ IDPConfigVerificationKey *crypto.KeyConfig
+ SMTPPasswordVerificationKey *crypto.KeyConfig
+ Multifactors MultifactorConfig
+ VerificationLifetimes VerificationLifetimes
+ DomainVerification DomainVerification
+ Notifications Notifications
+ KeyConfig KeyConfig
}
type ZitadelDocs struct {
@@ -35,15 +34,9 @@ type ZitadelDocs struct {
}
type SecretGenerators struct {
- PasswordSaltCost int
- ClientSecretGenerator crypto.GeneratorConfig
- InitializeUserCode crypto.GeneratorConfig
- EmailVerificationCode crypto.GeneratorConfig
- PhoneVerificationCode crypto.GeneratorConfig
- PasswordVerificationCode crypto.GeneratorConfig
- PasswordlessInitCode crypto.GeneratorConfig
- MachineKeySize uint32
- ApplicationKeySize uint32
+ PasswordSaltCost int
+ MachineKeySize uint32
+ ApplicationKeySize uint32
}
type MultifactorConfig struct {
@@ -69,10 +62,9 @@ type DomainVerification struct {
}
type Notifications struct {
- DebugMode bool
- Endpoints Endpoints
- Providers Channels
- TemplateData TemplateData
+ DebugMode bool
+ Endpoints Endpoints
+ Providers Channels
}
type Endpoints struct {
@@ -85,7 +77,6 @@ type Endpoints struct {
type Channels struct {
Chat chat.ChatConfig
- Email smtp.EmailConfig
Twilio twilio.TwilioConfig
FileSystem fs.FSConfig
Log log.LogConfig
diff --git a/internal/domain/secret_generator.go b/internal/domain/secret_generator.go
new file mode 100644
index 0000000000..83f3ffe1c5
--- /dev/null
+++ b/internal/domain/secret_generator.go
@@ -0,0 +1,23 @@
+package domain
+
+type SecretGeneratorType int32
+
+const (
+ SecretGeneratorTypeUnspecified SecretGeneratorType = iota
+ SecretGeneratorTypeInitCode
+ SecretGeneratorTypeVerifyEmailCode
+ SecretGeneratorTypeVerifyPhoneCode
+ SecretGeneratorTypePasswordResetCode
+ SecretGeneratorTypePasswordlessInitCode
+ SecretGeneratorTypeAppSecret
+
+ secretGeneratorTypeCount
+)
+
+type SecretGeneratorState int32
+
+const (
+ SecretGeneratorStateUnspecified SecretGeneratorState = iota
+ SecretGeneratorStateActive
+ SecretGeneratorStateRemoved
+)
diff --git a/internal/domain/smtp.go b/internal/domain/smtp.go
new file mode 100644
index 0000000000..aa190ab076
--- /dev/null
+++ b/internal/domain/smtp.go
@@ -0,0 +1,8 @@
+package domain
+
+type SMTPConfigState int32
+
+const (
+ SMTPConfigStateUnspecified SMTPConfigState = iota
+ SMTPConfigStateActive
+)
diff --git a/internal/notification/channels/smtp/channel.go b/internal/notification/channels/smtp/channel.go
index d57ee63968..251e66efa8 100644
--- a/internal/notification/channels/smtp/channel.go
+++ b/internal/notification/channels/smtp/channel.go
@@ -1,26 +1,30 @@
package smtp
import (
+ "context"
"crypto/tls"
"net"
"net/smtp"
- "github.com/caos/zitadel/internal/notification/messages"
-
"github.com/caos/logging"
+ "github.com/pkg/errors"
+
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/notification/channels"
- "github.com/pkg/errors"
+ "github.com/caos/zitadel/internal/notification/messages"
)
var _ channels.NotificationChannel = (*Email)(nil)
type Email struct {
- smtpClient *smtp.Client
+ smtpClient *smtp.Client
+ senderAddress string
+ senderName string
}
-func InitSMTPChannel(config EmailConfig) (*Email, error) {
- client, err := config.SMTP.connectToSMTP(config.Tls)
+func InitSMTPChannel(ctx context.Context, getSMTPConfig func(ctx context.Context) (*EmailConfig, error)) (*Email, error) {
+ smtpConfig, err := getSMTPConfig(ctx)
+ client, err := smtpConfig.SMTP.connectToSMTP(smtpConfig.Tls)
if err != nil {
return nil, err
}
@@ -42,12 +46,12 @@ func (email *Email) HandleMessage(message channels.Message) error {
if emailMsg.Content == "" || emailMsg.Subject == "" || len(emailMsg.Recipients) == 0 {
return caos_errs.ThrowInternalf(nil, "EMAIL-zGemZ", "subject, recipients and content must be set but got subject %s, recipients length %d and content length %d", emailMsg.Subject, len(emailMsg.Recipients), len(emailMsg.Content))
}
-
+ emailMsg.SenderEmail = email.senderAddress
+ emailMsg.SenderName = email.senderName
// To && From
if err := email.smtpClient.Mail(emailMsg.SenderEmail); err != nil {
return caos_errs.ThrowInternalf(err, "EMAIL-s3is3", "could not set sender: %v", emailMsg.SenderEmail)
}
-
for _, recp := range append(append(emailMsg.Recipients, emailMsg.CC...), emailMsg.BCC...) {
if err := email.smtpClient.Rcpt(recp); err != nil {
return caos_errs.ThrowInternalf(err, "EMAIL-s4is4", "could not set recipient: %v", recp)
diff --git a/internal/notification/messages/email.go b/internal/notification/messages/email.go
index 51e151c1d5..5525e8fd15 100644
--- a/internal/notification/messages/email.go
+++ b/internal/notification/messages/email.go
@@ -20,13 +20,18 @@ type Email struct {
BCC []string
CC []string
SenderEmail string
+ SenderName string
Subject string
Content string
}
func (msg *Email) GetContent() string {
headers := make(map[string]string)
- headers["From"] = msg.SenderEmail
+ from := msg.SenderEmail
+ if msg.SenderName != "" {
+ from = fmt.Sprintf("%s <%s>", msg.SenderName, msg.SenderEmail)
+ }
+ headers["From"] = from
headers["To"] = strings.Join(msg.Recipients, ", ")
headers["Cc"] = strings.Join(msg.CC, ", ")
diff --git a/internal/notification/notification.go b/internal/notification/notification.go
index fe1e747401..4a2169442a 100644
--- a/internal/notification/notification.go
+++ b/internal/notification/notification.go
@@ -4,6 +4,7 @@ import (
"database/sql"
"github.com/caos/logging"
+ "github.com/caos/zitadel/internal/crypto"
"github.com/rakyll/statik/fs"
"github.com/caos/zitadel/internal/command"
@@ -17,10 +18,10 @@ type Config struct {
Repository eventsourcing.Config
}
-func Start(config Config, systemDefaults sd.SystemDefaults, command *command.Commands, queries *query.Queries, dbClient *sql.DB, assetsPrefix string) {
+func Start(config Config, systemDefaults sd.SystemDefaults, command *command.Commands, queries *query.Queries, dbClient *sql.DB, assetsPrefix string, smtpPasswordEncAlg crypto.EncryptionAlgorithm) {
statikFS, err := fs.NewWithNamespace("notification")
logging.OnError(err).Panic("unable to start listener")
- _, err = eventsourcing.Start(config.Repository, statikFS, systemDefaults, command, queries, dbClient, assetsPrefix)
+ _, err = eventsourcing.Start(config.Repository, statikFS, systemDefaults, command, queries, dbClient, assetsPrefix, smtpPasswordEncAlg)
logging.OnError(err).Panic("unable to start app")
}
diff --git a/internal/notification/repository/eventsourcing/handler/handler.go b/internal/notification/repository/eventsourcing/handler/handler.go
index 0d6eba58e8..2182c881c5 100644
--- a/internal/notification/repository/eventsourcing/handler/handler.go
+++ b/internal/notification/repository/eventsourcing/handler/handler.go
@@ -34,14 +34,13 @@ func (h *handler) Eventstore() v1.Eventstore {
return h.es
}
-func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es v1.Eventstore, command *command.Commands, queries *query.Queries, systemDefaults sd.SystemDefaults, dir http.FileSystem, assetsPrefix string) []queryv1.Handler {
+func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es v1.Eventstore, command *command.Commands, queries *query.Queries, systemDefaults sd.SystemDefaults, dir http.FileSystem, assetsPrefix string, smtpPasswordEncAlg crypto.EncryptionAlgorithm) []queryv1.Handler {
aesCrypto, err := crypto.NewAESCrypto(systemDefaults.UserVerificationKey)
logging.OnError(err).Fatal("error create new aes crypto")
return []queryv1.Handler{
newNotifyUser(
handler{view, bulkLimit, configs.cycleDuration("User"), errorCount, es},
- systemDefaults.IamID,
queries,
),
newNotification(
@@ -52,6 +51,7 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es
aesCrypto,
dir,
assetsPrefix,
+ smtpPasswordEncAlg,
),
}
}
diff --git a/internal/notification/repository/eventsourcing/handler/notification.go b/internal/notification/repository/eventsourcing/handler/notification.go
index be4d9f24a9..c151861fd5 100644
--- a/internal/notification/repository/eventsourcing/handler/notification.go
+++ b/internal/notification/repository/eventsourcing/handler/notification.go
@@ -19,6 +19,7 @@ import (
queryv1 "github.com/caos/zitadel/internal/eventstore/v1/query"
"github.com/caos/zitadel/internal/eventstore/v1/spooler"
"github.com/caos/zitadel/internal/i18n"
+ "github.com/caos/zitadel/internal/notification/channels/smtp"
"github.com/caos/zitadel/internal/notification/types"
"github.com/caos/zitadel/internal/query"
user_repo "github.com/caos/zitadel/internal/repository/user"
@@ -34,13 +35,14 @@ const (
type Notification struct {
handler
- command *command.Commands
- systemDefaults sd.SystemDefaults
- AesCrypto crypto.EncryptionAlgorithm
- statikDir http.FileSystem
- subscription *v1.Subscription
- assetsPrefix string
- queries *query.Queries
+ command *command.Commands
+ systemDefaults sd.SystemDefaults
+ AesCrypto crypto.EncryptionAlgorithm
+ statikDir http.FileSystem
+ subscription *v1.Subscription
+ assetsPrefix string
+ queries *query.Queries
+ smtpPasswordCrypto crypto.EncryptionAlgorithm
}
func newNotification(
@@ -51,15 +53,17 @@ func newNotification(
aesCrypto crypto.EncryptionAlgorithm,
statikDir http.FileSystem,
assetsPrefix string,
+ smtpPasswordEncAlg crypto.EncryptionAlgorithm,
) *Notification {
h := &Notification{
- handler: handler,
- command: command,
- systemDefaults: defaults,
- statikDir: statikDir,
- AesCrypto: aesCrypto,
- assetsPrefix: assetsPrefix,
- queries: query,
+ handler: handler,
+ command: command,
+ systemDefaults: defaults,
+ statikDir: statikDir,
+ AesCrypto: aesCrypto,
+ assetsPrefix: assetsPrefix,
+ queries: query,
+ smtpPasswordCrypto: smtpPasswordEncAlg,
}
h.subscribe()
@@ -161,7 +165,7 @@ func (n *Notification) handleInitUserCode(event *models.Event) (err error) {
return err
}
- err = types.SendUserInitCode(string(template.Template), translator, user, initCode, n.systemDefaults, n.AesCrypto, colors, n.assetsPrefix)
+ err = types.SendUserInitCode(ctx, string(template.Template), translator, user, initCode, n.systemDefaults, n.getSMTPConfig, n.AesCrypto, colors, n.assetsPrefix)
if err != nil {
return err
}
@@ -199,7 +203,7 @@ func (n *Notification) handlePasswordCode(event *models.Event) (err error) {
if err != nil {
return err
}
- err = types.SendPasswordCode(string(template.Template), translator, user, pwCode, n.systemDefaults, n.AesCrypto, colors, n.assetsPrefix)
+ err = types.SendPasswordCode(ctx, string(template.Template), translator, user, pwCode, n.systemDefaults, n.getSMTPConfig, n.AesCrypto, colors, n.assetsPrefix)
if err != nil {
return err
}
@@ -238,7 +242,7 @@ func (n *Notification) handleEmailVerificationCode(event *models.Event) (err err
return err
}
- err = types.SendEmailVerificationCode(string(template.Template), translator, user, emailCode, n.systemDefaults, n.AesCrypto, colors, n.assetsPrefix)
+ err = types.SendEmailVerificationCode(ctx, string(template.Template), translator, user, emailCode, n.systemDefaults, n.getSMTPConfig, n.AesCrypto, colors, n.assetsPrefix)
if err != nil {
return err
}
@@ -303,7 +307,8 @@ func (n *Notification) handleDomainClaimed(event *models.Event) (err error) {
if err != nil {
return err
}
- err = types.SendDomainClaimed(string(template.Template), translator, user, data["userName"], n.systemDefaults, colors, n.assetsPrefix)
+
+ err = types.SendDomainClaimed(ctx, string(template.Template), translator, user, data["userName"], n.systemDefaults, n.getSMTPConfig, colors, n.assetsPrefix)
if err != nil {
return err
}
@@ -349,7 +354,8 @@ func (n *Notification) handlePasswordlessRegistrationLink(event *models.Event) (
if err != nil {
return err
}
- err = types.SendPasswordlessRegistrationLink(string(template.Template), translator, user, addedEvent, n.systemDefaults, n.AesCrypto, colors, n.assetsPrefix)
+
+ err = types.SendPasswordlessRegistrationLink(ctx, string(template.Template), translator, user, addedEvent, n.systemDefaults, n.getSMTPConfig, n.AesCrypto, colors, n.assetsPrefix)
if err != nil {
return err
}
@@ -410,12 +416,34 @@ func (n *Notification) getMailTemplate(ctx context.Context) (*query.MailTemplate
return n.queries.MailTemplateByOrg(ctx, authz.GetCtxData(ctx).OrgID)
}
-func (n *Notification) getTranslatorWithOrgTexts(orgID, textType string) (*i18n.Translator, error) {
- translator, err := i18n.NewTranslator(n.statikDir, i18n.TranslatorConfig{DefaultLanguage: n.systemDefaults.DefaultLanguage})
+// Read iam smtp config
+func (n *Notification) getSMTPConfig(ctx context.Context) (*smtp.EmailConfig, error) {
+ config, err := n.queries.SMTPConfigByAggregateID(ctx, domain.IAMID)
if err != nil {
return nil, err
}
- ctx := context.TODO()
+ password, err := crypto.Decrypt(config.Password, n.smtpPasswordCrypto)
+ if err != nil {
+ return nil, err
+ }
+ return &smtp.EmailConfig{
+ From: config.SenderAddress,
+ FromName: config.SenderName,
+ SMTP: smtp.SMTP{
+ Host: config.Host,
+ User: config.User,
+ Password: string(password),
+ },
+ }, nil
+}
+
+func (n *Notification) getTranslatorWithOrgTexts(orgID, textType string) (*i18n.Translator, error) {
+ ctx := context.Background()
+ translator, err := i18n.NewTranslator(n.statikDir, i18n.TranslatorConfig{DefaultLanguage: n.queries.GetDefaultLanguage(ctx)})
+ if err != nil {
+ return nil, err
+ }
+
allCustomTexts, err := n.queries.CustomTextListByTemplate(ctx, domain.IAMID, textType)
if err != nil {
return translator, nil
diff --git a/internal/notification/repository/eventsourcing/handler/notify_user.go b/internal/notification/repository/eventsourcing/handler/notify_user.go
index 8ed3ae84a0..4cef14f6f8 100644
--- a/internal/notification/repository/eventsourcing/handler/notify_user.go
+++ b/internal/notification/repository/eventsourcing/handler/notify_user.go
@@ -26,19 +26,16 @@ const (
type NotifyUser struct {
handler
- iamID string
subscription *v1.Subscription
queries *query2.Queries
}
func newNotifyUser(
handler handler,
- iamID string,
queries *query2.Queries,
) *NotifyUser {
h := &NotifyUser{
handler: handler,
- iamID: iamID,
queries: queries,
}
diff --git a/internal/notification/repository/eventsourcing/repository.go b/internal/notification/repository/eventsourcing/repository.go
index 142c688779..d9b4d4a9b3 100644
--- a/internal/notification/repository/eventsourcing/repository.go
+++ b/internal/notification/repository/eventsourcing/repository.go
@@ -6,6 +6,7 @@ import (
"github.com/caos/zitadel/internal/command"
sd "github.com/caos/zitadel/internal/config/systemdefaults"
+ "github.com/caos/zitadel/internal/crypto"
v1 "github.com/caos/zitadel/internal/eventstore/v1"
es_spol "github.com/caos/zitadel/internal/eventstore/v1/spooler"
"github.com/caos/zitadel/internal/notification/repository/eventsourcing/spooler"
@@ -21,7 +22,7 @@ type EsRepository struct {
spooler *es_spol.Spooler
}
-func Start(conf Config, dir http.FileSystem, systemDefaults sd.SystemDefaults, command *command.Commands, queries *query.Queries, dbClient *sql.DB, assetsPrefix string) (*EsRepository, error) {
+func Start(conf Config, dir http.FileSystem, systemDefaults sd.SystemDefaults, command *command.Commands, queries *query.Queries, dbClient *sql.DB, assetsPrefix string, smtpPasswordEncAlg crypto.EncryptionAlgorithm) (*EsRepository, error) {
es, err := v1.Start(dbClient)
if err != nil {
return nil, err
@@ -32,7 +33,7 @@ func Start(conf Config, dir http.FileSystem, systemDefaults sd.SystemDefaults, c
return nil, err
}
- spool := spooler.StartSpooler(conf.Spooler, es, view, dbClient, command, queries, systemDefaults, dir, assetsPrefix)
+ spool := spooler.StartSpooler(conf.Spooler, es, view, dbClient, command, queries, systemDefaults, dir, assetsPrefix, smtpPasswordEncAlg)
return &EsRepository{
spool,
diff --git a/internal/notification/repository/eventsourcing/spooler/spooler.go b/internal/notification/repository/eventsourcing/spooler/spooler.go
index 5954d59c4e..ac264e6de7 100644
--- a/internal/notification/repository/eventsourcing/spooler/spooler.go
+++ b/internal/notification/repository/eventsourcing/spooler/spooler.go
@@ -6,6 +6,7 @@ import (
"github.com/caos/zitadel/internal/command"
sd "github.com/caos/zitadel/internal/config/systemdefaults"
+ "github.com/caos/zitadel/internal/crypto"
v1 "github.com/caos/zitadel/internal/eventstore/v1"
"github.com/caos/zitadel/internal/eventstore/v1/spooler"
"github.com/caos/zitadel/internal/notification/repository/eventsourcing/handler"
@@ -20,12 +21,12 @@ type SpoolerConfig struct {
Handlers handler.Configs
}
-func StartSpooler(c SpoolerConfig, es v1.Eventstore, view *view.View, sql *sql.DB, command *command.Commands, queries *query.Queries, systemDefaults sd.SystemDefaults, dir http.FileSystem, assetsPrefix string) *spooler.Spooler {
+func StartSpooler(c SpoolerConfig, es v1.Eventstore, view *view.View, sql *sql.DB, command *command.Commands, queries *query.Queries, systemDefaults sd.SystemDefaults, dir http.FileSystem, assetsPrefix string, smtpPasswordEncAlg crypto.EncryptionAlgorithm) *spooler.Spooler {
spoolerConfig := spooler.Config{
Eventstore: es,
Locker: &locker{dbClient: sql},
ConcurrentWorkers: c.ConcurrentWorkers,
- ViewHandlers: handler.Register(c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, es, command, queries, systemDefaults, dir, assetsPrefix),
+ ViewHandlers: handler.Register(c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, es, command, queries, systemDefaults, dir, assetsPrefix, smtpPasswordEncAlg),
}
spool := spoolerConfig.New()
spool.Start()
diff --git a/internal/notification/senders/email.go b/internal/notification/senders/email.go
index 725be49af8..90240b5b53 100644
--- a/internal/notification/senders/email.go
+++ b/internal/notification/senders/email.go
@@ -1,12 +1,14 @@
package senders
import (
+ "context"
+
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/notification/channels"
"github.com/caos/zitadel/internal/notification/channels/smtp"
)
-func EmailChannels(config systemdefaults.Notifications) (channels.NotificationChannel, error) {
+func EmailChannels(ctx context.Context, config systemdefaults.Notifications, emailConfig func(ctx context.Context) (*smtp.EmailConfig, error)) (channels.NotificationChannel, error) {
debug, err := debugChannels(config)
if err != nil {
@@ -14,7 +16,7 @@ func EmailChannels(config systemdefaults.Notifications) (channels.NotificationCh
}
if !config.DebugMode {
- p, err := smtp.InitSMTPChannel(config.Providers.Email)
+ p, err := smtp.InitSMTPChannel(ctx, emailConfig)
if err != nil {
return nil, err
}
diff --git a/internal/notification/types/domain_claimed.go b/internal/notification/types/domain_claimed.go
index 430ad831fb..e22a689c16 100644
--- a/internal/notification/types/domain_claimed.go
+++ b/internal/notification/types/domain_claimed.go
@@ -1,11 +1,13 @@
package types
import (
+ "context"
"strings"
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/i18n"
+ "github.com/caos/zitadel/internal/notification/channels/smtp"
"github.com/caos/zitadel/internal/notification/templates"
"github.com/caos/zitadel/internal/query"
view_model "github.com/caos/zitadel/internal/user/repository/view/model"
@@ -16,7 +18,7 @@ type DomainClaimedData struct {
URL string
}
-func SendDomainClaimed(mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, username string, systemDefaults systemdefaults.SystemDefaults, colors *query.LabelPolicy, assetsPrefix string) error {
+func SendDomainClaimed(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, username string, systemDefaults systemdefaults.SystemDefaults, emailConfig func(ctx context.Context) (*smtp.EmailConfig, error), colors *query.LabelPolicy, assetsPrefix string) error {
url, err := templates.ParseTemplateText(systemDefaults.Notifications.Endpoints.DomainClaimed, &UrlData{UserID: user.ID})
if err != nil {
return err
@@ -33,5 +35,5 @@ func SendDomainClaimed(mailhtml string, translator *i18n.Translator, user *view_
if err != nil {
return err
}
- return generateEmail(user, domainClaimedData.Subject, template, systemDefaults.Notifications, true)
+ return generateEmail(ctx, user, domainClaimedData.Subject, template, systemDefaults.Notifications, emailConfig, true)
}
diff --git a/internal/notification/types/email_verification_code.go b/internal/notification/types/email_verification_code.go
index e667483ec6..37f4045253 100644
--- a/internal/notification/types/email_verification_code.go
+++ b/internal/notification/types/email_verification_code.go
@@ -1,10 +1,13 @@
package types
import (
+ "context"
+
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/i18n"
+ "github.com/caos/zitadel/internal/notification/channels/smtp"
"github.com/caos/zitadel/internal/notification/templates"
"github.com/caos/zitadel/internal/query"
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
@@ -16,7 +19,7 @@ type EmailVerificationCodeData struct {
URL string
}
-func SendEmailVerificationCode(mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *es_model.EmailCode, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix string) error {
+func SendEmailVerificationCode(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *es_model.EmailCode, systemDefaults systemdefaults.SystemDefaults, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix string) error {
codeString, err := crypto.DecryptString(code.Code, alg)
if err != nil {
return err
@@ -38,5 +41,5 @@ func SendEmailVerificationCode(mailhtml string, translator *i18n.Translator, use
if err != nil {
return err
}
- return generateEmail(user, emailCodeData.Subject, template, systemDefaults.Notifications, true)
+ return generateEmail(ctx, user, emailCodeData.Subject, template, systemDefaults.Notifications, smtpConfig, true)
}
diff --git a/internal/notification/types/init_code.go b/internal/notification/types/init_code.go
index c55bfa8e94..bf46e4c3c8 100644
--- a/internal/notification/types/init_code.go
+++ b/internal/notification/types/init_code.go
@@ -1,10 +1,13 @@
package types
import (
+ "context"
+
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/i18n"
+ "github.com/caos/zitadel/internal/notification/channels/smtp"
"github.com/caos/zitadel/internal/notification/templates"
"github.com/caos/zitadel/internal/query"
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
@@ -22,7 +25,7 @@ type UrlData struct {
PasswordSet bool
}
-func SendUserInitCode(mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *es_model.InitUserCode, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix string) error {
+func SendUserInitCode(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *es_model.InitUserCode, systemDefaults systemdefaults.SystemDefaults, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix string) error {
codeString, err := crypto.DecryptString(code.Code, alg)
if err != nil {
return err
@@ -42,5 +45,5 @@ func SendUserInitCode(mailhtml string, translator *i18n.Translator, user *view_m
if err != nil {
return err
}
- return generateEmail(user, initCodeData.Subject, template, systemDefaults.Notifications, true)
+ return generateEmail(ctx, user, initCodeData.Subject, template, systemDefaults.Notifications, smtpConfig, true)
}
diff --git a/internal/notification/types/password_code.go b/internal/notification/types/password_code.go
index 7eceb6ae43..2c98211c1a 100644
--- a/internal/notification/types/password_code.go
+++ b/internal/notification/types/password_code.go
@@ -1,10 +1,13 @@
package types
import (
+ "context"
+
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/i18n"
+ "github.com/caos/zitadel/internal/notification/channels/smtp"
"github.com/caos/zitadel/internal/notification/templates"
"github.com/caos/zitadel/internal/query"
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
@@ -18,7 +21,7 @@ type PasswordCodeData struct {
URL string
}
-func SendPasswordCode(mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *es_model.PasswordCode, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix string) error {
+func SendPasswordCode(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *es_model.PasswordCode, systemDefaults systemdefaults.SystemDefaults, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix string) error {
codeString, err := crypto.DecryptString(code.Code, alg)
if err != nil {
return err
@@ -43,6 +46,6 @@ func SendPasswordCode(mailhtml string, translator *i18n.Translator, user *view_m
if code.NotificationType == int32(domain.NotificationTypeSms) {
return generateSms(user, passwordResetData.Text, systemDefaults.Notifications, false)
}
- return generateEmail(user, passwordResetData.Subject, template, systemDefaults.Notifications, true)
+ return generateEmail(ctx, user, passwordResetData.Subject, template, systemDefaults.Notifications, smtpConfig, true)
}
diff --git a/internal/notification/types/passwordless_registration_link.go b/internal/notification/types/passwordless_registration_link.go
index 5801459ae7..5aa0461999 100644
--- a/internal/notification/types/passwordless_registration_link.go
+++ b/internal/notification/types/passwordless_registration_link.go
@@ -1,10 +1,13 @@
package types
import (
+ "context"
+
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/i18n"
+ "github.com/caos/zitadel/internal/notification/channels/smtp"
"github.com/caos/zitadel/internal/notification/templates"
"github.com/caos/zitadel/internal/query"
"github.com/caos/zitadel/internal/repository/user"
@@ -16,7 +19,7 @@ type PasswordlessRegistrationLinkData struct {
URL string
}
-func SendPasswordlessRegistrationLink(mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *user.HumanPasswordlessInitCodeRequestedEvent, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix string) error {
+func SendPasswordlessRegistrationLink(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *user.HumanPasswordlessInitCodeRequestedEvent, systemDefaults systemdefaults.SystemDefaults, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix string) error {
codeString, err := crypto.DecryptString(code.Code, alg)
if err != nil {
return err
@@ -33,5 +36,5 @@ func SendPasswordlessRegistrationLink(mailhtml string, translator *i18n.Translat
if err != nil {
return err
}
- return generateEmail(user, emailCodeData.Subject, template, systemDefaults.Notifications, true)
+ return generateEmail(ctx, user, emailCodeData.Subject, template, systemDefaults.Notifications, smtpConfig, true)
}
diff --git a/internal/notification/types/user_email.go b/internal/notification/types/user_email.go
index 9a54142c1d..d5ea7c483c 100644
--- a/internal/notification/types/user_email.go
+++ b/internal/notification/types/user_email.go
@@ -1,8 +1,10 @@
package types
import (
+ "context"
"html"
+ "github.com/caos/zitadel/internal/notification/channels/smtp"
"github.com/caos/zitadel/internal/notification/messages"
"github.com/caos/zitadel/internal/notification/senders"
@@ -10,19 +12,18 @@ import (
view_model "github.com/caos/zitadel/internal/user/repository/view/model"
)
-func generateEmail(user *view_model.NotifyUser, subject, content string, config systemdefaults.Notifications, lastEmail bool) error {
+func generateEmail(ctx context.Context, user *view_model.NotifyUser, subject, content string, config systemdefaults.Notifications, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), lastEmail bool) error {
content = html.UnescapeString(content)
message := &messages.Email{
- SenderEmail: config.Providers.Email.From,
- Recipients: []string{user.VerifiedEmail},
- Subject: subject,
- Content: content,
+ Recipients: []string{user.VerifiedEmail},
+ Subject: subject,
+ Content: content,
}
if lastEmail {
message.Recipients = []string{user.LastEmail}
}
- channels, err := senders.EmailChannels(config)
+ channels, err := senders.EmailChannels(ctx, config, smtpConfig)
if err != nil {
return err
}
diff --git a/internal/query/custom_text.go b/internal/query/custom_text.go
index 1fe8e4f70e..e809afd236 100644
--- a/internal/query/custom_text.go
+++ b/internal/query/custom_text.go
@@ -186,7 +186,7 @@ func (q *Queries) readLoginTranslationFile(lang string) ([]byte, error) {
if !ok {
contents, err = q.readTranslationFile(q.LoginDir, fmt.Sprintf("/i18n/%s.yaml", lang))
if errors.IsNotFound(err) {
- contents, err = q.readTranslationFile(q.LoginDir, fmt.Sprintf("/i18n/%s.yaml", q.DefaultLanguage.String()))
+ contents, err = q.readTranslationFile(q.LoginDir, fmt.Sprintf("/i18n/%s.yaml", q.GetDefaultLanguage(context.Background()).String()))
}
if err != nil {
return nil, err
diff --git a/internal/query/iam.go b/internal/query/iam.go
index a1dfb8460a..9554d0a4c5 100644
--- a/internal/query/iam.go
+++ b/internal/query/iam.go
@@ -10,6 +10,7 @@ import (
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/query/projection"
+ "golang.org/x/text/language"
)
var (
@@ -44,6 +45,10 @@ var (
name: projection.IAMColumnSetUpDone,
table: iamTable,
}
+ IAMColumnDefaultLanguage = Column{
+ name: projection.IAMColumnDefaultLanguage,
+ table: iamTable,
+ }
)
type IAM struct {
@@ -51,10 +56,11 @@ type IAM struct {
ChangeDate time.Time
Sequence uint64
- GlobalOrgID string
- IAMProjectID string
- SetupStarted domain.Step
- SetupDone domain.Step
+ GlobalOrgID string
+ IAMProjectID string
+ DefaultLanguage language.Tag
+ SetupStarted domain.Step
+ SetupDone domain.Step
}
type IAMSearchQueries struct {
@@ -83,6 +89,14 @@ func (q *Queries) IAMByID(ctx context.Context, id string) (*IAM, error) {
return scan(row)
}
+func (q *Queries) GetDefaultLanguage(ctx context.Context) language.Tag {
+ iam, err := q.IAMByID(ctx, domain.IAMID)
+ if err != nil {
+ return language.Und
+ }
+ return iam.DefaultLanguage
+}
+
func prepareIAMQuery() (sq.SelectBuilder, func(*sql.Row) (*IAM, error)) {
return sq.Select(
IAMColumnID.identifier(),
@@ -92,18 +106,21 @@ func prepareIAMQuery() (sq.SelectBuilder, func(*sql.Row) (*IAM, error)) {
IAMColumnProjectID.identifier(),
IAMColumnSetupStarted.identifier(),
IAMColumnSetupDone.identifier(),
+ IAMColumnDefaultLanguage.identifier(),
).
From(iamTable.identifier()).PlaceholderFormat(sq.Dollar),
func(row *sql.Row) (*IAM, error) {
- o := new(IAM)
+ iam := new(IAM)
+ lang := ""
err := row.Scan(
- &o.ID,
- &o.ChangeDate,
- &o.Sequence,
- &o.GlobalOrgID,
- &o.IAMProjectID,
- &o.SetupStarted,
- &o.SetupDone,
+ &iam.ID,
+ &iam.ChangeDate,
+ &iam.Sequence,
+ &iam.GlobalOrgID,
+ &iam.IAMProjectID,
+ &iam.SetupStarted,
+ &iam.SetupDone,
+ &lang,
)
if err != nil {
if errs.Is(err, sql.ErrNoRows) {
@@ -111,6 +128,7 @@ func prepareIAMQuery() (sq.SelectBuilder, func(*sql.Row) (*IAM, error)) {
}
return nil, errors.ThrowInternal(err, "QUERY-d9nw", "Errors.Internal")
}
- return o, nil
+ iam.DefaultLanguage = language.Make(lang)
+ return iam, nil
}
}
diff --git a/internal/query/iam_test.go b/internal/query/iam_test.go
index e5f7a92593..7bfc1630af 100644
--- a/internal/query/iam_test.go
+++ b/internal/query/iam_test.go
@@ -10,6 +10,7 @@ import (
"github.com/caos/zitadel/internal/domain"
errs "github.com/caos/zitadel/internal/errors"
+ "golang.org/x/text/language"
)
func Test_IAMPrepares(t *testing.T) {
@@ -34,7 +35,8 @@ func Test_IAMPrepares(t *testing.T) {
` zitadel.projections.iam.global_org_id,`+
` zitadel.projections.iam.iam_project_id,`+
` zitadel.projections.iam.setup_started,`+
- ` zitadel.projections.iam.setup_done`+
+ ` zitadel.projections.iam.setup_done,`+
+ ` zitadel.projections.iam.default_language`+
` FROM zitadel.projections.iam`),
nil,
nil,
@@ -59,7 +61,8 @@ func Test_IAMPrepares(t *testing.T) {
` zitadel.projections.iam.global_org_id,`+
` zitadel.projections.iam.iam_project_id,`+
` zitadel.projections.iam.setup_started,`+
- ` zitadel.projections.iam.setup_done`+
+ ` zitadel.projections.iam.setup_done,`+
+ ` zitadel.projections.iam.default_language`+
` FROM zitadel.projections.iam`),
[]string{
"id",
@@ -69,6 +72,7 @@ func Test_IAMPrepares(t *testing.T) {
"iam_project_id",
"setup_started",
"setup_done",
+ "default_language",
},
[]driver.Value{
"id",
@@ -78,17 +82,19 @@ func Test_IAMPrepares(t *testing.T) {
"project-id",
domain.Step2,
domain.Step1,
+ "en",
},
),
},
object: &IAM{
- ID: "id",
- ChangeDate: testNow,
- Sequence: 20211108,
- GlobalOrgID: "global-org-id",
- IAMProjectID: "project-id",
- SetupStarted: domain.Step2,
- SetupDone: domain.Step1,
+ ID: "id",
+ ChangeDate: testNow,
+ Sequence: 20211108,
+ GlobalOrgID: "global-org-id",
+ IAMProjectID: "project-id",
+ SetupStarted: domain.Step2,
+ SetupDone: domain.Step1,
+ DefaultLanguage: language.English,
},
},
{
@@ -102,7 +108,8 @@ func Test_IAMPrepares(t *testing.T) {
` zitadel.projections.iam.global_org_id,`+
` zitadel.projections.iam.iam_project_id,`+
` zitadel.projections.iam.setup_started,`+
- ` zitadel.projections.iam.setup_done`+
+ ` zitadel.projections.iam.setup_done,`+
+ ` zitadel.projections.iam.default_language`+
` FROM zitadel.projections.iam`),
sql.ErrConnDone,
),
diff --git a/internal/query/idp.go b/internal/query/idp.go
index 3831a06b3a..0ef6e4f17f 100644
--- a/internal/query/idp.go
+++ b/internal/query/idp.go
@@ -8,7 +8,7 @@ import (
sq "github.com/Masterminds/squirrel"
"github.com/lib/pq"
-
+
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors"
@@ -186,7 +186,7 @@ func (q *Queries) IDPByIDAndResourceOwner(ctx context.Context, id, resourceOwner
IDPResourceOwnerCol.identifier(): resourceOwner,
},
sq.Eq{
- IDPResourceOwnerCol.identifier(): q.iamID,
+ IDPResourceOwnerCol.identifier(): domain.IAMID,
},
},
},
diff --git a/internal/query/label_policy.go b/internal/query/label_policy.go
index ff073861f1..0f53124e20 100644
--- a/internal/query/label_policy.go
+++ b/internal/query/label_policy.go
@@ -47,7 +47,7 @@ func (q *Queries) ActiveLabelPolicyByOrg(ctx context.Context, orgID string) (*La
LabelPolicyColID.identifier(): orgID,
},
sq.Eq{
- LabelPolicyColID.identifier(): q.iamID,
+ LabelPolicyColID.identifier(): domain.IAMID,
},
},
sq.Eq{
@@ -73,7 +73,7 @@ func (q *Queries) PreviewLabelPolicyByOrg(ctx context.Context, orgID string) (*L
LabelPolicyColID.identifier(): orgID,
},
sq.Eq{
- LabelPolicyColID.identifier(): q.iamID,
+ LabelPolicyColID.identifier(): domain.IAMID,
},
},
sq.Eq{
@@ -93,7 +93,7 @@ func (q *Queries) PreviewLabelPolicyByOrg(ctx context.Context, orgID string) (*L
func (q *Queries) DefaultActiveLabelPolicy(ctx context.Context) (*LabelPolicy, error) {
stmt, scan := prepareLabelPolicyQuery()
query, args, err := stmt.Where(sq.Eq{
- LabelPolicyColID.identifier(): q.iamID,
+ LabelPolicyColID.identifier(): domain.IAMID,
LabelPolicyColState.identifier(): domain.LabelPolicyStateActive,
}).
OrderBy(LabelPolicyColIsDefault.identifier()).
@@ -109,7 +109,7 @@ func (q *Queries) DefaultActiveLabelPolicy(ctx context.Context) (*LabelPolicy, e
func (q *Queries) DefaultPreviewLabelPolicy(ctx context.Context) (*LabelPolicy, error) {
stmt, scan := prepareLabelPolicyQuery()
query, args, err := stmt.Where(sq.Eq{
- LabelPolicyColID.identifier(): q.iamID,
+ LabelPolicyColID.identifier(): domain.IAMID,
LabelPolicyColState.identifier(): domain.LabelPolicyStatePreview,
}).
OrderBy(LabelPolicyColIsDefault.identifier()).
diff --git a/internal/query/lockout_policy.go b/internal/query/lockout_policy.go
index 0a4b570a51..db5d474708 100644
--- a/internal/query/lockout_policy.go
+++ b/internal/query/lockout_policy.go
@@ -76,7 +76,7 @@ func (q *Queries) LockoutPolicyByOrg(ctx context.Context, orgID string) (*Lockou
LockoutColID.identifier(): orgID,
},
sq.Eq{
- LockoutColID.identifier(): q.iamID,
+ LockoutColID.identifier(): domain.IAMID,
},
}).
OrderBy(LockoutColIsDefault.identifier()).
@@ -92,7 +92,7 @@ func (q *Queries) LockoutPolicyByOrg(ctx context.Context, orgID string) (*Lockou
func (q *Queries) DefaultLockoutPolicy(ctx context.Context) (*LockoutPolicy, error) {
stmt, scan := prepareLockoutPolicyQuery()
query, args, err := stmt.Where(sq.Eq{
- LockoutColID.identifier(): q.iamID,
+ LockoutColID.identifier(): domain.IAMID,
}).
OrderBy(LockoutColIsDefault.identifier()).
Limit(1).ToSql()
diff --git a/internal/query/mail_template.go b/internal/query/mail_template.go
index b55ad9c8c7..daba2b7f25 100644
--- a/internal/query/mail_template.go
+++ b/internal/query/mail_template.go
@@ -65,7 +65,7 @@ func (q *Queries) MailTemplateByOrg(ctx context.Context, orgID string) (*MailTem
MailTemplateColAggregateID.identifier(): orgID,
},
sq.Eq{
- MailTemplateColAggregateID.identifier(): q.iamID,
+ MailTemplateColAggregateID.identifier(): domain.IAMID,
},
}).
OrderBy(MailTemplateColIsDefault.identifier()).
@@ -81,7 +81,7 @@ func (q *Queries) MailTemplateByOrg(ctx context.Context, orgID string) (*MailTem
func (q *Queries) DefaultMailTemplate(ctx context.Context) (*MailTemplate, error) {
stmt, scan := prepareMailTemplateQuery()
query, args, err := stmt.Where(sq.Eq{
- MailTemplateColAggregateID.identifier(): q.iamID,
+ MailTemplateColAggregateID.identifier(): domain.IAMID,
}).
OrderBy(MailTemplateColIsDefault.identifier()).
Limit(1).ToSql()
diff --git a/internal/query/message_text.go b/internal/query/message_text.go
index 6c9edb8668..ecdc67487d 100644
--- a/internal/query/message_text.go
+++ b/internal/query/message_text.go
@@ -119,7 +119,7 @@ func (q *Queries) MessageTextByOrg(ctx context.Context, orgID string) (*MessageT
MessageTextColAggregateID.identifier(): orgID,
},
sq.Eq{
- MessageTextColAggregateID.identifier(): q.iamID,
+ MessageTextColAggregateID.identifier(): domain.IAMID,
},
}).
OrderBy(MessageTextColAggregateID.identifier()).
@@ -135,7 +135,7 @@ func (q *Queries) MessageTextByOrg(ctx context.Context, orgID string) (*MessageT
func (q *Queries) DefaultMessageText(ctx context.Context) (*MessageText, error) {
stmt, scan := prepareMessageTextQuery()
query, args, err := stmt.Where(sq.Eq{
- MessageTextColAggregateID.identifier(): q.iamID,
+ MessageTextColAggregateID.identifier(): domain.IAMID,
}).
Limit(1).ToSql()
if err != nil {
@@ -230,7 +230,7 @@ func (q *Queries) readNotificationTextMessages(language string) ([]byte, error)
if !ok {
contents, err = q.readTranslationFile(q.NotificationDir, fmt.Sprintf("/i18n/%s.yaml", language))
if errors.IsNotFound(err) {
- contents, err = q.readTranslationFile(q.NotificationDir, fmt.Sprintf("/i18n/%s.yaml", q.DefaultLanguage.String()))
+ contents, err = q.readTranslationFile(q.NotificationDir, fmt.Sprintf("/i18n/%s.yaml", q.GetDefaultLanguage(context.Background()).String()))
}
if err != nil {
return nil, err
diff --git a/internal/query/org_iam_policy.go b/internal/query/org_iam_policy.go
index 3f85efaaad..f31439dfa5 100644
--- a/internal/query/org_iam_policy.go
+++ b/internal/query/org_iam_policy.go
@@ -71,7 +71,7 @@ func (q *Queries) OrgIAMPolicyByOrg(ctx context.Context, orgID string) (*OrgIAMP
OrgIAMColID.identifier(): orgID,
},
sq.Eq{
- OrgIAMColID.identifier(): q.iamID,
+ OrgIAMColID.identifier(): domain.IAMID,
},
}).
OrderBy(OrgIAMColIsDefault.identifier()).
@@ -87,7 +87,7 @@ func (q *Queries) OrgIAMPolicyByOrg(ctx context.Context, orgID string) (*OrgIAMP
func (q *Queries) DefaultOrgIAMPolicy(ctx context.Context) (*OrgIAMPolicy, error) {
stmt, scan := prepareOrgIAMPolicyQuery()
query, args, err := stmt.Where(sq.Eq{
- OrgIAMColID.identifier(): q.iamID,
+ OrgIAMColID.identifier(): domain.IAMID,
}).
OrderBy(OrgIAMColIsDefault.identifier()).
Limit(1).ToSql()
diff --git a/internal/query/password_age_policy.go b/internal/query/password_age_policy.go
index adbf220036..c0acf97508 100644
--- a/internal/query/password_age_policy.go
+++ b/internal/query/password_age_policy.go
@@ -76,7 +76,7 @@ func (q *Queries) PasswordAgePolicyByOrg(ctx context.Context, orgID string) (*Pa
PasswordAgeColID.identifier(): orgID,
},
sq.Eq{
- PasswordAgeColID.identifier(): q.iamID,
+ PasswordAgeColID.identifier(): domain.IAMID,
},
}).
OrderBy(PasswordAgeColIsDefault.identifier()).
@@ -92,7 +92,7 @@ func (q *Queries) PasswordAgePolicyByOrg(ctx context.Context, orgID string) (*Pa
func (q *Queries) DefaultPasswordAgePolicy(ctx context.Context) (*PasswordAgePolicy, error) {
stmt, scan := preparePasswordAgePolicyQuery()
query, args, err := stmt.Where(sq.Eq{
- PasswordAgeColID.identifier(): q.iamID,
+ PasswordAgeColID.identifier(): domain.IAMID,
}).
OrderBy(PasswordAgeColIsDefault.identifier()).
Limit(1).ToSql()
diff --git a/internal/query/password_complexity_policy.go b/internal/query/password_complexity_policy.go
index 409370b2b5..b3c1b0c48b 100644
--- a/internal/query/password_complexity_policy.go
+++ b/internal/query/password_complexity_policy.go
@@ -37,7 +37,7 @@ func (q *Queries) PasswordComplexityPolicyByOrg(ctx context.Context, orgID strin
PasswordComplexityColID.identifier(): orgID,
},
sq.Eq{
- PasswordComplexityColID.identifier(): q.iamID,
+ PasswordComplexityColID.identifier(): domain.IAMID,
},
}).
OrderBy(PasswordComplexityColIsDefault.identifier()).
@@ -53,7 +53,7 @@ func (q *Queries) PasswordComplexityPolicyByOrg(ctx context.Context, orgID strin
func (q *Queries) DefaultPasswordComplexityPolicy(ctx context.Context) (*PasswordComplexityPolicy, error) {
stmt, scan := preparePasswordComplexityPolicyQuery()
query, args, err := stmt.Where(sq.Eq{
- PasswordComplexityColID.identifier(): q.iamID,
+ PasswordComplexityColID.identifier(): domain.IAMID,
}).
OrderBy(PasswordComplexityColIsDefault.identifier()).
Limit(1).ToSql()
diff --git a/internal/query/privacy_policy.go b/internal/query/privacy_policy.go
index e21054e6b4..7c4c613a0f 100644
--- a/internal/query/privacy_policy.go
+++ b/internal/query/privacy_policy.go
@@ -76,7 +76,7 @@ func (q *Queries) PrivacyPolicyByOrg(ctx context.Context, orgID string) (*Privac
PrivacyColID.identifier(): orgID,
},
sq.Eq{
- PrivacyColID.identifier(): q.iamID,
+ PrivacyColID.identifier(): domain.IAMID,
},
}).
OrderBy(PrivacyColIsDefault.identifier()).
@@ -92,7 +92,7 @@ func (q *Queries) PrivacyPolicyByOrg(ctx context.Context, orgID string) (*Privac
func (q *Queries) DefaultPrivacyPolicy(ctx context.Context) (*PrivacyPolicy, error) {
stmt, scan := preparePrivacyPolicyQuery()
query, args, err := stmt.Where(sq.Eq{
- PrivacyColID.identifier(): q.iamID,
+ PrivacyColID.identifier(): domain.IAMID,
}).
OrderBy(PrivacyColIsDefault.identifier()).
Limit(1).ToSql()
diff --git a/internal/query/projection/iam.go b/internal/query/projection/iam.go
index 7de91f3efd..cf565e9df3 100644
--- a/internal/query/projection/iam.go
+++ b/internal/query/projection/iam.go
@@ -40,6 +40,10 @@ func (p *IAMProjection) reducers() []handler.AggregateReducer {
Event: iam.ProjectSetEventType,
Reduce: p.reduceIAMProjectSet,
},
+ {
+ Event: iam.DefaultLanguageSetEventType,
+ Reduce: p.reduceDefaultLanguageSet,
+ },
{
Event: iam.SetupStartedEventType,
Reduce: p.reduceSetupEvent,
@@ -56,13 +60,14 @@ func (p *IAMProjection) reducers() []handler.AggregateReducer {
type IAMColumn string
const (
- IAMColumnID = "id"
- IAMColumnChangeDate = "change_date"
- IAMColumnGlobalOrgID = "global_org_id"
- IAMColumnProjectID = "iam_project_id"
- IAMColumnSequence = "sequence"
- IAMColumnSetUpStarted = "setup_started"
- IAMColumnSetUpDone = "setup_done"
+ IAMColumnID = "id"
+ IAMColumnChangeDate = "change_date"
+ IAMColumnGlobalOrgID = "global_org_id"
+ IAMColumnProjectID = "iam_project_id"
+ IAMColumnSequence = "sequence"
+ IAMColumnSetUpStarted = "setup_started"
+ IAMColumnSetUpDone = "setup_done"
+ IAMColumnDefaultLanguage = "default_language"
)
func (p *IAMProjection) reduceGlobalOrgSet(event eventstore.Event) (*handler.Statement, error) {
@@ -99,6 +104,23 @@ func (p *IAMProjection) reduceIAMProjectSet(event eventstore.Event) (*handler.St
), nil
}
+func (p *IAMProjection) reduceDefaultLanguageSet(event eventstore.Event) (*handler.Statement, error) {
+ e, ok := event.(*iam.DefaultLanguageSetEvent)
+ if !ok {
+ logging.LogWithFields("HANDL-3n9le", "seq", event.Sequence(), "expectedType", iam.DefaultLanguageSetEventType).Error("wrong event type")
+ return nil, errors.ThrowInvalidArgument(nil, "HANDL-30o0e", "reduce.wrong.event.type")
+ }
+ return crdb.NewUpsertStatement(
+ e,
+ []handler.Column{
+ handler.NewCol(IAMColumnID, e.Aggregate().ID),
+ handler.NewCol(IAMColumnChangeDate, e.CreationDate()),
+ handler.NewCol(IAMColumnSequence, e.Sequence()),
+ handler.NewCol(IAMColumnDefaultLanguage, e.Language.String()),
+ },
+ ), nil
+}
+
func (p *IAMProjection) reduceSetupEvent(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*iam.SetupStepEvent)
if !ok {
diff --git a/internal/query/projection/iam_test.go b/internal/query/projection/iam_test.go
index 148fe1f02f..b3bd0ce012 100644
--- a/internal/query/projection/iam_test.go
+++ b/internal/query/projection/iam_test.go
@@ -52,7 +52,7 @@ func TestIAMProjection_reduces(t *testing.T) {
},
},
{
- name: "reduceGlobalOrgSet",
+ name: "reduceProjectIDSet",
args: args{
event: getEvent(testEvent(
repository.EventType(iam.ProjectSetEventType),
@@ -81,6 +81,36 @@ func TestIAMProjection_reduces(t *testing.T) {
},
},
},
+ {
+ name: "reduceDefaultLanguageSet",
+ args: args{
+ event: getEvent(testEvent(
+ repository.EventType(iam.DefaultLanguageSetEventType),
+ iam.AggregateType,
+ []byte(`{"language": "en"}`),
+ ), iam.DefaultLanguageSetMapper),
+ },
+ reduce: (&IAMProjection{}).reduceDefaultLanguageSet,
+ want: wantReduce{
+ projection: IAMProjectionTable,
+ aggregateType: eventstore.AggregateType("iam"),
+ sequence: 15,
+ previousSequence: 10,
+ executer: &testExecuter{
+ executions: []execution{
+ {
+ expectedStmt: "UPSERT INTO zitadel.projections.iam (id, change_date, sequence, default_language) VALUES ($1, $2, $3, $4)",
+ expectedArgs: []interface{}{
+ "agg-id",
+ anyArg{},
+ uint64(15),
+ "en",
+ },
+ },
+ },
+ },
+ },
+ },
{
name: "reduceSetupStarted",
args: args{
diff --git a/internal/query/projection/projection.go b/internal/query/projection/projection.go
index 41d5f5462d..6004f7411a 100644
--- a/internal/query/projection/projection.go
+++ b/internal/query/projection/projection.go
@@ -69,6 +69,8 @@ func Start(ctx context.Context, sqlClient *sql.DB, es *eventstore.Eventstore, co
NewUserMetadataProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["user_metadata"]))
NewUserAuthMethodProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["user_auth_method"]))
NewIAMProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["iam"]))
+ NewSecretGeneratorProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["secret_generators"]))
+ NewSMTPConfigProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["smtp_configs"]))
_, err := NewKeyProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["keys"]), keyConfig, keyChan)
return err
diff --git a/internal/query/projection/secret_generator.go b/internal/query/projection/secret_generator.go
new file mode 100644
index 0000000000..4ce6cc841e
--- /dev/null
+++ b/internal/query/projection/secret_generator.go
@@ -0,0 +1,144 @@
+package projection
+
+import (
+ "context"
+
+ "github.com/caos/logging"
+ "github.com/caos/zitadel/internal/errors"
+ "github.com/caos/zitadel/internal/eventstore"
+ "github.com/caos/zitadel/internal/eventstore/handler"
+ "github.com/caos/zitadel/internal/eventstore/handler/crdb"
+ "github.com/caos/zitadel/internal/repository/iam"
+ "github.com/caos/zitadel/internal/repository/project"
+)
+
+type SecretGeneratorProjection struct {
+ crdb.StatementHandler
+}
+
+const (
+ SecretGeneratorProjectionTable = "zitadel.projections.secret_generators"
+)
+
+func NewSecretGeneratorProjection(ctx context.Context, config crdb.StatementHandlerConfig) *SecretGeneratorProjection {
+ p := &SecretGeneratorProjection{}
+ config.ProjectionName = SecretGeneratorProjectionTable
+ config.Reducers = p.reducers()
+ p.StatementHandler = crdb.NewStatementHandler(ctx, config)
+ return p
+}
+
+func (p *SecretGeneratorProjection) reducers() []handler.AggregateReducer {
+ return []handler.AggregateReducer{
+ {
+ Aggregate: project.AggregateType,
+ EventRedusers: []handler.EventReducer{
+ {
+ Event: iam.SecretGeneratorAddedEventType,
+ Reduce: p.reduceSecretGeneratorAdded,
+ },
+ {
+ Event: iam.SecretGeneratorChangedEventType,
+ Reduce: p.reduceSecretGeneratorChanged,
+ },
+ {
+ Event: iam.SecretGeneratorRemovedEventType,
+ Reduce: p.reduceSecretGeneratorRemoved,
+ },
+ },
+ },
+ }
+}
+
+const (
+ SecretGeneratorColumnGeneratorType = "generator_type"
+ SecretGeneratorColumnAggregateID = "aggregate_id"
+ SecretGeneratorColumnCreationDate = "creation_date"
+ SecretGeneratorColumnChangeDate = "change_date"
+ SecretGeneratorColumnResourceOwner = "resource_owner"
+ SecretGeneratorColumnSequence = "sequence"
+ SecretGeneratorColumnLength = "length"
+ SecretGeneratorColumnExpiry = "expiry"
+ SecretGeneratorColumnIncludeLowerLetters = "include_lower_letters"
+ SecretGeneratorColumnIncludeUpperLetters = "include_upper_letters"
+ SecretGeneratorColumnIncludeDigits = "include_digits"
+ SecretGeneratorColumnIncludeSymbols = "include_symbols"
+)
+
+func (p *SecretGeneratorProjection) reduceSecretGeneratorAdded(event eventstore.Event) (*handler.Statement, error) {
+ e, ok := event.(*iam.SecretGeneratorAddedEvent)
+ if !ok {
+ logging.LogWithFields("HANDL-nf9sl", "seq", event.Sequence(), "expectedType", iam.SecretGeneratorAddedEventType).Error("wrong event type")
+ return nil, errors.ThrowInvalidArgument(nil, "HANDL-sk99F", "reduce.wrong.event.type")
+ }
+ return crdb.NewCreateStatement(
+ e,
+ []handler.Column{
+ handler.NewCol(SecretGeneratorColumnAggregateID, e.Aggregate().ID),
+ handler.NewCol(SecretGeneratorColumnGeneratorType, e.GeneratorType),
+ handler.NewCol(SecretGeneratorColumnCreationDate, e.CreationDate()),
+ handler.NewCol(SecretGeneratorColumnChangeDate, e.CreationDate()),
+ handler.NewCol(SecretGeneratorColumnResourceOwner, e.Aggregate().ResourceOwner),
+ handler.NewCol(SecretGeneratorColumnSequence, e.Sequence()),
+ handler.NewCol(SecretGeneratorColumnLength, e.Length),
+ handler.NewCol(SecretGeneratorColumnExpiry, e.Expiry),
+ handler.NewCol(SecretGeneratorColumnIncludeLowerLetters, e.IncludeLowerLetters),
+ handler.NewCol(SecretGeneratorColumnIncludeUpperLetters, e.IncludeUpperLetters),
+ handler.NewCol(SecretGeneratorColumnIncludeDigits, e.IncludeDigits),
+ handler.NewCol(SecretGeneratorColumnIncludeSymbols, e.IncludeSymbols),
+ },
+ ), nil
+}
+
+func (p *SecretGeneratorProjection) reduceSecretGeneratorChanged(event eventstore.Event) (*handler.Statement, error) {
+ e, ok := event.(*iam.SecretGeneratorChangedEvent)
+ if !ok {
+ logging.LogWithFields("HANDL-sn9jd", "seq", event.Sequence(), "expected", iam.SecretGeneratorChangedEventType).Error("wrong event type")
+ return nil, errors.ThrowInvalidArgument(nil, "HANDL-s00Fs", "reduce.wrong.event.type")
+ }
+
+ columns := make([]handler.Column, 0, 7)
+ columns = append(columns, handler.NewCol(SecretGeneratorColumnChangeDate, e.CreationDate()),
+ handler.NewCol(SecretGeneratorColumnSequence, e.Sequence()))
+ if e.Length != nil {
+ columns = append(columns, handler.NewCol(SecretGeneratorColumnLength, *e.Length))
+ }
+ if e.Expiry != nil {
+ columns = append(columns, handler.NewCol(SecretGeneratorColumnExpiry, *e.Expiry))
+ }
+ if e.IncludeLowerLetters != nil {
+ columns = append(columns, handler.NewCol(SecretGeneratorColumnIncludeLowerLetters, *e.IncludeLowerLetters))
+ }
+ if e.IncludeUpperLetters != nil {
+ columns = append(columns, handler.NewCol(SecretGeneratorColumnIncludeUpperLetters, *e.IncludeUpperLetters))
+ }
+ if e.IncludeDigits != nil {
+ columns = append(columns, handler.NewCol(SecretGeneratorColumnIncludeDigits, *e.IncludeDigits))
+ }
+ if e.IncludeSymbols != nil {
+ columns = append(columns, handler.NewCol(SecretGeneratorColumnIncludeSymbols, *e.IncludeSymbols))
+ }
+ return crdb.NewUpdateStatement(
+ e,
+ columns,
+ []handler.Condition{
+ handler.NewCond(SecretGeneratorColumnAggregateID, e.Aggregate().ID),
+ handler.NewCond(SecretGeneratorColumnGeneratorType, e.GeneratorType),
+ },
+ ), nil
+}
+
+func (p *SecretGeneratorProjection) reduceSecretGeneratorRemoved(event eventstore.Event) (*handler.Statement, error) {
+ e, ok := event.(*iam.SecretGeneratorRemovedEvent)
+ if !ok {
+ logging.LogWithFields("HANDL-30oEF", "seq", event.Sequence(), "expectedType", iam.SecretGeneratorRemovedEventType).Error("wrong event type")
+ return nil, errors.ThrowInvalidArgument(nil, "HANDL-fmiIf", "reduce.wrong.event.type")
+ }
+ return crdb.NewDeleteStatement(
+ e,
+ []handler.Condition{
+ handler.NewCond(SecretGeneratorColumnAggregateID, e.Aggregate().ID),
+ handler.NewCond(SecretGeneratorColumnGeneratorType, e.GeneratorType),
+ },
+ ), nil
+}
diff --git a/internal/query/projection/secret_generator_test.go b/internal/query/projection/secret_generator_test.go
new file mode 100644
index 0000000000..36c8217d3e
--- /dev/null
+++ b/internal/query/projection/secret_generator_test.go
@@ -0,0 +1,141 @@
+package projection
+
+import (
+ "testing"
+ "time"
+
+ "github.com/caos/zitadel/internal/domain"
+ "github.com/caos/zitadel/internal/errors"
+ "github.com/caos/zitadel/internal/eventstore"
+ "github.com/caos/zitadel/internal/eventstore/handler"
+ "github.com/caos/zitadel/internal/eventstore/repository"
+ "github.com/caos/zitadel/internal/repository/iam"
+)
+
+func TestSecretGeneratorProjection_reduces(t *testing.T) {
+ type args struct {
+ event func(t *testing.T) eventstore.Event
+ }
+ tests := []struct {
+ name string
+ args args
+ reduce func(event eventstore.Event) (*handler.Statement, error)
+ want wantReduce
+ }{
+ {
+ name: "reduceSecretGeneratorRemoved",
+ args: args{
+ event: getEvent(testEvent(
+ repository.EventType(iam.SecretGeneratorRemovedEventType),
+ iam.AggregateType,
+ []byte(`{"generatorType": 1}`),
+ ), iam.SecretGeneratorRemovedEventMapper),
+ },
+ reduce: (&SecretGeneratorProjection{}).reduceSecretGeneratorRemoved,
+ want: wantReduce{
+ projection: SecretGeneratorProjectionTable,
+ aggregateType: eventstore.AggregateType("iam"),
+ sequence: 15,
+ previousSequence: 10,
+ executer: &testExecuter{
+ executions: []execution{
+ {
+ expectedStmt: "DELETE FROM zitadel.projections.secret_generators WHERE (aggregate_id = $1) AND (generator_type = $2)",
+ expectedArgs: []interface{}{
+ "agg-id",
+ domain.SecretGeneratorTypeInitCode,
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: "reduceSecretGeneratorChanged",
+ args: args{
+ event: getEvent(testEvent(
+ repository.EventType(iam.SecretGeneratorChangedEventType),
+ iam.AggregateType,
+ []byte(`{"generatorType": 1, "length": 4, "expiry": 10000000, "includeLowerLetters": true, "includeUpperLetters": true, "includeDigits": true, "includeSymbols": true}`),
+ ), iam.SecretGeneratorChangedEventMapper),
+ },
+ reduce: (&SecretGeneratorProjection{}).reduceSecretGeneratorChanged,
+ want: wantReduce{
+ projection: SecretGeneratorProjectionTable,
+ aggregateType: eventstore.AggregateType("iam"),
+ sequence: 15,
+ previousSequence: 10,
+ executer: &testExecuter{
+ executions: []execution{
+ {
+ expectedStmt: "UPDATE zitadel.projections.secret_generators SET (change_date, sequence, length, expiry, include_lower_letters, include_upper_letters, include_digits, include_symbols) = ($1, $2, $3, $4, $5, $6, $7, $8) WHERE (aggregate_id = $9) AND (generator_type = $10)",
+ expectedArgs: []interface{}{
+ anyArg{},
+ uint64(15),
+ uint(4),
+ time.Millisecond * 10,
+ true,
+ true,
+ true,
+ true,
+ "agg-id",
+ domain.SecretGeneratorTypeInitCode,
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: "reduceSecretGeneratorAdded",
+ args: args{
+ event: getEvent(testEvent(
+ repository.EventType(iam.SecretGeneratorAddedEventType),
+ iam.AggregateType,
+ []byte(`{"generatorType": 1, "length": 4, "expiry": 10000000, "includeLowerLetters": true, "includeUpperLetters": true, "includeDigits": true, "includeSymbols": true}`),
+ ), iam.SecretGeneratorAddedEventMapper),
+ },
+ reduce: (&SecretGeneratorProjection{}).reduceSecretGeneratorAdded,
+ want: wantReduce{
+ projection: SecretGeneratorProjectionTable,
+ aggregateType: eventstore.AggregateType("iam"),
+ sequence: 15,
+ previousSequence: 10,
+ executer: &testExecuter{
+ executions: []execution{
+ {
+ expectedStmt: "INSERT INTO zitadel.projections.secret_generators (aggregate_id, generator_type, creation_date, change_date, resource_owner, sequence, length, expiry, include_lower_letters, include_upper_letters, include_digits, include_symbols) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)",
+ expectedArgs: []interface{}{
+ "agg-id",
+ domain.SecretGeneratorTypeInitCode,
+ anyArg{},
+ anyArg{},
+ "ro-id",
+ uint64(15),
+ uint(4),
+ time.Millisecond * 10,
+ true,
+ true,
+ true,
+ true,
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ event := baseEvent(t)
+ got, err := tt.reduce(event)
+ if _, ok := err.(errors.InvalidArgument); !ok {
+ t.Errorf("no wrong event mapping: %v, got: %v", err, got)
+ }
+
+ event = tt.args.event(t)
+ got, err = tt.reduce(event)
+ assertReduce(t, got, err, tt.want)
+ })
+ }
+}
diff --git a/internal/query/projection/smtp.go b/internal/query/projection/smtp.go
new file mode 100644
index 0000000000..a31399ff2a
--- /dev/null
+++ b/internal/query/projection/smtp.go
@@ -0,0 +1,143 @@
+package projection
+
+import (
+ "context"
+
+ "github.com/caos/logging"
+ "github.com/caos/zitadel/internal/errors"
+ "github.com/caos/zitadel/internal/eventstore"
+ "github.com/caos/zitadel/internal/eventstore/handler"
+ "github.com/caos/zitadel/internal/eventstore/handler/crdb"
+ "github.com/caos/zitadel/internal/repository/iam"
+ "github.com/caos/zitadel/internal/repository/project"
+)
+
+type SMTPConfigProjection struct {
+ crdb.StatementHandler
+}
+
+const (
+ SMTPConfigProjectionTable = "zitadel.projections.smtp_configs"
+)
+
+func NewSMTPConfigProjection(ctx context.Context, config crdb.StatementHandlerConfig) *SMTPConfigProjection {
+ p := &SMTPConfigProjection{}
+ config.ProjectionName = SMTPConfigProjectionTable
+ config.Reducers = p.reducers()
+ p.StatementHandler = crdb.NewStatementHandler(ctx, config)
+ return p
+}
+
+func (p *SMTPConfigProjection) reducers() []handler.AggregateReducer {
+ return []handler.AggregateReducer{
+ {
+ Aggregate: project.AggregateType,
+ EventRedusers: []handler.EventReducer{
+ {
+ Event: iam.SMTPConfigAddedEventType,
+ Reduce: p.reduceSMTPConfigAdded,
+ },
+ {
+ Event: iam.SMTPConfigChangedEventType,
+ Reduce: p.reduceSMTPConfigChanged,
+ },
+ {
+ Event: iam.SMTPConfigPasswordChangedEventType,
+ Reduce: p.reduceSMTPConfigPasswordChanged,
+ },
+ },
+ },
+ }
+}
+
+const (
+ SMTPConfigColumnAggregateID = "aggregate_id"
+ SMTPConfigColumnCreationDate = "creation_date"
+ SMTPConfigColumnChangeDate = "change_date"
+ SMTPConfigColumnResourceOwner = "resource_owner"
+ SMTPConfigColumnSequence = "sequence"
+ SMTPConfigColumnTLS = "tls"
+ SMTPConfigColumnFromAddress = "sender_address"
+ SMTPConfigColumnFromName = "sender_name"
+ SMTPConfigColumnSMTPHost = "host"
+ SMTPConfigColumnSMTPUser = "username"
+ SMTPConfigColumnSMTPPassword = "password"
+)
+
+func (p *SMTPConfigProjection) reduceSMTPConfigAdded(event eventstore.Event) (*handler.Statement, error) {
+ e, ok := event.(*iam.SMTPConfigAddedEvent)
+ if !ok {
+ logging.LogWithFields("HANDL-wkofs", "seq", event.Sequence(), "expectedType", iam.SMTPConfigAddedEventType).Error("wrong event type")
+ return nil, errors.ThrowInvalidArgument(nil, "HANDL-sk99F", "reduce.wrong.event.type")
+ }
+ return crdb.NewCreateStatement(
+ e,
+ []handler.Column{
+ handler.NewCol(SMTPConfigColumnAggregateID, e.Aggregate().ID),
+ handler.NewCol(SMTPConfigColumnCreationDate, e.CreationDate()),
+ handler.NewCol(SMTPConfigColumnChangeDate, e.CreationDate()),
+ handler.NewCol(SMTPConfigColumnResourceOwner, e.Aggregate().ResourceOwner),
+ handler.NewCol(SMTPConfigColumnSequence, e.Sequence()),
+ handler.NewCol(SMTPConfigColumnTLS, e.TLS),
+ handler.NewCol(SMTPConfigColumnFromAddress, e.SenderAddress),
+ handler.NewCol(SMTPConfigColumnFromName, e.SenderName),
+ handler.NewCol(SMTPConfigColumnSMTPHost, e.Host),
+ handler.NewCol(SMTPConfigColumnSMTPUser, e.User),
+ handler.NewCol(SMTPConfigColumnSMTPPassword, e.Password),
+ },
+ ), nil
+}
+
+func (p *SMTPConfigProjection) reduceSMTPConfigChanged(event eventstore.Event) (*handler.Statement, error) {
+ e, ok := event.(*iam.SMTPConfigChangedEvent)
+ if !ok {
+ logging.LogWithFields("HANDL-wo00f", "seq", event.Sequence(), "expected", iam.SMTPConfigChangedEventType).Error("wrong event type")
+ return nil, errors.ThrowInvalidArgument(nil, "HANDL-wl0wd", "reduce.wrong.event.type")
+ }
+
+ columns := make([]handler.Column, 0, 7)
+ columns = append(columns, handler.NewCol(SMTPConfigColumnChangeDate, e.CreationDate()),
+ handler.NewCol(SMTPConfigColumnSequence, e.Sequence()))
+ if e.TLS != nil {
+ columns = append(columns, handler.NewCol(SMTPConfigColumnTLS, *e.TLS))
+ }
+ if e.FromAddress != nil {
+ columns = append(columns, handler.NewCol(SMTPConfigColumnFromAddress, *e.FromAddress))
+ }
+ if e.FromName != nil {
+ columns = append(columns, handler.NewCol(SMTPConfigColumnFromName, *e.FromName))
+ }
+ if e.Host != nil {
+ columns = append(columns, handler.NewCol(SMTPConfigColumnSMTPHost, *e.Host))
+ }
+ if e.User != nil {
+ columns = append(columns, handler.NewCol(SMTPConfigColumnSMTPUser, *e.User))
+ }
+ return crdb.NewUpdateStatement(
+ e,
+ columns,
+ []handler.Condition{
+ handler.NewCond(SMTPConfigColumnAggregateID, e.Aggregate().ID),
+ },
+ ), nil
+}
+
+func (p *SMTPConfigProjection) reduceSMTPConfigPasswordChanged(event eventstore.Event) (*handler.Statement, error) {
+ e, ok := event.(*iam.SMTPConfigPasswordChangedEvent)
+ if !ok {
+ logging.LogWithFields("HANDL-f92sf", "seq", event.Sequence(), "expected", iam.SMTPConfigChangedEventType).Error("wrong event type")
+ return nil, errors.ThrowInvalidArgument(nil, "HANDL-fk02f", "reduce.wrong.event.type")
+ }
+
+ return crdb.NewUpdateStatement(
+ e,
+ []handler.Column{
+ handler.NewCol(SMTPConfigColumnChangeDate, e.CreationDate()),
+ handler.NewCol(SMTPConfigColumnSequence, e.Sequence()),
+ handler.NewCol(SMTPConfigColumnSMTPPassword, e.Password),
+ },
+ []handler.Condition{
+ handler.NewCond(SMTPConfigColumnAggregateID, e.Aggregate().ID),
+ },
+ ), nil
+}
diff --git a/internal/query/projection/smtp_test.go b/internal/query/projection/smtp_test.go
new file mode 100644
index 0000000000..5c6343fa36
--- /dev/null
+++ b/internal/query/projection/smtp_test.go
@@ -0,0 +1,162 @@
+package projection
+
+import (
+ "testing"
+
+ "github.com/caos/zitadel/internal/errors"
+ "github.com/caos/zitadel/internal/eventstore"
+ "github.com/caos/zitadel/internal/eventstore/handler"
+ "github.com/caos/zitadel/internal/eventstore/repository"
+ "github.com/caos/zitadel/internal/repository/iam"
+)
+
+func TestSMTPConfigProjection_reduces(t *testing.T) {
+ type args struct {
+ event func(t *testing.T) eventstore.Event
+ }
+ tests := []struct {
+ name string
+ args args
+ reduce func(event eventstore.Event) (*handler.Statement, error)
+ want wantReduce
+ }{
+ {
+ name: "reduceSMTPConfigChanged",
+ args: args{
+ event: getEvent(testEvent(
+ repository.EventType(iam.SMTPConfigChangedEventType),
+ iam.AggregateType,
+ []byte(`{
+ "tls": true,
+ "senderAddress": "sender",
+ "senderName": "name",
+ "host": "host",
+ "user": "user"
+ }`,
+ ),
+ ), iam.SMTPConfigChangedEventMapper),
+ },
+ reduce: (&SMTPConfigProjection{}).reduceSMTPConfigChanged,
+ want: wantReduce{
+ projection: SMTPConfigProjectionTable,
+ aggregateType: eventstore.AggregateType("iam"),
+ sequence: 15,
+ previousSequence: 10,
+ executer: &testExecuter{
+ executions: []execution{
+ {
+ expectedStmt: "UPDATE zitadel.projections.smtp_configs SET (change_date, sequence, tls, sender_address, sender_name, host, username) = ($1, $2, $3, $4, $5, $6, $7) WHERE (aggregate_id = $8)",
+ expectedArgs: []interface{}{
+ anyArg{},
+ uint64(15),
+ true,
+ "sender",
+ "name",
+ "host",
+ "user",
+ "agg-id",
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: "reduceSMTPConfigAdded",
+ args: args{
+ event: getEvent(testEvent(
+ repository.EventType(iam.SMTPConfigAddedEventType),
+ iam.AggregateType,
+ []byte(`{
+ "tls": true,
+ "senderAddress": "sender",
+ "senderName": "name",
+ "host": "host",
+ "user": "user",
+ "password": {
+ "cryptoType": 0,
+ "algorithm": "RSA-265",
+ "keyId": "key-id"
+ }
+ }`),
+ ), iam.SMTPConfigAddedEventMapper),
+ },
+ reduce: (&SMTPConfigProjection{}).reduceSMTPConfigAdded,
+ want: wantReduce{
+ projection: SMTPConfigProjectionTable,
+ aggregateType: eventstore.AggregateType("iam"),
+ sequence: 15,
+ previousSequence: 10,
+ executer: &testExecuter{
+ executions: []execution{
+ {
+ expectedStmt: "INSERT INTO zitadel.projections.smtp_configs (aggregate_id, creation_date, change_date, resource_owner, sequence, tls, sender_address, sender_name, host, username, password) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)",
+ expectedArgs: []interface{}{
+ "agg-id",
+ anyArg{},
+ anyArg{},
+ "ro-id",
+ uint64(15),
+ true,
+ "sender",
+ "name",
+ "host",
+ "user",
+ anyArg{},
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ name: "reduceSMTPConfigPasswordChanged",
+ args: args{
+ event: getEvent(testEvent(
+ repository.EventType(iam.SMTPConfigPasswordChangedEventType),
+ iam.AggregateType,
+ []byte(`{
+ "password": {
+ "cryptoType": 0,
+ "algorithm": "RSA-265",
+ "keyId": "key-id"
+ }
+ }`),
+ ), iam.SMTPConfigPasswordChangedEventMapper),
+ },
+ reduce: (&SMTPConfigProjection{}).reduceSMTPConfigPasswordChanged,
+ want: wantReduce{
+ projection: SMTPConfigProjectionTable,
+ aggregateType: eventstore.AggregateType("iam"),
+ sequence: 15,
+ previousSequence: 10,
+ executer: &testExecuter{
+ executions: []execution{
+ {
+ expectedStmt: "UPDATE zitadel.projections.smtp_configs SET (change_date, sequence, password) = ($1, $2, $3) WHERE (aggregate_id = $4)",
+ expectedArgs: []interface{}{
+ anyArg{},
+ uint64(15),
+ anyArg{},
+ "agg-id",
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ event := baseEvent(t)
+ got, err := tt.reduce(event)
+ if _, ok := err.(errors.InvalidArgument); !ok {
+ t.Errorf("no wrong event mapping: %v, got: %v", err, got)
+ }
+
+ event = tt.args.event(t)
+ got, err = tt.reduce(event)
+ assertReduce(t, got, err, tt.want)
+ })
+ }
+}
diff --git a/internal/query/query.go b/internal/query/query.go
index fe4d562838..99f29c20d9 100644
--- a/internal/query/query.go
+++ b/internal/query/query.go
@@ -25,7 +25,6 @@ import (
)
type Queries struct {
- iamID string
eventstore *eventstore.Eventstore
client *sql.DB
@@ -51,10 +50,9 @@ func StartQueries(ctx context.Context, es *eventstore.Eventstore, sqlClient *sql
}
repo = &Queries{
- iamID: defaults.IamID,
eventstore: es,
client: sqlClient,
- DefaultLanguage: defaults.DefaultLanguage,
+ DefaultLanguage: language.Und,
LoginDir: statikLoginFS,
NotificationDir: statikNotificationFS,
LoginTranslationFileContents: make(map[string][]byte),
diff --git a/internal/query/secret_generator_test.go b/internal/query/secret_generator_test.go
new file mode 100644
index 0000000000..3f4617d1e7
--- /dev/null
+++ b/internal/query/secret_generator_test.go
@@ -0,0 +1,386 @@
+package query
+
+import (
+ "database/sql"
+ "database/sql/driver"
+ "errors"
+ "fmt"
+ "regexp"
+ "testing"
+ "time"
+
+ "github.com/caos/zitadel/internal/domain"
+ errs "github.com/caos/zitadel/internal/errors"
+)
+
+func Test_SecretGeneratorsPrepares(t *testing.T) {
+ type want struct {
+ sqlExpectations sqlExpectation
+ err checkErr
+ }
+ tests := []struct {
+ name string
+ prepare interface{}
+ want want
+ object interface{}
+ }{
+ {
+ name: "prepareSecretGeneratorsQuery no result",
+ prepare: prepareSecretGeneratorsQuery,
+ want: want{
+ sqlExpectations: mockQueries(
+ regexp.QuoteMeta(`SELECT zitadel.projections.secret_generators.aggregate_id,`+
+ ` zitadel.projections.secret_generators.generator_type,`+
+ ` zitadel.projections.secret_generators.creation_date,`+
+ ` zitadel.projections.secret_generators.change_date,`+
+ ` zitadel.projections.secret_generators.resource_owner,`+
+ ` zitadel.projections.secret_generators.sequence,`+
+ ` zitadel.projections.secret_generators.length,`+
+ ` zitadel.projections.secret_generators.expiry,`+
+ ` zitadel.projections.secret_generators.include_lower_letters,`+
+ ` zitadel.projections.secret_generators.include_upper_letters,`+
+ ` zitadel.projections.secret_generators.include_digits,`+
+ ` zitadel.projections.secret_generators.include_symbols,`+
+ ` COUNT(*) OVER ()`+
+ ` FROM zitadel.projections.secret_generators`),
+ nil,
+ nil,
+ ),
+ },
+ object: &SecretGenerators{SecretGenerators: []*SecretGenerator{}},
+ },
+ {
+ name: "prepareSecretGeneratorsQuery one result",
+ prepare: prepareSecretGeneratorsQuery,
+ want: want{
+ sqlExpectations: mockQueries(
+ regexp.QuoteMeta(`SELECT zitadel.projections.secret_generators.aggregate_id,`+
+ ` zitadel.projections.secret_generators.generator_type,`+
+ ` zitadel.projections.secret_generators.creation_date,`+
+ ` zitadel.projections.secret_generators.change_date,`+
+ ` zitadel.projections.secret_generators.resource_owner,`+
+ ` zitadel.projections.secret_generators.sequence,`+
+ ` zitadel.projections.secret_generators.length,`+
+ ` zitadel.projections.secret_generators.expiry,`+
+ ` zitadel.projections.secret_generators.include_lower_letters,`+
+ ` zitadel.projections.secret_generators.include_upper_letters,`+
+ ` zitadel.projections.secret_generators.include_digits,`+
+ ` zitadel.projections.secret_generators.include_symbols,`+
+ ` COUNT(*) OVER ()`+
+ ` FROM zitadel.projections.secret_generators`),
+ []string{
+ "aggregate_id",
+ "generator_type",
+ "creation_date",
+ "change_date",
+ "resource_owner",
+ "sequence",
+ "length",
+ "expiry",
+ "include_lower_letters",
+ "include_upper_letters",
+ "include_digits",
+ "include_symbols",
+ "count",
+ },
+ [][]driver.Value{
+ {
+ "agg-id",
+ domain.SecretGeneratorTypeInitCode,
+ testNow,
+ testNow,
+ "ro",
+ uint64(20211108),
+ 4,
+ time.Minute * 1,
+ true,
+ true,
+ true,
+ true,
+ },
+ },
+ ),
+ },
+ object: &SecretGenerators{
+ SearchResponse: SearchResponse{
+ Count: 1,
+ },
+ SecretGenerators: []*SecretGenerator{
+ {
+ AggregateID: "agg-id",
+ GeneratorType: 1,
+ CreationDate: testNow,
+ ChangeDate: testNow,
+ ResourceOwner: "ro",
+ Sequence: 20211108,
+ Length: 4,
+ Expiry: time.Minute * 1,
+ IncludeLowerLetters: true,
+ IncludeUpperLetters: true,
+ IncludeDigits: true,
+ IncludeSymbols: true,
+ },
+ },
+ },
+ },
+ {
+ name: "prepareSecretGeneratorsQuery multiple result",
+ prepare: prepareSecretGeneratorsQuery,
+ want: want{
+ sqlExpectations: mockQueries(
+ regexp.QuoteMeta(`SELECT zitadel.projections.secret_generators.aggregate_id,`+
+ ` zitadel.projections.secret_generators.generator_type,`+
+ ` zitadel.projections.secret_generators.creation_date,`+
+ ` zitadel.projections.secret_generators.change_date,`+
+ ` zitadel.projections.secret_generators.resource_owner,`+
+ ` zitadel.projections.secret_generators.sequence,`+
+ ` zitadel.projections.secret_generators.length,`+
+ ` zitadel.projections.secret_generators.expiry,`+
+ ` zitadel.projections.secret_generators.include_lower_letters,`+
+ ` zitadel.projections.secret_generators.include_upper_letters,`+
+ ` zitadel.projections.secret_generators.include_digits,`+
+ ` zitadel.projections.secret_generators.include_symbols,`+
+ ` COUNT(*) OVER ()`+
+ ` FROM zitadel.projections.secret_generators`),
+ []string{
+ "aggregate_id",
+ "generator_type",
+ "creation_date",
+ "change_date",
+ "resource_owner",
+ "sequence",
+ "length",
+ "expiry",
+ "include_lower_letters",
+ "include_upper_letters",
+ "include_digits",
+ "include_symbols",
+ "count",
+ },
+ [][]driver.Value{
+ {
+ "agg-id",
+ domain.SecretGeneratorTypeInitCode,
+ testNow,
+ testNow,
+ "ro",
+ uint64(20211108),
+ 4,
+ time.Minute * 1,
+ true,
+ true,
+ true,
+ true,
+ },
+ {
+ "agg-id",
+ domain.SecretGeneratorTypeVerifyEmailCode,
+ testNow,
+ testNow,
+ "ro",
+ uint64(20211108),
+ 4,
+ time.Minute * 1,
+ true,
+ true,
+ true,
+ true,
+ },
+ },
+ ),
+ },
+ object: &SecretGenerators{
+ SearchResponse: SearchResponse{
+ Count: 2,
+ },
+ SecretGenerators: []*SecretGenerator{
+ {
+ AggregateID: "agg-id",
+ GeneratorType: 1,
+ CreationDate: testNow,
+ ChangeDate: testNow,
+ ResourceOwner: "ro",
+ Sequence: 20211108,
+ Length: 4,
+ Expiry: time.Minute * 1,
+ IncludeLowerLetters: true,
+ IncludeUpperLetters: true,
+ IncludeDigits: true,
+ IncludeSymbols: true,
+ },
+ {
+ AggregateID: "agg-id",
+ GeneratorType: 2,
+ CreationDate: testNow,
+ ChangeDate: testNow,
+ ResourceOwner: "ro",
+ Sequence: 20211108,
+ Length: 4,
+ Expiry: time.Minute * 1,
+ IncludeLowerLetters: true,
+ IncludeUpperLetters: true,
+ IncludeDigits: true,
+ IncludeSymbols: true,
+ },
+ },
+ },
+ },
+ {
+ name: "prepareSecretGeneratorsQuery sql err",
+ prepare: prepareSecretGeneratorsQuery,
+ want: want{
+ sqlExpectations: mockQueryErr(
+ regexp.QuoteMeta(`SELECT zitadel.projections.secret_generators.aggregate_id,`+
+ ` zitadel.projections.secret_generators.generator_type,`+
+ ` zitadel.projections.secret_generators.creation_date,`+
+ ` zitadel.projections.secret_generators.change_date,`+
+ ` zitadel.projections.secret_generators.resource_owner,`+
+ ` zitadel.projections.secret_generators.sequence,`+
+ ` zitadel.projections.secret_generators.length,`+
+ ` zitadel.projections.secret_generators.expiry,`+
+ ` zitadel.projections.secret_generators.include_lower_letters,`+
+ ` zitadel.projections.secret_generators.include_upper_letters,`+
+ ` zitadel.projections.secret_generators.include_digits,`+
+ ` zitadel.projections.secret_generators.include_symbols,`+
+ ` COUNT(*) OVER ()`+
+ ` FROM zitadel.projections.secret_generators`),
+ sql.ErrConnDone,
+ ),
+ err: func(err error) (error, bool) {
+ if !errors.Is(err, sql.ErrConnDone) {
+ return fmt.Errorf("err should be sql.ErrConnDone got: %w", err), false
+ }
+ return nil, true
+ },
+ },
+ object: nil,
+ },
+ {
+ name: "prepareSecretGeneratorQuery no result",
+ prepare: prepareSecretGeneratorQuery,
+ want: want{
+ sqlExpectations: mockQueries(
+ `SELECT zitadel.projections.secret_generators.aggregate_id,`+
+ ` zitadel.projections.secret_generators.generator_type,`+
+ ` zitadel.projections.secret_generators.creation_date,`+
+ ` zitadel.projections.secret_generators.change_date,`+
+ ` zitadel.projections.secret_generators.resource_owner,`+
+ ` zitadel.projections.secret_generators.sequence,`+
+ ` zitadel.projections.secret_generators.length,`+
+ ` zitadel.projections.secret_generators.expiry,`+
+ ` zitadel.projections.secret_generators.include_lower_letters,`+
+ ` zitadel.projections.secret_generators.include_upper_letters,`+
+ ` zitadel.projections.secret_generators.include_digits,`+
+ ` zitadel.projections.secret_generators.include_symbols`+
+ ` FROM zitadel.projections.secret_generators`,
+ nil,
+ nil,
+ ),
+ err: func(err error) (error, bool) {
+ if !errs.IsNotFound(err) {
+ return fmt.Errorf("err should be zitadel.NotFoundError got: %w", err), false
+ }
+ return nil, true
+ },
+ },
+ object: (*SecretGenerator)(nil),
+ },
+ {
+ name: "prepareSecretGeneratorQuery found",
+ prepare: prepareSecretGeneratorQuery,
+ want: want{
+ sqlExpectations: mockQuery(
+ regexp.QuoteMeta(`SELECT zitadel.projections.secret_generators.aggregate_id,`+
+ ` zitadel.projections.secret_generators.generator_type,`+
+ ` zitadel.projections.secret_generators.creation_date,`+
+ ` zitadel.projections.secret_generators.change_date,`+
+ ` zitadel.projections.secret_generators.resource_owner,`+
+ ` zitadel.projections.secret_generators.sequence,`+
+ ` zitadel.projections.secret_generators.length,`+
+ ` zitadel.projections.secret_generators.expiry,`+
+ ` zitadel.projections.secret_generators.include_lower_letters,`+
+ ` zitadel.projections.secret_generators.include_upper_letters,`+
+ ` zitadel.projections.secret_generators.include_digits,`+
+ ` zitadel.projections.secret_generators.include_symbols`+
+ ` FROM zitadel.projections.secret_generators`),
+ []string{
+ "aggregate_id",
+ "generator_type",
+ "creation_date",
+ "change_date",
+ "resource_owner",
+ "sequence",
+ "length",
+ "expiry",
+ "include_lower_letters",
+ "include_upper_letters",
+ "include_digits",
+ "include_symbols",
+ },
+ []driver.Value{
+ "agg-id",
+ domain.SecretGeneratorTypeInitCode,
+ testNow,
+ testNow,
+ "ro",
+ uint64(20211108),
+ 4,
+ time.Minute * 1,
+ true,
+ true,
+ true,
+ true,
+ },
+ ),
+ },
+ object: &SecretGenerator{
+ AggregateID: "agg-id",
+ GeneratorType: domain.SecretGeneratorTypeInitCode,
+ CreationDate: testNow,
+ ChangeDate: testNow,
+ ResourceOwner: "ro",
+ Sequence: 20211108,
+ Length: 4,
+ Expiry: time.Minute * 1,
+ IncludeLowerLetters: true,
+ IncludeUpperLetters: true,
+ IncludeDigits: true,
+ IncludeSymbols: true,
+ },
+ },
+ {
+ name: "prepareSecretGeneratorQuery sql err",
+ prepare: prepareSecretGeneratorQuery,
+ want: want{
+ sqlExpectations: mockQueryErr(
+ regexp.QuoteMeta(`SELECT zitadel.projections.secret_generators.aggregate_id,`+
+ ` zitadel.projections.secret_generators.generator_type,`+
+ ` zitadel.projections.secret_generators.creation_date,`+
+ ` zitadel.projections.secret_generators.change_date,`+
+ ` zitadel.projections.secret_generators.resource_owner,`+
+ ` zitadel.projections.secret_generators.sequence,`+
+ ` zitadel.projections.secret_generators.length,`+
+ ` zitadel.projections.secret_generators.expiry,`+
+ ` zitadel.projections.secret_generators.include_lower_letters,`+
+ ` zitadel.projections.secret_generators.include_upper_letters,`+
+ ` zitadel.projections.secret_generators.include_digits,`+
+ ` zitadel.projections.secret_generators.include_symbols`+
+ ` FROM zitadel.projections.secret_generators`),
+ sql.ErrConnDone,
+ ),
+ err: func(err error) (error, bool) {
+ if !errors.Is(err, sql.ErrConnDone) {
+ return fmt.Errorf("err should be sql.ErrConnDone got: %w", err), false
+ }
+ return nil, true
+ },
+ },
+ object: nil,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ assertPrepare(t, tt.prepare, tt.object, tt.want.sqlExpectations, tt.want.err)
+ })
+ }
+}
diff --git a/internal/query/secret_generators.go b/internal/query/secret_generators.go
new file mode 100644
index 0000000000..f029c07fd8
--- /dev/null
+++ b/internal/query/secret_generators.go
@@ -0,0 +1,268 @@
+package query
+
+import (
+ "context"
+ "database/sql"
+ errs "errors"
+ "time"
+
+ sq "github.com/Masterminds/squirrel"
+ "github.com/caos/zitadel/internal/domain"
+
+ "github.com/caos/zitadel/internal/crypto"
+ "github.com/caos/zitadel/internal/query/projection"
+
+ "github.com/caos/zitadel/internal/errors"
+)
+
+var (
+ secretGeneratorsTable = table{
+ name: projection.SecretGeneratorProjectionTable,
+ }
+ SecretGeneratorColumnAggregateID = Column{
+ name: projection.SecretGeneratorColumnAggregateID,
+ table: secretGeneratorsTable,
+ }
+ SecretGeneratorColumnGeneratorType = Column{
+ name: projection.SecretGeneratorColumnGeneratorType,
+ table: secretGeneratorsTable,
+ }
+ SecretGeneratorColumnCreationDate = Column{
+ name: projection.SecretGeneratorColumnCreationDate,
+ table: secretGeneratorsTable,
+ }
+ SecretGeneratorColumnChangeDate = Column{
+ name: projection.SecretGeneratorColumnChangeDate,
+ table: secretGeneratorsTable,
+ }
+ SecretGeneratorColumnResourceOwner = Column{
+ name: projection.SecretGeneratorColumnResourceOwner,
+ table: secretGeneratorsTable,
+ }
+ SecretGeneratorColumnSequence = Column{
+ name: projection.SecretGeneratorColumnSequence,
+ table: secretGeneratorsTable,
+ }
+ SecretGeneratorColumnLength = Column{
+ name: projection.SecretGeneratorColumnLength,
+ table: secretGeneratorsTable,
+ }
+ SecretGeneratorColumnExpiry = Column{
+ name: projection.SecretGeneratorColumnExpiry,
+ table: secretGeneratorsTable,
+ }
+ SecretGeneratorColumnIncludeLowerLetters = Column{
+ name: projection.SecretGeneratorColumnIncludeLowerLetters,
+ table: secretGeneratorsTable,
+ }
+ SecretGeneratorColumnIncludeUpperLetters = Column{
+ name: projection.SecretGeneratorColumnIncludeUpperLetters,
+ table: secretGeneratorsTable,
+ }
+ SecretGeneratorColumnIncludeDigits = Column{
+ name: projection.SecretGeneratorColumnIncludeDigits,
+ table: secretGeneratorsTable,
+ }
+ SecretGeneratorColumnIncludeSymbols = Column{
+ name: projection.SecretGeneratorColumnIncludeSymbols,
+ table: secretGeneratorsTable,
+ }
+)
+
+type SecretGenerators struct {
+ SearchResponse
+ SecretGenerators []*SecretGenerator
+}
+
+type SecretGenerator struct {
+ AggregateID string
+ CreationDate time.Time
+ ChangeDate time.Time
+ ResourceOwner string
+ Sequence uint64
+
+ GeneratorType domain.SecretGeneratorType
+ Length uint
+ Expiry time.Duration
+ IncludeLowerLetters bool
+ IncludeUpperLetters bool
+ IncludeDigits bool
+ IncludeSymbols bool
+}
+
+type SecretGeneratorSearchQueries struct {
+ SearchRequest
+ Queries []SearchQuery
+}
+
+func (q *Queries) InitEncryptionGenerator(ctx context.Context, generatorType domain.SecretGeneratorType, algorithm crypto.EncryptionAlgorithm) (crypto.Generator, error) {
+ generatorConfig, err := q.SecretGeneratorByType(ctx, generatorType)
+ if err != nil {
+ return nil, err
+ }
+ cryptoConfig := crypto.GeneratorConfig{
+ Length: generatorConfig.Length,
+ Expiry: generatorConfig.Expiry,
+ IncludeLowerLetters: generatorConfig.IncludeLowerLetters,
+ IncludeUpperLetters: generatorConfig.IncludeUpperLetters,
+ IncludeDigits: generatorConfig.IncludeDigits,
+ IncludeSymbols: generatorConfig.IncludeSymbols,
+ }
+ return crypto.NewEncryptionGenerator(cryptoConfig, algorithm), nil
+}
+
+func (q *Queries) InitHashGenerator(ctx context.Context, generatorType domain.SecretGeneratorType, algorithm crypto.HashAlgorithm) (crypto.Generator, error) {
+ generatorConfig, err := q.SecretGeneratorByType(ctx, generatorType)
+ if err != nil {
+ return nil, err
+ }
+ cryptoConfig := crypto.GeneratorConfig{
+ Length: generatorConfig.Length,
+ Expiry: generatorConfig.Expiry,
+ IncludeLowerLetters: generatorConfig.IncludeLowerLetters,
+ IncludeUpperLetters: generatorConfig.IncludeUpperLetters,
+ IncludeDigits: generatorConfig.IncludeDigits,
+ IncludeSymbols: generatorConfig.IncludeSymbols,
+ }
+ return crypto.NewHashGenerator(cryptoConfig, algorithm), nil
+}
+
+func (q *Queries) SecretGeneratorByType(ctx context.Context, generatorType domain.SecretGeneratorType) (*SecretGenerator, error) {
+ stmt, scan := prepareSecretGeneratorQuery()
+ query, args, err := stmt.Where(sq.Eq{
+ SecretGeneratorColumnGeneratorType.identifier(): generatorType,
+ }).ToSql()
+ if err != nil {
+ return nil, errors.ThrowInternal(err, "QUERY-3k99f", "Errors.Query.SQLStatment")
+ }
+
+ row := q.client.QueryRowContext(ctx, query, args...)
+ return scan(row)
+}
+
+func (q *Queries) SearchSecretGenerators(ctx context.Context, queries *SecretGeneratorSearchQueries) (secretGenerators *SecretGenerators, err error) {
+ query, scan := prepareSecretGeneratorsQuery()
+ stmt, args, err := queries.toQuery(query).ToSql()
+ if err != nil {
+ return nil, errors.ThrowInvalidArgument(err, "QUERY-sn9lw", "Errors.Query.InvalidRequest")
+ }
+
+ rows, err := q.client.QueryContext(ctx, stmt, args...)
+ if err != nil {
+ return nil, errors.ThrowInternal(err, "QUERY-4miii", "Errors.Internal")
+ }
+ secretGenerators, err = scan(rows)
+ if err != nil {
+ return nil, err
+ }
+ secretGenerators.LatestSequence, err = q.latestSequence(ctx, secretGeneratorsTable)
+ return secretGenerators, err
+}
+
+func (q *SecretGeneratorSearchQueries) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
+ query = q.SearchRequest.toQuery(query)
+ for _, q := range q.Queries {
+ query = q.toQuery(query)
+ }
+ return query
+}
+
+func NewSecretGeneratorTypeSearchQuery(value int32) (SearchQuery, error) {
+ return NewNumberQuery(SecretGeneratorColumnGeneratorType, value, NumberEquals)
+}
+
+func prepareSecretGeneratorQuery() (sq.SelectBuilder, func(*sql.Row) (*SecretGenerator, error)) {
+ return sq.Select(
+ SecretGeneratorColumnAggregateID.identifier(),
+ SecretGeneratorColumnGeneratorType.identifier(),
+ SecretGeneratorColumnCreationDate.identifier(),
+ SecretGeneratorColumnChangeDate.identifier(),
+ SecretGeneratorColumnResourceOwner.identifier(),
+ SecretGeneratorColumnSequence.identifier(),
+ SecretGeneratorColumnLength.identifier(),
+ SecretGeneratorColumnExpiry.identifier(),
+ SecretGeneratorColumnIncludeLowerLetters.identifier(),
+ SecretGeneratorColumnIncludeUpperLetters.identifier(),
+ SecretGeneratorColumnIncludeDigits.identifier(),
+ SecretGeneratorColumnIncludeSymbols.identifier()).
+ From(secretGeneratorsTable.identifier()).PlaceholderFormat(sq.Dollar),
+ func(row *sql.Row) (*SecretGenerator, error) {
+ secretGenerator := new(SecretGenerator)
+ err := row.Scan(
+ &secretGenerator.AggregateID,
+ &secretGenerator.GeneratorType,
+ &secretGenerator.CreationDate,
+ &secretGenerator.ChangeDate,
+ &secretGenerator.ResourceOwner,
+ &secretGenerator.Sequence,
+ &secretGenerator.Length,
+ &secretGenerator.Expiry,
+ &secretGenerator.IncludeLowerLetters,
+ &secretGenerator.IncludeUpperLetters,
+ &secretGenerator.IncludeDigits,
+ &secretGenerator.IncludeSymbols,
+ )
+ if err != nil {
+ if errs.Is(err, sql.ErrNoRows) {
+ return nil, errors.ThrowNotFound(err, "QUERY-m9wff", "Errors.SecretGenerator.NotFound")
+ }
+ return nil, errors.ThrowInternal(err, "QUERY-2k99d", "Errors.Internal")
+ }
+ return secretGenerator, nil
+ }
+}
+
+func prepareSecretGeneratorsQuery() (sq.SelectBuilder, func(*sql.Rows) (*SecretGenerators, error)) {
+ return sq.Select(
+ SecretGeneratorColumnAggregateID.identifier(),
+ SecretGeneratorColumnGeneratorType.identifier(),
+ SecretGeneratorColumnCreationDate.identifier(),
+ SecretGeneratorColumnChangeDate.identifier(),
+ SecretGeneratorColumnResourceOwner.identifier(),
+ SecretGeneratorColumnSequence.identifier(),
+ SecretGeneratorColumnLength.identifier(),
+ SecretGeneratorColumnExpiry.identifier(),
+ SecretGeneratorColumnIncludeLowerLetters.identifier(),
+ SecretGeneratorColumnIncludeUpperLetters.identifier(),
+ SecretGeneratorColumnIncludeDigits.identifier(),
+ SecretGeneratorColumnIncludeSymbols.identifier(),
+ countColumn.identifier()).
+ From(secretGeneratorsTable.identifier()).PlaceholderFormat(sq.Dollar),
+ func(rows *sql.Rows) (*SecretGenerators, error) {
+ secretGenerators := make([]*SecretGenerator, 0)
+ var count uint64
+ for rows.Next() {
+ secretGenerator := new(SecretGenerator)
+ err := rows.Scan(
+ &secretGenerator.AggregateID,
+ &secretGenerator.GeneratorType,
+ &secretGenerator.CreationDate,
+ &secretGenerator.ChangeDate,
+ &secretGenerator.ResourceOwner,
+ &secretGenerator.Sequence,
+ &secretGenerator.Length,
+ &secretGenerator.Expiry,
+ &secretGenerator.IncludeLowerLetters,
+ &secretGenerator.IncludeUpperLetters,
+ &secretGenerator.IncludeDigits,
+ &secretGenerator.IncludeSymbols,
+ &count,
+ )
+ if err != nil {
+ return nil, err
+ }
+ secretGenerators = append(secretGenerators, secretGenerator)
+ }
+
+ if err := rows.Close(); err != nil {
+ return nil, errors.ThrowInternal(err, "QUERY-em9fs", "Errors.Query.CloseRows")
+ }
+
+ return &SecretGenerators{
+ SecretGenerators: secretGenerators,
+ SearchResponse: SearchResponse{
+ Count: count,
+ },
+ }, nil
+ }
+}
diff --git a/internal/query/smtp.go b/internal/query/smtp.go
new file mode 100644
index 0000000000..7386e65b49
--- /dev/null
+++ b/internal/query/smtp.go
@@ -0,0 +1,139 @@
+package query
+
+import (
+ "context"
+ "database/sql"
+ errs "errors"
+ "time"
+
+ sq "github.com/Masterminds/squirrel"
+ "github.com/caos/zitadel/internal/crypto"
+ "github.com/caos/zitadel/internal/query/projection"
+
+ "github.com/caos/zitadel/internal/errors"
+)
+
+var (
+ smtpConfigsTable = table{
+ name: projection.SMTPConfigProjectionTable,
+ }
+ SMTPConfigColumnAggregateID = Column{
+ name: projection.SMTPConfigColumnAggregateID,
+ table: smtpConfigsTable,
+ }
+ SMTPConfigColumnCreationDate = Column{
+ name: projection.SMTPConfigColumnCreationDate,
+ table: smtpConfigsTable,
+ }
+ SMTPConfigColumnChangeDate = Column{
+ name: projection.SMTPConfigColumnChangeDate,
+ table: smtpConfigsTable,
+ }
+ SMTPConfigColumnResourceOwner = Column{
+ name: projection.SMTPConfigColumnResourceOwner,
+ table: smtpConfigsTable,
+ }
+ SMTPConfigColumnSequence = Column{
+ name: projection.SMTPConfigColumnSequence,
+ table: smtpConfigsTable,
+ }
+ SMTPConfigColumnTLS = Column{
+ name: projection.SMTPConfigColumnTLS,
+ table: smtpConfigsTable,
+ }
+ SMTPConfigColumnFromAddress = Column{
+ name: projection.SMTPConfigColumnFromAddress,
+ table: smtpConfigsTable,
+ }
+ SMTPConfigColumnFromName = Column{
+ name: projection.SMTPConfigColumnFromName,
+ table: smtpConfigsTable,
+ }
+ SMTPConfigColumnSMTPHost = Column{
+ name: projection.SMTPConfigColumnSMTPHost,
+ table: smtpConfigsTable,
+ }
+ SMTPConfigColumnSMTPUser = Column{
+ name: projection.SMTPConfigColumnSMTPUser,
+ table: smtpConfigsTable,
+ }
+ SMTPConfigColumnSMTPPassword = Column{
+ name: projection.SMTPConfigColumnSMTPPassword,
+ table: smtpConfigsTable,
+ }
+)
+
+type SMTPConfigs struct {
+ SearchResponse
+ SMTPConfigs []*SMTPConfig
+}
+
+type SMTPConfig struct {
+ AggregateID string
+ CreationDate time.Time
+ ChangeDate time.Time
+ ResourceOwner string
+ Sequence uint64
+
+ TLS bool
+ SenderAddress string
+ SenderName string
+ Host string
+ User string
+ Password *crypto.CryptoValue
+}
+
+func (q *Queries) SMTPConfigByAggregateID(ctx context.Context, aggregateID string) (*SMTPConfig, error) {
+ stmt, scan := prepareSMTPConfigQuery()
+ query, args, err := stmt.Where(sq.Eq{
+ SMTPConfigColumnAggregateID.identifier(): aggregateID,
+ }).ToSql()
+ if err != nil {
+ return nil, errors.ThrowInternal(err, "QUERY-3m9sl", "Errors.Query.SQLStatment")
+ }
+
+ row := q.client.QueryRowContext(ctx, query, args...)
+ return scan(row)
+}
+
+func prepareSMTPConfigQuery() (sq.SelectBuilder, func(*sql.Row) (*SMTPConfig, error)) {
+ password := new(crypto.CryptoValue)
+
+ return sq.Select(
+ SMTPConfigColumnAggregateID.identifier(),
+ SMTPConfigColumnCreationDate.identifier(),
+ SMTPConfigColumnChangeDate.identifier(),
+ SMTPConfigColumnResourceOwner.identifier(),
+ SMTPConfigColumnSequence.identifier(),
+ SMTPConfigColumnTLS.identifier(),
+ SMTPConfigColumnFromAddress.identifier(),
+ SMTPConfigColumnFromName.identifier(),
+ SMTPConfigColumnSMTPHost.identifier(),
+ SMTPConfigColumnSMTPUser.identifier(),
+ SMTPConfigColumnSMTPPassword.identifier()).
+ From(smtpConfigsTable.identifier()).PlaceholderFormat(sq.Dollar),
+ func(row *sql.Row) (*SMTPConfig, error) {
+ config := new(SMTPConfig)
+ err := row.Scan(
+ &config.AggregateID,
+ &config.CreationDate,
+ &config.ChangeDate,
+ &config.ResourceOwner,
+ &config.Sequence,
+ &config.TLS,
+ &config.SenderAddress,
+ &config.SenderName,
+ &config.Host,
+ &config.User,
+ &password,
+ )
+ if err != nil {
+ if errs.Is(err, sql.ErrNoRows) {
+ return nil, errors.ThrowNotFound(err, "QUERY-fwofw", "Errors.SMTPConfig.NotFound")
+ }
+ return nil, errors.ThrowInternal(err, "QUERY-9k87F", "Errors.Internal")
+ }
+ config.Password = password
+ return config, nil
+ }
+}
diff --git a/internal/query/smtp_test.go b/internal/query/smtp_test.go
new file mode 100644
index 0000000000..b818bc82af
--- /dev/null
+++ b/internal/query/smtp_test.go
@@ -0,0 +1,148 @@
+package query
+
+import (
+ "database/sql"
+ "database/sql/driver"
+ "errors"
+ "fmt"
+ "regexp"
+ "testing"
+
+ "github.com/caos/zitadel/internal/crypto"
+ errs "github.com/caos/zitadel/internal/errors"
+)
+
+func Test_SMTPConfigsPrepares(t *testing.T) {
+ type want struct {
+ sqlExpectations sqlExpectation
+ err checkErr
+ }
+ tests := []struct {
+ name string
+ prepare interface{}
+ want want
+ object interface{}
+ }{
+ {
+ name: "prepareSMTPConfigQuery no result",
+ prepare: prepareSMTPConfigQuery,
+ want: want{
+ sqlExpectations: mockQueries(
+ `SELECT zitadel.projections.smtp_configs.aggregate_id,`+
+ ` zitadel.projections.smtp_configs.creation_date,`+
+ ` zitadel.projections.smtp_configs.change_date,`+
+ ` zitadel.projections.smtp_configs.resource_owner,`+
+ ` zitadel.projections.smtp_configs.sequence,`+
+ ` zitadel.projections.smtp_configs.tls,`+
+ ` zitadel.projections.smtp_configs.sender_address,`+
+ ` zitadel.projections.smtp_configs.sender_name,`+
+ ` zitadel.projections.smtp_configs.host,`+
+ ` zitadel.projections.smtp_configs.username,`+
+ ` zitadel.projections.smtp_configs.password`+
+ ` FROM zitadel.projections.smtp_configs`,
+ nil,
+ nil,
+ ),
+ err: func(err error) (error, bool) {
+ if !errs.IsNotFound(err) {
+ return fmt.Errorf("err should be zitadel.NotFoundError got: %w", err), false
+ }
+ return nil, true
+ },
+ },
+ object: (*SMTPConfig)(nil),
+ },
+ {
+ name: "prepareSMTPConfigQuery found",
+ prepare: prepareSMTPConfigQuery,
+ want: want{
+ sqlExpectations: mockQuery(
+ regexp.QuoteMeta(`SELECT zitadel.projections.smtp_configs.aggregate_id,`+
+ ` zitadel.projections.smtp_configs.creation_date,`+
+ ` zitadel.projections.smtp_configs.change_date,`+
+ ` zitadel.projections.smtp_configs.resource_owner,`+
+ ` zitadel.projections.smtp_configs.sequence,`+
+ ` zitadel.projections.smtp_configs.tls,`+
+ ` zitadel.projections.smtp_configs.sender_address,`+
+ ` zitadel.projections.smtp_configs.sender_name,`+
+ ` zitadel.projections.smtp_configs.host,`+
+ ` zitadel.projections.smtp_configs.username,`+
+ ` zitadel.projections.smtp_configs.password`+
+ ` FROM zitadel.projections.smtp_configs`),
+ []string{
+ "aggregate_id",
+ "creation_date",
+ "change_date",
+ "resource_owner",
+ "sequence",
+ "tls",
+ "sender_address",
+ "sender_name",
+ "smtp_host",
+ "smtp_user",
+ "smtp_password",
+ },
+ []driver.Value{
+ "agg-id",
+ testNow,
+ testNow,
+ "ro",
+ uint64(20211108),
+ true,
+ "sender",
+ "name",
+ "host",
+ "user",
+ &crypto.CryptoValue{},
+ },
+ ),
+ },
+ object: &SMTPConfig{
+ AggregateID: "agg-id",
+ CreationDate: testNow,
+ ChangeDate: testNow,
+ ResourceOwner: "ro",
+ Sequence: 20211108,
+ TLS: true,
+ SenderAddress: "sender",
+ SenderName: "name",
+ Host: "host",
+ User: "user",
+ Password: &crypto.CryptoValue{},
+ },
+ },
+ {
+ name: "prepareSMTPConfigQuery sql err",
+ prepare: prepareSMTPConfigQuery,
+ want: want{
+ sqlExpectations: mockQueryErr(
+ regexp.QuoteMeta(`SELECT zitadel.projections.smtp_configs.aggregate_id,`+
+ ` zitadel.projections.smtp_configs.creation_date,`+
+ ` zitadel.projections.smtp_configs.change_date,`+
+ ` zitadel.projections.smtp_configs.resource_owner,`+
+ ` zitadel.projections.smtp_configs.sequence,`+
+ ` zitadel.projections.smtp_configs.tls,`+
+ ` zitadel.projections.smtp_configs.sender_address,`+
+ ` zitadel.projections.smtp_configs.sender_name,`+
+ ` zitadel.projections.smtp_configs.host,`+
+ ` zitadel.projections.smtp_configs.username,`+
+ ` zitadel.projections.smtp_configs.password`+
+ ` FROM zitadel.projections.smtp_configs`),
+ sql.ErrConnDone,
+ ),
+ err: func(err error) (error, bool) {
+ if !errors.Is(err, sql.ErrConnDone) {
+ return fmt.Errorf("err should be sql.ErrConnDone got: %w", err), false
+ }
+ return nil, true
+ },
+ },
+ object: nil,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ assertPrepare(t, tt.prepare, tt.object, tt.want.sqlExpectations, tt.want.err)
+ })
+ }
+}
diff --git a/internal/repository/iam/event_default_language.go b/internal/repository/iam/event_default_language.go
new file mode 100644
index 0000000000..d45320d8db
--- /dev/null
+++ b/internal/repository/iam/event_default_language.go
@@ -0,0 +1,57 @@
+package iam
+
+import (
+ "context"
+ "encoding/json"
+
+ "github.com/caos/zitadel/internal/eventstore"
+ "golang.org/x/text/language"
+
+ "github.com/caos/zitadel/internal/errors"
+ "github.com/caos/zitadel/internal/eventstore/repository"
+)
+
+const (
+ DefaultLanguageSetEventType eventstore.EventType = "iam.default.language.set"
+)
+
+type DefaultLanguageSetEvent struct {
+ eventstore.BaseEvent `json:"-"`
+
+ Language language.Tag `json:"language"`
+}
+
+func (e *DefaultLanguageSetEvent) Data() interface{} {
+ return e
+}
+
+func (e *DefaultLanguageSetEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
+ return nil
+}
+
+func NewDefaultLanguageSetEvent(
+ ctx context.Context,
+ aggregate *eventstore.Aggregate,
+ language language.Tag,
+) *DefaultLanguageSetEvent {
+ return &DefaultLanguageSetEvent{
+ BaseEvent: *eventstore.NewBaseEventForPush(
+ ctx,
+ aggregate,
+ DefaultLanguageSetEventType,
+ ),
+ Language: language,
+ }
+}
+
+func DefaultLanguageSetMapper(event *repository.Event) (eventstore.Event, error) {
+ e := &DefaultLanguageSetEvent{
+ BaseEvent: *eventstore.BaseEventFromRepo(event),
+ }
+ err := json.Unmarshal(event.Data, e)
+ if err != nil {
+ return nil, errors.ThrowInternal(err, "IAM-3j9fs", "unable to unmarshal default language set")
+ }
+
+ return e, nil
+}
diff --git a/internal/repository/iam/eventstore.go b/internal/repository/iam/eventstore.go
index c173a0eef3..764cf4cc94 100644
--- a/internal/repository/iam/eventstore.go
+++ b/internal/repository/iam/eventstore.go
@@ -9,6 +9,13 @@ func RegisterEventMappers(es *eventstore.Eventstore) {
RegisterFilterEventMapper(SetupDoneEventType, SetupStepMapper).
RegisterFilterEventMapper(GlobalOrgSetEventType, GlobalOrgSetMapper).
RegisterFilterEventMapper(ProjectSetEventType, ProjectSetMapper).
+ RegisterFilterEventMapper(DefaultLanguageSetEventType, DefaultLanguageSetMapper).
+ RegisterFilterEventMapper(SecretGeneratorAddedEventType, SecretGeneratorAddedEventMapper).
+ RegisterFilterEventMapper(SecretGeneratorChangedEventType, SecretGeneratorChangedEventMapper).
+ RegisterFilterEventMapper(SecretGeneratorRemovedEventType, SecretGeneratorRemovedEventMapper).
+ RegisterFilterEventMapper(SMTPConfigAddedEventType, SMTPConfigAddedEventMapper).
+ RegisterFilterEventMapper(SMTPConfigChangedEventType, SMTPConfigChangedEventMapper).
+ RegisterFilterEventMapper(SMTPConfigPasswordChangedEventType, SMTPConfigPasswordChangedEventMapper).
RegisterFilterEventMapper(UniqueConstraintsMigratedEventType, MigrateUniqueConstraintEventMapper).
RegisterFilterEventMapper(LabelPolicyAddedEventType, LabelPolicyAddedEventMapper).
RegisterFilterEventMapper(LabelPolicyChangedEventType, LabelPolicyChangedEventMapper).
diff --git a/internal/repository/iam/secret_generator.go b/internal/repository/iam/secret_generator.go
new file mode 100644
index 0000000000..c53e0cf40a
--- /dev/null
+++ b/internal/repository/iam/secret_generator.go
@@ -0,0 +1,228 @@
+package iam
+
+import (
+ "context"
+ "encoding/json"
+ "time"
+
+ "github.com/caos/zitadel/internal/domain"
+ "github.com/caos/zitadel/internal/errors"
+ "github.com/caos/zitadel/internal/eventstore"
+ "github.com/caos/zitadel/internal/eventstore/repository"
+)
+
+const (
+ UniqueSecretGeneratorType = "secret_generator"
+ secretGeneratorPrefix = "secret.generator."
+ SecretGeneratorAddedEventType = iamEventTypePrefix + secretGeneratorPrefix + "added"
+ SecretGeneratorChangedEventType = iamEventTypePrefix + secretGeneratorPrefix + "changed"
+ SecretGeneratorRemovedEventType = iamEventTypePrefix + secretGeneratorPrefix + "removed"
+)
+
+func NewAddSecretGeneratorTypeUniqueConstraint(generatorType domain.SecretGeneratorType) *eventstore.EventUniqueConstraint {
+ return eventstore.NewAddEventUniqueConstraint(
+ UniqueSecretGeneratorType,
+ string(generatorType),
+ "Errors.SecretGenerator.AlreadyExists")
+}
+
+func NewRemoveSecretGeneratorTypeUniqueConstraint(generatorType domain.SecretGeneratorType) *eventstore.EventUniqueConstraint {
+ return eventstore.NewRemoveEventUniqueConstraint(
+ UniqueSecretGeneratorType,
+ string(generatorType))
+}
+
+type SecretGeneratorAddedEvent struct {
+ eventstore.BaseEvent `json:"-"`
+
+ GeneratorType domain.SecretGeneratorType `json:"generatorType"`
+ Length uint `json:"length,omitempty"`
+ Expiry time.Duration `json:"expiry,omitempty"`
+ IncludeLowerLetters bool `json:"includeLowerLetters,omitempty"`
+ IncludeUpperLetters bool `json:"includeUpperLetters,omitempty"`
+ IncludeDigits bool `json:"includeDigits,omitempty"`
+ IncludeSymbols bool `json:"includeSymbols,omitempty"`
+}
+
+func NewSecretGeneratorAddedEvent(
+ ctx context.Context,
+ aggregate *eventstore.Aggregate,
+ generatorType domain.SecretGeneratorType,
+ length uint,
+ expiry time.Duration,
+ includeLowerLetters,
+ includeUpperLetters,
+ includeDigits,
+ includeSymbols bool,
+) *SecretGeneratorAddedEvent {
+ return &SecretGeneratorAddedEvent{
+ BaseEvent: *eventstore.NewBaseEventForPush(
+ ctx,
+ aggregate,
+ SecretGeneratorAddedEventType,
+ ),
+ GeneratorType: generatorType,
+ Length: length,
+ Expiry: expiry,
+ IncludeLowerLetters: includeLowerLetters,
+ IncludeUpperLetters: includeUpperLetters,
+ IncludeDigits: includeDigits,
+ IncludeSymbols: includeSymbols,
+ }
+}
+
+func (e *SecretGeneratorAddedEvent) Data() interface{} {
+ return e
+}
+
+func (e *SecretGeneratorAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
+ return []*eventstore.EventUniqueConstraint{NewAddSecretGeneratorTypeUniqueConstraint(e.GeneratorType)}
+}
+
+func SecretGeneratorAddedEventMapper(event *repository.Event) (eventstore.Event, error) {
+ secretGeneratorAdded := &SecretGeneratorAddedEvent{
+ BaseEvent: *eventstore.BaseEventFromRepo(event),
+ }
+ err := json.Unmarshal(event.Data, secretGeneratorAdded)
+ if err != nil {
+ return nil, errors.ThrowInternal(err, "IAM-en9f4", "unable to unmarshal secret generator added")
+ }
+
+ return secretGeneratorAdded, nil
+}
+
+type SecretGeneratorChangedEvent struct {
+ eventstore.BaseEvent `json:"-"`
+
+ GeneratorType domain.SecretGeneratorType `json:"generatorType"`
+ Length *uint `json:"length,omitempty"`
+ Expiry *time.Duration `json:"expiry,omitempty"`
+ IncludeLowerLetters *bool `json:"includeLowerLetters,omitempty"`
+ IncludeUpperLetters *bool `json:"includeUpperLetters,omitempty"`
+ IncludeDigits *bool `json:"includeDigits,omitempty"`
+ IncludeSymbols *bool `json:"includeSymbols,omitempty"`
+}
+
+func (e *SecretGeneratorChangedEvent) Data() interface{} {
+ return e
+}
+
+func (e *SecretGeneratorChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
+ return nil
+}
+
+func NewSecretGeneratorChangeEvent(
+ ctx context.Context,
+ aggregate *eventstore.Aggregate,
+ generatorType domain.SecretGeneratorType,
+ changes []SecretGeneratorChanges,
+) (*SecretGeneratorChangedEvent, error) {
+ if len(changes) == 0 {
+ return nil, errors.ThrowPreconditionFailed(nil, "IAM-j2jfw", "Errors.NoChangesFound")
+ }
+ changeEvent := &SecretGeneratorChangedEvent{
+ BaseEvent: *eventstore.NewBaseEventForPush(
+ ctx,
+ aggregate,
+ SecretGeneratorChangedEventType,
+ ),
+ GeneratorType: generatorType,
+ }
+ for _, change := range changes {
+ change(changeEvent)
+ }
+ return changeEvent, nil
+}
+
+type SecretGeneratorChanges func(event *SecretGeneratorChangedEvent)
+
+func ChangeSecretGeneratorLength(length uint) func(event *SecretGeneratorChangedEvent) {
+ return func(e *SecretGeneratorChangedEvent) {
+ e.Length = &length
+ }
+}
+
+func ChangeSecretGeneratorExpiry(expiry time.Duration) func(event *SecretGeneratorChangedEvent) {
+ return func(e *SecretGeneratorChangedEvent) {
+ e.Expiry = &expiry
+ }
+}
+
+func ChangeSecretGeneratorIncludeLowerLetters(includeLowerLetters bool) func(event *SecretGeneratorChangedEvent) {
+ return func(e *SecretGeneratorChangedEvent) {
+ e.IncludeLowerLetters = &includeLowerLetters
+ }
+}
+
+func ChangeSecretGeneratorIncludeUpperLetters(includeUpperLetters bool) func(event *SecretGeneratorChangedEvent) {
+ return func(e *SecretGeneratorChangedEvent) {
+ e.IncludeUpperLetters = &includeUpperLetters
+ }
+}
+
+func ChangeSecretGeneratorIncludeDigits(includeDigits bool) func(event *SecretGeneratorChangedEvent) {
+ return func(e *SecretGeneratorChangedEvent) {
+ e.IncludeDigits = &includeDigits
+ }
+}
+
+func ChangeSecretGeneratorIncludeSymbols(includeSymbols bool) func(event *SecretGeneratorChangedEvent) {
+ return func(e *SecretGeneratorChangedEvent) {
+ e.IncludeDigits = &includeSymbols
+ }
+}
+
+func SecretGeneratorChangedEventMapper(event *repository.Event) (eventstore.Event, error) {
+ e := &SecretGeneratorChangedEvent{
+ BaseEvent: *eventstore.BaseEventFromRepo(event),
+ }
+
+ err := json.Unmarshal(event.Data, e)
+ if err != nil {
+ return nil, errors.ThrowInternal(err, "IAM-2m09e", "unable to unmarshal secret generator changed")
+ }
+
+ return e, nil
+}
+
+type SecretGeneratorRemovedEvent struct {
+ eventstore.BaseEvent `json:"-"`
+
+ GeneratorType domain.SecretGeneratorType `json:"generatorType"`
+}
+
+func (e *SecretGeneratorRemovedEvent) Data() interface{} {
+ return e
+}
+
+func (e *SecretGeneratorRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
+ return []*eventstore.EventUniqueConstraint{NewRemoveSecretGeneratorTypeUniqueConstraint(e.GeneratorType)}
+}
+
+func NewSecretGeneratorRemovedEvent(
+ ctx context.Context,
+ aggregate *eventstore.Aggregate,
+ generatorType domain.SecretGeneratorType,
+) *SecretGeneratorRemovedEvent {
+ return &SecretGeneratorRemovedEvent{
+ BaseEvent: *eventstore.NewBaseEventForPush(
+ ctx,
+ aggregate,
+ SecretGeneratorRemovedEventType,
+ ),
+ GeneratorType: generatorType,
+ }
+}
+
+func SecretGeneratorRemovedEventMapper(event *repository.Event) (eventstore.Event, error) {
+ e := &SecretGeneratorRemovedEvent{
+ BaseEvent: *eventstore.BaseEventFromRepo(event),
+ }
+
+ err := json.Unmarshal(event.Data, e)
+ if err != nil {
+ return nil, errors.ThrowInternal(err, "IAM-m09ke", "unable to unmarshal secret generator removed")
+ }
+
+ return e, nil
+}
diff --git a/internal/repository/iam/smtp_config.go b/internal/repository/iam/smtp_config.go
new file mode 100644
index 0000000000..91e2c26e6f
--- /dev/null
+++ b/internal/repository/iam/smtp_config.go
@@ -0,0 +1,199 @@
+package iam
+
+import (
+ "context"
+ "encoding/json"
+
+ "github.com/caos/zitadel/internal/crypto"
+ "github.com/caos/zitadel/internal/errors"
+ "github.com/caos/zitadel/internal/eventstore"
+ "github.com/caos/zitadel/internal/eventstore/repository"
+)
+
+const (
+ smtpConfigPrefix = "smtp.config"
+ SMTPConfigAddedEventType = iamEventTypePrefix + smtpConfigPrefix + "added"
+ SMTPConfigChangedEventType = iamEventTypePrefix + smtpConfigPrefix + "changed"
+ SMTPConfigPasswordChangedEventType = iamEventTypePrefix + smtpConfigPrefix + "password.changed"
+)
+
+type SMTPConfigAddedEvent struct {
+ eventstore.BaseEvent `json:"-"`
+
+ SenderAddress string `json:"senderAddress,omitempty"`
+ SenderName string `json:"senderName,omitempty"`
+ TLS bool `json:"tls,omitempty"`
+ Host string `json:"host,omitempty"`
+ User string `json:"user,omitempty"`
+ Password *crypto.CryptoValue `json:"password,omitempty"`
+}
+
+func NewSMTPConfigAddedEvent(
+ ctx context.Context,
+ aggregate *eventstore.Aggregate,
+ tls bool,
+ senderAddress,
+ senderName,
+ host,
+ user string,
+ password *crypto.CryptoValue,
+) *SMTPConfigAddedEvent {
+ return &SMTPConfigAddedEvent{
+ BaseEvent: *eventstore.NewBaseEventForPush(
+ ctx,
+ aggregate,
+ SMTPConfigAddedEventType,
+ ),
+ TLS: tls,
+ SenderAddress: senderAddress,
+ SenderName: senderName,
+ Host: host,
+ User: user,
+ Password: password,
+ }
+}
+
+func (e *SMTPConfigAddedEvent) Data() interface{} {
+ return e
+}
+
+func (e *SMTPConfigAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
+ return nil
+}
+
+func SMTPConfigAddedEventMapper(event *repository.Event) (eventstore.Event, error) {
+ smtpConfigAdded := &SMTPConfigAddedEvent{
+ BaseEvent: *eventstore.BaseEventFromRepo(event),
+ }
+ err := json.Unmarshal(event.Data, smtpConfigAdded)
+ if err != nil {
+ return nil, errors.ThrowInternal(err, "IAM-39fks", "unable to unmarshal smtp config added")
+ }
+
+ return smtpConfigAdded, nil
+}
+
+type SMTPConfigChangedEvent struct {
+ eventstore.BaseEvent `json:"-"`
+
+ FromAddress *string `json:"senderAddress,omitempty"`
+ FromName *string `json:"senderName,omitempty"`
+ TLS *bool `json:"tls,omitempty"`
+ Host *string `json:"host,omitempty"`
+ User *string `json:"user,omitempty"`
+}
+
+func (e *SMTPConfigChangedEvent) Data() interface{} {
+ return e
+}
+
+func (e *SMTPConfigChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
+ return nil
+}
+
+func NewSMTPConfigChangeEvent(
+ ctx context.Context,
+ aggregate *eventstore.Aggregate,
+ changes []SMTPConfigChanges,
+) (*SMTPConfigChangedEvent, error) {
+ if len(changes) == 0 {
+ return nil, errors.ThrowPreconditionFailed(nil, "IAM-o0pWf", "Errors.NoChangesFound")
+ }
+ changeEvent := &SMTPConfigChangedEvent{
+ BaseEvent: *eventstore.NewBaseEventForPush(
+ ctx,
+ aggregate,
+ SMTPConfigChangedEventType,
+ ),
+ }
+ for _, change := range changes {
+ change(changeEvent)
+ }
+ return changeEvent, nil
+}
+
+type SMTPConfigChanges func(event *SMTPConfigChangedEvent)
+
+func ChangeSMTPConfigTLS(tls bool) func(event *SMTPConfigChangedEvent) {
+ return func(e *SMTPConfigChangedEvent) {
+ e.TLS = &tls
+ }
+}
+
+func ChangeSMTPConfigFromAddress(senderAddress string) func(event *SMTPConfigChangedEvent) {
+ return func(e *SMTPConfigChangedEvent) {
+ e.FromAddress = &senderAddress
+ }
+}
+
+func ChangeSMTPConfigFromName(senderName string) func(event *SMTPConfigChangedEvent) {
+ return func(e *SMTPConfigChangedEvent) {
+ e.FromName = &senderName
+ }
+}
+
+func ChangeSMTPConfigSMTPHost(smtpHost string) func(event *SMTPConfigChangedEvent) {
+ return func(e *SMTPConfigChangedEvent) {
+ e.Host = &smtpHost
+ }
+}
+
+func ChangeSMTPConfigSMTPUser(smtpUser string) func(event *SMTPConfigChangedEvent) {
+ return func(e *SMTPConfigChangedEvent) {
+ e.User = &smtpUser
+ }
+}
+
+func SMTPConfigChangedEventMapper(event *repository.Event) (eventstore.Event, error) {
+ e := &SMTPConfigChangedEvent{
+ BaseEvent: *eventstore.BaseEventFromRepo(event),
+ }
+
+ err := json.Unmarshal(event.Data, e)
+ if err != nil {
+ return nil, errors.ThrowInternal(err, "IAM-m09oo", "unable to unmarshal smtp changed")
+ }
+
+ return e, nil
+}
+
+type SMTPConfigPasswordChangedEvent struct {
+ eventstore.BaseEvent `json:"-"`
+
+ Password *crypto.CryptoValue `json:"password,omitempty"`
+}
+
+func NewSMTPConfigPasswordChangedEvent(
+ ctx context.Context,
+ aggregate *eventstore.Aggregate,
+ password *crypto.CryptoValue,
+) *SMTPConfigPasswordChangedEvent {
+ return &SMTPConfigPasswordChangedEvent{
+ BaseEvent: *eventstore.NewBaseEventForPush(
+ ctx,
+ aggregate,
+ SMTPConfigPasswordChangedEventType,
+ ),
+ Password: password,
+ }
+}
+
+func (e *SMTPConfigPasswordChangedEvent) Data() interface{} {
+ return e
+}
+
+func (e *SMTPConfigPasswordChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
+ return nil
+}
+
+func SMTPConfigPasswordChangedEventMapper(event *repository.Event) (eventstore.Event, error) {
+ smtpConfigPasswordChagned := &SMTPConfigPasswordChangedEvent{
+ BaseEvent: *eventstore.BaseEventFromRepo(event),
+ }
+ err := json.Unmarshal(event.Data, smtpConfigPasswordChagned)
+ if err != nil {
+ return nil, errors.ThrowInternal(err, "IAM-99iNF", "unable to unmarshal smtp config password changed")
+ }
+
+ return smtpConfigPasswordChagned, nil
+}
diff --git a/internal/static/i18n/de.yaml b/internal/static/i18n/de.yaml
index 8752ea4622..01eecb3bb9 100644
--- a/internal/static/i18n/de.yaml
+++ b/internal/static/i18n/de.yaml
@@ -27,6 +27,8 @@ Errors:
RemoveFailed: Objekt konnte nicht gelöscht werden
Limit:
ExceedsDefault: Limit überschreitet default Limit
+ Language:
+ NotParsed: Sprache konnte nicht gemapped werde
User:
NotFound: Benutzer konnte nicht gefunden werden
AlreadyExists: Benutzer existierts bereits
diff --git a/internal/static/i18n/en.yaml b/internal/static/i18n/en.yaml
index 8041c9c19f..70bb408df2 100644
--- a/internal/static/i18n/en.yaml
+++ b/internal/static/i18n/en.yaml
@@ -27,6 +27,8 @@ Errors:
RemoveFailed: Object could not be removed
Limit:
ExceedsDefault: Limit exceeds default limit
+ Language:
+ NotParsed: Could not parse languge
User:
NotFound: User could not be found
AlreadyExists: User already exists
diff --git a/internal/static/i18n/it.yaml b/internal/static/i18n/it.yaml
index 0e0d88c964..92ea347056 100644
--- a/internal/static/i18n/it.yaml
+++ b/internal/static/i18n/it.yaml
@@ -27,6 +27,8 @@ Errors:
RemoveFailed: L'oggetto non può essere rimosso
Limit:
ExceedsDefault: Il limite supera quello predefinito
+ Language:
+ NotParsed: Impossibile analizzare la lingua
User:
NotFound: L'utente non è stato trovato
AlreadyExists: L'utente già esistente
diff --git a/migrations/cockroach/V1.111__settings.sql b/migrations/cockroach/V1.111__settings.sql
new file mode 100644
index 0000000000..3a5de4368a
--- /dev/null
+++ b/migrations/cockroach/V1.111__settings.sql
@@ -0,0 +1,36 @@
+ALTER TABLE zitadel.projections.iam ADD COLUMN default_language TEXT DEFAULT '';
+
+CREATE TABLE zitadel.projections.secret_generators (
+ generator_type INT2 NOT NULL
+ , aggregate_id STRING NOT NULL
+ , creation_date TIMESTAMPTZ NOT NULL
+ , change_date TIMESTAMPTZ NOT NULL
+ , resource_owner STRING NOT NULL
+ , sequence INT8 NOT NULL
+
+ , length BIGINT NOT NULL
+ , expiry BIGINT NOT NULL
+ , include_lower_letters BOOLEAN NOT NULL
+ , include_upper_letters BOOLEAN NOT NULL
+ , include_digits BOOLEAN NOT NULL
+ , include_symbols BOOLEAN NOT NULL
+
+ , PRIMARY KEY (generator_type, aggregate_id)
+);
+
+CREATE TABLE zitadel.projections.smtp_configs (
+ aggregate_id STRING NOT NULL
+ , creation_date TIMESTAMPTZ NOT NULL
+ , change_date TIMESTAMPTZ NOT NULL
+ , resource_owner STRING NOT NULL
+ , sequence INT8 NOT NULL
+
+ , tls BOOLEAN NOT NULL
+ , sender_address STRING NOT NULL
+ , sender_name STRING NOT NULL
+ , host STRING NOT NULL
+ , username STRING NOT NULL DEFAULT ''
+ , password JSONB
+
+ , PRIMARY KEY (aggregate_id)
+);
diff --git a/proto/zitadel/admin.proto b/proto/zitadel/admin.proto
index a6b89d739f..daedc780ce 100644
--- a/proto/zitadel/admin.proto
+++ b/proto/zitadel/admin.proto
@@ -2,6 +2,7 @@ syntax = "proto3";
import "zitadel/idp.proto";
import "zitadel/user.proto";
+import "zitadel/settings.proto";
import "zitadel/object.proto";
import "zitadel/options.proto";
import "zitadel/org.proto";
@@ -161,6 +162,98 @@ service AdminService {
};
}
+ // Set the default language
+ rpc SetDefaultLanguage(SetDefaultLanguageRequest) returns (SetDefaultLanguageResponse) {
+ option (google.api.http) = {
+ put: "/languages/default/{language}";
+ };
+
+ option (zitadel.v1.auth_option) = {
+ permission: "iam.write";
+ };
+ }
+
+ // Set the default language
+ rpc GetDefaultLanguage(GetDefaultLanguageRequest) returns (GetDefaultLanguageResponse) {
+ option (google.api.http) = {
+ get: "/languages/default";
+ };
+
+ option (zitadel.v1.auth_option) = {
+ permission: "iam.read";
+ };
+ }
+
+ // Set the default language
+ rpc ListSecretGenerators(ListSecretGeneratorsRequest) returns (ListSecretGeneratorsResponse) {
+ option (google.api.http) = {
+ post: "/secretgenerators/_search"
+ body: "*"
+ };
+
+ option (zitadel.v1.auth_option) = {
+ permission: "iam.read";
+ };
+ }
+
+ // Get Secret Generator by type (e.g PasswordResetCode)
+ rpc GetSecretGenerator(GetSecretGeneratorRequest) returns (GetSecretGeneratorResponse) {
+ option (google.api.http) = {
+ get: "/secretgenerators/{generator_type}";
+ };
+
+ option (zitadel.v1.auth_option) = {
+ permission: "iam.read";
+ };
+ }
+
+ // Update secret generator configuration
+ rpc UpdateSecretGenerator(UpdateSecretGeneratorRequest) returns (UpdateSecretGeneratorResponse) {
+ option (google.api.http) = {
+ put: "/secretgenerators/{generator_type}";
+ body: "*"
+ };
+
+ option (zitadel.v1.auth_option) = {
+ permission: "iam.write";
+ };
+ }
+
+ // Get system smtp configuration
+ rpc GetSMTPConfig(GetSMTPConfigRequest) returns (GetSMTPConfigResponse) {
+ option (google.api.http) = {
+ get: "/smtp";
+ };
+
+ option (zitadel.v1.auth_option) = {
+ permission: "iam.read";
+ };
+ }
+
+ // Update system smtp configuration
+ rpc UpdateSMTPConfig(UpdateSMTPConfigRequest) returns (UpdateSMTPConfigResponse) {
+ option (google.api.http) = {
+ put: "/smtp";
+ body: "*"
+ };
+
+ option (zitadel.v1.auth_option) = {
+ permission: "iam.write";
+ };
+ }
+
+ // Update system smtp configuration password for host
+ rpc UpdateSMTPConfigPassword(UpdateSMTPConfigPasswordRequest) returns (UpdateSMTPConfigPasswordResponse) {
+ option (google.api.http) = {
+ put: "/smtp/password";
+ body: "*"
+ };
+
+ option (zitadel.v1.auth_option) = {
+ permission: "iam.write";
+ };
+ }
+
// Returns an organisation by id
rpc GetOrgByID(GetOrgByIDRequest) returns (GetOrgByIDResponse) {
option (google.api.http) = {
@@ -2262,6 +2355,82 @@ message GetSupportedLanguagesResponse {
repeated string languages = 1;
}
+message SetDefaultLanguageRequest {
+ string language = 1 [(validate.rules).string = {min_len: 1, max_len: 10}];
+}
+
+message SetDefaultLanguageResponse {
+ zitadel.v1.ObjectDetails details = 1;
+}
+
+//This is an empty request
+message GetDefaultLanguageRequest {}
+
+message GetDefaultLanguageResponse {
+ string language = 1;
+}
+
+message ListSecretGeneratorsRequest {
+ //list limitations and ordering
+ zitadel.v1.ListQuery query = 1;
+ //criterias the client is looking for
+ repeated zitadel.settings.v1.SecretGeneratorQuery queries = 2;
+}
+
+message ListSecretGeneratorsResponse {
+ zitadel.v1.ListDetails details = 1;
+ repeated zitadel.settings.v1.SecretGenerator result = 3;
+}
+
+message GetSecretGeneratorRequest {
+ zitadel.settings.v1.SecretGeneratorType generator_type = 1 [(validate.rules).enum = {defined_only: true, not_in: [0]}];
+}
+
+message GetSecretGeneratorResponse {
+ zitadel.settings.v1.SecretGenerator secret_generator = 1;
+}
+
+message UpdateSecretGeneratorRequest {
+ zitadel.settings.v1.SecretGeneratorType generator_type = 1 [(validate.rules).enum = {defined_only: true, not_in: [0]}];
+ uint32 length = 2;
+ google.protobuf.Duration expiry = 3;
+ bool include_lower_letters = 4;
+ bool include_upper_letters = 5;
+ bool include_digits = 6;
+ bool include_symbols = 7;
+}
+
+message UpdateSecretGeneratorResponse {
+ zitadel.v1.ObjectDetails details = 1;
+}
+
+//This is an empty request
+message GetSMTPConfigRequest {}
+
+message GetSMTPConfigResponse {
+ zitadel.settings.v1.SMTPConfig smtp_config = 1;
+}
+
+message UpdateSMTPConfigRequest {
+ string sender_address = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
+ string sender_name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
+ bool tls = 3;
+ string host = 4 [(validate.rules).string = {min_len: 1, max_len: 500}];
+ string user = 5;
+}
+
+message UpdateSMTPConfigResponse {
+ zitadel.v1.ObjectDetails details = 1;
+}
+
+message UpdateSMTPConfigPasswordRequest {
+ string password = 1;
+}
+
+message UpdateSMTPConfigPasswordResponse {
+ zitadel.v1.ObjectDetails details = 1;
+}
+
// if name or domain is already in use, org is not unique
message IsOrgUniqueRequest {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
diff --git a/proto/zitadel/management.proto b/proto/zitadel/management.proto
index 48c9c3402e..0e83dc66d6 100644
--- a/proto/zitadel/management.proto
+++ b/proto/zitadel/management.proto
@@ -290,6 +290,7 @@ service ManagementService {
rpc UpdateUserName(UpdateUserNameRequest) returns (UpdateUserNameResponse) {
option (google.api.http) = {
put: "/users/{user_id}/username"
+ body: "*"
};
option (zitadel.v1.auth_option) = {
diff --git a/proto/zitadel/settings.proto b/proto/zitadel/settings.proto
new file mode 100644
index 0000000000..81de52fd96
--- /dev/null
+++ b/proto/zitadel/settings.proto
@@ -0,0 +1,53 @@
+syntax = "proto3";
+
+import "zitadel/object.proto";
+import "validate/validate.proto";
+import "google/protobuf/duration.proto";
+import "protoc-gen-openapiv2/options/annotations.proto";
+
+package zitadel.settings.v1;
+
+option go_package ="github.com/caos/zitadel/pkg/grpc/settings";
+
+message SecretGenerator {
+ SecretGeneratorType generator_type = 1;
+ zitadel.v1.ObjectDetails details = 2;
+ uint32 length = 3;
+ google.protobuf.Duration expiry = 4;
+ bool include_lower_letters = 5;
+ bool include_upper_letters = 6;
+ bool include_digits = 7;
+ bool include_symbols = 8;
+}
+
+
+message SecretGeneratorQuery {
+ oneof query {
+ option (validate.required) = true;
+
+ SecretGeneratorTypeQuery type_query = 1;
+ }
+}
+
+message SecretGeneratorTypeQuery {
+ SecretGeneratorType generator_type = 1;
+}
+
+enum SecretGeneratorType {
+ SECRET_GENERATOR_TYPE_UNSPECIFIED = 0;
+ SECRET_GENERATOR_TYPE_INIT_CODE = 1;
+ SECRET_GENERATOR_TYPE_VERIFY_EMAIL_CODE = 2;
+ SECRET_GENERATOR_TYPE_VERIFY_PHONE_CODE = 3;
+ SECRET_GENERATOR_TYPE_PASSWORD_RESET_CODE = 4;
+ SECRET_GENERATOR_TYPE_PASSWORDLESS_INIT_CODE = 5;
+ SECRET_GENERATOR_TYPE_APP_SECRET = 6;
+}
+
+message SMTPConfig {
+ zitadel.v1.ObjectDetails details = 1;
+ string sender_address = 2;
+ string sender_name = 3;
+ bool tls = 4;
+ string host = 5;
+ string user = 6;
+}