mirror of
https://github.com/zitadel/zitadel.git
synced 2025-06-11 01:48:32 +00:00
feat: ResetPassword endpoint
This commit is contained in:
parent
04f5ed8d1c
commit
50e0c758de
@ -62,7 +62,7 @@ func TestServer_SetPassword(t *testing.T) {
|
||||
},
|
||||
NewPassword: &user.SetPassword{
|
||||
Type: &user.SetPassword_Password{
|
||||
Password: gofakeit.Password(true, true, true, true, false, 12),
|
||||
Password: fakePassword(),
|
||||
},
|
||||
ChangeRequired: false,
|
||||
},
|
||||
@ -85,7 +85,7 @@ func TestServer_SetPassword(t *testing.T) {
|
||||
},
|
||||
NewPassword: &user.SetPassword{
|
||||
Type: &user.SetPassword_Password{
|
||||
Password: gofakeit.Password(true, true, true, true, false, 12),
|
||||
Password: fakePassword(),
|
||||
},
|
||||
ChangeRequired: false,
|
||||
},
|
||||
@ -131,7 +131,7 @@ func TestServer_SetPassword(t *testing.T) {
|
||||
},
|
||||
NewPassword: &user.SetPassword{
|
||||
Type: &user.SetPassword_Password{
|
||||
Password: gofakeit.Password(true, true, true, true, false, 12),
|
||||
Password: fakePassword(),
|
||||
},
|
||||
ChangeRequired: false,
|
||||
},
|
||||
@ -139,7 +139,7 @@ func TestServer_SetPassword(t *testing.T) {
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "username add, user not existing",
|
||||
name: "password set, user not existing",
|
||||
ctx: isolatedIAMOwnerCTX,
|
||||
|
||||
req: &user.SetPasswordRequest{
|
||||
@ -151,7 +151,7 @@ func TestServer_SetPassword(t *testing.T) {
|
||||
Id: "not existing",
|
||||
NewPassword: &user.SetPassword{
|
||||
Type: &user.SetPassword_Password{
|
||||
Password: gofakeit.Password(true, true, true, true, false, 12),
|
||||
Password: fakePassword(),
|
||||
},
|
||||
ChangeRequired: false,
|
||||
},
|
||||
@ -174,7 +174,7 @@ func TestServer_SetPassword(t *testing.T) {
|
||||
},
|
||||
NewPassword: &user.SetPassword{
|
||||
Type: &user.SetPassword_Password{
|
||||
Password: gofakeit.Password(true, true, true, true, false, 12),
|
||||
Password: fakePassword(),
|
||||
},
|
||||
ChangeRequired: false,
|
||||
},
|
||||
@ -200,7 +200,7 @@ func TestServer_SetPassword(t *testing.T) {
|
||||
req: &user.SetPasswordRequest{
|
||||
NewPassword: &user.SetPassword{
|
||||
Type: &user.SetPassword_Password{
|
||||
Password: gofakeit.Password(true, true, true, true, false, 12),
|
||||
Password: fakePassword(),
|
||||
},
|
||||
ChangeRequired: false,
|
||||
},
|
||||
@ -215,6 +215,174 @@ func TestServer_SetPassword(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "password set, code, 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()
|
||||
instance.SetAuthenticatorPassword(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), req.Id, fakePassword())
|
||||
passwordResp := instance.RequestAuthenticatorPasswordReset(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), req.Id)
|
||||
req.NewPassword.Verification = &user.SetPassword_VerificationCode{VerificationCode: passwordResp.GetVerificationCode()}
|
||||
return nil
|
||||
},
|
||||
req: &user.SetPasswordRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
NewPassword: &user.SetPassword{
|
||||
Type: &user.SetPassword_Password{
|
||||
Password: fakePassword(),
|
||||
},
|
||||
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, code, failed",
|
||||
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()
|
||||
instance.SetAuthenticatorPassword(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), req.Id, fakePassword())
|
||||
instance.RequestAuthenticatorPasswordReset(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), req.Id)
|
||||
return nil
|
||||
},
|
||||
req: &user.SetPasswordRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
NewPassword: &user.SetPassword{
|
||||
Type: &user.SetPassword_Password{
|
||||
Password: fakePassword(),
|
||||
},
|
||||
ChangeRequired: false,
|
||||
Verification: &user.SetPassword_VerificationCode{VerificationCode: "notreally"},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "password set, code, no set",
|
||||
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()
|
||||
instance.SetAuthenticatorPassword(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), req.Id, fakePassword())
|
||||
return nil
|
||||
},
|
||||
req: &user.SetPasswordRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
NewPassword: &user.SetPassword{
|
||||
Type: &user.SetPassword_Password{
|
||||
Password: fakePassword(),
|
||||
},
|
||||
ChangeRequired: false,
|
||||
Verification: &user.SetPassword_VerificationCode{VerificationCode: "notreally"},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "password set, current password, 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()
|
||||
password := fakePassword()
|
||||
instance.SetAuthenticatorPassword(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), req.Id, password)
|
||||
req.NewPassword.Verification = &user.SetPassword_CurrentPassword{CurrentPassword: password}
|
||||
return nil
|
||||
},
|
||||
req: &user.SetPasswordRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
NewPassword: &user.SetPassword{
|
||||
Type: &user.SetPassword_Password{
|
||||
Password: fakePassword(),
|
||||
},
|
||||
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, current password, failed",
|
||||
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()
|
||||
instance.SetAuthenticatorPassword(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), req.Id, fakePassword())
|
||||
return nil
|
||||
},
|
||||
req: &user.SetPasswordRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
NewPassword: &user.SetPassword{
|
||||
Type: &user.SetPassword_Password{
|
||||
Password: fakePassword(),
|
||||
},
|
||||
ChangeRequired: false,
|
||||
Verification: &user.SetPassword_CurrentPassword{CurrentPassword: fakePassword()},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "password set, current password, not set",
|
||||
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: fakePassword(),
|
||||
},
|
||||
ChangeRequired: false,
|
||||
Verification: &user.SetPassword_CurrentPassword{CurrentPassword: fakePassword()},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@ -232,3 +400,428 @@ func TestServer_SetPassword(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func fakePassword() string {
|
||||
return gofakeit.Password(true, true, true, true, false, 5) + "Password1!"
|
||||
}
|
||||
|
||||
func TestServer_RequestPasswordReset(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
|
||||
code bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
ctx context.Context
|
||||
dep func(req *user.RequestPasswordResetRequest) error
|
||||
req *user.RequestPasswordResetRequest
|
||||
res res
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "password reset, no context",
|
||||
ctx: context.Background(),
|
||||
dep: func(req *user.RequestPasswordResetRequest) error {
|
||||
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
|
||||
req.Id = userResp.GetDetails().GetId()
|
||||
instance.SetAuthenticatorPassword(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), req.Id, fakePassword())
|
||||
return nil
|
||||
},
|
||||
req: &user.RequestPasswordResetRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
Medium: &user.RequestPasswordResetRequest_SendEmail{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "password reset, no permission",
|
||||
ctx: instance.WithAuthorization(CTX, integration.UserTypeLogin),
|
||||
dep: func(req *user.RequestPasswordResetRequest) error {
|
||||
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
|
||||
req.Id = userResp.GetDetails().GetId()
|
||||
instance.SetAuthenticatorPassword(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), req.Id, fakePassword())
|
||||
return nil
|
||||
},
|
||||
req: &user.RequestPasswordResetRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
Medium: &user.RequestPasswordResetRequest_SendEmail{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "password reset, no password set",
|
||||
ctx: isolatedIAMOwnerCTX,
|
||||
dep: func(req *user.RequestPasswordResetRequest) error {
|
||||
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
|
||||
req.Id = userResp.GetDetails().GetId()
|
||||
return nil
|
||||
},
|
||||
req: &user.RequestPasswordResetRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
Medium: &user.RequestPasswordResetRequest_SendEmail{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "password reset, user not existing in org",
|
||||
ctx: isolatedIAMOwnerCTX,
|
||||
dep: func(req *user.RequestPasswordResetRequest) error {
|
||||
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
|
||||
req.Id = userResp.GetDetails().GetId()
|
||||
instance.SetAuthenticatorPassword(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), req.Id, fakePassword())
|
||||
return nil
|
||||
},
|
||||
req: &user.RequestPasswordResetRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: "notexisting",
|
||||
},
|
||||
},
|
||||
Medium: &user.RequestPasswordResetRequest_SendEmail{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "password reset, user not existing",
|
||||
ctx: isolatedIAMOwnerCTX,
|
||||
req: &user.RequestPasswordResetRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: "notexisting",
|
||||
},
|
||||
},
|
||||
Id: "not existing",
|
||||
Medium: &user.RequestPasswordResetRequest_SendEmail{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "password reset, email, ok",
|
||||
ctx: isolatedIAMOwnerCTX,
|
||||
dep: func(req *user.RequestPasswordResetRequest) error {
|
||||
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
|
||||
req.Id = userResp.GetDetails().GetId()
|
||||
instance.SetAuthenticatorPassword(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), req.Id, fakePassword())
|
||||
return nil
|
||||
},
|
||||
req: &user.RequestPasswordResetRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
Medium: &user.RequestPasswordResetRequest_SendEmail{},
|
||||
},
|
||||
res: res{
|
||||
want: &resource_object.Details{
|
||||
Changed: timestamppb.Now(),
|
||||
Owner: &object.Owner{
|
||||
Type: object.OwnerType_OWNER_TYPE_ORG,
|
||||
Id: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "password reset, no org, ok",
|
||||
ctx: isolatedIAMOwnerCTX,
|
||||
dep: func(req *user.RequestPasswordResetRequest) error {
|
||||
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
|
||||
req.Id = userResp.GetDetails().GetId()
|
||||
instance.SetAuthenticatorPassword(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), req.Id, fakePassword())
|
||||
return nil
|
||||
},
|
||||
req: &user.RequestPasswordResetRequest{
|
||||
Medium: &user.RequestPasswordResetRequest_SendEmail{},
|
||||
},
|
||||
res: res{
|
||||
want: &resource_object.Details{
|
||||
Changed: timestamppb.Now(),
|
||||
Owner: &object.Owner{
|
||||
Type: object.OwnerType_OWNER_TYPE_ORG,
|
||||
Id: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "password reset, phone, ok",
|
||||
ctx: isolatedIAMOwnerCTX,
|
||||
dep: func(req *user.RequestPasswordResetRequest) error {
|
||||
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
|
||||
req.Id = userResp.GetDetails().GetId()
|
||||
instance.SetAuthenticatorPassword(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), req.Id, fakePassword())
|
||||
return nil
|
||||
},
|
||||
req: &user.RequestPasswordResetRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
Medium: &user.RequestPasswordResetRequest_SendSms{},
|
||||
},
|
||||
res: res{
|
||||
want: &resource_object.Details{
|
||||
Changed: timestamppb.Now(),
|
||||
Owner: &object.Owner{
|
||||
Type: object.OwnerType_OWNER_TYPE_ORG,
|
||||
Id: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "password reset, code, ok",
|
||||
ctx: isolatedIAMOwnerCTX,
|
||||
dep: func(req *user.RequestPasswordResetRequest) error {
|
||||
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
|
||||
req.Id = userResp.GetDetails().GetId()
|
||||
instance.SetAuthenticatorPassword(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), req.Id, fakePassword())
|
||||
return nil
|
||||
},
|
||||
req: &user.RequestPasswordResetRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
Medium: &user.RequestPasswordResetRequest_ReturnCode{},
|
||||
},
|
||||
res: res{
|
||||
want: &resource_object.Details{
|
||||
Changed: timestamppb.Now(),
|
||||
Owner: &object.Owner{
|
||||
Type: object.OwnerType_OWNER_TYPE_ORG,
|
||||
Id: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
code: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
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.RequestPasswordReset(tt.ctx, tt.req)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
integration.AssertResourceDetails(t, tt.res.want, got.Details)
|
||||
if tt.res.code {
|
||||
require.NotEmpty(t, got.VerificationCode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_RemovePassword(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.RemovePasswordRequest) error
|
||||
req *user.RemovePasswordRequest
|
||||
res res
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "password remove, no context",
|
||||
ctx: context.Background(),
|
||||
dep: func(req *user.RemovePasswordRequest) error {
|
||||
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
|
||||
req.Id = userResp.GetDetails().GetId()
|
||||
instance.SetAuthenticatorPassword(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), req.Id, fakePassword())
|
||||
return nil
|
||||
},
|
||||
req: &user.RemovePasswordRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "password remove, no permission",
|
||||
ctx: instance.WithAuthorization(CTX, integration.UserTypeLogin),
|
||||
dep: func(req *user.RemovePasswordRequest) error {
|
||||
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
|
||||
req.Id = userResp.GetDetails().GetId()
|
||||
instance.SetAuthenticatorPassword(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), req.Id, fakePassword())
|
||||
return nil
|
||||
},
|
||||
req: &user.RemovePasswordRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "password remove, no password set",
|
||||
ctx: isolatedIAMOwnerCTX,
|
||||
dep: func(req *user.RemovePasswordRequest) error {
|
||||
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
|
||||
req.Id = userResp.GetDetails().GetId()
|
||||
return nil
|
||||
},
|
||||
req: &user.RemovePasswordRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "password remove, user not existing in org",
|
||||
ctx: isolatedIAMOwnerCTX,
|
||||
dep: func(req *user.RemovePasswordRequest) error {
|
||||
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
|
||||
req.Id = userResp.GetDetails().GetId()
|
||||
instance.SetAuthenticatorPassword(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), req.Id, fakePassword())
|
||||
return nil
|
||||
},
|
||||
req: &user.RemovePasswordRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: "notexisting",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "password remove, user not existing",
|
||||
ctx: isolatedIAMOwnerCTX,
|
||||
req: &user.RemovePasswordRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: "notexisting",
|
||||
},
|
||||
},
|
||||
Id: "not existing",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "password remove, ok",
|
||||
ctx: isolatedIAMOwnerCTX,
|
||||
dep: func(req *user.RemovePasswordRequest) error {
|
||||
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
|
||||
req.Id = userResp.GetDetails().GetId()
|
||||
instance.SetAuthenticatorPassword(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), req.Id, fakePassword())
|
||||
return nil
|
||||
},
|
||||
req: &user.RemovePasswordRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &resource_object.Details{
|
||||
Changed: timestamppb.Now(),
|
||||
Owner: &object.Owner{
|
||||
Type: object.OwnerType_OWNER_TYPE_ORG,
|
||||
Id: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "password remove, no org, ok",
|
||||
ctx: isolatedIAMOwnerCTX,
|
||||
dep: func(req *user.RemovePasswordRequest) error {
|
||||
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
|
||||
req.Id = userResp.GetDetails().GetId()
|
||||
instance.SetAuthenticatorPassword(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), req.Id, fakePassword())
|
||||
return nil
|
||||
},
|
||||
req: &user.RemovePasswordRequest{},
|
||||
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.RemovePassword(tt.ctx, tt.req)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
integration.AssertResourceDetails(t, tt.res.want, got.Details)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/brianvoe/gofakeit/v6"
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/zitadel/logging"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
@ -212,16 +213,16 @@ func TestServer_CreateUser(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := instance.Client.UserV3Alpha.CreateUser(tt.ctx, tt.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
integration.AssertResourceDetails(t, tt.res.want, got.Details)
|
||||
if tt.res.returnCodeEmail {
|
||||
require.NotNil(t, got.EmailCode)
|
||||
assert.NotNil(t, got.EmailCode)
|
||||
}
|
||||
if tt.res.returnCodePhone {
|
||||
require.NotNil(t, got.PhoneCode)
|
||||
assert.NotNil(t, got.PhoneCode)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -628,16 +629,16 @@ func TestServer_PatchUser(t *testing.T) {
|
||||
}
|
||||
got, err := instance.Client.UserV3Alpha.PatchUser(tt.ctx, tt.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
integration.AssertResourceDetails(t, tt.res.want, got.Details)
|
||||
if tt.res.returnCodeEmail {
|
||||
require.NotNil(t, got.EmailCode)
|
||||
assert.NotNil(t, got.EmailCode)
|
||||
}
|
||||
if tt.res.returnCodePhone {
|
||||
require.NotNil(t, got.PhoneCode)
|
||||
assert.NotNil(t, got.PhoneCode)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -843,10 +844,10 @@ func TestServer_DeleteUser(t *testing.T) {
|
||||
}
|
||||
got, err := instance.Client.UserV3Alpha.DeleteUser(tt.ctx, tt.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
integration.AssertResourceDetails(t, tt.want, got.Details)
|
||||
})
|
||||
}
|
||||
@ -1054,10 +1055,10 @@ func TestServer_LockUser(t *testing.T) {
|
||||
}
|
||||
got, err := instance.Client.UserV3Alpha.LockUser(tt.ctx, tt.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
integration.AssertResourceDetails(t, tt.want, got.Details)
|
||||
})
|
||||
}
|
||||
@ -1237,10 +1238,10 @@ func TestServer_UnlockUser(t *testing.T) {
|
||||
}
|
||||
got, err := instance.Client.UserV3Alpha.UnlockUser(tt.ctx, tt.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
integration.AssertResourceDetails(t, tt.want, got.Details)
|
||||
})
|
||||
}
|
||||
@ -1439,10 +1440,10 @@ func TestServer_DeactivateUser(t *testing.T) {
|
||||
}
|
||||
got, err := instance.Client.UserV3Alpha.DeactivateUser(tt.ctx, tt.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
integration.AssertResourceDetails(t, tt.want, got.Details)
|
||||
})
|
||||
}
|
||||
@ -1622,10 +1623,10 @@ func TestServer_ActivateUser(t *testing.T) {
|
||||
}
|
||||
got, err := instance.Client.UserV3Alpha.ActivateUser(tt.ctx, tt.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
integration.AssertResourceDetails(t, tt.want, got.Details)
|
||||
})
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
|
||||
"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"
|
||||
@ -369,10 +368,10 @@ func TestServer_AddUsername(t *testing.T) {
|
||||
}
|
||||
got, err := instance.Client.UserV3Alpha.AddUsername(tt.ctx, tt.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
integration.AssertResourceDetails(t, tt.res.want, got.Details)
|
||||
})
|
||||
}
|
||||
@ -412,8 +411,9 @@ func TestServer_DeleteUsername(t *testing.T) {
|
||||
ctx: context.Background(),
|
||||
dep: func(req *user.RemoveUsernameRequest) error {
|
||||
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
|
||||
req.Id = userResp.GetDetails().GetId()
|
||||
usernameResp := instance.AddAuthenticatorUsername(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), userResp.GetDetails().GetId(), gofakeit.Username(), false)
|
||||
req.Id = usernameResp.GetUsernameId()
|
||||
req.UsernameId = usernameResp.GetUsernameId()
|
||||
return nil
|
||||
},
|
||||
req: &user.RemoveUsernameRequest{
|
||||
@ -430,8 +430,9 @@ func TestServer_DeleteUsername(t *testing.T) {
|
||||
ctx: instance.WithAuthorization(CTX, integration.UserTypeLogin),
|
||||
dep: func(req *user.RemoveUsernameRequest) error {
|
||||
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
|
||||
req.Id = userResp.GetDetails().GetId()
|
||||
usernameResp := instance.AddAuthenticatorUsername(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), userResp.GetDetails().GetId(), gofakeit.Username(), false)
|
||||
req.Id = usernameResp.GetUsernameId()
|
||||
req.UsernameId = usernameResp.GetUsernameId()
|
||||
return nil
|
||||
},
|
||||
req: &user.RemoveUsernameRequest{
|
||||
@ -452,6 +453,20 @@ func TestServer_DeleteUsername(t *testing.T) {
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
UsernameId: "notempty",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "username delete, userid empty",
|
||||
ctx: instance.WithAuthorization(CTX, integration.UserTypeLogin),
|
||||
req: &user.RemoveUsernameRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
Id: "notempty",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
@ -464,7 +479,25 @@ func TestServer_DeleteUsername(t *testing.T) {
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
Id: "notexisting",
|
||||
UsernameId: "notempty",
|
||||
Id: "notexisting",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "username remove, no username",
|
||||
ctx: isolatedIAMOwnerCTX,
|
||||
dep: func(req *user.RemoveUsernameRequest) error {
|
||||
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
|
||||
req.Id = userResp.GetDetails().GetId()
|
||||
return nil
|
||||
},
|
||||
req: &user.RemoveUsernameRequest{
|
||||
Organization: &object.Organization{
|
||||
Property: &object.Organization_OrgId{
|
||||
OrgId: orgResp.GetOrganizationId(),
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
@ -473,8 +506,9 @@ func TestServer_DeleteUsername(t *testing.T) {
|
||||
ctx: isolatedIAMOwnerCTX,
|
||||
dep: func(req *user.RemoveUsernameRequest) error {
|
||||
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
|
||||
req.Id = userResp.GetDetails().GetId()
|
||||
usernameResp := instance.AddAuthenticatorUsername(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), userResp.GetDetails().GetId(), gofakeit.Username(), false)
|
||||
req.Id = usernameResp.GetUsernameId()
|
||||
req.UsernameId = usernameResp.GetUsernameId()
|
||||
return nil
|
||||
},
|
||||
req: &user.RemoveUsernameRequest{
|
||||
@ -501,8 +535,8 @@ func TestServer_DeleteUsername(t *testing.T) {
|
||||
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
|
||||
req.Id = userResp.GetDetails().GetId()
|
||||
resp := instance.AddAuthenticatorUsername(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), req.Id, gofakeit.Username(), false)
|
||||
req.Id = resp.GetUsernameId()
|
||||
instance.RemoveAuthenticatorUsername(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), resp.GetUsernameId())
|
||||
req.UsernameId = resp.GetUsernameId()
|
||||
instance.RemoveAuthenticatorUsername(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), req.Id, req.UsernameId)
|
||||
return nil
|
||||
},
|
||||
req: &user.RemoveUsernameRequest{
|
||||
@ -519,8 +553,9 @@ func TestServer_DeleteUsername(t *testing.T) {
|
||||
ctx: isolatedIAMOwnerCTX,
|
||||
dep: func(req *user.RemoveUsernameRequest) error {
|
||||
userResp := instance.CreateSchemaUser(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), schemaResp.GetDetails().GetId(), []byte("{\"name\": \"user\"}"))
|
||||
req.Id = userResp.GetDetails().GetId()
|
||||
usernameResp := instance.AddAuthenticatorUsername(isolatedIAMOwnerCTX, orgResp.GetOrganizationId(), userResp.GetDetails().GetId(), gofakeit.Username(), true)
|
||||
req.Id = usernameResp.GetUsernameId()
|
||||
req.UsernameId = usernameResp.GetUsernameId()
|
||||
return nil
|
||||
},
|
||||
req: &user.RemoveUsernameRequest{
|
||||
@ -549,11 +584,13 @@ func TestServer_DeleteUsername(t *testing.T) {
|
||||
}
|
||||
got, err := instance.Client.UserV3Alpha.RemoveUsername(tt.ctx, tt.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
assert.Error(t, err)
|
||||
return
|
||||
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
integration.AssertResourceDetails(t, tt.res.want, got.Details)
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,11 @@ package user
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/muhlemmer/gu"
|
||||
|
||||
resource_object "github.com/zitadel/zitadel/internal/api/grpc/resources/object/v3alpha"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
object "github.com/zitadel/zitadel/pkg/grpc/object/v3alpha"
|
||||
user "github.com/zitadel/zitadel/pkg/grpc/resources/user/v3alpha"
|
||||
)
|
||||
@ -29,23 +32,11 @@ func setPasswordRequestToSetSchemaUserPassword(req *user.SetPasswordRequest) *co
|
||||
Password: req.GetNewPassword().GetPassword(),
|
||||
EncodedPasswordHash: req.GetNewPassword().GetHash(),
|
||||
ChangeRequired: req.GetNewPassword().GetChangeRequired(),
|
||||
VerificationCode: req.GetNewPassword().GetVerificationCode(),
|
||||
CurrentPassword: req.GetNewPassword().GetCurrentPassword(),
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func (s *Server) RemovePassword(ctx context.Context, req *user.RemovePasswordRequest) (_ *user.RemovePasswordResponse, err error) {
|
||||
if err := checkUserSchemaEnabled(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
details, err := s.command.DeleteSchemaUserPassword(ctx, organizationToUpdateResourceOwner(req.Organization), req.GetId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user.RemovePasswordResponse{
|
||||
Details: resource_object.DomainToDetailsPb(details, object.OwnerType_OWNER_TYPE_ORG, details.ResourceOwner),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) RemovePassword(ctx context.Context, req *user.RemovePasswordRequest) (_ *user.RemovePasswordResponse, err error) {
|
||||
if err := checkUserSchemaEnabled(ctx); err != nil {
|
||||
return nil, err
|
||||
@ -58,4 +49,35 @@ func (s *Server) RemovePassword(ctx context.Context, req *user.RemovePasswordReq
|
||||
Details: resource_object.DomainToDetailsPb(details, object.OwnerType_OWNER_TYPE_ORG, details.ResourceOwner),
|
||||
}, nil
|
||||
}
|
||||
*/
|
||||
|
||||
func (s *Server) RequestPasswordReset(ctx context.Context, req *user.RequestPasswordResetRequest) (_ *user.RequestPasswordResetResponse, err error) {
|
||||
if err := checkUserSchemaEnabled(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
schemauser := requestPasswordResetRequestToRequestSchemaUserPasswordReset(req)
|
||||
details, err := s.command.RequestSchemaUserPasswordReset(ctx, schemauser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user.RequestPasswordResetResponse{
|
||||
Details: resource_object.DomainToDetailsPb(details, object.OwnerType_OWNER_TYPE_ORG, details.ResourceOwner),
|
||||
VerificationCode: gu.Ptr(schemauser.PlainCode),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func requestPasswordResetRequestToRequestSchemaUserPasswordReset(req *user.RequestPasswordResetRequest) *command.RequestSchemaUserPasswordReset {
|
||||
var notificationType domain.NotificationType
|
||||
if req.GetSendEmail() != nil {
|
||||
notificationType = domain.NotificationTypeEmail
|
||||
}
|
||||
if req.GetSendSms() != nil {
|
||||
notificationType = domain.NotificationTypeSms
|
||||
}
|
||||
return &command.RequestSchemaUserPasswordReset{
|
||||
ResourceOwner: organizationToUpdateResourceOwner(req.Organization),
|
||||
UserID: req.GetId(),
|
||||
URLTemplate: req.GetSendEmail().GetUrlTemplate(),
|
||||
ReturnCode: req.GetReturnCode() != nil,
|
||||
NotificationType: notificationType,
|
||||
}
|
||||
}
|
||||
|
@ -32,11 +32,11 @@ func addUsernameRequestToAddUsername(req *user.AddUsernameRequest) *command.AddU
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) DeleteUsername(ctx context.Context, req *user.RemoveUsernameRequest) (_ *user.RemoveUsernameResponse, err error) {
|
||||
func (s *Server) RemoveUsername(ctx context.Context, req *user.RemoveUsernameRequest) (_ *user.RemoveUsernameResponse, err error) {
|
||||
if err := checkUserSchemaEnabled(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
details, err := s.command.DeleteUsername(ctx, organizationToUpdateResourceOwner(req.Organization), req.GetId())
|
||||
details, err := s.command.DeleteUsername(ctx, organizationToUpdateResourceOwner(req.Organization), req.GetId(), req.GetUsernameId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -789,8 +789,40 @@ func (i *Instance) AddAuthenticatorUsername(ctx context.Context, orgID string, u
|
||||
return user
|
||||
}
|
||||
|
||||
func (i *Instance) RemoveAuthenticatorUsername(ctx context.Context, orgID string, id string) *user_v3alpha.RemoveUsernameResponse {
|
||||
func (i *Instance) RemoveAuthenticatorUsername(ctx context.Context, orgID string, userid, id string) *user_v3alpha.RemoveUsernameResponse {
|
||||
user, err := i.Client.UserV3Alpha.RemoveUsername(ctx, &user_v3alpha.RemoveUsernameRequest{
|
||||
Organization: &object_v3alpha.Organization{Property: &object_v3alpha.Organization_OrgId{OrgId: orgID}},
|
||||
Id: userid,
|
||||
UsernameId: id,
|
||||
})
|
||||
logging.OnError(err).Fatal("remove username")
|
||||
return user
|
||||
}
|
||||
|
||||
func (i *Instance) SetAuthenticatorPassword(ctx context.Context, orgID string, userID string, password string) *user_v3alpha.SetPasswordResponse {
|
||||
user, err := i.Client.UserV3Alpha.SetPassword(ctx, &user_v3alpha.SetPasswordRequest{
|
||||
Organization: &object_v3alpha.Organization{Property: &object_v3alpha.Organization_OrgId{OrgId: orgID}},
|
||||
Id: userID,
|
||||
NewPassword: &user_v3alpha.SetPassword{
|
||||
Type: &user_v3alpha.SetPassword_Password{Password: password},
|
||||
},
|
||||
})
|
||||
logging.OnError(err).Fatal("create password")
|
||||
return user
|
||||
}
|
||||
|
||||
func (i *Instance) RequestAuthenticatorPasswordReset(ctx context.Context, orgID string, userID string) *user_v3alpha.RequestPasswordResetResponse {
|
||||
user, err := i.Client.UserV3Alpha.RequestPasswordReset(ctx, &user_v3alpha.RequestPasswordResetRequest{
|
||||
Organization: &object_v3alpha.Organization{Property: &object_v3alpha.Organization_OrgId{OrgId: orgID}},
|
||||
Id: userID,
|
||||
Medium: &user_v3alpha.RequestPasswordResetRequest_ReturnCode{},
|
||||
})
|
||||
logging.OnError(err).Fatal("reset password")
|
||||
return user
|
||||
}
|
||||
|
||||
func (i *Instance) RemoveAuthenticatorPassword(ctx context.Context, orgID string, id string) *user_v3alpha.RemovePasswordResponse {
|
||||
user, err := i.Client.UserV3Alpha.RemovePassword(ctx, &user_v3alpha.RemovePasswordRequest{
|
||||
Organization: &object_v3alpha.Organization{Property: &object_v3alpha.Organization_OrgId{OrgId: orgID}},
|
||||
Id: id,
|
||||
})
|
||||
|
@ -6,6 +6,7 @@ func init() {
|
||||
eventstore.RegisterFilterEventMapper(AggregateType, UsernameCreatedType, eventstore.GenericEventMapper[UsernameCreatedEvent])
|
||||
eventstore.RegisterFilterEventMapper(AggregateType, UsernameDeletedType, eventstore.GenericEventMapper[UsernameDeletedEvent])
|
||||
eventstore.RegisterFilterEventMapper(AggregateType, PasswordCreatedType, eventstore.GenericEventMapper[PasswordCreatedEvent])
|
||||
eventstore.RegisterFilterEventMapper(AggregateType, PasswordCodeAddedType, eventstore.GenericEventMapper[PasswordCodeAddedEvent])
|
||||
eventstore.RegisterFilterEventMapper(AggregateType, PasswordDeletedType, eventstore.GenericEventMapper[PasswordDeletedEvent])
|
||||
eventstore.RegisterFilterEventMapper(AggregateType, JWTCreatedType, eventstore.GenericEventMapper[JWTCreatedEvent])
|
||||
eventstore.RegisterFilterEventMapper(AggregateType, JWTDeletedType, eventstore.GenericEventMapper[JWTDeletedEvent])
|
||||
|
@ -1540,6 +1540,16 @@ message RemoveUsernameRequest {
|
||||
example: "\"69629026806489455\"";
|
||||
}
|
||||
];
|
||||
// unique identifier of the username.
|
||||
string username_id = 4 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
(google.api.field_behavior) = REQUIRED,
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
min_length: 1,
|
||||
max_length: 200,
|
||||
example: "\"69629023906488334\"";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
message RemoveUsernameResponse {
|
||||
|
Loading…
x
Reference in New Issue
Block a user