fix: set password in management api (#1766)

* fix: set password in management api

* comment

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
This commit is contained in:
Fabi 2021-06-03 13:28:24 +02:00 committed by GitHub
parent 8d163163f1
commit c0d9d86b09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 151 additions and 10 deletions

View File

@ -298,7 +298,18 @@ An sms will be sent to the given phone number to finish the phone verification p
> **rpc** SetHumanInitialPassword([SetHumanInitialPasswordRequest](#sethumaninitialpasswordrequest)) > **rpc** SetHumanInitialPassword([SetHumanInitialPasswordRequest](#sethumaninitialpasswordrequest))
[SetHumanInitialPasswordResponse](#sethumaninitialpasswordresponse) [SetHumanInitialPasswordResponse](#sethumaninitialpasswordresponse)
A Manager is only allowed to set an initial password, on the next login the user has to change his password deprecated: use SetHumanPassword
### SetHumanPassword
> **rpc** SetHumanPassword([SetHumanPasswordRequest](#sethumanpasswordrequest))
[SetHumanPasswordResponse](#sethumanpasswordresponse)
Set a new password for a user, on default the user has to change the password on the next login
Set no_change_required to true if the user does not have to change the password on the next login
@ -4862,6 +4873,30 @@ This is an empty request
### SetHumanPasswordRequest
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| user_id | string | - | string.min_len: 1<br /> |
| password | string | - | string.min_len: 1<br /> string.max_len: 72<br /> |
| no_change_required | bool | - | |
### SetHumanPasswordResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| details | zitadel.v1.ObjectDetails | - | |
### SetPrimaryOrgDomainRequest ### SetPrimaryOrgDomainRequest

View File

@ -324,7 +324,7 @@ func (s *Server) ResendHumanPhoneVerification(ctx context.Context, req *mgmt_pb.
} }
func (s *Server) SetHumanInitialPassword(ctx context.Context, req *mgmt_pb.SetHumanInitialPasswordRequest) (*mgmt_pb.SetHumanInitialPasswordResponse, error) { func (s *Server) SetHumanInitialPassword(ctx context.Context, req *mgmt_pb.SetHumanInitialPasswordRequest) (*mgmt_pb.SetHumanInitialPasswordResponse, error) {
objectDetails, err := s.command.SetOneTimePassword(ctx, authz.GetCtxData(ctx).OrgID, req.UserId, req.Password) objectDetails, err := s.command.SetPassword(ctx, authz.GetCtxData(ctx).OrgID, req.UserId, req.Password, true)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -333,6 +333,16 @@ func (s *Server) SetHumanInitialPassword(ctx context.Context, req *mgmt_pb.SetHu
}, nil }, nil
} }
func (s *Server) SetHumanPassword(ctx context.Context, req *mgmt_pb.SetHumanPasswordRequest) (*mgmt_pb.SetHumanPasswordResponse, error) {
objectDetails, err := s.command.SetPassword(ctx, authz.GetCtxData(ctx).OrgID, req.UserId, req.Password, !req.NoChangeRequired)
if err != nil {
return nil, err
}
return &mgmt_pb.SetHumanPasswordResponse{
Details: obj_grpc.DomainToChangeDetailsPb(objectDetails),
}, nil
}
func (s *Server) SendHumanResetPasswordNotification(ctx context.Context, req *mgmt_pb.SendHumanResetPasswordNotificationRequest) (*mgmt_pb.SendHumanResetPasswordNotificationResponse, error) { func (s *Server) SendHumanResetPasswordNotification(ctx context.Context, req *mgmt_pb.SendHumanResetPasswordNotificationRequest) (*mgmt_pb.SendHumanResetPasswordNotificationResponse, error) {
objectDetails, err := s.command.RequestSetPassword(ctx, req.UserId, authz.GetCtxData(ctx).OrgID, notifyTypeToDomain(req.Type)) objectDetails, err := s.command.RequestSetPassword(ctx, req.UserId, authz.GetCtxData(ctx).OrgID, notifyTypeToDomain(req.Type))
if err != nil { if err != nil {

View File

@ -11,7 +11,7 @@ import (
"github.com/caos/zitadel/internal/telemetry/tracing" "github.com/caos/zitadel/internal/telemetry/tracing"
) )
func (c *Commands) SetOneTimePassword(ctx context.Context, orgID, userID, passwordString string) (objectDetails *domain.ObjectDetails, err error) { func (c *Commands) SetPassword(ctx context.Context, orgID, userID, passwordString string, oneTime bool) (objectDetails *domain.ObjectDetails, err error) {
ctx, span := tracing.NewSpan(ctx) ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }() defer func() { span.EndWithError(err) }()
if userID == "" { if userID == "" {
@ -26,7 +26,7 @@ func (c *Commands) SetOneTimePassword(ctx context.Context, orgID, userID, passwo
} }
password := &domain.Password{ password := &domain.Password{
SecretString: passwordString, SecretString: passwordString,
ChangeRequired: true, ChangeRequired: oneTime,
} }
userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel) userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel)
passwordEvent, err := c.changePassword(ctx, "", password, userAgg, existingPassword) passwordEvent, err := c.changePassword(ctx, "", password, userAgg, existingPassword)
@ -44,7 +44,7 @@ func (c *Commands) SetOneTimePassword(ctx context.Context, orgID, userID, passwo
return writeModelToObjectDetails(&existingPassword.WriteModel), nil return writeModelToObjectDetails(&existingPassword.WriteModel), nil
} }
func (c *Commands) SetPassword(ctx context.Context, orgID, userID, code, passwordString, userAgentID string) (err error) { func (c *Commands) SetPasswordWithVerifyCode(ctx context.Context, orgID, userID, code, passwordString, userAgentID string) (err error) {
ctx, span := tracing.NewSpan(ctx) ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }() defer func() { span.EndWithError(err) }()

View File

@ -28,6 +28,7 @@ func TestCommandSide_SetOneTimePassword(t *testing.T) {
userID string userID string
resourceOwner string resourceOwner string
password string password string
oneTime bool
} }
type res struct { type res struct {
want *domain.ObjectDetails want *domain.ObjectDetails
@ -72,7 +73,7 @@ func TestCommandSide_SetOneTimePassword(t *testing.T) {
}, },
}, },
{ {
name: "change password, ok", name: "change password onetime, ok",
fields: fields{ fields: fields{
eventstore: eventstoreExpect( eventstore: eventstoreExpect(
t, t,
@ -134,6 +135,78 @@ func TestCommandSide_SetOneTimePassword(t *testing.T) {
userID: "user1", userID: "user1",
resourceOwner: "org1", resourceOwner: "org1",
password: "password", password: "password",
oneTime: true,
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
{
name: "change password no one time, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
user.NewHumanAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
"username",
"firstname",
"lastname",
"nickname",
"displayname",
language.German,
domain.GenderUnspecified,
"email@test.ch",
true,
),
),
eventFromEventPusher(
user.NewHumanEmailVerifiedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
),
),
),
expectFilter(
eventFromEventPusher(
org.NewPasswordComplexityPolicyAddedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
1,
false,
false,
false,
false,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
user.NewHumanPasswordChangedEvent(context.Background(),
&user.NewAggregate("user1", "org1").Aggregate,
&crypto.CryptoValue{
CryptoType: crypto.TypeHash,
Algorithm: "hash",
KeyID: "",
Crypted: []byte("password"),
},
false,
"",
),
),
},
),
),
userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
},
args: args{
ctx: context.Background(),
userID: "user1",
resourceOwner: "org1",
password: "password",
oneTime: false,
}, },
res: res{ res: res{
want: &domain.ObjectDetails{ want: &domain.ObjectDetails{
@ -148,7 +221,7 @@ func TestCommandSide_SetOneTimePassword(t *testing.T) {
eventstore: tt.fields.eventstore, eventstore: tt.fields.eventstore,
userPasswordAlg: tt.fields.userPasswordAlg, userPasswordAlg: tt.fields.userPasswordAlg,
} }
got, err := r.SetOneTimePassword(tt.args.ctx, tt.args.resourceOwner, tt.args.userID, tt.args.password) got, err := r.SetPassword(tt.args.ctx, tt.args.resourceOwner, tt.args.userID, tt.args.password, tt.args.oneTime)
if tt.res.err == nil { if tt.res.err == nil {
assert.NoError(t, err) assert.NoError(t, err)
} }
@ -410,7 +483,7 @@ func TestCommandSide_SetPassword(t *testing.T) {
userPasswordAlg: tt.fields.userPasswordAlg, userPasswordAlg: tt.fields.userPasswordAlg,
passwordVerificationCode: tt.fields.secretGenerator, passwordVerificationCode: tt.fields.secretGenerator,
} }
err := r.SetPassword(tt.args.ctx, tt.args.resourceOwner, tt.args.userID, tt.args.code, tt.args.password, tt.args.agentID) err := r.SetPasswordWithVerifyCode(tt.args.ctx, tt.args.resourceOwner, tt.args.userID, tt.args.code, tt.args.password, tt.args.agentID)
if tt.res.err == nil { if tt.res.err == nil {
assert.NoError(t, err) assert.NoError(t, err)
} }

View File

@ -69,7 +69,7 @@ func (l *Login) checkPWCode(w http.ResponseWriter, r *http.Request, authReq *dom
userOrg = authReq.UserOrgID userOrg = authReq.UserOrgID
} }
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context()) userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
err = l.command.SetPassword(setContext(r.Context(), userOrg), userOrg, data.UserID, data.Code, data.Password, userAgentID) err = l.command.SetPasswordWithVerifyCode(setContext(r.Context(), userOrg), userOrg, data.UserID, data.Code, data.Password, userAgentID)
if err != nil { if err != nil {
l.renderInitPassword(w, r, authReq, data.UserID, "", err) l.renderInitPassword(w, r, authReq, data.UserID, "", err)
return return

View File

@ -392,7 +392,7 @@ service ManagementService {
}; };
} }
// A Manager is only allowed to set an initial password, on the next login the user has to change his password // deprecated: use SetHumanPassword
rpc SetHumanInitialPassword(SetHumanInitialPasswordRequest) returns (SetHumanInitialPasswordResponse) { rpc SetHumanInitialPassword(SetHumanInitialPasswordRequest) returns (SetHumanInitialPasswordResponse) {
option (google.api.http) = { option (google.api.http) = {
post: "/users/{user_id}/password/_initialize" post: "/users/{user_id}/password/_initialize"
@ -404,6 +404,19 @@ service ManagementService {
}; };
} }
// Set a new password for a user, on default the user has to change the password on the next login
// Set no_change_required to true if the user does not have to change the password on the next login
rpc SetHumanPassword(SetHumanPasswordRequest) returns (SetHumanPasswordResponse) {
option (google.api.http) = {
post: "/users/{user_id}/password"
body: "*"
};
option (zitadel.v1.auth_option) = {
permission: "user.write"
};
}
// An email will be sent to the given address to reset the password of the user // An email will be sent to the given address to reset the password of the user
rpc SendHumanResetPasswordNotification(SendHumanResetPasswordNotificationRequest) returns (SendHumanResetPasswordNotificationResponse) { rpc SendHumanResetPasswordNotification(SendHumanResetPasswordNotificationRequest) returns (SendHumanResetPasswordNotificationResponse) {
option (google.api.http) = { option (google.api.http) = {
@ -2380,6 +2393,16 @@ message SetHumanInitialPasswordResponse {
zitadel.v1.ObjectDetails details = 1; zitadel.v1.ObjectDetails details = 1;
} }
message SetHumanPasswordRequest {
string user_id = 1 [(validate.rules).string.min_len = 1];
string password = 2 [(validate.rules).string = {min_len: 1, max_len: 72}];
bool no_change_required = 3;
}
message SetHumanPasswordResponse {
zitadel.v1.ObjectDetails details = 1;
}
message SendHumanResetPasswordNotificationRequest { message SendHumanResetPasswordNotificationRequest {
enum Type { enum Type {
TYPE_EMAIL = 0; TYPE_EMAIL = 0;