2021-05-09 10:42:12 +00:00
|
|
|
package web
|
|
|
|
|
|
|
|
import (
|
2021-06-29 15:10:48 +00:00
|
|
|
"fmt"
|
2021-05-09 11:09:27 +00:00
|
|
|
"io"
|
2021-05-09 10:42:12 +00:00
|
|
|
"log"
|
2021-06-29 18:43:04 +00:00
|
|
|
"mime"
|
2021-05-09 10:42:12 +00:00
|
|
|
"net/http"
|
2021-06-29 10:34:36 +00:00
|
|
|
"sort"
|
2021-06-29 15:10:48 +00:00
|
|
|
"strings"
|
2021-05-09 10:42:12 +00:00
|
|
|
|
|
|
|
"github.com/bouncepaw/mycorrhiza/cfg"
|
|
|
|
"github.com/bouncepaw/mycorrhiza/user"
|
|
|
|
"github.com/bouncepaw/mycorrhiza/util"
|
|
|
|
"github.com/bouncepaw/mycorrhiza/views"
|
|
|
|
)
|
|
|
|
|
2021-05-09 11:09:27 +00:00
|
|
|
// initAdmin sets up /admin routes if auth is used. Call it after you have decided if you want to use auth.
|
|
|
|
func initAdmin() {
|
2021-07-02 08:20:03 +00:00
|
|
|
if cfg.UseAuth {
|
2021-06-29 18:43:04 +00:00
|
|
|
http.HandleFunc("/admin/", handlerAdmin)
|
|
|
|
http.HandleFunc("/admin/shutdown/", handlerAdminShutdown)
|
|
|
|
http.HandleFunc("/admin/reindex-users/", handlerAdminReindexUsers)
|
2021-06-29 10:34:36 +00:00
|
|
|
|
2021-06-29 15:10:48 +00:00
|
|
|
http.HandleFunc("/admin/users/", handlerAdminUsers)
|
2021-07-02 12:02:42 +00:00
|
|
|
http.HandleFunc("/admin/user/new", handlerAdminUserNew)
|
2021-05-09 10:42:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-09 11:09:27 +00:00
|
|
|
// handlerAdmin provides the admin panel.
|
|
|
|
func handlerAdmin(w http.ResponseWriter, rq *http.Request) {
|
2021-05-09 10:42:12 +00:00
|
|
|
util.PrepareRq(rq)
|
|
|
|
if user.CanProceed(rq, "admin") {
|
|
|
|
w.Header().Set("Content-Type", "text/html;charset=utf-8")
|
|
|
|
w.WriteHeader(http.StatusOK)
|
2021-05-09 11:09:27 +00:00
|
|
|
_, err := io.WriteString(w, views.BaseHTML("Admin panel", views.AdminPanelHTML(), user.FromRequest(rq)))
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
}
|
2021-05-09 10:42:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-09 11:09:27 +00:00
|
|
|
// handlerAdminShutdown kills the wiki.
|
|
|
|
func handlerAdminShutdown(w http.ResponseWriter, rq *http.Request) {
|
2021-05-09 10:42:12 +00:00
|
|
|
util.PrepareRq(rq)
|
|
|
|
if user.CanProceed(rq, "admin/shutdown") && rq.Method == "POST" {
|
|
|
|
log.Fatal("An admin commanded the wiki to shutdown")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-09 11:09:27 +00:00
|
|
|
// handlerAdminReindexUsers reinitialises the user system.
|
|
|
|
func handlerAdminReindexUsers(w http.ResponseWriter, rq *http.Request) {
|
2021-05-09 10:42:12 +00:00
|
|
|
util.PrepareRq(rq)
|
|
|
|
if user.CanProceed(rq, "admin") && rq.Method == "POST" {
|
|
|
|
user.ReadUsersFromFilesystem()
|
2021-06-29 15:10:48 +00:00
|
|
|
redirectTo := rq.Referer()
|
|
|
|
if redirectTo == "" {
|
2021-06-29 18:43:04 +00:00
|
|
|
redirectTo = "/hypha/" + cfg.UserHypha
|
2021-06-29 15:10:48 +00:00
|
|
|
}
|
|
|
|
http.Redirect(w, rq, redirectTo, http.StatusSeeOther)
|
2021-05-09 10:42:12 +00:00
|
|
|
}
|
|
|
|
}
|
2021-06-29 10:34:36 +00:00
|
|
|
|
|
|
|
func handlerAdminUsers(w http.ResponseWriter, r *http.Request) {
|
|
|
|
util.PrepareRq(r)
|
|
|
|
if user.CanProceed(r, "admin") {
|
2021-06-29 15:10:48 +00:00
|
|
|
path := strings.TrimPrefix(r.URL.Path, "/admin/users")
|
|
|
|
parts := strings.Split(path, "/")[1:]
|
|
|
|
|
|
|
|
// Users dashboard
|
|
|
|
if len(parts) == 0 {
|
|
|
|
// Get a sorted list of users
|
|
|
|
var userList []*user.User
|
|
|
|
for u := range user.YieldUsers() {
|
|
|
|
userList = append(userList, u)
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Slice(userList, func(i, j int) bool {
|
|
|
|
less := userList[i].RegisteredAt.Before(userList[j].RegisteredAt)
|
|
|
|
return less
|
|
|
|
})
|
|
|
|
|
|
|
|
html := views.AdminUsersPanelHTML(userList)
|
|
|
|
html = views.BaseHTML("Manage users", html, user.FromRequest(r))
|
|
|
|
|
|
|
|
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
|
|
|
|
if _, err := io.WriteString(w, html); err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
}
|
|
|
|
return
|
2021-06-29 10:34:36 +00:00
|
|
|
}
|
|
|
|
|
2021-07-02 14:04:00 +00:00
|
|
|
if len(parts) != 2 {
|
|
|
|
util.HTTP404Page(w, "404 page not found")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
u := user.UserByName(parts[0])
|
|
|
|
if u == nil {
|
|
|
|
util.HTTP404Page(w, "404 page not found")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
switch parts[1] {
|
|
|
|
case "edit":
|
|
|
|
f := util.FormDataFromRequest(r, []string{"group"})
|
2021-06-29 10:34:36 +00:00
|
|
|
|
2021-07-02 14:04:00 +00:00
|
|
|
if r.Method == http.MethodPost {
|
|
|
|
oldGroup := u.Group
|
|
|
|
newGroup := f.Get("group")
|
2021-06-29 10:34:36 +00:00
|
|
|
|
2021-07-02 14:04:00 +00:00
|
|
|
if user.ValidGroup(newGroup) {
|
|
|
|
u.Group = newGroup
|
|
|
|
if err := user.SaveUserDatabase(); err != nil {
|
|
|
|
u.Group = oldGroup
|
2021-06-29 15:10:48 +00:00
|
|
|
log.Println(err)
|
2021-07-02 14:04:00 +00:00
|
|
|
f = f.WithError(err)
|
2021-06-29 15:10:48 +00:00
|
|
|
} else {
|
2021-07-02 14:04:00 +00:00
|
|
|
http.Redirect(w, r, "/admin/users/", http.StatusSeeOther)
|
|
|
|
return
|
2021-06-29 15:10:48 +00:00
|
|
|
}
|
2021-07-02 14:04:00 +00:00
|
|
|
} else {
|
|
|
|
f = f.WithError(fmt.Errorf("invalid group \"%s\"", newGroup))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
f.Put("group", u.Group)
|
|
|
|
|
|
|
|
html := views.AdminUserEditHTML(u, f)
|
|
|
|
html = views.BaseHTML(fmt.Sprintf("User %s", u.Name), html, user.FromRequest(r))
|
|
|
|
|
|
|
|
if f.HasError() {
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
}
|
|
|
|
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
|
|
|
|
io.WriteString(w, html)
|
|
|
|
return
|
|
|
|
case "delete":
|
|
|
|
f := util.NewFormData()
|
|
|
|
|
|
|
|
if r.Method == http.MethodPost {
|
|
|
|
f = f.WithError(user.DeleteUser(u.Name))
|
|
|
|
if !f.HasError() {
|
|
|
|
http.Redirect(w, r, "/admin/users/", http.StatusSeeOther)
|
|
|
|
} else {
|
|
|
|
log.Println(f.Error())
|
2021-06-29 15:10:48 +00:00
|
|
|
}
|
|
|
|
}
|
2021-07-02 14:04:00 +00:00
|
|
|
|
|
|
|
html := views.AdminUserDeleteHTML(u, util.NewFormData())
|
|
|
|
html = views.BaseHTML(fmt.Sprintf("User %s", u.Name), html, user.FromRequest(r))
|
|
|
|
|
|
|
|
if f.HasError() {
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
}
|
|
|
|
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
|
|
|
|
io.WriteString(w, html)
|
|
|
|
return
|
2021-06-29 10:34:36 +00:00
|
|
|
}
|
2021-07-02 12:02:42 +00:00
|
|
|
|
2021-07-02 14:04:00 +00:00
|
|
|
util.HTTP404Page(w, "404 page not found")
|
|
|
|
}
|
2021-07-02 12:02:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func handlerAdminUserNew(w http.ResponseWriter, r *http.Request) {
|
|
|
|
util.PrepareRq(r)
|
|
|
|
if user.CanProceed(r, "admin") {
|
|
|
|
if r.Method == http.MethodGet {
|
|
|
|
// New user form
|
|
|
|
html := views.AdminUserNewHTML(util.NewFormData())
|
|
|
|
html = views.BaseHTML("New user", html, user.FromRequest(r))
|
2021-06-29 15:10:48 +00:00
|
|
|
|
2021-07-02 12:02:42 +00:00
|
|
|
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
|
|
|
|
io.WriteString(w, html)
|
|
|
|
return
|
|
|
|
} else if r.Method == http.MethodPost {
|
|
|
|
// Create a user
|
|
|
|
f := util.FormDataFromRequest(r, []string{"name", "password", "group"})
|
|
|
|
|
|
|
|
err := user.Register(f.Get("name"), f.Get("password"), f.Get("group"), true)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
html := views.AdminUserNewHTML(f.WithError(err))
|
|
|
|
html = views.BaseHTML("New user", html, user.FromRequest(r))
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
|
|
|
|
io.WriteString(w, html)
|
|
|
|
} else {
|
|
|
|
http.Redirect(w, r, "/admin/users/", http.StatusSeeOther)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
2021-06-29 10:34:36 +00:00
|
|
|
}
|
2021-07-02 12:02:42 +00:00
|
|
|
|
|
|
|
util.HTTP404Page(w, "404 page not found")
|
2021-06-29 10:34:36 +00:00
|
|
|
}
|