mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 18:57:32 +00:00
feat: organization settings for user uniqueness (#10246)
# Which Problems Are Solved Currently the username uniqueness is on instance level, we want to achieve a way to set it at organization level. # How the Problems Are Solved Addition of endpoints and a resource on organization level, where this setting can be managed. If nothing it set, the uniqueness is expected to be at instance level, where only users with instance permissions should be able to change this setting. # Additional Changes None # Additional Context Includes #10086 Closes #9964 --------- Co-authored-by: Marco A. <marco@zitadel.com>
This commit is contained in:
@@ -190,6 +190,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(),
|
||||
expectFilter(),
|
||||
),
|
||||
@@ -231,6 +232,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectPush(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&userAgg.Aggregate,
|
||||
@@ -299,6 +301,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectPush(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&userAgg.Aggregate,
|
||||
@@ -368,6 +371,77 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectPush(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&userAgg.Aggregate,
|
||||
"username",
|
||||
"firstname",
|
||||
"lastname",
|
||||
"",
|
||||
"firstname lastname",
|
||||
AllowedLanguage,
|
||||
domain.GenderUnspecified,
|
||||
"email@test.ch",
|
||||
true,
|
||||
),
|
||||
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
||||
&userAgg.Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("userinit"),
|
||||
},
|
||||
time.Hour*1,
|
||||
"",
|
||||
),
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
newCode: mockEncryptedCode("userinit", time.Hour),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
human: &AddHuman{
|
||||
Username: "username",
|
||||
FirstName: "firstname",
|
||||
LastName: "lastname",
|
||||
Email: Email{
|
||||
Address: "email@test.ch",
|
||||
},
|
||||
PreferredLanguage: AllowedLanguage,
|
||||
},
|
||||
secretGenerator: GetMockSecretGenerator(t),
|
||||
allowInitMail: true,
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
Sequence: 0,
|
||||
EventDate: time.Time{},
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
wantID: "user1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add human (with initial code), orgScopedUsername, ok",
|
||||
fields: fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&userAgg.Aggregate,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", true, true),
|
||||
expectPush(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&userAgg.Aggregate,
|
||||
@@ -437,6 +511,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
@@ -507,6 +582,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
@@ -580,6 +656,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
@@ -654,6 +731,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
@@ -714,6 +792,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
@@ -777,6 +856,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
@@ -840,6 +920,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
@@ -963,6 +1044,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
@@ -1042,6 +1124,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
@@ -1151,6 +1234,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectPush(
|
||||
newAddHumanEvent("", false, true, "+41711234567", AllowedLanguage),
|
||||
user.NewHumanInitialCodeAddedEvent(
|
||||
@@ -1216,6 +1300,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
@@ -1326,6 +1411,7 @@ func TestCommandSide_AddHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectPush(
|
||||
newAddHumanEvent("", false, true, "", AllowedLanguage),
|
||||
user.NewHumanInitialCodeAddedEvent(
|
||||
@@ -1518,6 +1604,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(),
|
||||
expectFilter(),
|
||||
),
|
||||
@@ -1557,6 +1644,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
@@ -1602,6 +1690,94 @@ func TestCommandSide_ImportHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
1,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
newAddHumanEvent("$plain$x$password", true, true, "", AllowedLanguage),
|
||||
user.NewHumanInitialCodeAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
time.Hour*1,
|
||||
"",
|
||||
),
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
},
|
||||
args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
human: &domain.Human{
|
||||
Username: "username",
|
||||
Password: &domain.Password{
|
||||
SecretString: "password",
|
||||
ChangeRequired: true,
|
||||
},
|
||||
Profile: &domain.Profile{
|
||||
FirstName: "firstname",
|
||||
LastName: "lastname",
|
||||
PreferredLanguage: AllowedLanguage,
|
||||
},
|
||||
Email: &domain.Email{
|
||||
EmailAddress: "email@test.ch",
|
||||
},
|
||||
},
|
||||
secretGenerator: GetMockSecretGenerator(t),
|
||||
}
|
||||
},
|
||||
res: res{
|
||||
wantHuman: &domain.Human{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "user1",
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
Username: "username",
|
||||
Profile: &domain.Profile{
|
||||
FirstName: "firstname",
|
||||
LastName: "lastname",
|
||||
DisplayName: "firstname lastname",
|
||||
PreferredLanguage: AllowedLanguage,
|
||||
},
|
||||
Email: &domain.Email{
|
||||
EmailAddress: "email@test.ch",
|
||||
},
|
||||
State: domain.UserStateInitial,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add human (with password and initial code), orgScopedUsername, ok",
|
||||
given: func(t *testing.T) (fields, args) {
|
||||
return fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewDomainPolicyAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", true, true),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
@@ -1688,6 +1864,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
@@ -1768,6 +1945,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
@@ -1867,6 +2045,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
@@ -1970,6 +2149,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
@@ -2105,6 +2285,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
@@ -2200,6 +2381,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
@@ -2285,6 +2467,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
@@ -2371,6 +2554,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
@@ -2516,6 +2700,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
@@ -2659,6 +2844,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
@@ -2831,6 +3017,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
@@ -3003,6 +3190,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
@@ -3182,6 +3370,7 @@ func TestCommandSide_ImportHuman(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilterOrganizationSettings("org1", false, false),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
|
||||
@@ -3829,6 +4018,10 @@ func TestAddHumanCommand(t *testing.T) {
|
||||
),
|
||||
}, nil
|
||||
}).
|
||||
Append(
|
||||
func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return expectFilterOrganizationSettingsEvents(ctx, "org1", false, false), nil
|
||||
}).
|
||||
Append(
|
||||
func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return []eventstore.Event{
|
||||
@@ -3882,6 +4075,87 @@ func TestAddHumanCommand(t *testing.T) {
|
||||
),
|
||||
}, nil
|
||||
}).
|
||||
Append(
|
||||
func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return expectFilterOrganizationSettingsEvents(ctx, "org1", false, false), nil
|
||||
}).
|
||||
Append(
|
||||
func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return []eventstore.Event{
|
||||
org.NewPasswordComplexityPolicyAddedEvent(
|
||||
ctx,
|
||||
&org.NewAggregate("id").Aggregate,
|
||||
2,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
),
|
||||
}, nil
|
||||
}).
|
||||
Filter(),
|
||||
},
|
||||
want: Want{
|
||||
Commands: []eventstore.Command{
|
||||
func() *user.HumanAddedEvent {
|
||||
event := user.NewHumanAddedEvent(
|
||||
context.Background(),
|
||||
&agg.Aggregate,
|
||||
"username",
|
||||
"gigi",
|
||||
"giraffe",
|
||||
"",
|
||||
"gigi giraffe",
|
||||
AllowedLanguage,
|
||||
0,
|
||||
"support@zitadel.com",
|
||||
true,
|
||||
)
|
||||
event.AddPasswordData("$plain$x$password", false)
|
||||
return event
|
||||
}(),
|
||||
user.NewHumanEmailVerifiedEvent(context.Background(), &agg.Aggregate),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "correct, orgScopedUsername",
|
||||
fields: fields{
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id"),
|
||||
},
|
||||
args: args{
|
||||
human: &AddHuman{
|
||||
Email: Email{Address: "support@zitadel.com", Verified: true},
|
||||
FirstName: "gigi",
|
||||
LastName: "giraffe",
|
||||
Password: "password",
|
||||
Username: "username",
|
||||
PreferredLanguage: AllowedLanguage,
|
||||
},
|
||||
orgID: "ro",
|
||||
hasher: mockPasswordHasher("x"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
filter: NewMultiFilter().Append(
|
||||
func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return []eventstore.Event{}, nil
|
||||
}).
|
||||
Append(
|
||||
func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return []eventstore.Event{
|
||||
org.NewDomainPolicyAddedEvent(
|
||||
ctx,
|
||||
&org.NewAggregate("id").Aggregate,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
}, nil
|
||||
}).
|
||||
Append(
|
||||
func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
// never set, as only used in creation of instance and org
|
||||
return expectFilterOrganizationSettingsEvents(ctx, "org1", false, false), nil
|
||||
}).
|
||||
Append(
|
||||
func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return []eventstore.Event{
|
||||
@@ -3953,6 +4227,10 @@ func TestAddHumanCommand(t *testing.T) {
|
||||
),
|
||||
}, nil
|
||||
}).
|
||||
Append(
|
||||
func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return expectFilterOrganizationSettingsEvents(ctx, "org1", false, false), nil
|
||||
}).
|
||||
Append(
|
||||
func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return []eventstore.Event{
|
||||
@@ -4025,6 +4303,10 @@ func TestAddHumanCommand(t *testing.T) {
|
||||
),
|
||||
}, nil
|
||||
}).
|
||||
Append(
|
||||
func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return expectFilterOrganizationSettingsEvents(ctx, "org1", false, false), nil
|
||||
}).
|
||||
Append(
|
||||
func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return []eventstore.Event{
|
||||
@@ -4097,6 +4379,10 @@ func TestAddHumanCommand(t *testing.T) {
|
||||
),
|
||||
}, nil
|
||||
}).
|
||||
Append(
|
||||
func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return expectFilterOrganizationSettingsEvents(ctx, "org1", false, false), nil
|
||||
}).
|
||||
Append(
|
||||
func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return []eventstore.Event{
|
||||
|
Reference in New Issue
Block a user