feat: Config to eventstore (#3158)

* feat: add default language to eventstore

* feat: add secret generator configs events

* feat: tests

* feat: secret generators in eventstore

* feat: secret generators in eventstore

* feat: smtp config in eventstore

* feat: smtp config in eventstore

* feat: smtp config in eventstore

* feat: smtp config in eventstore

* feat: smtp config in eventstore

* fix: migrations

* fix migration version

* fix test

* feat: change secret generator type to enum

* feat: change smtp attribute names

* feat: change smtp attribute names

* feat: remove engryption algorithms from command side

* feat: remove engryption algorithms from command side

* feat: smtp config

* feat: smtp config

* format smtp from header

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
This commit is contained in:
Fabi 2022-02-16 16:49:17 +01:00 committed by GitHub
parent 4272ea6fe1
commit e3528ff0b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
118 changed files with 5216 additions and 686 deletions

View File

@ -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)
}

View File

@ -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

View File

@ -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<br /> enum.not_in: [0]<br /> |
### 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<br /> string.max_len: 10<br /> |
### 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<br /> string.max_len: 200<br /> |
| sender_name | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
| tls | bool | - | |
| host | string | - | string.min_len: 1<br /> string.max_len: 500<br /> |
| 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<br /> enum.not_in: [0]<br /> |
| 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

2
go.mod
View File

@ -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

2
go.sum
View File

@ -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=

View File

@ -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())

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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,
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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,
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -1,6 +1,7 @@
package management
import (
"github.com/caos/zitadel/internal/crypto"
"google.golang.org/grpc"
"github.com/caos/zitadel/internal/api/authz"
@ -23,14 +24,18 @@ type Server struct {
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,
PasswordHashAlg: crypto.NewBCrypt(sd.SecretGenerators.PasswordSaltCost),
UserCodeAlg: userCrypto,
}
}

View File

@ -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
}

View File

@ -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)

View File

@ -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(),
),

View File

@ -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

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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()

View File

@ -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

View File

@ -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)
}

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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))
}

View File

@ -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(

View File

@ -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)

View File

@ -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,
}

View File

@ -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,

View File

@ -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}),
}

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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 {
@ -14,6 +15,7 @@ type IAMWriteModel struct {
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()

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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
}
type args struct {
ctx context.Context
apiApp *domain.APIApp
resourceOwner string
secretGenerator crypto.Generator
}
type res struct {
want *domain.APIApp
@ -145,7 +146,6 @@ func TestCommandSide_AddAPIApplication(t *testing.T) {
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "app1", "client1"),
secretGenerator: GetMockSecretGenerator(t),
},
args: args{
ctx: context.Background(),
@ -157,6 +157,7 @@ func TestCommandSide_AddAPIApplication(t *testing.T) {
AuthMethodType: domain.APIAuthMethodTypeBasic,
},
resourceOwner: "org1",
secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.APIApp{
@ -240,9 +241,8 @@ func TestCommandSide_AddAPIApplication(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
idGenerator: tt.fields.idGenerator,
applicationSecretGenerator: tt.fields.secretGenerator,
}
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)
}
@ -473,13 +473,13 @@ func TestCommandSide_ChangeAPIApplication(t *testing.T) {
func TestCommandSide_ChangeAPIApplicationSecret(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
secretGenerator crypto.Generator
}
type args struct {
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",
secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.APIApp{
@ -613,9 +613,8 @@ func TestCommandSide_ChangeAPIApplicationSecret(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
applicationSecretGenerator: tt.fields.secretGenerator,
}
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)
}

View File

@ -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,14 +13,12 @@ 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
}
type args struct {
@ -127,7 +127,6 @@ func TestCommandSide_AddAPIApplicationKey(t *testing.T) {
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "key1"),
secretGenerator: GetMockSecretGenerator(t),
},
args: args{
ctx: context.Background(),
@ -174,7 +173,6 @@ func TestCommandSide_AddAPIApplicationKey(t *testing.T) {
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "key1"),
secretGenerator: GetMockSecretGenerator(t),
keySize: 10,
},
args: args{
@ -197,7 +195,6 @@ func TestCommandSide_AddAPIApplicationKey(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
idGenerator: tt.fields.idGenerator,
applicationSecretGenerator: tt.fields.secretGenerator,
applicationKeySize: tt.fields.keySize,
}
got, err := r.AddApplicationKey(tt.args.ctx, tt.args.key, tt.args.resourceOwner)

View File

@ -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
}

View File

@ -22,12 +22,12 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
idGenerator id.Generator
secretGenerator crypto.Generator
}
type args struct {
ctx context.Context
oidcApp *domain.OIDCApp
resourceOwner string
secretGenerator crypto.Generator
}
type res struct {
want *domain.OIDCApp
@ -161,7 +161,6 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) {
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "app1", "client1"),
secretGenerator: GetMockSecretGenerator(t),
},
args: args{
ctx: context.Background(),
@ -186,6 +185,7 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) {
AdditionalOrigins: []string{"https://sub.test.ch"},
},
resourceOwner: "org1",
secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.OIDCApp{
@ -222,9 +222,8 @@ func TestCommandSide_AddOIDCApplication(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
idGenerator: tt.fields.idGenerator,
applicationSecretGenerator: tt.fields.secretGenerator,
}
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)
}
@ -550,13 +549,13 @@ func TestCommandSide_ChangeOIDCApplication(t *testing.T) {
func TestCommandSide_ChangeOIDCApplicationSecret(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
secretGenerator crypto.Generator
}
type args struct {
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",
secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.OIDCApp{
@ -716,9 +715,8 @@ func TestCommandSide_ChangeOIDCApplicationSecret(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
applicationSecretGenerator: tt.fields.secretGenerator,
}
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)
}

View File

@ -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
}

119
internal/command/smtp.go Normal file
View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -20,12 +20,12 @@ import (
func TestCommandSide_ChangeHumanEmail(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
secretGenerator crypto.Generator
}
type args struct {
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(),
@ -274,6 +273,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
EmailAddress: "email-changed@test.ch",
},
resourceOwner: "org1",
secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.Email{
@ -290,9 +290,8 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
emailVerificationCode: tt.fields.secretGenerator,
}
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)
}
@ -309,13 +308,13 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) {
func TestCommandSide_VerifyHumanEmail(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
secretGenerator crypto.Generator
}
type args struct {
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",
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",
secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.ObjectDetails{
@ -527,9 +526,8 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
emailVerificationCode: tt.fields.secretGenerator,
}
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)
}
@ -546,12 +544,12 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) {
func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
secretGenerator crypto.Generator
}
type args struct {
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",
secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.ObjectDetails{
@ -737,9 +735,8 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
emailVerificationCode: tt.fields.secretGenerator,
}
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)
}

View File

@ -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")

View File

@ -21,13 +21,13 @@ import (
func TestCommandSide_ResendInitialMail(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
secretGenerator crypto.Generator
}
type args struct {
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",
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",
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",
secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.ObjectDetails{
@ -288,9 +288,8 @@ func TestCommandSide_ResendInitialMail(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
initializeUserCode: tt.fields.secretGenerator,
}
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,7 +306,6 @@ 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 {
@ -316,6 +314,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) {
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",
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",
secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.ObjectDetails{
@ -596,7 +595,6 @@ func TestCommandSide_VerifyInitCode(t *testing.T) {
},
),
),
secretGenerator: GetMockSecretGenerator(t),
userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
},
args: args{
@ -605,6 +603,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) {
code: "a",
resourceOwner: "org1",
password: "password",
secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.ObjectDetails{
@ -617,10 +616,9 @@ func TestCommandSide_VerifyInitCode(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
initializeUserCode: tt.fields.secretGenerator,
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)
}

View File

@ -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
}

View File

@ -239,7 +239,6 @@ func TestCommandSide_SetPassword(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
userPasswordAlg crypto.HashAlgorithm
secretGenerator crypto.Generator
}
type args struct {
ctx context.Context
@ -248,6 +247,7 @@ func TestCommandSide_SetPassword(t *testing.T) {
resourceOwner string
password string
agentID string
secretGenerator crypto.Generator
}
type res struct {
want *domain.ObjectDetails
@ -377,7 +377,6 @@ func TestCommandSide_SetPassword(t *testing.T) {
),
),
),
secretGenerator: GetMockSecretGenerator(t),
},
args: args{
ctx: context.Background(),
@ -385,6 +384,7 @@ func TestCommandSide_SetPassword(t *testing.T) {
code: "test",
resourceOwner: "org1",
password: "password",
secretGenerator: GetMockSecretGenerator(t),
},
res: res{
err: caos_errs.IsPreconditionFailed,
@ -459,7 +459,6 @@ func TestCommandSide_SetPassword(t *testing.T) {
},
),
),
secretGenerator: GetMockSecretGenerator(t),
userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
},
args: args{
@ -468,6 +467,7 @@ func TestCommandSide_SetPassword(t *testing.T) {
resourceOwner: "org1",
password: "password",
code: "a",
secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.ObjectDetails{
@ -481,9 +481,8 @@ func TestCommandSide_SetPassword(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
userPasswordAlg: tt.fields.userPasswordAlg,
passwordVerificationCode: tt.fields.secretGenerator,
}
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)
}
@ -779,13 +778,13 @@ func TestCommandSide_ChangePassword(t *testing.T) {
func TestCommandSide_RequestSetPassword(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
secretGenerator crypto.Generator
}
type args struct {
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",
secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.ObjectDetails{
@ -943,9 +942,8 @@ func TestCommandSide_RequestSetPassword(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
passwordVerificationCode: tt.fields.secretGenerator,
}
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)
}

View File

@ -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
}

View File

@ -20,12 +20,12 @@ import (
func TestCommandSide_ChangeHumanPhone(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
secretGenerator crypto.Generator
}
type args struct {
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(),
@ -243,6 +242,7 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
PhoneNumber: "0711234567",
},
resourceOwner: "org1",
secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.Phone{
@ -259,9 +259,8 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
phoneVerificationCode: tt.fields.secretGenerator,
}
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)
}
@ -278,13 +277,13 @@ func TestCommandSide_ChangeHumanPhone(t *testing.T) {
func TestCommandSide_VerifyHumanPhone(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
secretGenerator crypto.Generator
}
type args struct {
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",
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",
secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.ObjectDetails{
@ -508,9 +507,8 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
phoneVerificationCode: tt.fields.secretGenerator,
}
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)
}
@ -527,12 +525,12 @@ func TestCommandSide_VerifyHumanPhone(t *testing.T) {
func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
secretGenerator crypto.Generator
}
type args struct {
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",
secretGenerator: GetMockSecretGenerator(t),
},
res: res{
want: &domain.ObjectDetails{
@ -681,9 +679,8 @@ func TestCommandSide_CreateVerificationCodeHumanPhone(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
phoneVerificationCode: tt.fields.secretGenerator,
}
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)
}

View File

@ -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
secretGenerator crypto.Generator
}
type res struct {
want *domain.Human
@ -229,7 +229,6 @@ func TestCommandSide_AddHuman(t *testing.T) {
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
secretGenerator: GetMockSecretGenerator(t),
},
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{
@ -490,7 +490,6 @@ func TestCommandSide_AddHuman(t *testing.T) {
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
secretGenerator: GetMockSecretGenerator(t),
},
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{
@ -583,7 +583,6 @@ func TestCommandSide_AddHuman(t *testing.T) {
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
secretGenerator: GetMockSecretGenerator(t),
},
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{
@ -632,11 +632,9 @@ func TestCommandSide_AddHuman(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,
}
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)
}
@ -654,15 +652,15 @@ func TestCommandSide_ImportHuman(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
idGenerator id.Generator
secretGenerator crypto.Generator
userPasswordAlg crypto.HashAlgorithm
passwordlessInitCode crypto.Generator
}
type args struct {
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{
@ -1026,9 +1024,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1", "code1"),
secretGenerator: GetMockSecretGenerator(t),
userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
passwordlessInitCode: GetMockSecretGenerator(t),
},
args: args{
ctx: context.Background(),
@ -1045,6 +1041,8 @@ func TestCommandSide_ImportHuman(t *testing.T) {
},
},
passwordless: true,
secretGenerator: GetMockSecretGenerator(t),
passwordlessInitCode: GetMockSecretGenerator(t),
},
res: res{
wantHuman: &domain.Human{
@ -1130,9 +1128,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
),
),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1", "code1"),
secretGenerator: GetMockSecretGenerator(t),
userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
passwordlessInitCode: GetMockSecretGenerator(t),
},
args: args{
ctx: context.Background(),
@ -1153,6 +1149,8 @@ func TestCommandSide_ImportHuman(t *testing.T) {
},
},
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{
@ -1394,12 +1392,9 @@ func TestCommandSide_ImportHuman(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,
}
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,7 +1413,6 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
idGenerator id.Generator
secretGenerator crypto.Generator
userPasswordAlg crypto.HashAlgorithm
}
type args struct {
@ -1427,6 +1421,7 @@ func TestCommandSide_RegisterHuman(t *testing.T) {
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{
@ -2324,11 +2319,9 @@ func TestCommandSide_RegisterHuman(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,
}
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)
}

View File

@ -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))

View File

@ -9,7 +9,6 @@ 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"
)
@ -21,10 +20,10 @@ type SystemDefaults struct {
SecretGenerators SecretGenerators
UserVerificationKey *crypto.KeyConfig
IDPConfigVerificationKey *crypto.KeyConfig
SMTPPasswordVerificationKey *crypto.KeyConfig
Multifactors MultifactorConfig
VerificationLifetimes VerificationLifetimes
DomainVerification DomainVerification
IamID string
Notifications Notifications
KeyConfig KeyConfig
}
@ -36,12 +35,6 @@ 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
}
@ -72,7 +65,6 @@ type Notifications struct {
DebugMode bool
Endpoints Endpoints
Providers Channels
TemplateData TemplateData
}
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

View File

@ -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
)

8
internal/domain/smtp.go Normal file
View File

@ -0,0 +1,8 @@
package domain
type SMTPConfigState int32
const (
SMTPConfigStateUnspecified SMTPConfigState = iota
SMTPConfigStateActive
)

View File

@ -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
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)

View File

@ -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, ", ")

View File

@ -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")
}

View File

@ -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,
),
}
}

View File

@ -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"
@ -41,6 +42,7 @@ type Notification struct {
subscription *v1.Subscription
assetsPrefix string
queries *query.Queries
smtpPasswordCrypto crypto.EncryptionAlgorithm
}
func newNotification(
@ -51,6 +53,7 @@ func newNotification(
aesCrypto crypto.EncryptionAlgorithm,
statikDir http.FileSystem,
assetsPrefix string,
smtpPasswordEncAlg crypto.EncryptionAlgorithm,
) *Notification {
h := &Notification{
handler: handler,
@ -60,6 +63,7 @@ func newNotification(
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

View File

@ -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,
}

View File

@ -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,

View File

@ -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()

View File

@ -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
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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,10 +12,9 @@ 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,
@ -22,7 +23,7 @@ func generateEmail(user *view_model.NotifyUser, subject, content string, config
message.Recipients = []string{user.LastEmail}
}
channels, err := senders.EmailChannels(config)
channels, err := senders.EmailChannels(ctx, config, smtpConfig)
if err != nil {
return err
}

View File

@ -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

View File

@ -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 {
@ -53,6 +58,7 @@ type IAM struct {
GlobalOrgID string
IAMProjectID string
DefaultLanguage language.Tag
SetupStarted domain.Step
SetupDone domain.Step
}
@ -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
}
}

View File

@ -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,6 +82,7 @@ func Test_IAMPrepares(t *testing.T) {
"project-id",
domain.Step2,
domain.Step1,
"en",
},
),
},
@ -89,6 +94,7 @@ func Test_IAMPrepares(t *testing.T) {
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,
),

View File

@ -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,
},
},
},

View File

@ -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()).

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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,
@ -63,6 +67,7 @@ const (
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 {

View File

@ -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{

View File

@ -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

View File

@ -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
}

View File

@ -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)
})
}
}

Some files were not shown because too many files have changed in this diff Show More