{"record":{"id":"eb18c908402de05a","repo":"getredash/redash","slug":"must-provide-current-password-to-update-password","errorCode":null,"errorMessage":"Must provide current password to update password.","messagePattern":"Must provide current password to update password\\.","errorType":"http","errorClass":null,"httpStatus":403,"severity":"error","filePath":"redash/handlers/users.py","lineNumber":215,"sourceCode":"\n    def get(self, user_id):\n        require_permission_or_owner(\"list_users\", user_id)\n        user = get_object_or_404(models.User.get_by_id_and_org, user_id, self.current_org)\n\n        self.record_event({\"action\": \"view\", \"object_id\": user_id, \"object_type\": \"user\"})\n\n        return user.to_dict(with_api_key=is_admin_or_owner(user_id))\n\n    def post(self, user_id):  # noqa: C901\n        require_admin_or_owner(user_id)\n        user = models.User.get_by_id_and_org(user_id, self.current_org)\n\n        req = request.get_json(True)\n\n        params = project(req, (\"email\", \"name\", \"password\", \"old_password\", \"group_ids\"))\n\n        if \"password\" in params and \"old_password\" not in params:\n            abort(403, message=\"Must provide current password to update password.\")\n\n        if \"old_password\" in params and not user.verify_password(params[\"old_password\"]):\n            abort(403, message=\"Incorrect current password.\")\n\n        if \"password\" in params:\n            user.hash_password(params.pop(\"password\"))\n            params.pop(\"old_password\")\n\n        if \"group_ids\" in params:\n            if not self.current_user.has_permission(\"admin\"):\n                abort(403, message=\"Must be admin to change groups membership.\")\n\n            for group_id in params[\"group_ids\"]:\n                try:\n                    models.Group.get_by_id_and_org(group_id, self.current_org)\n                except NoResultFound:\n                    abort(400, message=\"Group id {} is invalid.\".format(group_id))\n","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/getredash/redash/blob/ca79fe988d81cdac9675b412f3dfcab107bc1fbc/redash/handlers/users.py#L197-L233","documentation":"Raised by the user-update route in redash/handlers/users.py when the request includes a new 'password' but not 'old_password'. Changing a user's password requires proving knowledge of the current password, so omitting it returns 403.","triggerScenarios":"PATCH/POST to update a user with {\"password\": \"newsecret\"} and no old_password field (self-service profile update path).","commonSituations":"Profile-update forms that expose a password field without a current-password field; admin scripts trying to reset passwords via the profile endpoint instead of the admin reset route.","solutions":["Include old_password alongside password in the request body.","If you are an admin resetting another user's password, use the admin reset_password endpoint instead.","Build UI forms that always require the current password when a new one is set."],"exampleFix":"# before\nclient.post(f'/api/users/{uid}', json={'password': 'newsecret'})\n\n# after\nclient.post(f'/api/users/{uid}', json={'password': 'newsecret', 'old_password': 'currentsecret'})","handlingStrategy":"validation","validationCode":"if 'password' in payload:\n    assert 'old_password' in payload, 'old_password required to change password'","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Profile forms: render the current-password input whenever new-password is shown.","Admins: use the dedicated reset_password route instead of the profile update."],"tags":["redash","users","password","forbidden","validation"],"backgroundTag":"password-change-requires-current-password","analyzedSha":"ca79fe988d81cdac9675b412f3dfcab107bc1fbc","analyzedAt":"2026-08-28T18:32:34.637Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}