feat: SetPassword endpoint

This commit is contained in:
Stefan Benz 2024-09-23 20:05:04 +02:00
parent aa13e451e3
commit 1e9d58c924
No known key found for this signature in database
GPG Key ID: 071AA751ED4F9D31
7 changed files with 888 additions and 1 deletions

View File

@ -0,0 +1,234 @@
//go:build integration
package user_test
import (
"context"
"testing"
"github.com/brianvoe/gofakeit/v6"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/zitadel/zitadel/internal/integration"
object "github.com/zitadel/zitadel/pkg/grpc/object/v3alpha"
resource_object "github.com/zitadel/zitadel/pkg/grpc/resources/object/v3alpha"
user "github.com/zitadel/zitadel/pkg/grpc/resources/user/v3alpha"
)
func TestServer_SetPassword(t *testing.T) {
t.Parallel()
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
schema := []byte(`{
"$schema": "urn:zitadel:schema:v1",
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
}`)
schemaResp := instance.CreateUserSchema(isolatedIAMOwnerCTX, schema)
orgResp := instance.CreateOrganization(isolatedIAMOwnerCTX, gofakeit.Name(), gofakeit.Email())
type res struct {
want *resource_object.Details
}
tests := []struct {
name string
ctx context.Context
dep func(req *user.SetPasswordRequest) error
req *user.SetPasswordRequest
res res
wantErr bool
}{
{
name: "password set, no context",
ctx: context.Background(),
dep: func(req *user.SetPasswordRequest) error {
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
req.Id = userResp.GetDetails().GetId()
return nil
},
req: &user.SetPasswordRequest{
Organization: &object.Organization{
Property: &object.Organization_OrgId{
OrgId: orgResp.GetOrganizationId(),
},
},
NewPassword: &user.SetPassword{
Type: &user.SetPassword_Password{
Password: gofakeit.Password(true, true, true, true, false, 12),
},
ChangeRequired: false,
},
},
wantErr: true,
},
{
name: "password set, no permission",
ctx: instance.WithAuthorization(CTX, integration.UserTypeLogin),
dep: func(req *user.SetPasswordRequest) error {
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
req.Id = userResp.GetDetails().GetId()
return nil
},
req: &user.SetPasswordRequest{
Organization: &object.Organization{
Property: &object.Organization_OrgId{
OrgId: orgResp.GetOrganizationId(),
},
},
NewPassword: &user.SetPassword{
Type: &user.SetPassword_Password{
Password: gofakeit.Password(true, true, true, true, false, 12),
},
ChangeRequired: false,
},
},
wantErr: true,
},
{
name: "password set, password empty",
ctx: instance.WithAuthorization(CTX, integration.UserTypeLogin),
dep: func(req *user.SetPasswordRequest) error {
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
req.Id = userResp.GetDetails().GetId()
return nil
},
req: &user.SetPasswordRequest{
Organization: &object.Organization{
Property: &object.Organization_OrgId{
OrgId: orgResp.GetOrganizationId(),
},
},
NewPassword: &user.SetPassword{
Type: &user.SetPassword_Password{
Password: "",
},
ChangeRequired: false,
},
},
wantErr: true,
},
{
name: "password set, user not existing in org",
ctx: isolatedIAMOwnerCTX,
dep: func(req *user.SetPasswordRequest) error {
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
req.Id = userResp.GetDetails().GetId()
return nil
},
req: &user.SetPasswordRequest{
Organization: &object.Organization{
Property: &object.Organization_OrgId{
OrgId: "notexisting",
},
},
NewPassword: &user.SetPassword{
Type: &user.SetPassword_Password{
Password: gofakeit.Password(true, true, true, true, false, 12),
},
ChangeRequired: false,
},
},
wantErr: true,
},
{
name: "username add, user not existing",
ctx: isolatedIAMOwnerCTX,
req: &user.SetPasswordRequest{
Organization: &object.Organization{
Property: &object.Organization_OrgId{
OrgId: "notexisting",
},
},
Id: "not existing",
NewPassword: &user.SetPassword{
Type: &user.SetPassword_Password{
Password: gofakeit.Password(true, true, true, true, false, 12),
},
ChangeRequired: false,
},
},
wantErr: true,
},
{
name: "password set, ok",
ctx: isolatedIAMOwnerCTX,
dep: func(req *user.SetPasswordRequest) error {
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
req.Id = userResp.GetDetails().GetId()
return nil
},
req: &user.SetPasswordRequest{
Organization: &object.Organization{
Property: &object.Organization_OrgId{
OrgId: orgResp.GetOrganizationId(),
},
},
NewPassword: &user.SetPassword{
Type: &user.SetPassword_Password{
Password: gofakeit.Password(true, true, true, true, false, 12),
},
ChangeRequired: false,
},
},
res: res{
want: &resource_object.Details{
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_ORG,
Id: orgResp.GetOrganizationId(),
},
},
},
},
{
name: "password set, no org, ok",
ctx: isolatedIAMOwnerCTX,
dep: func(req *user.SetPasswordRequest) error {
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
req.Id = userResp.GetDetails().GetId()
return nil
},
req: &user.SetPasswordRequest{
NewPassword: &user.SetPassword{
Type: &user.SetPassword_Password{
Password: gofakeit.Password(true, true, true, true, false, 12),
},
ChangeRequired: false,
},
},
res: res{
want: &resource_object.Details{
Changed: timestamppb.Now(),
Owner: &object.Owner{
Type: object.OwnerType_OWNER_TYPE_ORG,
Id: orgResp.GetOrganizationId(),
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.dep != nil {
err := tt.dep(tt.req)
assert.NoError(t, err)
}
got, err := instance.Client.UserV3Alpha.SetPassword(tt.ctx, tt.req)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
integration.AssertResourceDetails(t, tt.res.want, got.Details)
})
}
}

View File

@ -0,0 +1,33 @@
package user
import (
"context"
resource_object "github.com/zitadel/zitadel/internal/api/grpc/resources/object/v3alpha"
"github.com/zitadel/zitadel/internal/command"
object "github.com/zitadel/zitadel/pkg/grpc/object/v3alpha"
user "github.com/zitadel/zitadel/pkg/grpc/resources/user/v3alpha"
)
func (s *Server) SetPassword(ctx context.Context, req *user.SetPasswordRequest) (_ *user.SetPasswordResponse, err error) {
if err := checkUserSchemaEnabled(ctx); err != nil {
return nil, err
}
details, err := s.command.SetSchemaUserPassword(ctx, setPasswordRequestToSetSchemaUserPassword(req))
if err != nil {
return nil, err
}
return &user.SetPasswordResponse{
Details: resource_object.DomainToDetailsPb(details, object.OwnerType_OWNER_TYPE_ORG, details.ResourceOwner),
}, nil
}
func setPasswordRequestToSetSchemaUserPassword(req *user.SetPasswordRequest) *command.SetSchemaUserPassword {
return &command.SetSchemaUserPassword{
ResourceOwner: organizationToUpdateResourceOwner(req.Organization),
UserID: req.GetId(),
Password: req.GetNewPassword().GetPassword(),
EncodedPasswordHash: req.GetNewPassword().GetHash(),
ChangeRequired: req.GetNewPassword().GetChangeRequired(),
}
}

View File

@ -0,0 +1,123 @@
package command
import (
"context"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/repository/user/authenticator"
"github.com/zitadel/zitadel/internal/zerrors"
)
type SetSchemaUserPassword struct {
ResourceOwner string
UserID string
Password string
EncodedPasswordHash string
ChangeRequired bool
}
func (p *SetSchemaUserPassword) Validate(hasher *crypto.Hasher) (err error) {
if p.UserID == "" {
return zerrors.ThrowInvalidArgument(nil, "COMMAND-aS3Vz5t6BS", "Errors.IDMissing")
}
if p.EncodedPasswordHash != "" {
if !hasher.EncodingSupported(p.EncodedPasswordHash) {
return zerrors.ThrowInvalidArgument(nil, "COMMAND-oz74onzvqr", "Errors.User.Password.NotSupported")
}
}
if p.Password == "" && p.EncodedPasswordHash == "" {
return zerrors.ThrowInvalidArgument(nil, "COMMAND-3klek4sbns", "Errors.User.Password.Empty")
}
return nil
}
func (c *Commands) SetSchemaUserPassword(ctx context.Context, username *SetSchemaUserPassword) (*domain.ObjectDetails, error) {
if err := username.Validate(c.userPasswordHasher); err != nil {
return nil, err
}
existing, err := c.getPasswordExistsWithVerification(ctx, username.ResourceOwner, username.UserID)
if err != nil {
return nil, err
}
resourceOwner := existing.ResourceOwner
if existing.EncodedHash == "" {
existingUser, err := c.getSchemaUserExists(ctx, username.ResourceOwner, username.UserID)
if err != nil {
return nil, err
}
if !existingUser.Exists() {
return nil, zerrors.ThrowNotFound(nil, "TODO", "TODO")
}
resourceOwner = existingUser.ResourceOwner
}
// If password is provided, let's check if is compliant with the policy.
// If only a encodedPassword is passed, we can skip this.
if username.Password != "" {
if err = c.checkPasswordComplexity(ctx, username.Password, resourceOwner); err != nil {
return nil, err
}
}
encodedPassword := username.EncodedPasswordHash
if username.Password != "" {
encodedPassword, err = c.userPasswordHasher.Hash(username.Password)
if err = convertPasswapErr(err); err != nil {
return nil, err
}
}
events, err := c.eventstore.Push(ctx,
authenticator.NewPasswordCreatedEvent(ctx,
&authenticator.NewAggregate(username.UserID, resourceOwner).Aggregate,
existing.UserID,
encodedPassword,
username.ChangeRequired,
),
)
if err != nil {
return nil, err
}
return pushedEventsToObjectDetails(events), nil
}
func (c *Commands) DeleteSchemaUserPassword(ctx context.Context, resourceOwner, id string) (_ *domain.ObjectDetails, err error) {
existing, err := c.getPasswordExistsWithVerification(ctx, resourceOwner, id)
if err != nil {
return nil, err
}
if existing.EncodedHash == "" {
return nil, zerrors.ThrowNotFound(nil, "TODO", "TODO")
}
events, err := c.eventstore.Push(ctx,
authenticator.NewPasswordDeletedEvent(ctx,
&authenticator.NewAggregate(id, existing.ResourceOwner).Aggregate,
),
)
if err != nil {
return nil, err
}
return pushedEventsToObjectDetails(events), nil
}
func (c *Commands) getPasswordExistsWithVerification(ctx context.Context, resourceOwner, id string) (*PasswordV3WriteModel, error) {
if id == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-PoSU5BOZCi", "Errors.IDMissing")
}
writeModel := NewPasswordV3WriteModel(resourceOwner, id)
if err := c.eventstore.FilterToQueryReducer(ctx, writeModel); err != nil {
return nil, err
}
// TODO permission through old password and password code
if err := c.checkPermissionUpdateUser(ctx, writeModel.ResourceOwner, writeModel.UserID); err != nil {
return nil, err
}
return writeModel, nil
}

View File

@ -0,0 +1,53 @@
package command
import (
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/repository/user/authenticator"
"github.com/zitadel/zitadel/internal/repository/user/schemauser"
)
type PasswordV3WriteModel struct {
eventstore.WriteModel
UserID string
EncodedHash string
ChangeRequired bool
}
func NewPasswordV3WriteModel(resourceOwner, id string) *PasswordV3WriteModel {
return &PasswordV3WriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: id,
ResourceOwner: resourceOwner,
},
UserID: id,
}
}
func (wm *PasswordV3WriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *authenticator.PasswordCreatedEvent:
wm.UserID = e.UserID
wm.EncodedHash = e.EncodedHash
wm.ChangeRequired = e.ChangeRequired
case *authenticator.PasswordDeletedEvent:
wm.UserID = ""
wm.EncodedHash = ""
wm.ChangeRequired = false
}
}
return wm.WriteModel.Reduce()
}
func (wm *PasswordV3WriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
ResourceOwner(wm.ResourceOwner).
AddQuery().
AggregateTypes(schemauser.AggregateType).
AggregateIDs(wm.AggregateID).
EventTypes(
authenticator.PasswordCreatedType,
authenticator.PasswordDeletedType,
).Builder()
}

View File

@ -0,0 +1,416 @@
package command
import (
"context"
"errors"
"testing"
"github.com/stretchr/testify/assert"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/repository/org"
"github.com/zitadel/zitadel/internal/repository/user/authenticator"
"github.com/zitadel/zitadel/internal/zerrors"
)
func filterSchemaUserPasswordExisting() expect {
return expectFilter(
eventFromEventPusher(
authenticator.NewPasswordCreatedEvent(
context.Background(),
&authenticator.NewAggregate("user1", "org1").Aggregate,
"user1",
"encoded",
false,
),
),
)
}
func filterPasswordComplexityPolicyExisting() expect {
return expectFilter(
eventFromEventPusher(
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate,
1,
false,
false,
false,
false,
),
),
)
}
func TestCommands_SetSchemaUserPassword(t *testing.T) {
type fields struct {
eventstore func(t *testing.T) *eventstore.Eventstore
userPasswordHasher *crypto.Hasher
checkPermission domain.PermissionCheck
}
type args struct {
ctx context.Context
user *SetSchemaUserPassword
}
type res struct {
details *domain.ObjectDetails
err func(error) bool
}
tests := []struct {
name string
fields fields
args args
res res
}{
{
"no userID, error",
fields{
eventstore: expectEventstore(),
checkPermission: newMockPermissionCheckAllowed(),
},
args{
ctx: authz.NewMockContext("instanceID", "", ""),
user: &SetSchemaUserPassword{},
},
res{
err: func(err error) bool {
return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "COMMAND-aS3Vz5t6BS", "Errors.IDMissing"))
},
},
},
{
"no password, error",
fields{
eventstore: expectEventstore(),
checkPermission: newMockPermissionCheckAllowed(),
},
args{
ctx: authz.NewMockContext("instanceID", "", ""),
user: &SetSchemaUserPassword{
UserID: "user1",
},
},
res{
err: func(err error) bool {
return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "COMMAND-3klek4sbns", "Errors.User.Password.Empty"))
},
},
},
{
"user not existing, error",
fields{
eventstore: expectEventstore(
expectFilter(),
expectFilter(),
),
checkPermission: newMockPermissionCheckAllowed(),
},
args{
ctx: authz.NewMockContext("instanceID", "", ""),
user: &SetSchemaUserPassword{
UserID: "notexisting",
Password: "password",
},
},
res{
err: func(err error) bool {
return errors.Is(err, zerrors.ThrowNotFound(nil, "TODO", "TODO"))
},
},
},
{
"no permission, error",
fields{
eventstore: expectEventstore(
expectFilter(),
),
checkPermission: newMockPermissionCheckNotAllowed(),
},
args{
ctx: authz.NewMockContext("instanceID", "", ""),
user: &SetSchemaUserPassword{
UserID: "user1",
Password: "password",
},
},
res{
err: func(err error) bool {
return errors.Is(err, zerrors.ThrowPermissionDenied(nil, "AUTHZ-HKJD33", "Errors.PermissionDenied"))
},
},
},
{
"password added, ok",
fields{
eventstore: expectEventstore(
expectFilter(),
filterSchemaUserExisting(),
filterPasswordComplexityPolicyExisting(),
expectPush(
authenticator.NewPasswordCreatedEvent(
context.Background(),
&authenticator.NewAggregate("user1", "org1").Aggregate,
"user1",
"$plain$x$password",
false,
),
),
),
checkPermission: newMockPermissionCheckAllowed(),
userPasswordHasher: mockPasswordHasher("x"),
},
args{
ctx: authz.NewMockContext("instanceID", "", ""),
user: &SetSchemaUserPassword{
UserID: "user1",
Password: "password",
ChangeRequired: false,
},
},
res{
details: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
{
"password set, ok",
fields{
eventstore: expectEventstore(
filterSchemaUserPasswordExisting(),
filterPasswordComplexityPolicyExisting(),
expectPush(
authenticator.NewPasswordCreatedEvent(
context.Background(),
&authenticator.NewAggregate("user1", "org1").Aggregate,
"user1",
"$plain$x$password",
false,
),
),
),
checkPermission: newMockPermissionCheckAllowed(),
userPasswordHasher: mockPasswordHasher("x"),
},
args{
ctx: authz.NewMockContext("instanceID", "", ""),
user: &SetSchemaUserPassword{
UserID: "user1",
Password: "password",
ChangeRequired: false,
},
},
res{
details: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
{
"password set, changeRequired, ok",
fields{
eventstore: expectEventstore(
filterSchemaUserPasswordExisting(),
filterPasswordComplexityPolicyExisting(),
expectPush(
authenticator.NewPasswordCreatedEvent(
context.Background(),
&authenticator.NewAggregate("user1", "org1").Aggregate,
"user1",
"$plain$x$password",
true,
),
),
),
checkPermission: newMockPermissionCheckAllowed(),
userPasswordHasher: mockPasswordHasher("x"),
},
args{
ctx: authz.NewMockContext("instanceID", "", ""),
user: &SetSchemaUserPassword{
UserID: "user1",
Password: "password",
ChangeRequired: true,
},
},
res{
details: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &Commands{
eventstore: tt.fields.eventstore(t),
checkPermission: tt.fields.checkPermission,
userPasswordHasher: tt.fields.userPasswordHasher,
}
details, err := c.SetSchemaUserPassword(tt.args.ctx, tt.args.user)
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.details, details)
}
})
}
}
func TestCommands_DeleteSchemaUserPassword(t *testing.T) {
type fields struct {
eventstore func(t *testing.T) *eventstore.Eventstore
checkPermission domain.PermissionCheck
}
type args struct {
ctx context.Context
resourceOwner string
id string
}
type res struct {
details *domain.ObjectDetails
err func(error) bool
}
tests := []struct {
name string
fields fields
args args
res res
}{
{
"no ID, error",
fields{
eventstore: expectEventstore(),
checkPermission: newMockPermissionCheckAllowed(),
},
args{
ctx: authz.NewMockContext("instanceID", "", ""),
id: "",
},
res{
err: func(err error) bool {
return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "COMMAND-PoSU5BOZCi", "Errors.IDMissing"))
},
},
},
{
"password not existing, error",
fields{
eventstore: expectEventstore(
expectFilter(),
),
checkPermission: newMockPermissionCheckAllowed(),
},
args{
ctx: authz.NewMockContext("instanceID", "", ""),
id: "notexisting",
},
res{
err: func(err error) bool {
return errors.Is(err, zerrors.ThrowNotFound(nil, "TODO", "TODO"))
},
},
},
{
"password already removed, error",
fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
authenticator.NewPasswordCreatedEvent(
context.Background(),
&authenticator.NewAggregate("user1", "org1").Aggregate,
"id1",
"hash",
false,
),
),
eventFromEventPusher(
authenticator.NewPasswordDeletedEvent(
context.Background(),
&authenticator.NewAggregate("user1", "org1").Aggregate,
),
),
),
),
checkPermission: newMockPermissionCheckAllowed(),
},
args{
ctx: authz.NewMockContext("instanceID", "", ""),
id: "user1",
},
res{
err: func(err error) bool {
return errors.Is(err, zerrors.ThrowNotFound(nil, "TODO", "TODO"))
},
},
},
{
"no permission, error",
fields{
eventstore: expectEventstore(
filterSchemaUserPasswordExisting(),
),
checkPermission: newMockPermissionCheckNotAllowed(),
},
args{
ctx: authz.NewMockContext("instanceID", "", ""),
id: "user1",
},
res{
err: func(err error) bool {
return errors.Is(err, zerrors.ThrowPermissionDenied(nil, "AUTHZ-HKJD33", "Errors.PermissionDenied"))
},
},
},
{
"password removed, ok",
fields{
eventstore: expectEventstore(
filterSchemaUserPasswordExisting(),
expectPush(
authenticator.NewPasswordDeletedEvent(
context.Background(),
&authenticator.NewAggregate("user1", "org1").Aggregate,
),
),
),
checkPermission: newMockPermissionCheckAllowed(),
},
args{
ctx: authz.NewMockContext("instanceID", "", ""),
id: "user1",
},
res{
details: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &Commands{
eventstore: tt.fields.eventstore(t),
checkPermission: tt.fields.checkPermission,
}
details, err := c.DeleteSchemaUserPassword(tt.args.ctx, tt.args.resourceOwner, tt.args.id)
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.details, details)
}
})
}
}

View File

@ -75,6 +75,34 @@ func (c *Commands) getSchemaUsernameExistsWithPermission(ctx context.Context, re
return writeModel, nil
}
func existingSchemaUser(ctx context.Context, c *Commands, resourceOwner, userID string) (*UserV3WriteModel, error) {
if userID == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-aS3Vz5t6BS", "Errors.IDMissing")
}
existingUser, err := c.getSchemaUserExists(ctx, resourceOwner, userID)
if err != nil {
return nil, err
}
if !existingUser.Exists() {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-6T2xrOHxTx", "Errors.User.NotFound")
}
if err := c.checkPermissionUpdateUser(ctx, existingUser.ResourceOwner, existingUser.AggregateID); err != nil {
return nil, err
}
existingSchema, err := c.getSchemaWriteModelByID(ctx, "", existingUser.SchemaID)
if err != nil {
return nil, err
}
if !existingSchema.Exists() {
return nil, zerrors.ThrowNotFound(nil, "COMMAND-6T2xrOHxTx", "TODO")
}
//TODO possible authenticators check
return existingUser, nil
}
func existingSchemaUserWithPermission(ctx context.Context, c *Commands, resourceOwner, userID string) (*UserV3WriteModel, error) {
if userID == "" {
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-aS3Vz5t6BS", "Errors.IDMissing")

View File

@ -391,7 +391,7 @@ func TestCommands_DeleteUsername(t *testing.T) {
},
},
{
"username added, isOrgSpecific, ok",
"username removed, isOrgSpecific, ok",
fields{
eventstore: expectEventstore(
filterUsernameExisting(true),