cerca

lean forum software (pmc local branch)
Log | Files | Refs | README | LICENSE

commit 391ef20c129638b3e67594f8182633120dea67de
parent 6016072301ff0702511e518cfaa3546f2e76054a
Author: cblgh <cblgh@cblgh.org>
Date:   Fri, 15 Dec 2023 09:41:34 +0100

introduce admin demotion functionality

Diffstat:
Mconstants/constants.go | 3+--
Mdatabase/database.go | 27+++++++++++++++++++++++++++
Mhtml/admin.html | 18++++++++++++++----
Mi18n/i18n.go | 3+++
Mserver/server.go | 50++++++++++++++++++++++++++++++++++++++++++++++----
5 files changed, 91 insertions(+), 10 deletions(-)

diff --git a/constants/constants.go b/constants/constants.go @@ -4,10 +4,9 @@ const ( MODLOG_RESETPW = iota MODLOG_ADMIN_VETO MODLOG_ADMIN_MAKE - // MODLOG_ADMIN_PROPOSE - // MODLOG_ADMIN_CONFIRM MODLOG_REMOVE_USER MODLOG_ADMIN_ADD_USER + MODLOG_ADMIN_DEMOTE /* NOTE: when adding new values, only add them after already existing values! otherwise the existing variables will * receive new values */ // MODLOG_DELETE_VETO diff --git a/database/database.go b/database/database.go @@ -632,6 +632,33 @@ func (d DB) AddAdmin(userid int) error { return nil } +func (d DB) DemoteAdmin(userid int) error { + ed := util.Describe("demote admin") + // make sure the id exists + exists, err := d.CheckUserExists(userid) + if !exists { + return errors.New(fmt.Sprintf("demote admin: userid %d did not exist", userid)) + } + if err != nil { + return ed.Eout(err, "CheckUserExists had an error") + } + isAdmin, err := d.IsUserAdmin(userid) + if !isAdmin { + return errors.New(fmt.Sprintf("demote admin: userid %d was not an admin", userid)) + } + if err != nil { + // some kind of error, let's bubble it up + return ed.Eout(err, "IsUserAdmin") + } + // all checks are done: perform the removal + stmt := `DELETE FROM admins WHERE id = ?` + _, err = d.db.Exec(stmt, userid) + if err != nil { + return ed.Eout(err, "inserting new admin") + } + return nil +} + func (d DB) IsUserAdmin (userid int) (bool, error) { stmt := `SELECT 1 FROM admins WHERE id = ?` return d.existsQuery(stmt, userid) diff --git a/html/admin.html b/html/admin.html @@ -4,10 +4,14 @@ <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>. + <form method="POST" id="demote-self" action="/demote-admin"> + <input type="hidden" name="userid" value="{{ .LoggedInID }}"> + </form> + <p> + Does someone wish admittance? You can <button form="add-user" type="submit">Add new user</button>. </p> <p> - If you want to stop being an admin, you can <button>Step down</button>. + If you want to stop being an admin, you can <button form="demote-self" type="submit">Step down</button>. </p> <p> View past actions in the <a href="/moderations">moderation log</a>. @@ -19,9 +23,15 @@ {{ if len .Data.Admins | eq 0 }} <p> there are no admins; chaos reigns </p> {{ else }} - {{ $userID := .LoggedInID }} <!-- do some kind of extra styling to indicate "hey this is you!" --> + {{ $userID := .LoggedInID }} {{ range $index, $user := .Data.Admins }} - <p> {{ $user.Name }} ({{ $user.ID }}) {{ if eq $userID $user.ID }} <i>(this is you!)</i>{{ end }}</p> + <form method="POST" id="demote-admin-{{$user.ID}}" action="/demote-admin"> + <input type="hidden" name="userid" value="{{ $user.ID }}"> + </form> + <p> {{ $user.Name }} ({{ $user.ID }}) + {{ if eq $userID $user.ID }} <i>(this is you!)</i> + {{ else }}<button type="submit" form="demote-admin-{{$user.ID}}">Demote</button>{{ end }} + </p> {{ end }} {{ end }} </section> diff --git a/i18n/i18n.go b/i18n/i18n.go @@ -31,6 +31,9 @@ var English = map[string]string{ "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}}</b> an admin`, "modlogAddUser": `<code>{{ .Data.Time }}</code> <b>{{ .Data.ActingUsername }}</b> manually registered an account for <b> {{ .Data.RecipientUsername }}</b>`, + "modlogDemoteAdmin": `<code>{{ .Data.Time }}</code> <b>{{ .Data.ActingUsername }}</b> demoted <b> + {{ if eq .Data.ActingUsername .Data.RecipientUsername }} themselves + {{ else }} {{ .Data.RecipientUsername}} {{ end }}</b> from admin back to normal user`, // "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 @@ -327,7 +327,6 @@ func (h *RequestHandler) AdminRemoveUser(res http.ResponseWriter, req *http.Requ loggedIn, _ := h.IsLoggedIn(req) isAdmin, adminUserid := h.IsAdmin(req) if req.Method == "GET" || !loggedIn || !isAdmin { - // redirect to index IndexRedirect(res, req) return } @@ -359,7 +358,6 @@ func (h *RequestHandler) AdminMakeUserAdmin(res http.ResponseWriter, req *http.R loggedIn, _ := h.IsLoggedIn(req) isAdmin, adminUserid := h.IsAdmin(req) if req.Method == "GET" || !loggedIn || !isAdmin { - // redirect to index IndexRedirect(res, req) return } @@ -396,13 +394,55 @@ func (h *RequestHandler) AdminMakeUserAdmin(res http.ResponseWriter, req *http.R h.renderGenericMessage(res, req, data) } +func (h *RequestHandler) AdminDemoteAdmin(res http.ResponseWriter, req *http.Request) { + ed := util.Describe("demote admin route") + loggedIn, _ := h.IsLoggedIn(req) + isAdmin, adminUserid := h.IsAdmin(req) + if req.Method == "GET" || !loggedIn || !isAdmin { + IndexRedirect(res, req) + return + } + useridString := req.PostFormValue("userid") + targetUserid, err := strconv.Atoi(useridString) + util.Check(err, "convert user id string to a plain userid") + + // TODO (2023-12-10): introduce 2-quorom + err = h.db.DemoteAdmin(targetUserid) + + if err != nil { + errMsg := ed.Eout(err, "demote admin failed") + fmt.Println(errMsg) + data := GenericMessageData{ + Title: "Demote admin", + Message: errMsg.Error(), + } + h.renderGenericMessage(res, req, data) + return + } + + username, _ := h.db.GetUsername(targetUserid) + err = h.db.AddModerationLog(adminUserid, targetUserid, constants.MODLOG_ADMIN_DEMOTE) + 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: "Demote admin success", + Message: fmt.Sprintf("User %s is now a regular user", username), + LinkMessage: "Go back to the", + LinkText: "admin view", + Link: "/admin", + } + 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 } @@ -474,7 +514,6 @@ func (h *RequestHandler) AdminResetUserPassword(res http.ResponseWriter, req *ht loggedIn, _ := h.IsLoggedIn(req) isAdmin, adminUserid := h.IsAdmin(req) if req.Method == "GET" || !loggedIn || !isAdmin { - // redirect to index IndexRedirect(res, req) return } @@ -546,6 +585,8 @@ func (h *RequestHandler) ModerationLogRoute(res http.ResponseWriter, req *http.R translationString = "modlogRemoveUser" case constants.MODLOG_ADMIN_ADD_USER: translationString = "modlogAddUser" + case constants.MODLOG_ADMIN_DEMOTE: + translationString = "modlogDemoteAdmin" } str := h.translator.TranslateWithData(translationString, i18n.TranslationData{Data: tdata}) viewData.Log = append(viewData.Log, str) @@ -1196,6 +1237,7 @@ 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("/demote-admin", handler.AdminDemoteAdmin) s.ServeMux.HandleFunc("/add-user", handler.AdminManualAddUserRoute) s.ServeMux.HandleFunc("/moderations", handler.ModerationLogRoute) s.ServeMux.HandleFunc("/about", handler.AboutRoute)