mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-06 19:36:41 +00:00
feat: ResetPassword endpoint
This commit is contained in:
@@ -210,6 +210,7 @@ func TestCommands_CreateDebugEvents(t *testing.T) {
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "instance1",
|
||||
ID: "dgb1",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -243,6 +244,7 @@ func TestCommands_CreateDebugEvents(t *testing.T) {
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "instance1",
|
||||
ID: "dgb1",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -275,6 +277,7 @@ func TestCommands_CreateDebugEvents(t *testing.T) {
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "instance1",
|
||||
ID: "dgb1",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -324,6 +327,7 @@ func TestCommands_CreateDebugEvents(t *testing.T) {
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "instance1",
|
||||
ID: "dgb1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "instance1",
|
||||
ID: "instance1",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -93,6 +94,7 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "instance1",
|
||||
ID: "instance1",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -111,6 +113,7 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "instance1",
|
||||
ID: "instance1",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -129,6 +132,7 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "instance1",
|
||||
ID: "instance1",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -147,6 +151,7 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "instance1",
|
||||
ID: "instance1",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -165,6 +170,7 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "instance1",
|
||||
ID: "instance1",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -224,6 +230,7 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "instance1",
|
||||
ID: "instance1",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -275,6 +282,7 @@ func TestCommands_SetInstanceFeatures(t *testing.T) {
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "instance1",
|
||||
ID: "instance1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "SYSTEM",
|
||||
ID: "SYSTEM",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -79,6 +80,7 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "SYSTEM",
|
||||
ID: "SYSTEM",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -97,6 +99,7 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "SYSTEM",
|
||||
ID: "SYSTEM",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -115,6 +118,7 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "SYSTEM",
|
||||
ID: "SYSTEM",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -133,6 +137,7 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "SYSTEM",
|
||||
ID: "SYSTEM",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -192,6 +197,7 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "SYSTEM",
|
||||
ID: "SYSTEM",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -253,6 +259,7 @@ func TestCommands_SetSystemFeatures(t *testing.T) {
|
||||
}},
|
||||
want: &domain.ObjectDetails{
|
||||
ResourceOwner: "SYSTEM",
|
||||
ID: "SYSTEM",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ func (c *Commands) SetSchemaUserPassword(ctx context.Context, user *SetSchemaUse
|
||||
return nil, err
|
||||
}
|
||||
resourceOwner := existing.ResourceOwner
|
||||
// when no password was set yet
|
||||
if existing.EncodedHash == "" {
|
||||
existingUser, err := c.getSchemaUserExists(ctx, user.ResourceOwner, user.UserID)
|
||||
if err != nil {
|
||||
@@ -79,7 +80,7 @@ func (c *Commands) SetSchemaUserPassword(ctx context.Context, user *SetSchemaUse
|
||||
}
|
||||
|
||||
encodedPassword := schemaUser.EncodedPasswordHash
|
||||
if user.Password != "" {
|
||||
if encodedPassword == "" && user.Password != "" {
|
||||
encodedPassword, err = c.userPasswordHasher.Hash(user.Password)
|
||||
if err = convertPasswapErr(err); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -5,9 +5,7 @@ import (
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/repository/user"
|
||||
"github.com/zitadel/zitadel/internal/repository/user/authenticator"
|
||||
"github.com/zitadel/zitadel/internal/repository/user/schemauser"
|
||||
)
|
||||
|
||||
type PasswordV3WriteModel struct {
|
||||
@@ -45,7 +43,7 @@ func (wm *PasswordV3WriteModel) Reduce() error {
|
||||
wm.EncodedHash = ""
|
||||
wm.ChangeRequired = false
|
||||
wm.Code = nil
|
||||
case *user.HumanPasswordCodeAddedEvent:
|
||||
case *authenticator.PasswordCodeAddedEvent:
|
||||
wm.Code = e.Code
|
||||
wm.CodeCreationDate = e.CreationDate()
|
||||
wm.CodeExpiry = e.Expiry
|
||||
@@ -58,7 +56,7 @@ func (wm *PasswordV3WriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
ResourceOwner(wm.ResourceOwner).
|
||||
AddQuery().
|
||||
AggregateTypes(schemauser.AggregateType).
|
||||
AggregateTypes(authenticator.AggregateType).
|
||||
AggregateIDs(wm.AggregateID).
|
||||
EventTypes(
|
||||
authenticator.PasswordCreatedType,
|
||||
|
||||
@@ -4,8 +4,11 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/zitadel/passwap"
|
||||
"go.uber.org/mock/gomock"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
@@ -50,6 +53,7 @@ func TestCommands_SetSchemaUserPassword(t *testing.T) {
|
||||
eventstore func(t *testing.T) *eventstore.Eventstore
|
||||
userPasswordHasher *crypto.Hasher
|
||||
checkPermission domain.PermissionCheck
|
||||
codeAlg crypto.EncryptionAlgorithm
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -117,7 +121,7 @@ func TestCommands_SetSchemaUserPassword(t *testing.T) {
|
||||
},
|
||||
res{
|
||||
err: func(err error) bool {
|
||||
return errors.Is(err, zerrors.ThrowNotFound(nil, "TODO", "TODO"))
|
||||
return errors.Is(err, zerrors.ThrowNotFound(nil, "COMMAND-TODO", "Errors.User.Password.NotFound"))
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -176,6 +180,39 @@ func TestCommands_SetSchemaUserPassword(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"password set, complexity failed",
|
||||
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, ok",
|
||||
fields{
|
||||
@@ -242,6 +279,40 @@ func TestCommands_SetSchemaUserPassword(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"password set, encoded, ok",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
filterSchemaUserPasswordExisting(),
|
||||
filterPasswordComplexityPolicyExisting(),
|
||||
expectPush(
|
||||
authenticator.NewPasswordCreatedEvent(
|
||||
context.Background(),
|
||||
&authenticator.NewAggregate("user1", "org1").Aggregate,
|
||||
"user1",
|
||||
"$plain$x$password2",
|
||||
false,
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
user: &SetSchemaUserPassword{
|
||||
UserID: "user1",
|
||||
Password: "passwordnotused",
|
||||
EncodedPasswordHash: "$plain$x$password2",
|
||||
ChangeRequired: false,
|
||||
},
|
||||
},
|
||||
res{
|
||||
details: &domain.ObjectDetails{
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"password set, current password, ok",
|
||||
fields{
|
||||
@@ -258,7 +329,6 @@ func TestCommands_SetSchemaUserPassword(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckNotAllowed(),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
},
|
||||
args{
|
||||
@@ -277,7 +347,7 @@ func TestCommands_SetSchemaUserPassword(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
"password set, code, ok",
|
||||
"password set, current password, ok",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
filterSchemaUserPasswordExisting(),
|
||||
@@ -292,15 +362,15 @@ func TestCommands_SetSchemaUserPassword(t *testing.T) {
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckNotAllowed(),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
user: &SetSchemaUserPassword{
|
||||
UserID: "user1",
|
||||
Password: "password2",
|
||||
ChangeRequired: false,
|
||||
UserID: "user1",
|
||||
Password: "password2",
|
||||
CurrentPassword: "password",
|
||||
ChangeRequired: false,
|
||||
},
|
||||
},
|
||||
res{
|
||||
@@ -308,6 +378,160 @@ func TestCommands_SetSchemaUserPassword(t *testing.T) {
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
},
|
||||
}, {
|
||||
"password set, current password, failed",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
filterSchemaUserPasswordExisting(),
|
||||
),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
user: &SetSchemaUserPassword{
|
||||
UserID: "user1",
|
||||
Password: "password2",
|
||||
CurrentPassword: "notreally",
|
||||
ChangeRequired: false,
|
||||
},
|
||||
},
|
||||
res{
|
||||
err: func(err error) bool {
|
||||
return errors.Is(err, zerrors.ThrowInvalidArgument(passwap.ErrPasswordMismatch, "COMMAND-3M0fs", "Errors.User.Password.Invalid"))
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"password set, code, ok",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
authenticator.NewPasswordCreatedEvent(
|
||||
context.Background(),
|
||||
&authenticator.NewAggregate("user1", "org1").Aggregate,
|
||||
"user1",
|
||||
"$plain$x$password",
|
||||
false,
|
||||
),
|
||||
),
|
||||
eventFromEventPusherWithCreationDateNow(
|
||||
authenticator.NewPasswordCodeAddedEvent(context.Background(),
|
||||
&authenticator.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("code"),
|
||||
},
|
||||
time.Hour*1,
|
||||
domain.NotificationTypeEmail,
|
||||
"",
|
||||
false,
|
||||
),
|
||||
),
|
||||
),
|
||||
filterPasswordComplexityPolicyExisting(),
|
||||
expectPush(
|
||||
authenticator.NewPasswordCreatedEvent(
|
||||
context.Background(),
|
||||
&authenticator.NewAggregate("user1", "org1").Aggregate,
|
||||
"user1",
|
||||
"$plain$x$password2",
|
||||
false,
|
||||
),
|
||||
),
|
||||
),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
user: &SetSchemaUserPassword{
|
||||
UserID: "user1",
|
||||
Password: "password2",
|
||||
VerificationCode: "code",
|
||||
ChangeRequired: false,
|
||||
},
|
||||
},
|
||||
res{
|
||||
details: &domain.ObjectDetails{
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"password set, code, failed",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
authenticator.NewPasswordCreatedEvent(
|
||||
context.Background(),
|
||||
&authenticator.NewAggregate("user1", "org1").Aggregate,
|
||||
"user1",
|
||||
"$plain$x$password",
|
||||
false,
|
||||
),
|
||||
),
|
||||
eventFromEventPusherWithCreationDateNow(
|
||||
authenticator.NewPasswordCodeAddedEvent(context.Background(),
|
||||
&authenticator.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("code"),
|
||||
},
|
||||
time.Hour*1,
|
||||
domain.NotificationTypeEmail,
|
||||
"",
|
||||
false,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
user: &SetSchemaUserPassword{
|
||||
UserID: "user1",
|
||||
Password: "password2",
|
||||
VerificationCode: "notreally",
|
||||
ChangeRequired: false,
|
||||
},
|
||||
},
|
||||
res{
|
||||
err: func(err error) bool {
|
||||
return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "CODE-woT0xc", "Errors.User.Code.Invalid"))
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"password set, code, no code",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
filterSchemaUserPasswordExisting(),
|
||||
),
|
||||
userPasswordHasher: mockPasswordHasher("x"),
|
||||
codeAlg: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
user: &SetSchemaUserPassword{
|
||||
UserID: "user1",
|
||||
Password: "password2",
|
||||
VerificationCode: "notreally",
|
||||
ChangeRequired: false,
|
||||
},
|
||||
},
|
||||
res{
|
||||
err: func(err error) bool {
|
||||
return errors.Is(err, zerrors.ThrowPreconditionFailed(nil, "COMMAND-TODO", "Errors.User.Code.NotFound"))
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -316,6 +540,7 @@ func TestCommands_SetSchemaUserPassword(t *testing.T) {
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
checkPermission: tt.fields.checkPermission,
|
||||
userPasswordHasher: tt.fields.userPasswordHasher,
|
||||
userEncryption: tt.fields.codeAlg,
|
||||
}
|
||||
details, err := c.SetSchemaUserPassword(tt.args.ctx, tt.args.user)
|
||||
if tt.res.err == nil {
|
||||
@@ -331,6 +556,224 @@ func TestCommands_SetSchemaUserPassword(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommands_RequestSchemaUserPasswordReset(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore func(t *testing.T) *eventstore.Eventstore
|
||||
checkPermission domain.PermissionCheck
|
||||
newCode encrypedCodeFunc
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
user *RequestSchemaUserPasswordReset
|
||||
}
|
||||
type res struct {
|
||||
details *domain.ObjectDetails
|
||||
plainCode string
|
||||
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: &RequestSchemaUserPasswordReset{},
|
||||
},
|
||||
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", "", ""),
|
||||
user: &RequestSchemaUserPasswordReset{
|
||||
UserID: "notexisting",
|
||||
},
|
||||
},
|
||||
res{
|
||||
err: func(err error) bool {
|
||||
return errors.Is(err, zerrors.ThrowNotFound(nil, "COMMAND-TODO", "Errors.User.Password.NotFound"))
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"no permission, error",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
expectFilter(),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckNotAllowed(),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
user: &RequestSchemaUserPasswordReset{
|
||||
UserID: "user1",
|
||||
},
|
||||
},
|
||||
res{
|
||||
err: func(err error) bool {
|
||||
return errors.Is(err, zerrors.ThrowPermissionDenied(nil, "AUTHZ-HKJD33", "Errors.PermissionDenied"))
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"password reset, email, ok",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
filterSchemaUserPasswordExisting(),
|
||||
expectPush(
|
||||
authenticator.NewPasswordCodeAddedEvent(
|
||||
context.Background(),
|
||||
&authenticator.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("code"),
|
||||
},
|
||||
10*time.Minute,
|
||||
domain.NotificationTypeEmail,
|
||||
"https://example.com/password/changey?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}",
|
||||
false,
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
newCode: mockEncryptedCode("code", 10*time.Minute),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
user: &RequestSchemaUserPasswordReset{
|
||||
UserID: "user1",
|
||||
NotificationType: domain.NotificationTypeEmail,
|
||||
URLTemplate: "https://example.com/password/changey?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}",
|
||||
},
|
||||
},
|
||||
res{
|
||||
details: &domain.ObjectDetails{
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"password reset, sms, ok",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
filterSchemaUserPasswordExisting(),
|
||||
expectPush(
|
||||
authenticator.NewPasswordCodeAddedEvent(
|
||||
context.Background(),
|
||||
&authenticator.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("code"),
|
||||
},
|
||||
10*time.Minute,
|
||||
domain.NotificationTypeSms,
|
||||
"",
|
||||
false,
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
newCode: mockEncryptedCode("code", 10*time.Minute),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
user: &RequestSchemaUserPasswordReset{
|
||||
UserID: "user1",
|
||||
NotificationType: domain.NotificationTypeSms,
|
||||
},
|
||||
},
|
||||
res{
|
||||
details: &domain.ObjectDetails{
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"password reset, returned, ok",
|
||||
fields{
|
||||
eventstore: expectEventstore(
|
||||
filterSchemaUserPasswordExisting(),
|
||||
expectPush(
|
||||
authenticator.NewPasswordCodeAddedEvent(
|
||||
context.Background(),
|
||||
&authenticator.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("code"),
|
||||
},
|
||||
10*time.Minute,
|
||||
domain.NotificationTypeEmail,
|
||||
"",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
newCode: mockEncryptedCode("code", 10*time.Minute),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
user: &RequestSchemaUserPasswordReset{
|
||||
UserID: "user1",
|
||||
ReturnCode: true,
|
||||
},
|
||||
},
|
||||
res{
|
||||
details: &domain.ObjectDetails{
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
plainCode: "code",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
checkPermission: tt.fields.checkPermission,
|
||||
newEncryptedCode: tt.fields.newCode,
|
||||
}
|
||||
details, err := c.RequestSchemaUserPasswordReset(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)
|
||||
}
|
||||
if tt.res.plainCode != "" {
|
||||
assert.Equal(t, tt.res.plainCode, tt.args.user.PlainCode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommands_DeleteSchemaUserPassword(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore func(t *testing.T) *eventstore.Eventstore
|
||||
|
||||
@@ -39,8 +39,8 @@ func (c *Commands) AddUsername(ctx context.Context, username *AddUsername) (*dom
|
||||
return pushedEventsToObjectDetails(events), nil
|
||||
}
|
||||
|
||||
func (c *Commands) DeleteUsername(ctx context.Context, resourceOwner, id string) (_ *domain.ObjectDetails, err error) {
|
||||
existing, err := c.getSchemaUsernameExistsWithPermission(ctx, resourceOwner, id)
|
||||
func (c *Commands) DeleteUsername(ctx context.Context, resourceOwner, userID, id string) (_ *domain.ObjectDetails, err error) {
|
||||
existing, err := c.getSchemaUsernameExistsWithPermission(ctx, resourceOwner, userID, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -57,11 +57,14 @@ func (c *Commands) DeleteUsername(ctx context.Context, resourceOwner, id string)
|
||||
return pushedEventsToObjectDetails(events), nil
|
||||
}
|
||||
|
||||
func (c *Commands) getSchemaUsernameExistsWithPermission(ctx context.Context, resourceOwner, id string) (*UsernameV3WriteModel, error) {
|
||||
func (c *Commands) getSchemaUsernameExistsWithPermission(ctx context.Context, resourceOwner, userID, id string) (*UsernameV3WriteModel, error) {
|
||||
if userID == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-J6ybG5WZiy", "Errors.IDMissing")
|
||||
}
|
||||
if id == "" {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-PoSU5BOZCi", "Errors.IDMissing")
|
||||
}
|
||||
writeModel := NewUsernameV3WriteModel(resourceOwner, id)
|
||||
writeModel := NewUsernameV3WriteModel(resourceOwner, userID, id)
|
||||
if err := c.eventstore.FilterToQueryReducer(ctx, writeModel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ 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 UsernameV3WriteModel struct {
|
||||
@@ -13,12 +12,13 @@ type UsernameV3WriteModel struct {
|
||||
IsOrgSpecific bool
|
||||
}
|
||||
|
||||
func NewUsernameV3WriteModel(resourceOwner, userID string) *UsernameV3WriteModel {
|
||||
func NewUsernameV3WriteModel(resourceOwner, userID, id string) *UsernameV3WriteModel {
|
||||
return &UsernameV3WriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: userID,
|
||||
AggregateID: id,
|
||||
ResourceOwner: resourceOwner,
|
||||
},
|
||||
UserID: userID,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,9 @@ func (wm *UsernameV3WriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *authenticator.UsernameCreatedEvent:
|
||||
if e.UserID != wm.UserID {
|
||||
continue
|
||||
}
|
||||
wm.UserID = e.UserID
|
||||
wm.Username = e.Username
|
||||
wm.IsOrgSpecific = e.IsOrgSpecific
|
||||
@@ -42,7 +45,7 @@ func (wm *UsernameV3WriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
ResourceOwner(wm.ResourceOwner).
|
||||
AddQuery().
|
||||
AggregateTypes(schemauser.AggregateType).
|
||||
AggregateTypes(authenticator.AggregateType).
|
||||
AggregateIDs(wm.AggregateID).
|
||||
EventTypes(
|
||||
authenticator.UsernameCreatedType,
|
||||
|
||||
@@ -63,7 +63,7 @@ func filterUsernameExisting(isOrgSpecifc bool) expect {
|
||||
authenticator.NewUsernameCreatedEvent(
|
||||
context.Background(),
|
||||
&authenticator.NewAggregate("username1", "org1").Aggregate,
|
||||
"id1",
|
||||
"user1",
|
||||
isOrgSpecifc,
|
||||
"username",
|
||||
),
|
||||
@@ -264,6 +264,7 @@ func TestCommands_DeleteUsername(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
resourceOwner string
|
||||
userID string
|
||||
id string
|
||||
}
|
||||
type res struct {
|
||||
@@ -277,7 +278,7 @@ func TestCommands_DeleteUsername(t *testing.T) {
|
||||
res res
|
||||
}{
|
||||
{
|
||||
"no ID, error",
|
||||
"no userID, error",
|
||||
fields{
|
||||
eventstore: expectEventstore(),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
@@ -286,6 +287,23 @@ func TestCommands_DeleteUsername(t *testing.T) {
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
id: "",
|
||||
},
|
||||
res{
|
||||
err: func(err error) bool {
|
||||
return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "COMMAND-J6ybG5WZiy", "Errors.IDMissing"))
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"no ID, error",
|
||||
fields{
|
||||
eventstore: expectEventstore(),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userID: "user1",
|
||||
id: "",
|
||||
},
|
||||
res{
|
||||
err: func(err error) bool {
|
||||
return errors.Is(err, zerrors.ThrowInvalidArgument(nil, "COMMAND-PoSU5BOZCi", "Errors.IDMissing"))
|
||||
@@ -301,8 +319,9 @@ func TestCommands_DeleteUsername(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
id: "notexisting",
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userID: "user1",
|
||||
id: "notexisting",
|
||||
},
|
||||
res{
|
||||
err: func(err error) bool {
|
||||
@@ -319,7 +338,7 @@ func TestCommands_DeleteUsername(t *testing.T) {
|
||||
authenticator.NewUsernameCreatedEvent(
|
||||
context.Background(),
|
||||
&authenticator.NewAggregate("username1", "org1").Aggregate,
|
||||
"id1",
|
||||
"user1",
|
||||
true,
|
||||
"username",
|
||||
),
|
||||
@@ -337,8 +356,9 @@ func TestCommands_DeleteUsername(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
id: "notexisting",
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userID: "user1",
|
||||
id: "notexisting",
|
||||
},
|
||||
res{
|
||||
err: func(err error) bool {
|
||||
@@ -355,8 +375,9 @@ func TestCommands_DeleteUsername(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckNotAllowed(),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
id: "username1",
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userID: "user1",
|
||||
id: "username1",
|
||||
},
|
||||
res{
|
||||
err: func(err error) bool {
|
||||
@@ -381,8 +402,9 @@ func TestCommands_DeleteUsername(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
id: "username1",
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userID: "user1",
|
||||
id: "username1",
|
||||
},
|
||||
res{
|
||||
details: &domain.ObjectDetails{
|
||||
@@ -407,8 +429,9 @@ func TestCommands_DeleteUsername(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
},
|
||||
args{
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
id: "username1",
|
||||
ctx: authz.NewMockContext("instanceID", "", ""),
|
||||
userID: "user1",
|
||||
id: "username1",
|
||||
},
|
||||
res{
|
||||
details: &domain.ObjectDetails{
|
||||
@@ -423,7 +446,7 @@ func TestCommands_DeleteUsername(t *testing.T) {
|
||||
eventstore: tt.fields.eventstore(t),
|
||||
checkPermission: tt.fields.checkPermission,
|
||||
}
|
||||
details, err := c.DeleteUsername(tt.args.ctx, tt.args.resourceOwner, tt.args.id)
|
||||
details, err := c.DeleteUsername(tt.args.ctx, tt.args.resourceOwner, tt.args.userID, tt.args.id)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user