mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-12 17:15:15 +00:00
feat(settings): add secret generator type to settings RT implementation (#11152)
# Which Problems Are Solved The `SecretGenerators` setting is missing among the instance settings in the new relational model. # How the Problems Are Solved This PR adds: * `SettingTypeSecretGenerator` type to the settings model * `SecretGeneratorSettingsRepository` and `secretGeneratorSettingsJSONChanges` interfaces to set, retrieve, and delete `secret_generator` settings, and corresponding unit tests. * projection reducers to reduce the following events: * `SecretGeneratorAddedEvent` * `SecretGeneratorChangedEvent` * `SecretGeneratorRemovedEvent` * integration tests to test that the events get reduced correctly # Additional Changes N/A # Additional Context - Part of https://github.com/zitadel/zitadel/issues/10207 --------- Co-authored-by: Copilot <[email protected]> Co-authored-by: Marco A. <[email protected]>
This commit is contained in:
co-authored by
Copilot
Marco A.
parent
826039c620
commit
3903355d18
@@ -8,7 +8,8 @@ CREATE TYPE zitadel.settings_type AS ENUM (
|
||||
'security',
|
||||
'organization',
|
||||
'notification',
|
||||
'legal_and_support'
|
||||
'legal_and_support',
|
||||
'secret_generator'
|
||||
);
|
||||
|
||||
CREATE TYPE zitadel.settings_state AS ENUM (
|
||||
|
||||
@@ -6,7 +6,8 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoChanges = errors.New("update must contain a change")
|
||||
ErrNoChanges = errors.New("update must contain a change")
|
||||
ErrInvalidChangeTarget = errors.New("change target must not be nil")
|
||||
)
|
||||
|
||||
type MissingConditionError struct {
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"github.com/zitadel/zitadel/pkg/grpc/admin"
|
||||
instance "github.com/zitadel/zitadel/pkg/grpc/instance/v2beta"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/policy"
|
||||
settings_pb "github.com/zitadel/zitadel/pkg/grpc/settings"
|
||||
)
|
||||
|
||||
//go:embed picture.png
|
||||
@@ -1387,3 +1388,150 @@ func TestServer_TestInstanceLegalAndSupportSettingsReduces(t *testing.T) {
|
||||
}, retryDuration, tick)
|
||||
})
|
||||
}
|
||||
|
||||
func TestServer_TestInstanceSecretGeneratorSettingsReduces(t *testing.T) {
|
||||
t.Parallel()
|
||||
settingsRepo := repository.SecretGeneratorSettingsRepository()
|
||||
newInstance := integration.NewInstance(t.Context())
|
||||
|
||||
IAMCTX := newInstance.WithAuthorizationToken(t.Context(), integration.UserTypeIAMOwner)
|
||||
|
||||
t.Run("test secret generator settings set", func(t *testing.T) {
|
||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*20)
|
||||
assert.EventuallyWithT(t, func(collect *assert.CollectT) {
|
||||
setting, err := settingsRepo.Get(
|
||||
IAMCTX, pool,
|
||||
database.WithCondition(
|
||||
settingsRepo.UniqueCondition(newInstance.ID(), nil, domain.SettingTypeSecretGenerator, domain.SettingStateActive),
|
||||
),
|
||||
)
|
||||
require.NoError(collect, err)
|
||||
// ClientSecret
|
||||
assert.Equal(collect, uint(64), *setting.ClientSecret.Length)
|
||||
assert.Equal(collect, true, *setting.ClientSecret.IncludeLowerLetters)
|
||||
assert.Equal(collect, true, *setting.ClientSecret.IncludeUpperLetters)
|
||||
assert.Equal(collect, true, *setting.ClientSecret.IncludeDigits)
|
||||
assert.Equal(collect, false, *setting.ClientSecret.IncludeSymbols)
|
||||
|
||||
// InitializeUserCode
|
||||
assert.Equal(collect, uint(6), *setting.InitializeUserCode.Length)
|
||||
assert.Equal(collect, 72*time.Hour, *setting.InitializeUserCode.Expiry)
|
||||
assert.Equal(collect, false, *setting.InitializeUserCode.IncludeLowerLetters)
|
||||
assert.Equal(collect, true, *setting.InitializeUserCode.IncludeUpperLetters)
|
||||
assert.Equal(collect, true, *setting.InitializeUserCode.IncludeDigits)
|
||||
assert.Equal(collect, false, *setting.InitializeUserCode.IncludeSymbols)
|
||||
|
||||
// EmailVerificationCode
|
||||
assert.Equal(collect, uint(6), *setting.EmailVerificationCode.Length)
|
||||
assert.Equal(collect, 1*time.Hour, *setting.EmailVerificationCode.Expiry)
|
||||
assert.Equal(collect, false, *setting.EmailVerificationCode.IncludeLowerLetters)
|
||||
assert.Equal(collect, true, *setting.EmailVerificationCode.IncludeUpperLetters)
|
||||
assert.Equal(collect, true, *setting.EmailVerificationCode.IncludeDigits)
|
||||
assert.Equal(collect, false, *setting.EmailVerificationCode.IncludeSymbols)
|
||||
|
||||
// PhoneVerificationCode
|
||||
assert.Equal(collect, uint(6), *setting.PhoneVerificationCode.Length)
|
||||
assert.Equal(collect, 1*time.Hour, *setting.PhoneVerificationCode.Expiry)
|
||||
assert.Equal(collect, false, *setting.PhoneVerificationCode.IncludeLowerLetters)
|
||||
assert.Equal(collect, true, *setting.PhoneVerificationCode.IncludeUpperLetters)
|
||||
assert.Equal(collect, true, *setting.PhoneVerificationCode.IncludeDigits)
|
||||
assert.Equal(collect, false, *setting.PhoneVerificationCode.IncludeSymbols)
|
||||
|
||||
// PasswordVerificationCode
|
||||
assert.Equal(collect, uint(6), *setting.PasswordVerificationCode.Length)
|
||||
assert.Equal(collect, 1*time.Hour, *setting.PasswordVerificationCode.Expiry)
|
||||
assert.Equal(collect, false, *setting.PasswordVerificationCode.IncludeLowerLetters)
|
||||
assert.Equal(collect, true, *setting.PasswordVerificationCode.IncludeUpperLetters)
|
||||
assert.Equal(collect, true, *setting.PasswordVerificationCode.IncludeDigits)
|
||||
assert.Equal(collect, false, *setting.PasswordVerificationCode.IncludeSymbols)
|
||||
|
||||
// PasswordlessInitCode
|
||||
assert.Equal(collect, uint(12), *setting.PasswordlessInitCode.Length)
|
||||
assert.Equal(collect, 1*time.Hour, *setting.PasswordlessInitCode.Expiry)
|
||||
assert.Equal(collect, true, *setting.PasswordlessInitCode.IncludeLowerLetters)
|
||||
assert.Equal(collect, true, *setting.PasswordlessInitCode.IncludeUpperLetters)
|
||||
assert.Equal(collect, true, *setting.PasswordlessInitCode.IncludeDigits)
|
||||
assert.Equal(collect, false, *setting.PasswordlessInitCode.IncludeSymbols)
|
||||
|
||||
// DomainVerification
|
||||
assert.Equal(collect, uint(32), *setting.DomainVerification.Length)
|
||||
assert.Equal(collect, true, *setting.DomainVerification.IncludeLowerLetters)
|
||||
assert.Equal(collect, true, *setting.DomainVerification.IncludeUpperLetters)
|
||||
assert.Equal(collect, true, *setting.DomainVerification.IncludeDigits)
|
||||
assert.Equal(collect, false, *setting.DomainVerification.IncludeSymbols)
|
||||
|
||||
// OTPSMS
|
||||
assert.Equal(collect, uint(8), *setting.OTPSMS.Length)
|
||||
assert.Equal(collect, 5*time.Minute, *setting.OTPSMS.Expiry)
|
||||
assert.Equal(collect, false, *setting.OTPSMS.IncludeLowerLetters)
|
||||
assert.Equal(collect, false, *setting.OTPSMS.IncludeUpperLetters)
|
||||
assert.Equal(collect, true, *setting.OTPSMS.IncludeDigits)
|
||||
assert.Equal(collect, false, *setting.OTPSMS.IncludeSymbols)
|
||||
|
||||
// OTPEmail
|
||||
assert.Equal(collect, uint(8), *setting.OTPEmail.Length)
|
||||
assert.Equal(collect, 5*time.Minute, *setting.OTPEmail.Expiry)
|
||||
assert.Equal(collect, false, *setting.OTPEmail.IncludeLowerLetters)
|
||||
assert.Equal(collect, false, *setting.OTPEmail.IncludeUpperLetters)
|
||||
assert.Equal(collect, true, *setting.OTPEmail.IncludeDigits)
|
||||
assert.Equal(collect, false, *setting.OTPEmail.IncludeSymbols)
|
||||
}, retryDuration, tick)
|
||||
})
|
||||
|
||||
t.Run("test secret generator settings update", func(t *testing.T) {
|
||||
before := time.Now()
|
||||
_, err := newInstance.Client.Admin.UpdateSecretGenerator(IAMCTX, &admin.UpdateSecretGeneratorRequest{
|
||||
GeneratorType: settings_pb.SecretGeneratorType_SECRET_GENERATOR_TYPE_INIT_CODE,
|
||||
IncludeLowerLetters: true,
|
||||
Expiry: durationpb.New(24 * time.Hour),
|
||||
Length: uint32(8),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
after := time.Now()
|
||||
|
||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*20)
|
||||
assert.EventuallyWithT(t, func(collect *assert.CollectT) {
|
||||
setting, err := settingsRepo.Get(
|
||||
IAMCTX, pool,
|
||||
database.WithCondition(
|
||||
settingsRepo.UniqueCondition(newInstance.ID(), nil, domain.SettingTypeSecretGenerator, domain.SettingStateActive),
|
||||
),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(collect, uint(8), *setting.InitializeUserCode.Length)
|
||||
assert.Equal(collect, 24*time.Hour, *setting.InitializeUserCode.Expiry)
|
||||
assert.Equal(collect, true, *setting.InitializeUserCode.IncludeLowerLetters)
|
||||
assert.WithinRange(collect, setting.UpdatedAt, before, after)
|
||||
}, retryDuration, tick)
|
||||
})
|
||||
|
||||
t.Run("test delete instance reduces", func(t *testing.T) {
|
||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*20)
|
||||
assert.EventuallyWithT(t, func(collect *assert.CollectT) {
|
||||
setting, err := settingsRepo.Get(
|
||||
IAMCTX, pool,
|
||||
database.WithCondition(
|
||||
settingsRepo.UniqueCondition(newInstance.ID(), nil, domain.SettingTypeSecretGenerator, domain.SettingStateActive),
|
||||
),
|
||||
)
|
||||
require.NoError(collect, err)
|
||||
require.NotNil(collect, setting)
|
||||
}, retryDuration, tick)
|
||||
|
||||
_, err := newInstance.Client.InstanceV2Beta.DeleteInstance(CTX, &instance.DeleteInstanceRequest{
|
||||
InstanceId: newInstance.ID(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
retryDuration, tick = integration.WaitForAndTickWithMaxDuration(IAMCTX, time.Second*20)
|
||||
assert.EventuallyWithT(t, func(collect *assert.CollectT) {
|
||||
_, err := settingsRepo.Get(
|
||||
IAMCTX, pool,
|
||||
database.WithCondition(
|
||||
settingsRepo.UniqueCondition(newInstance.ID(), nil, domain.SettingTypeSecretGenerator, domain.SettingStateActive),
|
||||
),
|
||||
)
|
||||
require.ErrorIs(collect, err, new(database.NoRowFoundError))
|
||||
}, retryDuration, tick)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -149,6 +149,10 @@ func (s settings) list(ctx context.Context, client database.QueryExecutor, opts
|
||||
}
|
||||
|
||||
func (s settings) set(ctx context.Context, client database.QueryExecutor, settings *domain.Settings, changes ...database.Change) error {
|
||||
if settings == nil {
|
||||
return database.ErrInvalidChangeTarget
|
||||
}
|
||||
|
||||
var (
|
||||
id, createdAt, updatedAt any = database.DefaultInstruction, database.DefaultInstruction, database.DefaultInstruction
|
||||
)
|
||||
@@ -378,6 +382,10 @@ func LoginSettingsRepository() domain.LoginSettingsRepository {
|
||||
var _ domain.LoginSettingsRepository = (*loginSettings)(nil)
|
||||
|
||||
func (s loginSettings) Set(ctx context.Context, client database.QueryExecutor, settings *domain.LoginSettings) error {
|
||||
if settings == nil {
|
||||
return database.ErrInvalidChangeTarget
|
||||
}
|
||||
|
||||
settings.Type = domain.SettingTypeLogin
|
||||
settings.State = domain.SettingStateActive
|
||||
|
||||
@@ -572,6 +580,10 @@ func BrandingSettingsRepository() domain.BrandingSettingsRepository {
|
||||
var _ domain.BrandingSettingsRepository = (*brandingSettings)(nil)
|
||||
|
||||
func (s brandingSettings) Set(ctx context.Context, client database.QueryExecutor, settings *domain.BrandingSettings) (err error) {
|
||||
if settings == nil {
|
||||
return database.ErrInvalidChangeTarget
|
||||
}
|
||||
|
||||
settings.Type = domain.SettingTypeBranding
|
||||
settings.State = domain.SettingStatePreview
|
||||
|
||||
@@ -719,6 +731,10 @@ func PasswordComplexitySettingsRepository() domain.PasswordComplexitySettingsRep
|
||||
var _ domain.PasswordComplexitySettingsRepository = (*passwordComplexitySettings)(nil)
|
||||
|
||||
func (s passwordComplexitySettings) Set(ctx context.Context, client database.QueryExecutor, settings *domain.PasswordComplexitySettings) (err error) {
|
||||
if settings == nil {
|
||||
return database.ErrInvalidChangeTarget
|
||||
}
|
||||
|
||||
settings.Type = domain.SettingTypePasswordComplexity
|
||||
settings.State = domain.SettingStateActive
|
||||
|
||||
@@ -801,6 +817,10 @@ func PasswordExpirySettingsRepository() domain.PasswordExpirySettingsRepository
|
||||
}
|
||||
|
||||
func (s passwordExpirySettings) Set(ctx context.Context, client database.QueryExecutor, settings *domain.PasswordExpirySettings) (err error) {
|
||||
if settings == nil {
|
||||
return database.ErrInvalidChangeTarget
|
||||
}
|
||||
|
||||
settings.Type = domain.SettingTypePasswordExpiry
|
||||
settings.State = domain.SettingStateActive
|
||||
|
||||
@@ -889,6 +909,10 @@ func LockoutSettingsRepository() domain.LockoutSettingsRepository {
|
||||
var _ domain.LockoutSettingsRepository = (*lockoutSettings)(nil)
|
||||
|
||||
func (s lockoutSettings) Set(ctx context.Context, client database.QueryExecutor, settings *domain.LockoutSettings) error {
|
||||
if settings == nil {
|
||||
return database.ErrInvalidChangeTarget
|
||||
}
|
||||
|
||||
settings.Type = domain.SettingTypeLockout
|
||||
settings.State = domain.SettingStateActive
|
||||
|
||||
@@ -977,6 +1001,10 @@ func SecuritySettingsRepository() domain.SecuritySettingsRepository {
|
||||
var _ domain.SecuritySettingsRepository = (*securitySettings)(nil)
|
||||
|
||||
func (s securitySettings) Set(ctx context.Context, client database.QueryExecutor, settings *domain.SecuritySettings) (err error) {
|
||||
if settings == nil {
|
||||
return database.ErrInvalidChangeTarget
|
||||
}
|
||||
|
||||
settings.Type = domain.SettingTypeSecurity
|
||||
settings.State = domain.SettingStateActive
|
||||
|
||||
@@ -1065,6 +1093,10 @@ func DomainSettingsRepository() domain.DomainSettingsRepository {
|
||||
var _ domain.DomainSettingsRepository = (*domainSettings)(nil)
|
||||
|
||||
func (s domainSettings) Set(ctx context.Context, client database.QueryExecutor, settings *domain.DomainSettings) (err error) {
|
||||
if settings == nil {
|
||||
return database.ErrInvalidChangeTarget
|
||||
}
|
||||
|
||||
settings.Type = domain.SettingTypeDomain
|
||||
settings.State = domain.SettingStateActive
|
||||
|
||||
@@ -1139,6 +1171,10 @@ func OrganizationSettingsRepository() domain.OrganizationSettingsRepository {
|
||||
var _ domain.OrganizationSettingsRepository = (*organizationSettings)(nil)
|
||||
|
||||
func (s organizationSettings) Set(ctx context.Context, client database.QueryExecutor, settings *domain.OrganizationSettings) (err error) {
|
||||
if settings == nil {
|
||||
return database.ErrInvalidChangeTarget
|
||||
}
|
||||
|
||||
settings.Type = domain.SettingTypeOrganization
|
||||
settings.State = domain.SettingStateActive
|
||||
|
||||
@@ -1213,6 +1249,10 @@ func NotificationSettingsRepository() domain.NotificationSettingsRepository {
|
||||
var _ domain.NotificationSettingsRepository = (*notificationSettings)(nil)
|
||||
|
||||
func (s notificationSettings) Set(ctx context.Context, client database.QueryExecutor, settings *domain.NotificationSettings) (err error) {
|
||||
if settings == nil {
|
||||
return database.ErrInvalidChangeTarget
|
||||
}
|
||||
|
||||
settings.Type = domain.SettingTypeNotification
|
||||
settings.State = domain.SettingStateActive
|
||||
|
||||
@@ -1329,6 +1369,10 @@ func LegalAndSupportSettingsRepository() domain.LegalAndSupportSettingsRepositor
|
||||
var _ domain.LegalAndSupportSettingsRepository = (*legalAndSupportSettings)(nil)
|
||||
|
||||
func (s legalAndSupportSettings) Set(ctx context.Context, client database.QueryExecutor, settings *domain.LegalAndSupportSettings) (err error) {
|
||||
if settings == nil {
|
||||
return database.ErrInvalidChangeTarget
|
||||
}
|
||||
|
||||
settings.Type = domain.SettingTypeLegalAndSupport
|
||||
settings.State = domain.SettingStateActive
|
||||
|
||||
@@ -1375,6 +1419,141 @@ func (s legalAndSupportSettings) Get(ctx context.Context, client database.QueryE
|
||||
return item, nil
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// secret generators
|
||||
// -------------------------------------------------------------
|
||||
|
||||
type secretGeneratorSettings struct {
|
||||
settings
|
||||
}
|
||||
|
||||
func (s secretGeneratorSettings) SetSettingFields(value domain.SecretGeneratorSettingsAttributes) database.Change {
|
||||
changes := make([]db_json.JsonUpdate, 0, 11)
|
||||
if value.ClientSecret != nil {
|
||||
changes = append(changes, s.SetClientSecret(*value.ClientSecret))
|
||||
}
|
||||
if value.InitializeUserCode != nil {
|
||||
changes = append(changes, s.SetInitializeUserCode(*value.InitializeUserCode))
|
||||
}
|
||||
if value.EmailVerificationCode != nil {
|
||||
changes = append(changes, s.SetEmailVerificationCode(*value.EmailVerificationCode))
|
||||
}
|
||||
if value.PhoneVerificationCode != nil {
|
||||
changes = append(changes, s.SetPhoneVerificationCode(*value.PhoneVerificationCode))
|
||||
}
|
||||
if value.PasswordVerificationCode != nil {
|
||||
changes = append(changes, s.SetPasswordVerificationCode(*value.PasswordVerificationCode))
|
||||
}
|
||||
if value.PasswordlessInitCode != nil {
|
||||
changes = append(changes, s.SetPasswordlessInitCode(*value.PasswordlessInitCode))
|
||||
}
|
||||
if value.DomainVerification != nil {
|
||||
changes = append(changes, s.SetDomainVerification(*value.DomainVerification))
|
||||
}
|
||||
if value.OTPSMS != nil {
|
||||
changes = append(changes, s.SetOTPSMS(*value.OTPSMS))
|
||||
}
|
||||
if value.OTPEmail != nil {
|
||||
changes = append(changes, s.SetOTPEmail(*value.OTPEmail))
|
||||
}
|
||||
return db_json.NewJsonChanges(s.SettingsColumn(), changes...)
|
||||
}
|
||||
|
||||
func (s secretGeneratorSettings) SetClientSecret(value domain.ClientSecretAttributes) db_json.JsonUpdate {
|
||||
return db_json.NewFieldChange([]string{"clientSecret"}, value)
|
||||
}
|
||||
|
||||
func (s secretGeneratorSettings) SetInitializeUserCode(value domain.InitializeUserCodeAttributes) db_json.JsonUpdate {
|
||||
return db_json.NewFieldChange([]string{"initializeUserCode"}, value)
|
||||
}
|
||||
|
||||
func (s secretGeneratorSettings) SetEmailVerificationCode(value domain.EmailVerificationCodeAttributes) db_json.JsonUpdate {
|
||||
return db_json.NewFieldChange([]string{"emailVerificationCode"}, value)
|
||||
}
|
||||
|
||||
func (s secretGeneratorSettings) SetPhoneVerificationCode(value domain.PhoneVerificationCodeAttributes) db_json.JsonUpdate {
|
||||
return db_json.NewFieldChange([]string{"phoneVerificationCode"}, value)
|
||||
}
|
||||
|
||||
func (s secretGeneratorSettings) SetPasswordVerificationCode(value domain.PasswordVerificationCodeAttributes) db_json.JsonUpdate {
|
||||
return db_json.NewFieldChange([]string{"passwordVerificationCode"}, value)
|
||||
}
|
||||
|
||||
func (s secretGeneratorSettings) SetPasswordlessInitCode(value domain.PasswordlessInitCodeAttributes) db_json.JsonUpdate {
|
||||
return db_json.NewFieldChange([]string{"passwordlessInitCode"}, value)
|
||||
}
|
||||
|
||||
func (s secretGeneratorSettings) SetDomainVerification(value domain.DomainVerificationAttributes) db_json.JsonUpdate {
|
||||
return db_json.NewFieldChange([]string{"domainVerification"}, value)
|
||||
}
|
||||
|
||||
func (s secretGeneratorSettings) SetOTPSMS(value domain.OTPSMSAttributes) db_json.JsonUpdate {
|
||||
return db_json.NewFieldChange([]string{"otpSms"}, value)
|
||||
}
|
||||
|
||||
func (s secretGeneratorSettings) SetOTPEmail(value domain.OTPEmailAttributes) db_json.JsonUpdate {
|
||||
return db_json.NewFieldChange([]string{"otpEmail"}, value)
|
||||
}
|
||||
|
||||
func SecretGeneratorSettingsRepository() domain.SecretGeneratorSettingsRepository {
|
||||
return &secretGeneratorSettings{
|
||||
settings{},
|
||||
}
|
||||
}
|
||||
|
||||
var _ domain.SecretGeneratorSettingsRepository = (*secretGeneratorSettings)(nil)
|
||||
|
||||
func (s secretGeneratorSettings) Set(ctx context.Context, client database.QueryExecutor, settings *domain.SecretGeneratorSettings) (err error) {
|
||||
if settings == nil {
|
||||
return database.ErrInvalidChangeTarget
|
||||
}
|
||||
|
||||
settings.Type = domain.SettingTypeSecretGenerator
|
||||
settings.State = domain.SettingStateActive
|
||||
|
||||
settingsJSON, err := json.Marshal(settings.SecretGeneratorSettingsAttributes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
settings.Settings.Settings = settingsJSON
|
||||
if err := s.set(ctx, client, &settings.Settings, s.SetSettingFields(settings.SecretGeneratorSettingsAttributes)); err != nil {
|
||||
return err
|
||||
}
|
||||
settings.Settings.Settings = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s secretGeneratorSettings) List(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) ([]*domain.SecretGeneratorSettings, error) {
|
||||
settings, err := s.list(ctx, client, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
list := make([]*domain.SecretGeneratorSettings, len(settings))
|
||||
for i := range settings {
|
||||
list[i] = &domain.SecretGeneratorSettings{Settings: *settings[i]}
|
||||
if err := json.Unmarshal(list[i].Settings.Settings, &list[i].SecretGeneratorSettingsAttributes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list[i].Settings.Settings = nil
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func (s secretGeneratorSettings) Get(ctx context.Context, client database.QueryExecutor, opts ...database.QueryOption) (*domain.SecretGeneratorSettings, error) {
|
||||
settings, err := s.get(ctx, client, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
item := &domain.SecretGeneratorSettings{Settings: *settings}
|
||||
if err = json.Unmarshal(item.Settings.Settings, &item.SecretGeneratorSettingsAttributes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item.Settings.Settings = nil
|
||||
return item, nil
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// helpers
|
||||
// -------------------------------------------------------------
|
||||
|
||||
@@ -498,6 +498,11 @@ func TestSetLoginSettings(t *testing.T) {
|
||||
},
|
||||
wantErr: new(database.ForeignKeyError),
|
||||
},
|
||||
{
|
||||
name: "nil settings",
|
||||
settings: nil,
|
||||
wantErr: database.ErrInvalidChangeTarget,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -1167,6 +1172,11 @@ func TestSetBrandingSettings(t *testing.T) {
|
||||
},
|
||||
wantErr: new(database.ForeignKeyError),
|
||||
},
|
||||
{
|
||||
name: "nil settings",
|
||||
settings: nil,
|
||||
wantErr: database.ErrInvalidChangeTarget,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -1912,6 +1922,11 @@ func TestSetPasswordComplexitySettings(t *testing.T) {
|
||||
},
|
||||
wantErr: new(database.ForeignKeyError),
|
||||
},
|
||||
{
|
||||
name: "nil settings",
|
||||
settings: nil,
|
||||
wantErr: database.ErrInvalidChangeTarget,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -2300,6 +2315,11 @@ func TestSetPasswordExpirySettings(t *testing.T) {
|
||||
},
|
||||
wantErr: new(database.ForeignKeyError),
|
||||
},
|
||||
{
|
||||
name: "nil settings",
|
||||
settings: nil,
|
||||
wantErr: database.ErrInvalidChangeTarget,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -2695,6 +2715,11 @@ func TestSetLockoutSettings(t *testing.T) {
|
||||
},
|
||||
wantErr: new(database.ForeignKeyError),
|
||||
},
|
||||
{
|
||||
name: "nil settings",
|
||||
settings: nil,
|
||||
wantErr: database.ErrInvalidChangeTarget,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -3092,6 +3117,11 @@ func TestSetSecuritySettings(t *testing.T) {
|
||||
},
|
||||
wantErr: new(database.ForeignKeyError),
|
||||
},
|
||||
{
|
||||
name: "nil settings",
|
||||
settings: nil,
|
||||
wantErr: database.ErrInvalidChangeTarget,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -3489,6 +3519,11 @@ func TestSetDomainSettings(t *testing.T) {
|
||||
},
|
||||
wantErr: new(database.ForeignKeyError),
|
||||
},
|
||||
{
|
||||
name: "nil settings",
|
||||
settings: nil,
|
||||
wantErr: database.ErrInvalidChangeTarget,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -3860,6 +3895,11 @@ func TestSetOrganizationSettings(t *testing.T) {
|
||||
},
|
||||
wantErr: new(database.ForeignKeyError),
|
||||
},
|
||||
{
|
||||
name: "nil settings",
|
||||
settings: nil,
|
||||
wantErr: database.ErrInvalidChangeTarget,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -4227,6 +4267,11 @@ func TestSetNotificationSettings(t *testing.T) {
|
||||
},
|
||||
wantErr: new(database.ForeignKeyError),
|
||||
},
|
||||
{
|
||||
name: "nil settings",
|
||||
settings: nil,
|
||||
wantErr: database.ErrInvalidChangeTarget,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -4672,6 +4717,11 @@ func TestSetLegalAndSupportSettings(t *testing.T) {
|
||||
},
|
||||
wantErr: new(database.ForeignKeyError),
|
||||
},
|
||||
{
|
||||
name: "nil settings",
|
||||
settings: nil,
|
||||
wantErr: database.ErrInvalidChangeTarget,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -4774,3 +4824,325 @@ func TestDeleteLegalAndSupportSettings(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSecretGeneratorSettings(t *testing.T) {
|
||||
tx, rollback := transactionForRollback(t)
|
||||
defer rollback()
|
||||
|
||||
firstInstanceID := createInstance(t, tx)
|
||||
secondInstanceID := createInstance(t, tx)
|
||||
firstOrgID := createOrganization(t, tx, firstInstanceID)
|
||||
secondOrgID := createOrganization(t, tx, secondInstanceID)
|
||||
repo := repository.SecretGeneratorSettingsRepository()
|
||||
|
||||
settings := []*domain.SecretGeneratorSettings{
|
||||
createSecretGeneratorSettings(firstInstanceID, nil),
|
||||
createSecretGeneratorSettings(secondInstanceID, nil),
|
||||
createSecretGeneratorSettings(firstInstanceID, gu.Ptr(firstOrgID)),
|
||||
createSecretGeneratorSettings(secondInstanceID, gu.Ptr(secondOrgID)),
|
||||
}
|
||||
|
||||
for _, setting := range settings {
|
||||
err := repo.Set(t.Context(), tx, setting)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
condition database.Condition
|
||||
want *domain.SecretGeneratorSettings
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "incomplete condition",
|
||||
condition: repo.IDCondition(settings[0].ID),
|
||||
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
||||
},
|
||||
{
|
||||
name: "not found",
|
||||
condition: repo.PrimaryKeyCondition(firstInstanceID, "nix"),
|
||||
wantErr: database.NewNoRowFoundError(nil),
|
||||
},
|
||||
{
|
||||
name: "too many",
|
||||
condition: repo.InstanceIDCondition(firstInstanceID),
|
||||
wantErr: database.NewMultipleRowsFoundError(nil),
|
||||
},
|
||||
{
|
||||
name: "ok, instance",
|
||||
condition: repo.PrimaryKeyCondition(firstInstanceID, settings[0].ID),
|
||||
want: settings[0],
|
||||
},
|
||||
{
|
||||
name: "ok, organization",
|
||||
condition: repo.PrimaryKeyCondition(secondInstanceID, settings[3].ID),
|
||||
want: settings[3],
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := repo.Get(t.Context(), tx, database.WithCondition(tt.condition))
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
assert.EqualExportedValues(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListSecretGeneratorSettings(t *testing.T) {
|
||||
tx, rollback := transactionForRollback(t)
|
||||
defer rollback()
|
||||
|
||||
firstInstanceID := createInstance(t, tx)
|
||||
secondInstanceID := createInstance(t, tx)
|
||||
firstOrgID := createOrganization(t, tx, firstInstanceID)
|
||||
secondOrgID := createOrganization(t, tx, secondInstanceID)
|
||||
repo := repository.SecretGeneratorSettingsRepository()
|
||||
|
||||
settings := []*domain.SecretGeneratorSettings{
|
||||
createSecretGeneratorSettings(firstInstanceID, nil),
|
||||
createSecretGeneratorSettings(secondInstanceID, nil),
|
||||
createSecretGeneratorSettings(firstInstanceID, gu.Ptr(firstOrgID)),
|
||||
createSecretGeneratorSettings(secondInstanceID, gu.Ptr(secondOrgID)),
|
||||
}
|
||||
|
||||
for _, setting := range settings {
|
||||
err := repo.Set(t.Context(), tx, setting)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
condition database.Condition
|
||||
want []*domain.SecretGeneratorSettings
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "incomplete condition",
|
||||
condition: repo.OrganizationIDCondition(gu.Ptr(firstOrgID)),
|
||||
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
||||
},
|
||||
{
|
||||
name: "no results, ok",
|
||||
condition: repo.PrimaryKeyCondition(firstInstanceID, "nix"),
|
||||
want: []*domain.SecretGeneratorSettings{},
|
||||
},
|
||||
{
|
||||
name: "all from instance",
|
||||
condition: repo.InstanceIDCondition(firstInstanceID),
|
||||
want: []*domain.SecretGeneratorSettings{settings[2], settings[0]},
|
||||
},
|
||||
{
|
||||
name: "only from instance",
|
||||
condition: database.And(
|
||||
repo.InstanceIDCondition(firstInstanceID),
|
||||
repo.OrganizationIDCondition(nil),
|
||||
),
|
||||
want: []*domain.SecretGeneratorSettings{settings[0]},
|
||||
},
|
||||
{
|
||||
name: "all from first org",
|
||||
condition: database.And(
|
||||
repo.InstanceIDCondition(firstInstanceID),
|
||||
repo.OrganizationIDCondition(gu.Ptr(firstOrgID)),
|
||||
),
|
||||
want: []*domain.SecretGeneratorSettings{settings[2]},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := repo.List(t.Context(), tx,
|
||||
database.WithCondition(tt.condition),
|
||||
database.WithOrderByAscending(repo.InstanceIDColumn(), repo.OrganizationIDColumn()),
|
||||
)
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
assert.EqualExportedValues(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetSecretGeneratorSettings(t *testing.T) {
|
||||
tx, rollback := transactionForRollback(t)
|
||||
defer rollback()
|
||||
|
||||
instanceID := createInstance(t, tx)
|
||||
orgID := createOrganization(t, tx, instanceID)
|
||||
repo := repository.SecretGeneratorSettingsRepository()
|
||||
|
||||
existingSettings := createSecretGeneratorSettings(instanceID, gu.Ptr(orgID))
|
||||
|
||||
err := repo.Set(t.Context(), tx, existingSettings)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
settings *domain.SecretGeneratorSettings
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "create instance",
|
||||
settings: createSecretGeneratorSettings(instanceID, nil),
|
||||
},
|
||||
{
|
||||
name: "update organization",
|
||||
settings: createSecretGeneratorSettings(instanceID, gu.Ptr(orgID)),
|
||||
},
|
||||
{
|
||||
name: "non-existing instance",
|
||||
settings: createSecretGeneratorSettings("foo", nil),
|
||||
wantErr: new(database.ForeignKeyError),
|
||||
},
|
||||
{
|
||||
name: "non-existing org",
|
||||
settings: createSecretGeneratorSettings(instanceID, gu.Ptr("foo")),
|
||||
wantErr: new(database.ForeignKeyError),
|
||||
},
|
||||
{
|
||||
name: "nil settings",
|
||||
settings: nil,
|
||||
wantErr: database.ErrInvalidChangeTarget,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
savepoint, rollback := savepointForRollback(t, tx)
|
||||
defer rollback()
|
||||
err := repo.Set(t.Context(), savepoint, tt.settings)
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteSecretGeneratorSettings(t *testing.T) {
|
||||
tx, rollback := transactionForRollback(t)
|
||||
defer rollback()
|
||||
|
||||
instanceID := createInstance(t, tx)
|
||||
orgID := createOrganization(t, tx, instanceID)
|
||||
repo := repository.SecretGeneratorSettingsRepository()
|
||||
|
||||
existingInstanceSettings := createSecretGeneratorSettings(instanceID, nil)
|
||||
err := repo.Set(t.Context(), tx, existingInstanceSettings)
|
||||
require.NoError(t, err)
|
||||
|
||||
existingOrganizationSettings := createSecretGeneratorSettings(instanceID, gu.Ptr(orgID))
|
||||
err = repo.Set(t.Context(), tx, existingOrganizationSettings)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
condition database.Condition
|
||||
wantRowsAffected int64
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "incomplete condition",
|
||||
condition: repo.InstanceIDCondition(instanceID),
|
||||
wantRowsAffected: 0,
|
||||
wantErr: database.NewMissingConditionError(repo.IDColumn()),
|
||||
},
|
||||
{
|
||||
name: "not found",
|
||||
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, gu.Ptr("foo"), domain.SettingTypeSecretGenerator, domain.SettingStateActive),
|
||||
wantRowsAffected: 0,
|
||||
},
|
||||
{
|
||||
name: "delete instance",
|
||||
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, existingInstanceSettings.OrganizationID, domain.SettingTypeSecretGenerator, domain.SettingStateActive),
|
||||
wantRowsAffected: 1,
|
||||
},
|
||||
{
|
||||
name: "delete instance twice",
|
||||
condition: repo.UniqueCondition(existingInstanceSettings.InstanceID, existingInstanceSettings.OrganizationID, domain.SettingTypeSecretGenerator, domain.SettingStateActive),
|
||||
wantRowsAffected: 0,
|
||||
},
|
||||
{
|
||||
name: "delete organization",
|
||||
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypeSecretGenerator, domain.SettingStateActive),
|
||||
wantRowsAffected: 1,
|
||||
},
|
||||
{
|
||||
name: "delete organization twice",
|
||||
condition: repo.UniqueCondition(existingOrganizationSettings.InstanceID, existingOrganizationSettings.OrganizationID, domain.SettingTypeSecretGenerator, domain.SettingStateActive),
|
||||
wantRowsAffected: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
rowsAffected, err := repo.Delete(t.Context(), tx, tt.condition)
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
assert.Equal(t, tt.wantRowsAffected, rowsAffected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func createDefaultSecretGeneratorAttrs() domain.SecretGeneratorAttrs {
|
||||
return domain.SecretGeneratorAttrs{
|
||||
Length: gu.Ptr(uint(32)),
|
||||
IncludeUpperLetters: gu.Ptr(true),
|
||||
IncludeLowerLetters: gu.Ptr(true),
|
||||
IncludeDigits: gu.Ptr(true),
|
||||
IncludeSymbols: gu.Ptr(false),
|
||||
}
|
||||
}
|
||||
|
||||
func createDefaultSecretGeneratorAttrsWithExpiry(expiry time.Duration) domain.SecretGeneratorAttrsWithExpiry {
|
||||
return domain.SecretGeneratorAttrsWithExpiry{
|
||||
Expiry: gu.Ptr(expiry),
|
||||
SecretGeneratorAttrs: domain.SecretGeneratorAttrs{
|
||||
Length: gu.Ptr(uint(32)),
|
||||
IncludeUpperLetters: gu.Ptr(true),
|
||||
IncludeLowerLetters: gu.Ptr(true),
|
||||
IncludeDigits: gu.Ptr(true),
|
||||
IncludeSymbols: gu.Ptr(false),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func createDefaultSecretGeneratorSettingsAttributes() domain.SecretGeneratorSettingsAttributes {
|
||||
attrs := createDefaultSecretGeneratorAttrs()
|
||||
oneDay := 24 * time.Hour
|
||||
thirtyMinutes := 30 * time.Minute
|
||||
tenMinutes := 10 * time.Minute
|
||||
|
||||
return domain.SecretGeneratorSettingsAttributes{
|
||||
ClientSecret: &domain.ClientSecretAttributes{
|
||||
SecretGeneratorAttrs: attrs,
|
||||
},
|
||||
InitializeUserCode: &domain.InitializeUserCodeAttributes{
|
||||
SecretGeneratorAttrsWithExpiry: createDefaultSecretGeneratorAttrsWithExpiry(oneDay),
|
||||
},
|
||||
EmailVerificationCode: &domain.EmailVerificationCodeAttributes{
|
||||
SecretGeneratorAttrsWithExpiry: createDefaultSecretGeneratorAttrsWithExpiry(thirtyMinutes),
|
||||
},
|
||||
PhoneVerificationCode: &domain.PhoneVerificationCodeAttributes{
|
||||
SecretGeneratorAttrsWithExpiry: createDefaultSecretGeneratorAttrsWithExpiry(thirtyMinutes),
|
||||
},
|
||||
PasswordVerificationCode: &domain.PasswordVerificationCodeAttributes{
|
||||
SecretGeneratorAttrsWithExpiry: createDefaultSecretGeneratorAttrsWithExpiry(thirtyMinutes),
|
||||
},
|
||||
PasswordlessInitCode: &domain.PasswordlessInitCodeAttributes{
|
||||
SecretGeneratorAttrsWithExpiry: createDefaultSecretGeneratorAttrsWithExpiry(tenMinutes),
|
||||
},
|
||||
DomainVerification: &domain.DomainVerificationAttributes{
|
||||
SecretGeneratorAttrs: attrs,
|
||||
},
|
||||
OTPSMS: &domain.OTPSMSAttributes{
|
||||
SecretGeneratorAttrsWithExpiry: createDefaultSecretGeneratorAttrsWithExpiry(thirtyMinutes),
|
||||
},
|
||||
OTPEmail: &domain.OTPEmailAttributes{
|
||||
SecretGeneratorAttrsWithExpiry: createDefaultSecretGeneratorAttrsWithExpiry(thirtyMinutes),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func createSecretGeneratorSettings(instanceID string, organizationID *string) *domain.SecretGeneratorSettings {
|
||||
return &domain.SecretGeneratorSettings{
|
||||
Settings: domain.Settings{
|
||||
InstanceID: instanceID,
|
||||
OrganizationID: organizationID,
|
||||
},
|
||||
SecretGeneratorSettingsAttributes: createDefaultSecretGeneratorSettingsAttributes(),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user