commit 6016072301ff0702511e518cfaa3546f2e76054a
parent 4acea12cf4463530874c8c32301a9d4dce13809a
Author: cblgh <cblgh@cblgh.org>
Date: Thu, 14 Dec 2023 09:40:36 +0100
new admin functionality: manually add users! :)
Diffstat:
5 files changed, 101 insertions(+), 18 deletions(-)
diff --git a/constants/constants.go b/constants/constants.go
@@ -7,6 +7,9 @@ const (
// MODLOG_ADMIN_PROPOSE
// MODLOG_ADMIN_CONFIRM
MODLOG_REMOVE_USER
+ MODLOG_ADMIN_ADD_USER
+ /* NOTE: when adding new values, only add them after already existing values! otherwise the existing variables will
+ * receive new values */
// MODLOG_DELETE_VETO
// MODLOG_DELETE_PROPOSE
// MODLOG_DELETE_CONFIRM
diff --git a/database/database.go b/database/database.go
@@ -495,10 +495,8 @@ func (d DB) RemoveUser(userid int) (finalErr error) {
_, err = postsStmt.Exec(deletedUserID, userid)
rollbackOnErr(ed.Eout(err, "exec posts stmt"))
_, err = modlogStmt1.Exec(deletedUserID, userid)
- fmt.Println("modlog1: err?", err)
rollbackOnErr(ed.Eout(err, "exec modlog #1 stmt"))
_, err = modlogStmt2.Exec(deletedUserID, userid)
- fmt.Println("modlog2: err?", err)
rollbackOnErr(ed.Eout(err, "exec modlog #2 stmt"))
_, err = stmtReg.Exec(userid)
rollbackOnErr(ed.Eout(err, "exec registration stmt"))
diff --git a/html/admin.html b/html/admin.html
@@ -1,15 +1,18 @@
{{ template "head" . }}
<main>
<h1>{{ .Title }}</h1>
- <!-- { if .IsAdmin } -->
- <section>
- <p>
- Does someone wish admittance? You can <button>Add new user</button>.
- </p>
- <p>
- If you want to stop being an admin, you can <button>Step down</button>.
- </p>
- </section>
+ <section>
+ <form method="GET" id="add-user" action="/add-user"></form>
+ <p>
+ Does someone wish admittance? You can <button form="add-user" type="submit" href="/add-user">Add new user</button>.
+ </p>
+ <p>
+ If you want to stop being an admin, you can <button>Step down</button>.
+ </p>
+ <p>
+ View past actions in the <a href="/moderations">moderation log</a>.
+ </p>
+ </section>
{{ if .LoggedIn }}
<section>
<h2> Admins </h2>
diff --git a/i18n/i18n.go b/i18n/i18n.go
@@ -29,7 +29,8 @@ var English = map[string]string{
"modlogResetPassword": `<code>{{ .Data.Time }}</code> <b>{{ .Data.ActingUsername }}</b> reset a user's password`,
"modlogResetPasswordAdmin": `<code>{{ .Data.Time }}</code> <b>{{ .Data.ActingUsername }}</b> reset <b> {{ .Data.RecipientUsername}}</b>'s password`,
- "modlogMakeAdmin": `<code>{{ .Data.Time }}</code> <b>{{ .Data.ActingUsername }}</b> made <b> {{ .Data.RecipientUsername}} an admin`,
+ "modlogMakeAdmin": `<code>{{ .Data.Time }}</code> <b>{{ .Data.ActingUsername }}</b> made <b> {{ .Data.RecipientUsername}}</b> an admin`,
+ "modlogAddUser": `<code>{{ .Data.Time }}</code> <b>{{ .Data.ActingUsername }}</b> manually registered an account for <b> {{ .Data.RecipientUsername }}</b>`,
// "modlogProposeAdmin": `<code>{{ .Data.Time }}</code> <b>{{ .Data.ActingUsername }}</b> made <b> {{ .Data.RecipientUsername}} an admin`,
// "modlogVetoAdmin": `<code>{{ .Data.Time }}</code> <b>{{ .Data.ActingUsername }}</b> vetoed making {{ .Data.RecipientUsername }} an admin`,
// "modlogConfirmAdmin": `<code>{{ .Data.Time }}</code> <b>{{ .Data.ActingUsername }}</b> confirmed making {{ .Data.RecipientUsername }} a new admin`,
diff --git a/server/server.go b/server/server.go
@@ -264,6 +264,7 @@ func generateTemplates(config types.Config, translator i18n.Translator) (*templa
"thread",
"admin",
"admins-list",
+ "admin-add-user",
"moderation-log",
"password-reset",
"change-password",
@@ -308,12 +309,14 @@ func (h RequestHandler) renderView(res http.ResponseWriter, viewName string, dat
func (h RequestHandler) renderGenericMessage(res http.ResponseWriter, req *http.Request, incomingData GenericMessageData) {
loggedIn, _ := h.IsLoggedIn(req)
+ isAdmin, _ := h.IsAdmin(req)
data := TemplateData{
Data: incomingData,
// the following two fields are defaults that usually are not set and which are cumbersome to set each time since
// they don't really matter / vary across invocations
HasRSS: h.config.RSS.URL != "",
LoggedIn: loggedIn,
+ IsAdmin: isAdmin,
}
h.renderView(res, "generic-message", data)
return
@@ -393,6 +396,79 @@ func (h *RequestHandler) AdminMakeUserAdmin(res http.ResponseWriter, req *http.R
h.renderGenericMessage(res, req, data)
}
+func (h *RequestHandler) AdminManualAddUserRoute(res http.ResponseWriter, req *http.Request) {
+ ed := util.Describe("admin manually add user")
+ loggedIn, _ := h.IsLoggedIn(req)
+ isAdmin, adminUserid := h.IsAdmin(req)
+
+ if !isAdmin {
+ // redirect to index
+ IndexRedirect(res, req)
+ return
+ }
+
+ type AddUser struct {
+ ErrorMessage string
+ }
+
+ var data AddUser
+ view := TemplateData{Title: "Add a new user", Data: &data, HasRSS: false, IsAdmin: isAdmin, LoggedIn: loggedIn}
+
+ if req.Method == "GET" {
+ h.renderView(res, "admin-add-user", view)
+ return
+ }
+
+ if req.Method == "POST" && isAdmin {
+ username := req.PostFormValue("username")
+
+ // do a lil quick checky check to see if we already have that username registered,
+ // and if we do re-render the page with an error
+ existed, err := h.db.CheckUsernameExists(username)
+ ed.Check(err, "check username exists")
+
+ if existed {
+ data.ErrorMessage = fmt.Sprintf("Username (%s) is already registered", username)
+ h.renderView(res, "admin-add-user", view)
+ return
+ }
+
+ // set up basic credentials
+ newPassword := crypto.GeneratePassword()
+ passwordHash, err := crypto.HashPassword(newPassword)
+ ed.Check(err, "hash password")
+ targetUserid, err := h.db.CreateUser(username, passwordHash)
+ ed.Check(err, "create new user %s", username)
+
+ // if err != nil {
+ // // TODO (2023-12-09): bubble up error to visible page as feedback for admin
+ // errMsg := ed.Eout(err, "reset password failed")
+ // fmt.Println(errMsg)
+ // data := GenericMessageData{
+ // Title: "Admin reset password",
+ // Message: errMsg.Error(),
+ // }
+ // h.renderGenericMessage(res, req, data)
+ // return
+ // }
+
+ err = h.db.AddModerationLog(adminUserid, targetUserid, constants.MODLOG_ADMIN_ADD_USER)
+ if err != nil {
+ fmt.Println(ed.Eout(err, "error adding moderation log"))
+ }
+
+ // output copy-pastable credentials page for admin to send to the user
+ data := GenericMessageData{
+ Title: "User successfully added",
+ Message: fmt.Sprintf("Instructions: %s's password was set to: %s. After logging in, please change your password by going to /reset", username, newPassword),
+ LinkMessage: "Go back to the",
+ LinkText: "add user view",
+ Link: "/add-user",
+ }
+ h.renderGenericMessage(res, req, data)
+ }
+}
+
func (h *RequestHandler) AdminResetUserPassword(res http.ResponseWriter, req *http.Request, targetUserid int) {
ed := util.Describe("admin reset password")
loggedIn, _ := h.IsLoggedIn(req)
@@ -468,6 +544,8 @@ func (h *RequestHandler) ModerationLogRoute(res http.ResponseWriter, req *http.R
translationString = "modlogMakeAdmin"
case constants.MODLOG_REMOVE_USER:
translationString = "modlogRemoveUser"
+ case constants.MODLOG_ADMIN_ADD_USER:
+ translationString = "modlogAddUser"
}
str := h.translator.TranslateWithData(translationString, i18n.TranslationData{Data: tdata})
viewData.Log = append(viewData.Log, str)
@@ -502,10 +580,10 @@ func (h *RequestHandler) AdminRoute(res http.ResponseWriter, req *http.Request)
}
return
}
- if req.Method == "GET" && loggedIn {
- if !isAdmin {
- // TODO (2023-12-10): redirect to /admins
- h.ListAdminsRoute(res, req)
+ if req.Method == "GET" {
+ if !loggedIn || !isAdmin {
+ // non-admin users get a different view
+ h.ListAdmins(res, req)
return
}
admins := h.db.GetAdmins()
@@ -516,7 +594,7 @@ func (h *RequestHandler) AdminRoute(res http.ResponseWriter, req *http.Request)
}
}
-func (h *RequestHandler) ListAdminsRoute(res http.ResponseWriter, req *http.Request) {
+func (h *RequestHandler) ListAdmins(res http.ResponseWriter, req *http.Request) {
loggedIn, _ := h.IsLoggedIn(req)
admins := h.db.GetAdmins()
data := AdminsData{Admins: admins}
@@ -1118,8 +1196,8 @@ func NewServer(allowlist []string, sessionKey, dir string, config types.Config)
// TODO (2022-01-10): introduce middleware to make sure there is never an issue with trailing slashes
s.ServeMux.HandleFunc("/reset/", handler.ResetPasswordRoute)
s.ServeMux.HandleFunc("/admin", handler.AdminRoute)
+ s.ServeMux.HandleFunc("/add-user", handler.AdminManualAddUserRoute)
s.ServeMux.HandleFunc("/moderations", handler.ModerationLogRoute)
- s.ServeMux.HandleFunc("/admins", handler.ListAdminsRoute)
s.ServeMux.HandleFunc("/about", handler.AboutRoute)
s.ServeMux.HandleFunc("/logout", handler.LogoutRoute)
s.ServeMux.HandleFunc("/login", handler.LoginRoute)