non-admins must supply existing passwd when changing passwd (#10249)

pull/10255/head
Matt Hook 2023-09-06 08:26:32 +12:00 committed by GitHub
parent 515b02813b
commit e5f7641e46
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 24 additions and 9 deletions

View File

@ -20,6 +20,7 @@ var (
errAdminCannotRemoveSelf = errors.New("Cannot remove your own user account. Contact another administrator") errAdminCannotRemoveSelf = errors.New("Cannot remove your own user account. Contact another administrator")
errCannotRemoveLastLocalAdmin = errors.New("Cannot remove the last local administrator account") errCannotRemoveLastLocalAdmin = errors.New("Cannot remove the last local administrator account")
errCryptoHashFailure = errors.New("Unable to hash data") errCryptoHashFailure = errors.New("Unable to hash data")
errWrongPassword = errors.New("Wrong password")
) )
func hideFields(user *portainer.User) { func hideFields(user *portainer.User) {

View File

@ -21,9 +21,10 @@ type themePayload struct {
} }
type userUpdatePayload struct { type userUpdatePayload struct {
Username string `validate:"required" example:"bob"` Username string `validate:"required" example:"bob"`
Password string `validate:"required" example:"cg9Wgky3"` Password string `validate:"required" example:"cg9Wgky3"`
Theme *themePayload NewPassword string `validate:"required" example:"asfj2emv"`
Theme *themePayload
// User role (1 for administrator account and 2 for regular account) // User role (1 for administrator account and 2 for regular account)
Role int `validate:"required" enums:"1,2" example:"2"` Role int `validate:"required" enums:"1,2" example:"2"`
@ -37,6 +38,7 @@ func (payload *userUpdatePayload) Validate(r *http.Request) error {
if payload.Role != 0 && payload.Role != 1 && payload.Role != 2 { if payload.Role != 0 && payload.Role != 1 && payload.Role != 2 {
return errors.New("invalid role value. Value must be one of: 1 (administrator) or 2 (regular user)") return errors.New("invalid role value. Value must be one of: 1 (administrator) or 2 (regular user)")
} }
return nil return nil
} }
@ -106,8 +108,20 @@ func (handler *Handler) userUpdate(w http.ResponseWriter, r *http.Request) *http
user.Username = payload.Username user.Username = payload.Username
} }
if payload.Password != "" { if payload.NewPassword != "" {
user.Password, err = handler.CryptoService.Hash(payload.Password) // Non-admins need to supply the previous password
if tokenData.Role != portainer.AdministratorRole {
err := handler.CryptoService.CompareHashAndData(user.Password, payload.Password)
if err != nil {
return httperror.Forbidden("Current password doesn't match. Password left unchanged", errors.New("Current password does not match the password provided. Please try again"))
}
}
if !handler.passwordStrengthChecker.Check(payload.NewPassword) {
return httperror.BadRequest("Password does not meet the minimum strength requirements", nil)
}
user.Password, err = handler.CryptoService.Hash(payload.NewPassword)
if err != nil { if err != nil {
return httperror.InternalServerError("Unable to hash user password", errCryptoHashFailure) return httperror.InternalServerError("Unable to hash user password", errCryptoHashFailure)
} }

View File

@ -87,7 +87,7 @@ func (handler *Handler) userUpdatePassword(w http.ResponseWriter, r *http.Reques
} }
if !handler.passwordStrengthChecker.Check(payload.NewPassword) { if !handler.passwordStrengthChecker.Check(payload.NewPassword) {
return httperror.BadRequest("Password does not meet the requirements", nil) return httperror.BadRequest("Password does not meet the minimum strength requirements", nil)
} }
user.Password, err = handler.CryptoService.Hash(payload.NewPassword) user.Password, err = handler.CryptoService.Hash(payload.NewPassword)

View File

@ -54,8 +54,8 @@ export function UserService($q, Users, TeamService, TeamMembershipService) {
return Users.remove({ id: id }).$promise; return Users.remove({ id: id }).$promise;
}; };
service.updateUser = function (id, { password, role, username }) { service.updateUser = function (id, { newPassword, role, username }) {
return Users.update({ id }, { password, role, username }).$promise; return Users.update({ id }, { newPassword, role, username }).$promise;
}; };
service.updateUserPassword = function (id, currentPassword, newPassword) { service.updateUserPassword = function (id, currentPassword, newPassword) {

View File

@ -72,7 +72,7 @@ angular.module('portainer.app').controller('UserController', [
if (!confirmed) { if (!confirmed) {
return; return;
} }
UserService.updateUser($scope.user.Id, { password: $scope.formValues.newPassword }) UserService.updateUser($scope.user.Id, { newPassword: $scope.formValues.newPassword })
.then(function success() { .then(function success() {
Notifications.success('Success', 'Password successfully updated'); Notifications.success('Success', 'Password successfully updated');