diff --git a/controllers/route.go b/controllers/route.go index e512c1c0..fae4ddc7 100644 --- a/controllers/route.go +++ b/controllers/route.go @@ -130,6 +130,7 @@ func (as *AdminServer) registerRoutes() { router.HandleFunc("/settings", mid.Use(as.Settings, mid.RequireLogin)) router.HandleFunc("/users", mid.Use(as.UserManagement, mid.RequirePermission(models.PermissionModifySystem), mid.RequireLogin)) router.HandleFunc("/webhooks", mid.Use(as.Webhooks, mid.RequirePermission(models.PermissionModifySystem), mid.RequireLogin)) + router.HandleFunc("/impersonate", mid.Use(as.Impersonate, mid.RequirePermission(models.PermissionModifySystem), mid.RequireLogin)) // Create the API routes api := api.NewServer(api.WithWorker(as.worker)) router.PathPrefix("/api/").Handler(api) @@ -269,6 +270,24 @@ func (as *AdminServer) Webhooks(w http.ResponseWriter, r *http.Request) { getTemplate(w, "webhooks").ExecuteTemplate(w, "base", params) } +// Impersonate allows an admin to login to a user account without needing the password +func (as *AdminServer) Impersonate(w http.ResponseWriter, r *http.Request) { + + if r.Method == "POST" { + username := r.FormValue("username") + u, err := models.GetUserByUsername(username) + if err != nil { + log.Error(err) + http.Error(w, err.Error(), http.StatusNotFound) + return + } + session := ctx.Get(r, "session").(*sessions.Session) + session.Values["id"] = u.Id + session.Save(r, w) + } + http.Redirect(w, r, "/", 302) +} + // Login handles the authentication flow for a user. If credentials are valid, // a session is created func (as *AdminServer) Login(w http.ResponseWriter, r *http.Request) { diff --git a/static/js/src/app/users.js b/static/js/src/app/users.js index b1e44c5b..ec4fd12f 100644 --- a/static/js/src/app/users.js +++ b/static/js/src/app/users.js @@ -115,6 +115,55 @@ const deleteUser = (id) => { }) } +const impersonate = (id) => { + var user = users.find(x => x.id == id) + if (!user) { + return + } + Swal.fire({ + title: "Are you sure?", + html: "You will be logged out of your account and logged in as " + escapeHtml(user.username) + "", + type: "warning", + animation: false, + showCancelButton: true, + confirmButtonText: "Swap User", + confirmButtonColor: "#428bca", + reverseButtons: true, + allowOutsideClick: false, + }).then((result) => { + if (result.value) { + + fetch('/impersonate', { + method: 'post', + body: "username=" + user.username + "&csrf_token=" + encodeURIComponent(csrf_token), + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + }).then((response) => { + if (response.status == 200) { + Swal.fire({ + title: "Success!", + html: "Successfully changed to user " + escapeHtml(user.username) + ".", + type: "success", + showCancelButton: false, + confirmButtonText: "Home", + allowOutsideClick: false, + }).then((result) => { + if (result.value) { + window.location.href = "/" + }}); + } else { + Swal.fire({ + title: "Error!", + type: "error", + html: "Failed to change to user " + escapeHtml(user.username) + ".", + showCancelButton: false, + }) + } + }) + } + }) +} const load = () => { $("#userTable").hide() @@ -136,7 +185,11 @@ const load = () => { userTable.row.add([ escapeHtml(user.username), escapeHtml(user.role.name), - "