Files
zitadel/internal/command/user_test.go
T
8fc11a7366 feat: user api requests to resource API (#9794)
# Which Problems Are Solved

This pull request addresses a significant gap in the user service v2
API, which currently lacks methods for managing machine users.

# How the Problems Are Solved

This PR adds new API endpoints to the user service v2 to manage machine
users including their secret, keys and personal access tokens.
Additionally, there's now a CreateUser and UpdateUser endpoints which
allow to create either a human or machine user and update them. The
existing `CreateHumanUser` endpoint has been deprecated along the
corresponding management service endpoints. For details check the
additional context section.

# Additional Context

- Closes https://github.com/zitadel/zitadel/issues/9349

## More details
- API changes: https://github.com/zitadel/zitadel/pull/9680
- Implementation: https://github.com/zitadel/zitadel/pull/9763
- Tests: https://github.com/zitadel/zitadel/pull/9771

## Follow-ups

- Metadata: support managing user metadata using resource API
https://github.com/zitadel/zitadel/pull/10005
- Machine token type: support managing the machine token type (migrate
to new enum with zero value unspecified?)

---------

Co-authored-by: Copilot <[email protected]>
Co-authored-by: Livio Spring <[email protected]>
2025-06-04 07:17:23 +00:00

1827 lines
38 KiB
Go

package command
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/assert"
"golang.org/x/text/language"
"github.com/zitadel/zitadel/internal/command/preparation"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/repository/instance"
"github.com/zitadel/zitadel/internal/repository/org"
"github.com/zitadel/zitadel/internal/repository/project"
"github.com/zitadel/zitadel/internal/repository/user"
"github.com/zitadel/zitadel/internal/zerrors"
)
func TestCommandSide_UsernameChange(t *testing.T) {
type fields struct {
eventstore func(*testing.T) *eventstore.Eventstore
}
type (
args struct {
ctx context.Context
orgID string
userID string
username string
}
)
type res struct {
want *domain.ObjectDetails
err func(error) bool
}
tests := []struct {
name string
fields fields
args args
res res
}{
{
name: "userid missing, invalid argument error",
fields: fields{
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "",
username: "username",
},
res: res{
err: zerrors.IsErrorInvalidArgument,
},
},
{
name: "orgid missing, invalid argument error",
fields: fields{
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
orgID: "",
userID: "user1",
username: "username",
},
res: res{
err: zerrors.IsErrorInvalidArgument,
},
},
{
name: "username missing, invalid argument error",
fields: fields{
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
username: "",
},
res: res{
err: zerrors.IsErrorInvalidArgument,
},
},
{
name: "username only spaces, invalid argument error",
fields: fields{
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
username: " ",
},
res: res{
err: zerrors.IsErrorInvalidArgument,
},
},
{
name: "user removed, not found error",
fields: fields{
eventstore: expectEventstore(
expectFilter(),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
username: "username",
},
res: res{
err: zerrors.IsNotFound,
},
},
{
name: "username not changed, precondition error",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"[email protected]",
true,
),
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
username: "username",
},
res: res{
err: zerrors.IsPreconditionFailed,
},
},
{
name: "username not changed (spaces), precondition error",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"[email protected]",
true,
),
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
username: "username ",
},
res: res{
err: zerrors.IsPreconditionFailed,
},
},
{
name: "org iam policy not found, precondition error",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"[email protected]",
true,
),
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
username: "username",
},
res: res{
err: zerrors.IsPreconditionFailed,
},
},
{
name: "domain verified, wrong org",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"[email protected]",
true,
),
),
),
expectFilter(),
expectFilter(
eventFromEventPusher(
instance.NewDomainPolicyAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
false,
true,
true,
),
),
),
expectFilter(
eventFromEventPusher(
org.NewDomainVerifiedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"test.ch",
),
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "wrong",
userID: "user1",
username: "[email protected]",
},
res: res{
err: zerrors.IsErrorInvalidArgument,
},
},
{
name: "email as username, ok",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"[email protected]",
true,
),
),
),
expectFilter(),
expectFilter(
eventFromEventPusher(
instance.NewDomainPolicyAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
true,
true,
true,
),
),
),
expectPush(
user.NewUsernameChangedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"[email protected]",
true,
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
username: "[email protected]",
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
{
name: "email as username, verified domain, ok",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"[email protected]",
true,
),
),
),
expectFilter(),
expectFilter(
eventFromEventPusher(
instance.NewDomainPolicyAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
false,
true,
true,
),
),
),
expectFilter(
eventFromEventPusher(
org.NewDomainVerifiedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"test.ch",
),
),
),
expectPush(
user.NewUsernameChangedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"[email protected]",
false,
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
username: "[email protected]",
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
{
name: "change username, ok",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"[email protected]",
true,
),
),
),
expectFilter(),
expectFilter(
eventFromEventPusher(
instance.NewDomainPolicyAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
true,
true,
true,
),
),
),
expectPush(
user.NewUsernameChangedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"username1",
true,
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
username: "username1",
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
{
name: "change username (remove spaces), ok",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"[email protected]",
true,
),
),
),
expectFilter(),
expectFilter(
eventFromEventPusher(
instance.NewDomainPolicyAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
true,
true,
true,
),
),
),
expectPush(
user.NewUsernameChangedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"username1",
true,
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
username: "username1 ",
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore(t),
}
got, err := r.ChangeUsername(tt.args.ctx, tt.args.orgID, tt.args.userID, tt.args.username)
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 {
assertObjectDetails(t, tt.res.want, got)
}
})
}
}
func TestCommandSide_DeactivateUser(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
}
type (
args struct {
ctx context.Context
orgID string
userID string
}
)
type res struct {
want *domain.ObjectDetails
err func(error) bool
}
tests := []struct {
name string
fields fields
args args
res res
}{
{
name: "userid missing, invalid argument error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "",
},
res: res{
err: zerrors.IsErrorInvalidArgument,
},
},
{
name: "user not existing, not found error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
},
res: res{
err: zerrors.IsNotFound,
},
},
{
name: "user already inactive, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"[email protected]",
true,
),
),
eventFromEventPusher(
user.NewUserDeactivatedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
),
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
},
res: res{
err: zerrors.IsPreconditionFailed,
},
},
{
name: "deactivate user, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"[email protected]",
true,
),
),
),
expectPush(
user.NewUserDeactivatedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
}
got, err := r.DeactivateUser(tt.args.ctx, tt.args.userID, tt.args.orgID)
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 {
assertObjectDetails(t, tt.res.want, got)
}
})
}
}
func TestCommandSide_ReactivateUser(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
}
type (
args struct {
ctx context.Context
orgID string
userID string
}
)
type res struct {
want *domain.ObjectDetails
err func(error) bool
}
tests := []struct {
name string
fields fields
args args
res res
}{
{
name: "userid missing, invalid argument error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "",
},
res: res{
err: zerrors.IsErrorInvalidArgument,
},
},
{
name: "user not existing, not found error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
},
res: res{
err: zerrors.IsNotFound,
},
},
{
name: "user already active, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"[email protected]",
true,
),
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
},
res: res{
err: zerrors.IsPreconditionFailed,
},
},
{
name: "reactivate user, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"[email protected]",
true,
),
),
eventFromEventPusher(
user.NewUserDeactivatedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate),
),
),
expectPush(
user.NewUserReactivatedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
}
got, err := r.ReactivateUser(tt.args.ctx, tt.args.userID, tt.args.orgID)
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 {
assertObjectDetails(t, tt.res.want, got)
}
})
}
}
func TestCommandSide_LockUser(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
}
type (
args struct {
ctx context.Context
orgID string
userID string
}
)
type res struct {
want *domain.ObjectDetails
err func(error) bool
}
tests := []struct {
name string
fields fields
args args
res res
}{
{
name: "userid missing, invalid argument error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "",
},
res: res{
err: zerrors.IsErrorInvalidArgument,
},
},
{
name: "user not existing, not found error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
},
res: res{
err: zerrors.IsNotFound,
},
},
{
name: "user already locked, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"[email protected]",
true,
),
),
eventFromEventPusher(
user.NewUserLockedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
),
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
},
res: res{
err: zerrors.IsPreconditionFailed,
},
},
{
name: "lock user, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"[email protected]",
true,
),
),
),
expectPush(
user.NewUserLockedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
}
got, err := r.LockUser(tt.args.ctx, tt.args.userID, tt.args.orgID)
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 {
assertObjectDetails(t, tt.res.want, got)
}
})
}
}
func TestCommandSide_UnlockUser(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
}
type (
args struct {
ctx context.Context
orgID string
userID string
}
)
type res struct {
want *domain.ObjectDetails
err func(error) bool
}
tests := []struct {
name string
fields fields
args args
res res
}{
{
name: "userid missing, invalid argument error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "",
},
res: res{
err: zerrors.IsErrorInvalidArgument,
},
},
{
name: "user not existing, not found error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
},
res: res{
err: zerrors.IsNotFound,
},
},
{
name: "user already active, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"[email protected]",
true,
),
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
},
res: res{
err: zerrors.IsPreconditionFailed,
},
},
{
name: "unlock user, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"[email protected]",
true,
),
),
eventFromEventPusher(
user.NewUserLockedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate),
),
),
expectPush(
user.NewUserUnlockedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
}
got, err := r.UnlockUser(tt.args.ctx, tt.args.userID, tt.args.orgID)
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 {
assertObjectDetails(t, tt.res.want, got)
}
})
}
}
func TestCommandSide_RemoveUser(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
}
type (
args struct {
ctx context.Context
instanceID string
orgID string
userID string
cascadeUserMemberships []*CascadingMembership
cascadeUserGrants []string
}
)
type res struct {
want *domain.ObjectDetails
err func(error) bool
}
tests := []struct {
name string
fields fields
args args
res res
}{
{
name: "userid missing, invalid argument error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "",
},
res: res{
err: zerrors.IsErrorInvalidArgument,
},
},
{
name: "user not existing, not found error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
},
res: res{
err: zerrors.IsNotFound,
},
},
{
name: "org iam policy not found, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"[email protected]",
true,
),
),
),
expectFilter(),
expectFilter(),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
},
res: res{
err: zerrors.IsPreconditionFailed,
},
},
{
name: "remove user, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"[email protected]",
true,
),
),
),
expectFilter(),
expectFilter(
eventFromEventPusher(
instance.NewDomainPolicyAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
true,
true,
true,
),
),
),
expectPush(
user.NewUserRemovedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
nil,
true,
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
{
name: "remove user with erxternal idp, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"[email protected]",
true,
),
),
eventFromEventPusher(
user.NewUserIDPLinkAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"idpConfigID",
"displayName",
"externalUserID",
),
),
),
expectFilter(),
expectFilter(
eventFromEventPusher(
instance.NewDomainPolicyAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
true,
true,
true,
),
),
),
expectPush(
user.NewUserRemovedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
[]*domain.UserIDPLink{
{
IDPConfigID: "idpConfigID",
ExternalUserID: "externalUserID",
},
},
true,
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
{
name: "remove user with user memberships, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"[email protected]",
true,
),
),
),
expectFilter(),
expectFilter(
eventFromEventPusher(
instance.NewDomainPolicyAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
true,
true,
true,
),
),
),
expectPush(
user.NewUserRemovedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
nil,
true,
),
instance.NewMemberCascadeRemovedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
"user1",
),
org.NewMemberCascadeRemovedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
"user1",
),
project.NewProjectMemberCascadeRemovedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
"user1",
),
project.NewProjectGrantMemberCascadeRemovedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
"user1",
"grant1",
),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
userID: "user1",
cascadeUserMemberships: []*CascadingMembership{
{
IAM: &CascadingIAMMembership{
IAMID: "INSTANCE",
},
UserID: "user1",
ResourceOwner: "org1",
},
{
Org: &CascadingOrgMembership{
OrgID: "org1",
},
UserID: "user1",
ResourceOwner: "org1",
},
{
Project: &CascadingProjectMembership{
ProjectID: "project1",
},
UserID: "user1",
ResourceOwner: "org1",
},
{
ProjectGrant: &CascadingProjectGrantMembership{
ProjectID: "project1",
GrantID: "grant1",
},
UserID: "user1",
ResourceOwner: "org1",
},
},
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
}
got, err := r.RemoveUser(tt.args.ctx, tt.args.userID, tt.args.orgID, tt.args.cascadeUserMemberships, tt.args.cascadeUserGrants...)
if tt.res.err == nil {
assert.NoError(t, err)
} else if !tt.res.err(err) {
t.Errorf("got wrong err: %v ", err)
return
}
assertObjectDetails(t, tt.res.want, got)
})
}
}
func TestCommands_RevokeAccessToken(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
}
type args struct {
ctx context.Context
userID string
orgID string
tokenID string
}
type res struct {
want *domain.ObjectDetails
err func(error) bool
}
tests := []struct {
name string
fields fields
args args
res res
}{
{
"id missing error",
fields{
eventstoreExpect(t),
},
args{
context.Background(),
"userID",
"orgID",
"",
},
res{
nil,
zerrors.IsErrorInvalidArgument,
},
},
{
"not active error",
fields{
eventstoreExpect(t,
expectFilter(
eventFromEventPusher(
user.NewUserTokenAddedEvent(context.Background(),
&user.NewAggregate("userID", "orgID").Aggregate,
"tokenID",
"clientID",
"agentID",
"de",
"refreshTokenID",
[]string{"clientID"},
[]string{"openid"},
[]string{"password"},
time.Now(),
time.Now(),
domain.TokenReasonAuthRequest,
nil,
),
),
),
),
},
args{
context.Background(),
"userID",
"orgID",
"tokenID",
},
res{
nil,
zerrors.IsNotFound,
},
},
{
"active ok",
fields{
eventstoreExpect(t,
expectFilter(
eventFromEventPusher(
user.NewUserTokenAddedEvent(context.Background(),
&user.NewAggregate("userID", "orgID").Aggregate,
"tokenID",
"clientID",
"agentID",
"de",
"refreshTokenID",
[]string{"clientID"},
[]string{"openid"},
[]string{"password"},
time.Now(),
time.Now().Add(5*time.Hour),
domain.TokenReasonAuthRequest,
nil,
),
),
),
expectPush(
user.NewUserTokenRemovedEvent(context.Background(),
&user.NewAggregate("userID", "orgID").Aggregate,
"tokenID",
),
),
),
},
args{
context.Background(),
"userID",
"orgID",
"tokenID",
},
res{
&domain.ObjectDetails{
ResourceOwner: "orgID",
},
nil,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &Commands{
eventstore: tt.fields.eventstore,
}
got, err := c.RevokeAccessToken(tt.args.ctx, tt.args.userID, tt.args.orgID, tt.args.tokenID)
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 {
assertObjectDetails(t, tt.res.want, got)
}
})
}
}
func TestCommandSide_UserDomainClaimedSent(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
}
type args struct {
ctx context.Context
userID string
resourceOwner string
}
type res struct {
err func(error) bool
}
tests := []struct {
name string
fields fields
args args
res res
}{
{
name: "userid missing, invalid argument error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
},
args: args{
ctx: context.Background(),
resourceOwner: "org1",
},
res: res{
err: zerrors.IsErrorInvalidArgument,
},
},
{
name: "user not existing, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
),
},
args: args{
ctx: context.Background(),
userID: "user1",
resourceOwner: "org1",
},
res: res{
err: zerrors.IsNotFound,
},
},
{
name: "code sent, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"[email protected]",
true,
),
),
),
expectPush(
user.NewDomainClaimedSentEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
),
),
),
},
args: args{
ctx: context.Background(),
userID: "user1",
resourceOwner: "org1",
},
res: res{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
}
err := r.UserDomainClaimedSent(tt.args.ctx, tt.args.resourceOwner, tt.args.userID)
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)
}
})
}
}
func TestExistsUser(t *testing.T) {
type args struct {
filter preparation.FilterToQueryReducer
id string
resourceOwner string
}
tests := []struct {
name string
args args
wantExists bool
wantErr bool
}{
{
name: "no events",
args: args{
filter: func(_ context.Context, _ *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
return []eventstore.Event{}, nil
},
id: "id",
resourceOwner: "ro",
},
wantExists: false,
wantErr: false,
},
{
name: "human registered",
args: args{
filter: func(_ context.Context, _ *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
return []eventstore.Event{
user.NewHumanRegisteredEvent(
context.Background(),
&user.NewAggregate("id", "ro").Aggregate,
"userName",
"firstName",
"lastName",
"nickName",
"displayName",
language.German,
domain.GenderFemale,
"[email protected]",
true,
"userAgentID",
),
}, nil
},
id: "id",
resourceOwner: "ro",
},
wantExists: true,
wantErr: false,
},
{
name: "human added",
args: args{
filter: func(_ context.Context, _ *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
return []eventstore.Event{
user.NewHumanAddedEvent(
context.Background(),
&user.NewAggregate("id", "ro").Aggregate,
"userName",
"firstName",
"lastName",
"nickName",
"displayName",
language.German,
domain.GenderFemale,
"[email protected]",
true,
),
}, nil
},
id: "id",
resourceOwner: "ro",
},
wantExists: true,
wantErr: false,
},
{
name: "machine added",
args: args{
filter: func(_ context.Context, _ *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
return []eventstore.Event{
user.NewMachineAddedEvent(
context.Background(),
&user.NewAggregate("id", "ro").Aggregate,
"userName",
"name",
"description",
true,
domain.OIDCTokenTypeBearer,
),
}, nil
},
id: "id",
resourceOwner: "ro",
},
wantExists: true,
wantErr: false,
},
{
name: "user removed",
args: args{
filter: func(_ context.Context, _ *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
return []eventstore.Event{
user.NewMachineAddedEvent(
context.Background(),
&user.NewAggregate("removed", "ro").Aggregate,
"userName",
"name",
"description",
true,
domain.OIDCTokenTypeBearer,
),
user.NewUserRemovedEvent(
context.Background(),
&user.NewAggregate("removed", "ro").Aggregate,
"userName",
nil,
true,
),
}, nil
},
id: "id",
resourceOwner: "ro",
},
wantExists: false,
wantErr: false,
},
{
name: "error durring filter",
args: args{
filter: func(_ context.Context, _ *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
return nil, zerrors.ThrowInternal(nil, "USER-Drebn", "Errors.Internal")
},
id: "id",
resourceOwner: "ro",
},
wantExists: false,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotExists, err := ExistsUser(context.Background(), tt.args.filter, tt.args.id, tt.args.resourceOwner, false)
if (err != nil) != tt.wantErr {
t.Errorf("ExistsUser() error = %v, wantErr %v", err, tt.wantErr)
return
}
if gotExists != tt.wantExists {
t.Errorf("ExistsUser() = %v, want %v", gotExists, tt.wantExists)
}
})
}
}