mirror of
https://github.com/Eggbertx/gochan.git
synced 2025-08-17 10:56:24 -07:00
Make ban adding (sorta) work again
This commit is contained in:
parent
34688190e2
commit
bfac3ef93c
5 changed files with 191 additions and 173 deletions
|
@ -2,22 +2,50 @@ package gcsql
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
ipBanQueryBase = `SELECT
|
||||
id, staff_id, board_id, banned_for_post_id, copy_post_text, is_thread_ban,
|
||||
is_active, ip, issued_at, appeal_at, expires_at, permanent, staff_note,
|
||||
message, can_appeal
|
||||
FROM DBPREFIXip_ban`
|
||||
)
|
||||
|
||||
var (
|
||||
ErrBanAlreadyInserted = errors.New("ban already submitted")
|
||||
)
|
||||
|
||||
type Ban interface {
|
||||
IsGlobalBan() bool
|
||||
}
|
||||
|
||||
func NewIPBan(ban *IPBan) error {
|
||||
const query = `INSERT INTO DBPREFIXip_ban
|
||||
(staff_id, board_id, banned_for_post_id, copy_post_text, is_thread_ban, is_active, ip,
|
||||
appeal_at, expires_at, permanent, staff_note, message, can_appeal)
|
||||
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
||||
if ban.ID > 0 {
|
||||
return ErrBanAlreadyInserted
|
||||
}
|
||||
var err error
|
||||
ban.ID, err = getNextFreeID("DBPREFIXip_ban")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = ExecSQL(query,
|
||||
ban.StaffID, ban.BoardID, ban.BannedForPostID, ban.CopyPostText, ban.IsThreadBan, ban.IsActive, ban.IP,
|
||||
ban.AppealAt, ban.ExpiresAt, ban.Permanent, ban.StaffNote, ban.Message, ban.CanAppeal)
|
||||
return err
|
||||
}
|
||||
|
||||
// CheckIPBan returns the latest active IP ban for the given IP, as well as any errors. If the
|
||||
// IPBan pointer is nil, the IP has no active bans
|
||||
func CheckIPBan(ip string, boardID int) (*IPBan, error) {
|
||||
const query = `SELECT
|
||||
id, staff_id, board_id, banned_for_post_id, copy_post_text, is_thread_ban,
|
||||
is_active, ip, issued_at, appeal_at, expires_at, permanent, staff_note,
|
||||
message, can_appeal
|
||||
FROM DBPREFIXip_ban WHERE ip = ? AND (board_id IS NULL OR board_id = ?) AND
|
||||
const query = ipBanQueryBase + ` WHERE ip = ? AND (board_id IS NULL OR board_id = ?) AND
|
||||
is_active AND (expires_at > CURRENT_TIMESTAMP OR permanent)
|
||||
ORDER BY id DESC LIMIT 1`
|
||||
var ban IPBan
|
||||
|
@ -31,12 +59,27 @@ func CheckIPBan(ip string, boardID int) (*IPBan, error) {
|
|||
return &ban, nil
|
||||
}
|
||||
|
||||
func GetIPBanByID(id int) (*IPBan, error) {
|
||||
const query = ipBanQueryBase + " WHERE id = ?"
|
||||
var ban IPBan
|
||||
err := QueryRowSQL(query, interfaceSlice(id), interfaceSlice(
|
||||
&ban.ID, &ban.StaffID, &ban.BoardID, &ban.BannedForPostID, &ban.CopyPostText, &ban.IsThreadBan,
|
||||
&ban.IsActive, &ban.IP, &ban.IssuedAt, &ban.AppealAt, &ban.ExpiresAt, &ban.Permanent, &ban.StaffNote,
|
||||
&ban.Message, &ban.CanAppeal))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ban, err
|
||||
}
|
||||
|
||||
func DeleteIPBanByID(id int) error {
|
||||
const query = `UPDATE DBPREFIXip_ban SET is_active = FALSE WHERE id = ?`
|
||||
_, err := ExecSQL(query, id)
|
||||
return err
|
||||
}
|
||||
|
||||
func GetIPBans(boardID int, limit int, onlyActive bool) ([]IPBan, error) {
|
||||
query := `SELECT
|
||||
id, staff_id, board_id, banned_for_post_id, copy_post_text, is_thread_ban,
|
||||
is_active, ip, issued_at, appeal_at, expires_at, permanent, staff_note,
|
||||
message, can_appeal
|
||||
FROM DBPREFIXip_ban`
|
||||
query := ipBanQueryBase
|
||||
if boardID > 0 {
|
||||
query += " WHERE board_id = ?"
|
||||
}
|
||||
|
|
|
@ -152,6 +152,15 @@ var funcMap = template.FuncMap{
|
|||
}
|
||||
return dict, nil
|
||||
},
|
||||
"until": func(t time.Time) string {
|
||||
return t.Sub(time.Now()).String()
|
||||
},
|
||||
"dereference": func(a *int) int {
|
||||
if a == nil {
|
||||
return 0
|
||||
}
|
||||
return *a
|
||||
},
|
||||
// Imageboard functions
|
||||
"bannedForever": func(ban *gcsql.IPBan) bool {
|
||||
return ban.IsActive && ban.Permanent && !ban.CanAppeal
|
||||
|
|
|
@ -222,31 +222,35 @@ var actions = []Action{
|
|||
},
|
||||
{
|
||||
ID: "bans",
|
||||
Title: "IP Bans",
|
||||
Title: "Bans",
|
||||
Permissions: ModPerms,
|
||||
JSONoutput: OptionalJSON,
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool, infoEv *zerolog.Event, errEv *zerolog.Event) (output interface{}, err error) {
|
||||
var outputStr string
|
||||
ip := request.FormValue("ip")
|
||||
postid := request.FormValue("postid")
|
||||
ban := gcsql.IPBan{
|
||||
BoardID: new(int),
|
||||
}
|
||||
*ban.BoardID = 32
|
||||
|
||||
if request.FormValue("do") == "add" {
|
||||
//
|
||||
}
|
||||
|
||||
boardIDstr := request.FormValue("boardid")
|
||||
var boardid int
|
||||
if boardIDstr != "" {
|
||||
if boardid, err = strconv.Atoi(boardIDstr); err != nil {
|
||||
var ban gcsql.IPBan
|
||||
ban.StaffID = staff.ID
|
||||
deleteIDStr := request.FormValue("delete")
|
||||
if deleteIDStr != "" {
|
||||
// deleting a ban
|
||||
ban.ID, err = strconv.Atoi(deleteIDStr)
|
||||
if err != nil {
|
||||
errEv.Err(err).
|
||||
Str("boardid", boardIDstr).Caller().Send()
|
||||
Str("delete", deleteIDStr).
|
||||
Caller().Send()
|
||||
return "", err
|
||||
}
|
||||
} else if request.FormValue("do") == "add" {
|
||||
err := ipBanFromRequest(&ban, request, errEv)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
infoEv.
|
||||
Str("bannedIP", ban.IP).
|
||||
Str("expires", ban.ExpiresAt.String()).
|
||||
Bool("permanent", ban.Permanent).
|
||||
Str("reason", ban.Message).
|
||||
Msg("Added IP ban")
|
||||
}
|
||||
|
||||
filterBoardIDstr := request.FormValue("filterboardid")
|
||||
var filterBoardID int
|
||||
if filterBoardIDstr != "" {
|
||||
|
@ -277,146 +281,16 @@ var actions = []Action{
|
|||
"systemCritical": config.GetSystemCriticalConfig(),
|
||||
"banlist": banlist,
|
||||
"allBoards": gcsql.AllBoards,
|
||||
"boardid": boardid,
|
||||
"ip": ip,
|
||||
"postid": postid,
|
||||
"ban": ban,
|
||||
"filterboardid": filterBoardID,
|
||||
"webroot": config.GetSystemCriticalConfig().WebRoot,
|
||||
}, manageBansBuffer, "text/html"); err != nil {
|
||||
errEv.Err(err).Str("template", "manage_bans.html").Caller().Send()
|
||||
return "", errors.New("Error executing ban management page template: " + err.Error())
|
||||
}
|
||||
outputStr += manageBansBuffer.String()
|
||||
return outputStr, nil
|
||||
},
|
||||
},
|
||||
/* {
|
||||
ID: "bans",
|
||||
Title: "Bans",
|
||||
Permissions: ModPerms,
|
||||
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (output interface{}, err error) { //TODO whatever this does idk man
|
||||
var outputStr string
|
||||
var post gcsql.Post
|
||||
errorEv := gcutil.LogError(nil).
|
||||
Str("action", "bans").
|
||||
Str("staff", staff.Username)
|
||||
defer errorEv.Discard()
|
||||
|
||||
if request.FormValue("do") == "add" {
|
||||
ip := request.FormValue("ip")
|
||||
name := request.FormValue("name")
|
||||
nameIsRegex := (request.FormValue("nameregex") == "on")
|
||||
checksum := request.FormValue("checksum")
|
||||
filename := request.FormValue("filename")
|
||||
durationForm := request.FormValue("duration")
|
||||
permaban := (durationForm == "" || durationForm == "0" || durationForm == "forever")
|
||||
duration, err := gcutil.ParseDurationString(durationForm)
|
||||
if err != nil {
|
||||
errorEv.Err(err).Send()
|
||||
return "", err
|
||||
}
|
||||
expires := time.Now().Add(duration)
|
||||
|
||||
boards := request.FormValue("boards")
|
||||
reason := html.EscapeString(request.FormValue("reason"))
|
||||
staffNote := html.EscapeString(request.FormValue("staffnote"))
|
||||
|
||||
if filename != "" {
|
||||
err = gcsql.CreateFileNameBan(filename, nameIsRegex, staff.Username, staffNote, boards)
|
||||
}
|
||||
if err != nil {
|
||||
outputStr += err.Error()
|
||||
errorEv.Err(err).Send()
|
||||
err = nil
|
||||
}
|
||||
if name != "" {
|
||||
if err = gcsql.CreateUserNameBan(name, nameIsRegex, staff.Username, permaban, staffNote, boards); err != nil {
|
||||
errorEv.
|
||||
Str("banType", "username").
|
||||
Str("user", name).Send()
|
||||
return "", err
|
||||
}
|
||||
infoEv.
|
||||
Str("banType", "username").
|
||||
Str("user", name).
|
||||
Bool("permaban", permaban).Send()
|
||||
}
|
||||
|
||||
if request.FormValue("fullban") == "on" {
|
||||
err = gcsql.CreateUserBan(ip, false, staff.Username, boards, expires, permaban, staffNote, reason, true, time.Now())
|
||||
if err != nil {
|
||||
errorEv.
|
||||
Str("banType", "ip").
|
||||
Str("banIP", ip).
|
||||
Bool("threadBan", false).
|
||||
Str("bannedFromBoards", boards).Send()
|
||||
return "", err
|
||||
}
|
||||
infoEv.
|
||||
Str("banType", "ip").
|
||||
Str("banIP", ip).
|
||||
Bool("threadBan", true).
|
||||
Str("bannedFromBoards", boards).Send()
|
||||
} else {
|
||||
if request.FormValue("threadban") == "on" {
|
||||
err = gcsql.CreateUserBan(ip, true, staff.Username, boards, expires, permaban, staffNote, reason, true, time.Now())
|
||||
if err != nil {
|
||||
errorEv.
|
||||
Str("banType", "ip").
|
||||
Str("banIP", ip).
|
||||
Bool("threadBan", true).
|
||||
Str("bannedFromBoards", boards).Send()
|
||||
return "", err
|
||||
}
|
||||
infoEv.
|
||||
Str("banType", "ip").
|
||||
Str("banIP", ip).
|
||||
Bool("threadBan", true).
|
||||
Str("bannedFromBoards", boards).Send()
|
||||
}
|
||||
if request.FormValue("imageban") == "on" {
|
||||
err = gcsql.CreateFileBan(checksum, staff.Username, staffNote, boards)
|
||||
if err != nil {
|
||||
errorEv.
|
||||
Str("banType", "fileBan").
|
||||
Str("checksum", checksum).Send()
|
||||
return "", err
|
||||
}
|
||||
infoEv.
|
||||
Str("banType", "fileBan").
|
||||
Str("checksum", checksum).Send()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if request.FormValue("postid") != "" {
|
||||
var err error
|
||||
post, err = gcsql.GetSpecificPostByString(request.FormValue("postid"), true)
|
||||
if err != nil {
|
||||
errorEv.Err(err).Str("postid", request.FormValue("postid")).Msg("Error getting post")
|
||||
err = errors.New("Error getting post: " + err.Error())
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
banlist, err := gcsql.GetAllBans()
|
||||
if err != nil {
|
||||
errorEv.Err(err).Msg("Error getting ban list")
|
||||
err = errors.New("Error getting ban list: " + err.Error())
|
||||
return "", err
|
||||
}
|
||||
manageBansBuffer := bytes.NewBufferString("")
|
||||
|
||||
if err = serverutil.MinifyTemplate(gctemplates.ManageBans, map[string]interface{}{
|
||||
// "systemCritical": config.GetSystemCriticalConfig(),
|
||||
"banlist": banlist,
|
||||
"post": post,
|
||||
}, manageBansBuffer, "text/html"); err != nil {
|
||||
errEv.Err(err).Str("template", "manage_bans.html").Caller().Send()
|
||||
return "", errors.New("Error executing ban management page template: " + err.Error())
|
||||
}
|
||||
outputStr += manageBansBuffer.String()
|
||||
return outputStr, nil
|
||||
}}, */
|
||||
}},
|
||||
{
|
||||
ID: "ipsearch",
|
||||
Title: "IP Search",
|
||||
|
|
81
pkg/manage/bans.go
Normal file
81
pkg/manage/bans.go
Normal file
|
@ -0,0 +1,81 @@
|
|||
package manage
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"html"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
func ipBanFromRequest(ban *gcsql.IPBan, request *http.Request, errEv *zerolog.Event) error {
|
||||
banIDStr := request.FormValue("edit")
|
||||
if banIDStr != "" && request.FormValue("do") == "edit" {
|
||||
banID, err := strconv.Atoi(banIDStr)
|
||||
if err != nil {
|
||||
errEv.Err(err).
|
||||
Str("editBanID", banIDStr).
|
||||
Caller().Send()
|
||||
return errors.New("invalid 'edit' field value (must be int)")
|
||||
}
|
||||
editing, err := gcsql.GetIPBanByID(banID)
|
||||
if err != nil {
|
||||
errEv.Err(err).
|
||||
Int("editBanID", banID).
|
||||
Caller().Send()
|
||||
return errors.New("Unable to get ban with id " + banIDStr + " (SQL error)")
|
||||
}
|
||||
*ban = *editing
|
||||
return nil
|
||||
}
|
||||
ban.IP = request.FormValue("ip")
|
||||
ban.Permanent = request.FormValue("permanent") == "on"
|
||||
if !ban.Permanent {
|
||||
durationStr := request.FormValue("duration")
|
||||
duration, err := gcutil.ParseDurationString(durationStr)
|
||||
if err != nil {
|
||||
errEv.Err(err).
|
||||
Str("duration", durationStr).
|
||||
Caller().Msg("Invalid duration")
|
||||
return err
|
||||
}
|
||||
ban.ExpiresAt = time.Now().Add(duration)
|
||||
}
|
||||
|
||||
ban.CanAppeal = request.FormValue("noappeals") != "on"
|
||||
if ban.CanAppeal {
|
||||
appealWaitStr := request.FormValue("appealwait")
|
||||
if appealWaitStr != "" {
|
||||
appealDuration, err := gcutil.ParseDurationString(appealWaitStr)
|
||||
if err != nil {
|
||||
errEv.Err(err).
|
||||
Str("appealwait", appealWaitStr).
|
||||
Caller().Msg("Invalid appeal delay duration string")
|
||||
return err
|
||||
}
|
||||
ban.AppealAt = time.Now().Add(appealDuration)
|
||||
}
|
||||
}
|
||||
|
||||
ban.IsThreadBan = request.FormValue("threadban") == "on"
|
||||
boardIDstr := request.FormValue("boardid")
|
||||
if boardIDstr != "" && boardIDstr != "0" {
|
||||
boardID, err := strconv.Atoi(boardIDstr)
|
||||
if err != nil {
|
||||
errEv.Err(err).
|
||||
Str("boardid", boardIDstr).
|
||||
Caller().Send()
|
||||
return err
|
||||
}
|
||||
ban.BoardID = new(int)
|
||||
*ban.BoardID = boardID
|
||||
}
|
||||
ban.Message = html.EscapeString(request.FormValue("reason"))
|
||||
ban.StaffNote = html.EscapeString(request.FormValue("staffnote"))
|
||||
ban.IsActive = true
|
||||
return gcsql.NewIPBan(ban)
|
||||
}
|
|
@ -1,32 +1,43 @@
|
|||
<form method="POST" action="/manage?action=bans">
|
||||
<form method="POST" action="{{webPath "manage?action=bans"}}">
|
||||
<input type="hidden" name="do" value="add" />
|
||||
<h2>Add IP ban</h2>
|
||||
<table>
|
||||
<tr><th>IP address</th><td><input type="text" name="ip" value="{{.post.IP}}" /></td></tr>
|
||||
<tr><th>Duration</th><td><input type="text" name="duration" /></td></tr>
|
||||
<tr><th>IP address</th><td><input type="text" name="ip" value="{{.ban.IP}}" style="width: 100%;"/></td></tr>
|
||||
<tr><th>Duration</th><td><input type="text" name="duration" style="width: 100%;" {{if gt .ban.ID 0}}value="{{until .ban.ExpiresAt}}"{{end}}/></td></tr>
|
||||
<tr><th></th><td>e.g. '1y2mo3w4d5h6m7s',<br />'1 year 2 months 3 weeks 4 days 5 hours 6 minutes 7 seconds'<br/>Optional if "Permanent" is checked, required otherwise</td></tr>
|
||||
<tr><th>Permanent</th><td><input type="checkbox" name="permanent" id="permanent" {{with $.permanent}}{{if eq $.permanent "on"}}checked{{end}}{{end}}> (overrides the duration)</td></tr>
|
||||
<tr><th>Appeal wait time</th><td><input type="text" name="appealwait" id="appealwait"></td></tr>
|
||||
<tr><th></th><td>Same syntax as above, but optional.</td></tr>
|
||||
<tr><th>Thread starting ban</th><td><input type="checkbox" name="threadban" /></td></tr>
|
||||
<tr><th>Permanent</th><td><input type="checkbox" name="permanent" id="permanent" {{if .ban.Permanent}}checked{{end}}> (overrides the duration)</td></tr>
|
||||
<tr><th>Appeal wait time</th><td><input type="text" name="appealwait" id="appealwait" style="width: 100%;"></td></tr>
|
||||
<tr><th></th><td>Same syntax as above, but optional.<br />If left blank, they can appeal immediately</td></tr>
|
||||
<tr><th>No appeals</th><td><input type="checkbox" name="noappeals" /></td> (if checked, Appeal wait time field is ignored)</tr>
|
||||
<tr><th>Thread starting ban</th><td><input type="checkbox" name="threadban" /> (user can reply to threads but can't make new threads)</td></tr>
|
||||
{{with $.bannedForPostID}}<tr><th>Banned for post ID</th><td>{{$.bannedForPostID}}</td></tr>{{end}}
|
||||
<tr><th>Board</th><td><select name="boardid" id="boardid">
|
||||
<option value="0">All boards</option>
|
||||
{{- range $b, $board := $.allBoards -}}
|
||||
<option value="{{$board.ID}}" {{if eq $.boardid $board.ID}}selected{{end}}>/{{$board.Dir}}/ - {{$board.Title}}</option>
|
||||
<option value="{{$board.ID}}" {{if eq (dereference $.ban.BoardID) $board.ID}}selected{{end}}>/{{$board.Dir}}/ - {{$board.Title}}</option>
|
||||
{{- end -}}
|
||||
</select></td></tr>
|
||||
<tr><th>Reason</th><td><textarea name="reason" rows="5" cols="30" placeholder="Message to be displayed to the banned user"></textarea></td></tr>
|
||||
<tr><th>Staff note</th><td><textarea name="staffnote" rows="5" cols="30" placeholder="Private note that only staff can see"></textarea></td></tr>
|
||||
<tr><th>Reason</th><td><textarea name="reason" style="width: 100%;" rows="6" placeholder="Message to be displayed to the banned user"></textarea></td></tr>
|
||||
<tr><th>Staff note</th><td><textarea name="staffnote" style="width: 100%;" rows="6" placeholder="Private note that only staff can see"></textarea></td></tr>
|
||||
</table>
|
||||
<input type="submit" value="Ban user" /> <input type="button" name="docancel" value="Cancel" onclick="window.location = './manage?action=bans'; return false"/>
|
||||
</form>
|
||||
|
||||
<h2 class="manage-header">Banlist</h2>
|
||||
<h2 id="banlist">Banlist</h2>
|
||||
<form action="{{webPath "manage?action=bans"}}" method="get">
|
||||
<input type="hidden" name="action" value="bans">
|
||||
Filter board: <select name="filterboardid" id="filterboardid" onchange="window.location = '{{webPath "manage?action=bans&filterboardid="}}' + this.value + '#banlist'">
|
||||
<option value="0">All boards</option>
|
||||
{{- range $b, $board := $.allBoards -}}
|
||||
<option value="{{$board.ID}}" {{if eq $.filterboardid $board.ID}}selected{{end}}>/{{$board.Dir}}/ - {{$board.Title}}</option>
|
||||
{{- end -}}
|
||||
</select> <input type="submit">
|
||||
</form>
|
||||
<table border="1">
|
||||
<tr><th>IP</th><th>Board</th><th>Reason</th><th>Staff</th><th>Staff note</th><th>Banned post text</th><th>Set</th><th>Expires</th><th>Appeal at</th></tr>
|
||||
<tr><th>Action</th><th>IP</th><th>Board</th><th>Reason</th><th>Staff</th><th>Staff note</th><th>Banned post text</th><th>Set</th><th>Expires</th><th>Appeal at</th></tr>
|
||||
{{range $_, $ban := $.banlist -}}
|
||||
<tr>
|
||||
<td> <a href="{{webPath "manage?action=bans&edit="}}{{$ban.ID}}">Edit</a> | <a href="{{webPath "manage?action=bans&delete="}}{{$ban.ID}}">Delete</a> </td>
|
||||
<td>{{$ban.IP}}</td>
|
||||
<td>{{if not $ban.BoardID}}<i>all</i>{{else}}/{{getBoardDirFromID $ban.BoardID}}/{{end}}</td>
|
||||
<td>{{$ban.Message}}</td>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue