1
0
Fork 0
mirror of https://github.com/Eggbertx/gochan.git synced 2025-09-13 09:26:23 -07:00

Add rank updating for admins

This commit is contained in:
Eggbertx 2024-12-14 22:17:45 -08:00
parent 9f34be91d2
commit 478e8a1abe
4 changed files with 97 additions and 36 deletions

View file

@ -77,7 +77,11 @@ func (s *Staff) ClearSessions() error {
if s.ID == 0 {
// ID field not set, get it from the DB
if err = QueryRowContextSQL(ctx, nil, query, []any{s.Username}, []any{&s.ID}); err != nil {
err = QueryRowContextSQL(ctx, nil, query, []any{s.Username}, []any{&s.ID})
if errors.Is(err, sql.ErrNoRows) {
return ErrUnrecognizedUsername
}
if err != nil {
return err
}
}
@ -96,11 +100,29 @@ func (s *Staff) RankTitle() string {
return ""
}
// UpdateStaff sets the rank and password of the staff account with the given username
func UpdateStaff(username string, rank int, password string) error {
// first check if it's a recognized username
id, err := GetStaffID(username)
if err != nil {
return err
}
const sqlUpdate = `UPDATE DBPREFIXstaff SET global_rank = ?, password_checksum = ? WHERE id = ?`
checksum := gcutil.BcryptSum(password)
_, err = ExecTimeoutSQL(nil, sqlUpdate, rank, checksum, id)
return err
}
// UpdateStaff sets the password of the staff account with the given username
func UpdatePassword(username string, newPassword string) error {
const sqlUPDATE = `UPDATE DBPREFIXstaff SET password_checksum = ? WHERE username = ?`
const sqlUPDATE = `UPDATE DBPREFIXstaff SET password_checksum = ? WHERE id = ?`
id, err := GetStaffID(username)
if err != nil {
return err
}
checksum := gcutil.BcryptSum(newPassword)
_, err := ExecTimeoutSQL(nil, sqlUPDATE, checksum, username)
_, err = ExecTimeoutSQL(nil, sqlUPDATE, checksum, id)
return err
}
@ -147,11 +169,15 @@ func GetStaffUsernameFromID(id int) (string, error) {
return username, err
}
// GetStaffID gets the ID of the given staff, given the username, and returns ErrUnrecognizedUsername if none match
func GetStaffID(username string) (int, error) {
const query = `SELECT id FROM DBPREFIXstaff WHERE username = ?`
const query = `SELECT id FROM DBPREFIXstaff WHERE username = ?`
var id int
err := QueryRowTimeoutSQL(nil, query, []any{username}, []any{&id})
if errors.Is(err, sql.ErrNoRows) {
err = ErrUnrecognizedUsername
}
return id, err
}

View file

@ -21,7 +21,6 @@ import (
)
var (
ErrPasswordConfirm = errors.New("passwords do not match")
ErrInsufficientPermission = errors.New("insufficient account permission")
)

View file

@ -17,6 +17,10 @@ import (
"github.com/rs/zerolog"
)
var (
ErrPasswordsDoNotMatch = errors.New("passwords do not match")
)
// manage actions that require at least janitor-level permission go here
func logoutCallback(writer http.ResponseWriter, request *http.Request, _ *gcsql.Staff, _ bool, _ *zerolog.Event, _ *zerolog.Event) (output interface{}, err error) {
@ -116,7 +120,6 @@ func announcementsCallback(_ http.ResponseWriter, _ *http.Request, _ *gcsql.Staf
}
func staffCallback(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool, _ *zerolog.Event, errEv *zerolog.Event) (output interface{}, err error) {
var outputStr string
do := request.FormValue("do")
allStaff, err := getAllStaffNopass(true)
if wantsJSON {
@ -130,16 +133,21 @@ func staffCallback(writer http.ResponseWriter, request *http.Request, staff *gcs
err = errors.New("Error getting staff list: " + err.Error())
return "", err
}
warnEv := gcutil.LogWarning().
Str("IP", gcutil.GetRealIP(request)).
Str("userAgent", request.UserAgent()).
Str("staff", staff.Username)
defer warnEv.Discard()
updateUsername := request.FormValue("update")
username := request.FormValue("username")
password := request.FormValue("password")
username := request.PostFormValue("username")
password := request.PostFormValue("password")
passwordConfirm := request.FormValue("passwordconfirm")
if (do == "add" || do == "update") && password != passwordConfirm {
return "", ErrPasswordConfirm
return "", ErrPasswordsDoNotMatch
}
rankStr := request.FormValue("rank")
rankStr := request.PostFormValue("rank")
var rank int
if rankStr != "" {
if rank, err = strconv.Atoi(rankStr); err != nil {
@ -149,11 +157,32 @@ func staffCallback(writer http.ResponseWriter, request *http.Request, staff *gcs
}
}
data := map[string]any{
"do": do,
"updateUsername": updateUsername,
"allstaff": allStaff,
"currentStaff": staff,
}
if updateUsername != "" && staff.Rank == AdminPerms {
var found bool
for _, user := range allStaff {
if user.Username == updateUsername {
data["updateRank"] = user.Rank
found = true
break
}
}
if !found {
writer.WriteHeader(http.StatusBadRequest)
errEv.Err(gcsql.ErrUnrecognizedUsername).Caller().Str("username", updateUsername).Send()
return "", gcsql.ErrUnrecognizedUsername
}
}
if do == "add" {
if staff.Rank < 3 {
writer.WriteHeader(http.StatusUnauthorized)
errEv.Err(ErrInsufficientPermission).Caller().
Int("rank", staff.Rank).Send()
warnEv.Caller().Str("username", username).Msg("non-admin tried to create a new account")
return "", ErrInsufficientPermission
}
if _, err = gcsql.NewStaff(username, password, rank); err != nil {
@ -168,8 +197,7 @@ func staffCallback(writer http.ResponseWriter, request *http.Request, staff *gcs
} else if do == "del" && username != "" {
if staff.Rank < 3 {
writer.WriteHeader(http.StatusUnauthorized)
errEv.Err(ErrInsufficientPermission).Caller().
Int("rank", staff.Rank).Send()
warnEv.Msg("non-admin tried to deactivate an account")
return "", ErrInsufficientPermission
}
if err = gcsql.DeactivateStaff(username); err != nil {
@ -180,40 +208,46 @@ func staffCallback(writer http.ResponseWriter, request *http.Request, staff *gcs
username, staff.Username, err.Error())
}
} else if do == "update" && updateUsername != "" {
if staff.Username != updateUsername && staff.Rank < 3 {
if (staff.Username != updateUsername || rank > 0) && staff.Rank < 3 {
writer.WriteHeader(http.StatusUnauthorized)
errEv.Err(ErrInsufficientPermission).Caller().
Int("rank", staff.Rank).Send()
warnEv.Caller().Str("username", username).Msg("non-admin tried to modify a staff account's rank")
return "", ErrInsufficientPermission
}
if err = gcsql.UpdatePassword(updateUsername, password); err != nil {
if rank > 0 {
err = gcsql.UpdateStaff(updateUsername, rank, password)
} else {
err = gcsql.UpdatePassword(updateUsername, password)
}
if err != nil {
logRank := rank
if logRank == 0 {
// user does not have admin rank and is updating their own account
logRank = staff.Rank
}
errEv.Err(err).Caller().
Str("updateStaff", username).
Msg("Error updating password")
return "", err
Int("updateRank", logRank).
Msg("Error updating account")
writer.WriteHeader(http.StatusInternalServerError)
return "", errors.New("unable to update staff account")
}
}
if do == "add" || do == "del" {
} else if do == "add" || do == "del" {
allStaff, err = getAllStaffNopass(true)
if err != nil {
errEv.Err(err).Caller().Msg("Error getting updated staff list")
err = errors.New("Error getting updated staff list: " + err.Error())
writer.WriteHeader(http.StatusInternalServerError)
err = errors.New("Unable to get updated staff list")
return "", err
}
}
staffBuffer := bytes.NewBufferString("")
if err = serverutil.MinifyTemplate(gctemplates.ManageStaff, map[string]interface{}{
"do": do,
"updateUsername": updateUsername,
"allstaff": allStaff,
"currentStaff": staff,
}, staffBuffer, "text/html"); err != nil {
if err = serverutil.MinifyTemplate(gctemplates.ManageStaff, data, staffBuffer, "text/html"); err != nil {
errEv.Err(err).Str("template", "manage_staff.html").Send()
return "", errors.New("Error executing staff management page template: " + err.Error())
writer.WriteHeader(http.StatusInternalServerError)
return "", errors.New("Unable to execute staff management page template")
}
outputStr += staffBuffer.String()
return outputStr, nil
return staffBuffer.String(), nil
}
func registerJanitorPages() {

View file

@ -32,12 +32,14 @@
<tr><td>Username:</td><td><input id="username" name="username" type="text" value="{{if $isAdmin}}{{.updateUsername}}{{else}}{{.currentStaff.Username}}{{end}}" {{if not $showNewStaffForm}}disabled{{end}}/></td></tr>
<tr><td>Password:</td><td><input id="password" name="password" type="password"/></td></tr>
<tr><td>Confirm password:</td><td><input id="passwordconfirm" name="passwordconfirm" type="password"/></td></tr>
{{if $showNewStaffForm -}}
{{if $isAdmin -}}
<tr><td>Rank:</td><td><select id="rank" name="rank">
<option value="3">Admin</option>
<option value="2">Moderator</option>
<option value="1">Janitor</option>
<option value="3"{{with .updateRank}}{{if eq $.updateRank 3}}selected="selected"{{end}}{{end}}>Admin</option>
<option value="2"{{with .updateRank}}{{if eq $.updateRank 2}}selected="selected"{{end}}{{end}}>Moderator</option>
<option value="1"{{with .updateRank}}{{if eq $.updateRank 1}}selected="selected"{{end}}{{end}}>Janitor</option>
</select></td></tr>
{{end -}}
{{if $showNewStaffForm -}}
<tr><td>
<input type="hidden" name="do" value="add" />
<input id="submitnewstaff" type="submit" value="Add" /></td></tr>