diff --git a/backend/apid/middlewares/authorization_attributes.go b/backend/apid/middlewares/authorization_attributes.go index cffa691c63..ca748cd313 100644 --- a/backend/apid/middlewares/authorization_attributes.go +++ b/backend/apid/middlewares/authorization_attributes.go @@ -93,9 +93,8 @@ func (a AuthorizationAttributes) Then(next http.Handler) http.Handler { attrs.Resource = types.LocalSelfUserResource } - // Change the resource to LocalSelfUserResource if a user tries to change - // its own password - if attrs.Verb == "update" && vars["subresource"] == "password" { + switch vars["subresource"] { + case "password", "change_password": attrs.Resource = types.LocalSelfUserResource } } diff --git a/backend/apid/routers/users.go b/backend/apid/routers/users.go index 2b81d4e913..9a200a1206 100644 --- a/backend/apid/routers/users.go +++ b/backend/apid/routers/users.go @@ -59,6 +59,9 @@ func (r *UsersRouter) Mount(parent *mux.Router) { // Password change & reset routes.Path("{id}/{subresource:password}", r.updatePassword).Methods(http.MethodPut) routes.Path("{id}/{subresource:reset_password}", r.resetPassword).Methods(http.MethodPut) + + // password update from web ui + routes.Path("{id}/{subresource:change_password}", r.changePasswordFromWeb).Methods(http.MethodPut) } func (r *UsersRouter) get(req *http.Request) (interface{}, error) { @@ -153,6 +156,35 @@ func (r *UsersRouter) updatePassword(req *http.Request) (interface{}, error) { return nil, err } +// changePasswordFromWeb updates user password when requests are sent from web UI +func (r *UsersRouter) changePasswordFromWeb(req *http.Request) (interface{}, error) { + params := map[string]string{} + if err := UnmarshalBody(req, ¶ms); err != nil { + return nil, err + } + + vars := mux.Vars(req) + username, err := url.PathUnescape(vars["id"]) + if err != nil { + return nil, err + } + newPassword := params["password_new"] + oldPassword := params["password"] + + user, err := r.controller.AuthenticateUser(req.Context(), username, oldPassword) + if err != nil { + return nil, err + } + + // set new password for updating into store + user.Password = newPassword + + // Remove any old password hash + user.PasswordHash = "" + err = r.controller.CreateOrReplace(req.Context(), user) + return nil, err +} + // resetPassword updates a user password without any kind of verification func (r *UsersRouter) resetPassword(req *http.Request) (interface{}, error) { params := map[string]string{} diff --git a/backend/apid/routers/users_test.go b/backend/apid/routers/users_test.go index 7d8b0cfe59..039e3cf9f3 100644 --- a/backend/apid/routers/users_test.go +++ b/backend/apid/routers/users_test.go @@ -235,6 +235,21 @@ func TestUsersRouter(t *testing.T) { }, wantStatusCode: http.StatusCreated, }, + { + name: "update password from web ui", + method: http.MethodPut, + path: path.Join(fixture.URIPath(), "change_password"), + body: []byte(`{"username":"foo","password":"admin123","password_new":"admin123"}`), + controllerFunc: func(c *mockUserController) { + c.On("AuthenticateUser", mock.Anything, mock.Anything, mock.Anything). + Return(&corev2.User{Username: "foo", Password: "admin123", PasswordHash: "admin123_hash"}, nil). + Once() + c.On("CreateOrReplace", mock.Anything, mock.Anything). + Return(nil). + Once() + }, + wantStatusCode: http.StatusCreated, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {