mirror of
				https://github.com/zitadel/zitadel.git
				synced 2025-11-04 05:52:51 +00:00 
			
		
		
		
	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:
		@@ -298,7 +298,18 @@ An sms will be sent to the given phone number to finish the phone verification p
 | 
			
		||||
> **rpc** SetHumanInitialPassword([SetHumanInitialPasswordRequest](#sethumaninitialpasswordrequest))
 | 
			
		||||
[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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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) {
 | 
			
		||||
	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 {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -333,6 +333,16 @@ func (s *Server) SetHumanInitialPassword(ctx context.Context, req *mgmt_pb.SetHu
 | 
			
		||||
	}, 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) {
 | 
			
		||||
	objectDetails, err := s.command.RequestSetPassword(ctx, req.UserId, authz.GetCtxData(ctx).OrgID, notifyTypeToDomain(req.Type))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ import (
 | 
			
		||||
	"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)
 | 
			
		||||
	defer func() { span.EndWithError(err) }()
 | 
			
		||||
	if userID == "" {
 | 
			
		||||
@@ -26,7 +26,7 @@ func (c *Commands) SetOneTimePassword(ctx context.Context, orgID, userID, passwo
 | 
			
		||||
	}
 | 
			
		||||
	password := &domain.Password{
 | 
			
		||||
		SecretString:   passwordString,
 | 
			
		||||
		ChangeRequired: true,
 | 
			
		||||
		ChangeRequired: oneTime,
 | 
			
		||||
	}
 | 
			
		||||
	userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel)
 | 
			
		||||
	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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
	defer func() { span.EndWithError(err) }()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@ func TestCommandSide_SetOneTimePassword(t *testing.T) {
 | 
			
		||||
		userID        string
 | 
			
		||||
		resourceOwner string
 | 
			
		||||
		password      string
 | 
			
		||||
		oneTime       bool
 | 
			
		||||
	}
 | 
			
		||||
	type res struct {
 | 
			
		||||
		want *domain.ObjectDetails
 | 
			
		||||
@@ -72,7 +73,7 @@ func TestCommandSide_SetOneTimePassword(t *testing.T) {
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "change password, ok",
 | 
			
		||||
			name: "change password onetime, ok",
 | 
			
		||||
			fields: fields{
 | 
			
		||||
				eventstore: eventstoreExpect(
 | 
			
		||||
					t,
 | 
			
		||||
@@ -134,6 +135,78 @@ func TestCommandSide_SetOneTimePassword(t *testing.T) {
 | 
			
		||||
				userID:        "user1",
 | 
			
		||||
				resourceOwner: "org1",
 | 
			
		||||
				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{
 | 
			
		||||
				want: &domain.ObjectDetails{
 | 
			
		||||
@@ -148,7 +221,7 @@ func TestCommandSide_SetOneTimePassword(t *testing.T) {
 | 
			
		||||
				eventstore:      tt.fields.eventstore,
 | 
			
		||||
				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 {
 | 
			
		||||
				assert.NoError(t, err)
 | 
			
		||||
			}
 | 
			
		||||
@@ -410,7 +483,7 @@ func TestCommandSide_SetPassword(t *testing.T) {
 | 
			
		||||
				userPasswordAlg:          tt.fields.userPasswordAlg,
 | 
			
		||||
				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 {
 | 
			
		||||
				assert.NoError(t, err)
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
@@ -69,7 +69,7 @@ func (l *Login) checkPWCode(w http.ResponseWriter, r *http.Request, authReq *dom
 | 
			
		||||
		userOrg = authReq.UserOrgID
 | 
			
		||||
	}
 | 
			
		||||
	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 {
 | 
			
		||||
		l.renderInitPassword(w, r, authReq, data.UserID, "", err)
 | 
			
		||||
		return
 | 
			
		||||
 
 | 
			
		||||
@@ -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) {
 | 
			
		||||
        option (google.api.http) = {
 | 
			
		||||
            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
 | 
			
		||||
    rpc SendHumanResetPasswordNotification(SendHumanResetPasswordNotificationRequest) returns (SendHumanResetPasswordNotificationResponse) {
 | 
			
		||||
        option (google.api.http) = {
 | 
			
		||||
@@ -2380,6 +2393,16 @@ message SetHumanInitialPasswordResponse {
 | 
			
		||||
    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 {
 | 
			
		||||
    enum Type {
 | 
			
		||||
        TYPE_EMAIL = 0;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user