diff --git a/controllers/user.go b/controllers/user.go
index 4c000764..52aac297 100644
--- a/controllers/user.go
+++ b/controllers/user.go
@@ -16,6 +16,8 @@ package controllers
import (
"encoding/json"
+ "fmt"
+ "strings"
"github.com/casdoor/casdoor/object"
)
@@ -103,3 +105,71 @@ func (c *ApiController) DeleteUser() {
c.Data["json"] = wrapActionResponse(object.DeleteUser(&user))
c.ServeJSON()
}
+
+// @Title SetPassword
+// @Description set password
+// @Param userOwner formData string true "The owner of the user"
+// @Param userName formData string true "The name of the user"
+// @Param oldPassword formData string true "The old password of the user"
+// @Param newPassword formData string true "The new password of the user"
+// @Success 200 {object} controllers.Response The Response object
+// @router /set-password [post]
+func (c *ApiController) SetPassword() {
+ userOwner := c.Ctx.Request.Form.Get("userOwner")
+ userName := c.Ctx.Request.Form.Get("userName")
+ oldPassword := c.Ctx.Request.Form.Get("oldPassword")
+ newPassword := c.Ctx.Request.Form.Get("newPassword")
+
+ requestUserId := c.GetSessionUser()
+ if requestUserId == "" {
+ c.ResponseError("Please login first.")
+ return
+ }
+ requestUser := object.GetUser(requestUserId)
+ if requestUser == nil {
+ c.ResponseError("Session outdated. Please login again.")
+ return
+ }
+
+ userId := fmt.Sprintf("%s/%s", userOwner, userName)
+ targetUser := object.GetUser(userId)
+ if targetUser == nil {
+ c.ResponseError("Invalid user id.")
+ return
+ }
+
+ hasPermission := false
+
+ if requestUser.IsGlobalAdmin {
+ hasPermission = true
+ } else if requestUserId == userId {
+ hasPermission = true
+ } else if targetUser.Owner == requestUser.Owner && requestUser.IsAdmin {
+ hasPermission = true
+ }
+
+ if !hasPermission {
+ c.ResponseError("You don't have the permission to do this.")
+ return
+ }
+
+ if oldPassword != targetUser.Password {
+ c.ResponseError("Old password wrong.")
+ return
+ }
+
+ if strings.Index(newPassword, " ") >= 0 {
+ c.ResponseError("New password contains blank space.")
+ return
+ }
+
+ if newPassword == "" {
+ c.ResponseError("Invalid new password")
+ return
+ }
+
+ targetUser.Password = newPassword
+ object.SetUserField(targetUser, "password", targetUser.Password)
+ c.Data["json"] = Response{Status: "ok"}
+ c.ServeJSON()
+}
diff --git a/controllers/util.go b/controllers/util.go
index 97a218e9..18b4a9c3 100644
--- a/controllers/util.go
+++ b/controllers/util.go
@@ -52,3 +52,8 @@ func InitHttpClient() {
//defer resp.Body.Close()
//println("Response status: %s", resp.Status)
}
+
+func (c *ApiController) ResponseError(error string) {
+ c.Data["json"] = Response{Status: "error", Msg: error}
+ c.ServeJSON()
+}
diff --git a/routers/router.go b/routers/router.go
index a489d3cb..875f6af2 100644
--- a/routers/router.go
+++ b/routers/router.go
@@ -59,6 +59,7 @@ func initAPI() {
beego.Router("/api/add-user", &controllers.ApiController{}, "POST:AddUser")
beego.Router("/api/delete-user", &controllers.ApiController{}, "POST:DeleteUser")
beego.Router("/api/upload-avatar", &controllers.ApiController{}, "POST:UploadAvatar")
+ beego.Router("/api/set-password", &controllers.ApiController{}, "POST:SetPassword")
beego.Router("/api/get-providers", &controllers.ApiController{}, "GET:GetProviders")
beego.Router("/api/get-default-providers", &controllers.ApiController{}, "GET:GetDefaultProviders")
diff --git a/web/src/PasswordModal.js b/web/src/PasswordModal.js
new file mode 100644
index 00000000..3ef2d249
--- /dev/null
+++ b/web/src/PasswordModal.js
@@ -0,0 +1,93 @@
+// Copyright 2021 The casbin Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import {Button, Col, Modal, Row, Input,} from "antd";
+import i18next from "i18next";
+import React from "react";
+import * as UserBackend from "./backend/UserBackend";
+import * as Setting from "./Setting";
+
+export const PasswordModal = (props) => {
+ const [visible, setVisible] = React.useState(false);
+ const [confirmLoading, setConfirmLoading] = React.useState(false);
+ const {user} = props;
+
+ const showModal = () => {
+ setVisible(true);
+ };
+
+ const handleCancel = () => {
+ setVisible(false);
+ };
+
+ const handleOk = () => {
+ let oldPassword = document.getElementById("old-password")?.value;
+ let newPassword = document.getElementById("new-password").value;
+ let rePassword = document.getElementById("re-new-password").value;
+ if (oldPassword === null || oldPassword === undefined) oldPassword = "";
+ if (newPassword === "" || rePassword === "") {
+ Setting.showMessage("error", i18next.t("user:Empty input!"));
+ return;
+ }
+ if (newPassword !== rePassword) {
+ Setting.showMessage("error", i18next.t("user:Two passwords you typed do not match."));
+ return;
+ }
+ setConfirmLoading(true);
+ UserBackend.setPassword(user.owner, user.name, oldPassword, newPassword).then((res) => {
+ setConfirmLoading(false);
+ if (res.status === "ok") {
+ Setting.showMessage("success", i18next.t("user:Password Set"));
+ setVisible(false);
+ }
+ else Setting.showMessage("error", i18next.t(`user:${res.msg}`));
+ })
+ }
+
+ let hasOldPassword = user.password !== "";
+
+ return (
+