Skip to content

Commit e566872

Browse files
authored
fix(coderd): improve password update logic (coder#15210)
Working on coder#15202 The main change is to fetch the user doing the action to verify if it should be able to change the password if there's no old_password set.
1 parent f258232 commit e566872

File tree

2 files changed

+40
-4
lines changed

2 files changed

+40
-4
lines changed

coderd/users.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -1018,6 +1018,7 @@ func (api *API) putUserPassword(rw http.ResponseWriter, r *http.Request) {
10181018
ctx = r.Context()
10191019
user = httpmw.UserParam(r)
10201020
params codersdk.UpdateUserPasswordRequest
1021+
apiKey = httpmw.APIKey(r)
10211022
auditor = *api.Auditor.Load()
10221023
aReq, commitAudit = audit.InitRequest[database.User](rw, &audit.RequestParams{
10231024
Audit: auditor,
@@ -1045,6 +1046,14 @@ func (api *API) putUserPassword(rw http.ResponseWriter, r *http.Request) {
10451046
return
10461047
}
10471048

1049+
// A user need to put its own password to update it
1050+
if apiKey.UserID == user.ID && params.OldPassword == "" {
1051+
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
1052+
Message: "Old password is required.",
1053+
})
1054+
return
1055+
}
1056+
10481057
err := userpassword.Validate(params.Password)
10491058
if err != nil {
10501059
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
@@ -1059,7 +1068,6 @@ func (api *API) putUserPassword(rw http.ResponseWriter, r *http.Request) {
10591068
return
10601069
}
10611070

1062-
// admins can change passwords without sending old_password
10631071
if params.OldPassword != "" {
10641072
// if they send something let's validate it
10651073
ok, err := userpassword.Compare(string(user.HashedPassword), params.OldPassword)

coderd/users_test.go

+31-3
Original file line numberDiff line numberDiff line change
@@ -1056,6 +1056,31 @@ func TestUpdateUserPassword(t *testing.T) {
10561056
require.NoError(t, err, "member should login successfully with the new password")
10571057
})
10581058

1059+
t.Run("AuditorCantUpdateOtherUserPassword", func(t *testing.T) {
1060+
t.Parallel()
1061+
client := coderdtest.New(t, nil)
1062+
owner := coderdtest.CreateFirstUser(t, client)
1063+
1064+
auditor, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleAuditor())
1065+
1066+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
1067+
defer cancel()
1068+
1069+
member, err := client.CreateUserWithOrgs(ctx, codersdk.CreateUserRequestWithOrgs{
1070+
Email: "coder@coder.com",
1071+
Username: "coder",
1072+
Password: "SomeStrongPassword!",
1073+
OrganizationIDs: []uuid.UUID{owner.OrganizationID},
1074+
})
1075+
require.NoError(t, err, "create member")
1076+
1077+
err = auditor.UpdateUserPassword(ctx, member.ID.String(), codersdk.UpdateUserPasswordRequest{
1078+
Password: "SomeNewStrongPassword!",
1079+
})
1080+
require.Error(t, err, "auditor should not be able to update member password")
1081+
require.ErrorContains(t, err, "unexpected status code 404: Resource not found or you do not have access to this resource")
1082+
})
1083+
10591084
t.Run("MemberCanUpdateOwnPassword", func(t *testing.T) {
10601085
t.Parallel()
10611086
auditor := audit.NewMock()
@@ -1097,6 +1122,7 @@ func TestUpdateUserPassword(t *testing.T) {
10971122
Password: "newpassword",
10981123
})
10991124
require.Error(t, err, "member should not be able to update own password without providing old password")
1125+
require.ErrorContains(t, err, "Old password is required.")
11001126
})
11011127

11021128
t.Run("AuditorCantTellIfPasswordIncorrect", func(t *testing.T) {
@@ -1133,7 +1159,7 @@ func TestUpdateUserPassword(t *testing.T) {
11331159
require.Equal(t, int32(http.StatusNotFound), auditor.AuditLogs()[numLogs-1].StatusCode)
11341160
})
11351161

1136-
t.Run("AdminCanUpdateOwnPasswordWithoutOldPassword", func(t *testing.T) {
1162+
t.Run("AdminCantUpdateOwnPasswordWithoutOldPassword", func(t *testing.T) {
11371163
t.Parallel()
11381164
auditor := audit.NewMock()
11391165
client := coderdtest.New(t, &coderdtest.Options{Auditor: auditor})
@@ -1150,7 +1176,8 @@ func TestUpdateUserPassword(t *testing.T) {
11501176
})
11511177
numLogs++ // add an audit log for user update
11521178

1153-
require.NoError(t, err, "admin should be able to update own password without providing old password")
1179+
require.Error(t, err, "admin should not be able to update own password without providing old password")
1180+
require.ErrorContains(t, err, "Old password is required.")
11541181

11551182
require.Len(t, auditor.AuditLogs(), numLogs)
11561183
require.Equal(t, database.AuditActionWrite, auditor.AuditLogs()[numLogs-1].Action)
@@ -1170,7 +1197,8 @@ func TestUpdateUserPassword(t *testing.T) {
11701197
require.NoError(t, err)
11711198

11721199
err = client.UpdateUserPassword(ctx, "me", codersdk.UpdateUserPasswordRequest{
1173-
Password: "MyNewSecurePassword!",
1200+
OldPassword: "SomeSecurePassword!",
1201+
Password: "MyNewSecurePassword!",
11741202
})
11751203
require.NoError(t, err)
11761204

0 commit comments

Comments
 (0)