Added ability to allow admin to 'su' to other accounts (#1812)

* Added ability to allow admin to 'su' to other accounts

* Naming convention and user message modifications

* Removed debug statement
pull/1557/merge
Glenn Wilkinson 2020-04-28 00:19:20 +01:00 committed by GitHub
parent 26e82cb2e3
commit 38a6a77c9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 76 additions and 1 deletions

View File

@ -130,6 +130,7 @@ func (as *AdminServer) registerRoutes() {
router.HandleFunc("/settings", mid.Use(as.Settings, mid.RequireLogin)) router.HandleFunc("/settings", mid.Use(as.Settings, mid.RequireLogin))
router.HandleFunc("/users", mid.Use(as.UserManagement, mid.RequirePermission(models.PermissionModifySystem), 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("/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 // Create the API routes
api := api.NewServer(api.WithWorker(as.worker)) api := api.NewServer(api.WithWorker(as.worker))
router.PathPrefix("/api/").Handler(api) 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) 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, // Login handles the authentication flow for a user. If credentials are valid,
// a session is created // a session is created
func (as *AdminServer) Login(w http.ResponseWriter, r *http.Request) { func (as *AdminServer) Login(w http.ResponseWriter, r *http.Request) {

View File

@ -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 <strong>" + escapeHtml(user.username) + "</strong>",
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 <strong>" + escapeHtml(user.username) + "</strong>.",
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 <strong>" + escapeHtml(user.username) + "</strong>.",
showCancelButton: false,
})
}
})
}
})
}
const load = () => { const load = () => {
$("#userTable").hide() $("#userTable").hide()
@ -136,7 +185,11 @@ const load = () => {
userTable.row.add([ userTable.row.add([
escapeHtml(user.username), escapeHtml(user.username),
escapeHtml(user.role.name), escapeHtml(user.role.name),
"<div class='pull-right'><button class='btn btn-primary edit_button' data-toggle='modal' data-backdrop='static' data-target='#modal' data-user-id='" + user.id + "'>\ "<div class='pull-right'>\
<button class='btn btn-warning impersonate_button' data-user-id='" + user.id + "'>\
<i class='fa fa-retweet'></i>\
</button>\
<button class='btn btn-primary edit_button' data-toggle='modal' data-backdrop='static' data-target='#modal' data-user-id='" + user.id + "'>\
<i class='fa fa-pencil'></i>\ <i class='fa fa-pencil'></i>\
</button>\ </button>\
<button class='btn btn-danger delete_button' data-user-id='" + user.id + "'>\ <button class='btn btn-danger delete_button' data-user-id='" + user.id + "'>\
@ -180,4 +233,7 @@ $(document).ready(function () {
$("#userTable").on('click', '.delete_button', function (e) { $("#userTable").on('click', '.delete_button', function (e) {
deleteUser($(this).attr('data-user-id')) deleteUser($(this).attr('data-user-id'))
}) })
$("#userTable").on('click', '.impersonate_button', function (e) {
impersonate($(this).attr('data-user-id'))
})
}); });