@@ -311,7 +311,75 @@ func (api *API) putUserProfile(rw http.ResponseWriter, r *http.Request) {
311
311
httpapi .Write (rw , http .StatusOK , convertUser (updatedUserProfile , organizationIDs ))
312
312
}
313
313
314
- func (api * API ) putUserStatus (status database.UserStatus ) func (rw http.ResponseWriter , r * http.Request ) {
314
+ func (api * API ) putUserSecurity (rw http.ResponseWriter , r * http.Request ) {
315
+ user := httpmw .UserParam (r )
316
+
317
+ // this route is for the owning user so we need to check the old password
318
+ // to protect against a compromised session being able to change the user's password.
319
+ if ! api .Authorize (rw , r , rbac .ActionUpdate , rbac .ResourceUserData .WithOwner (user .ID .String ())) {
320
+ return
321
+ }
322
+
323
+ var params codersdk.UpdateUserSecurityRequest
324
+ if ! httpapi .Read (rw , r , & params ) {
325
+ return
326
+ }
327
+
328
+ ok , err := userpassword .Compare (string (user .HashedPassword ), params .OldPassword )
329
+ if err != nil {
330
+ httpapi .Write (rw , http .StatusInternalServerError , httpapi.Response {
331
+ Message : fmt .Sprintf ("compare user password: %s" , err .Error ()),
332
+ })
333
+ return
334
+ }
335
+ if ! ok {
336
+ httpapi .Write (rw , http .StatusBadRequest , httpapi.Response {
337
+ Errors : []httpapi.Error {
338
+ {
339
+ Field : "old_password" ,
340
+ Detail : "Old password is incorrect." ,
341
+ },
342
+ },
343
+ })
344
+ return
345
+ }
346
+
347
+ err = userpassword .Validate (params .Password )
348
+ if err != nil {
349
+ httpapi .Write (rw , http .StatusBadRequest , httpapi.Response {
350
+ Errors : []httpapi.Error {
351
+ {
352
+ Field : "password" ,
353
+ Detail : err .Error (),
354
+ },
355
+ },
356
+ })
357
+ return
358
+ }
359
+
360
+ hashedPassword , err := userpassword .Hash (params .Password )
361
+ if err != nil {
362
+ httpapi .Write (rw , http .StatusInternalServerError , httpapi.Response {
363
+ Message : fmt .Sprintf ("hash password: %s" , err .Error ()),
364
+ })
365
+ return
366
+ }
367
+
368
+ err = api .Database .UpdateUserHashedPassword (r .Context (), database.UpdateUserHashedPasswordParams {
369
+ ID : user .ID ,
370
+ HashedPassword : []byte (hashedPassword ),
371
+ })
372
+ if err != nil {
373
+ httpapi .Write (rw , http .StatusInternalServerError , httpapi.Response {
374
+ Message : fmt .Sprintf ("put user password: %s" , err .Error ()),
375
+ })
376
+ return
377
+ }
378
+
379
+ httpapi .Write (rw , http .StatusNoContent , nil )
380
+ }
381
+
382
+ func (api * api ) putUserStatus (status database.UserStatus ) func (rw http.ResponseWriter , r * http.Request ) {
315
383
return func (rw http.ResponseWriter , r * http.Request ) {
316
384
user := httpmw .UserParam (r )
317
385
apiKey := httpmw .APIKey (r )
@@ -358,14 +426,28 @@ func (api *API) putUserPassword(rw http.ResponseWriter, r *http.Request) {
358
426
params codersdk.UpdateUserPasswordRequest
359
427
)
360
428
361
- if ! api .Authorize (rw , r , rbac .ActionUpdate , rbac .ResourceUserData .WithOwner (user .ID .String ())) {
429
+ // this route is for admins so we don't need to require an old password.
430
+ if ! api .Authorize (rw , r , rbac .ActionUpdate , rbac .ResourceUser .WithID (user .ID .String ())) {
362
431
return
363
432
}
364
433
365
434
if ! httpapi .Read (rw , r , & params ) {
366
435
return
367
436
}
368
437
438
+ err := userpassword .Validate (params .Password )
439
+ if err != nil {
440
+ httpapi .Write (rw , http .StatusBadRequest , httpapi.Response {
441
+ Errors : []httpapi.Error {
442
+ {
443
+ Field : "password" ,
444
+ Detail : err .Error (),
445
+ },
446
+ },
447
+ })
448
+ return
449
+ }
450
+
369
451
hashedPassword , err := userpassword .Hash (params .Password )
370
452
if err != nil {
371
453
httpapi .Write (rw , http .StatusInternalServerError , httpapi.Response {
0 commit comments