1
0
Fork 0
mirror of https://github.com/Eggbertx/gochan.git synced 2025-08-17 10:56:24 -07:00

Update IP ban interface for new gcsql API

This commit is contained in:
Eggbertx 2022-11-16 15:11:55 -08:00
parent efccbdd001
commit 34688190e2
8 changed files with 216 additions and 146 deletions

View file

@ -3,6 +3,7 @@ package gcsql
import (
"database/sql"
"regexp"
"strconv"
)
type Ban interface {
@ -30,6 +31,45 @@ func CheckIPBan(ip string, boardID int) (*IPBan, error) {
return &ban, nil
}
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`
if boardID > 0 {
query += " WHERE board_id = ?"
}
query += " LIMIT " + strconv.Itoa(limit)
var rows *sql.Rows
var err error
if boardID > 0 {
rows, err = QuerySQL(query, boardID)
} else {
rows, err = QuerySQL(query)
}
if err != nil {
return nil, err
}
defer rows.Close()
var bans []IPBan
for rows.Next() {
var ban IPBan
if err = rows.Scan(
&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,
); err != nil {
return nil, err
}
if onlyActive && !ban.IsActive {
continue
}
bans = append(bans, ban)
}
return bans, nil
}
// IsGlobalBan returns true if BoardID is a nil int, meaning they are banned on all boards, as opposed to a specific one
func (ipb IPBan) IsGlobalBan() bool {
return ipb.BoardID == nil
@ -99,7 +139,9 @@ func CheckFilenameBan(filename string, boardID int) (*FilenameBan, error) {
}, nil
}
func CheckFileBan(checksum string, boardID int) (*FileBan, error) {
// CheckFileChecksumBan checks to see if the given checksum is banned on the given boardID, or on all boards.
// It returns the ban info (or nil if it is not banned) and any errors
func CheckFileChecksumBan(checksum string, boardID int) (*FileBan, error) {
const query = `SELECT
id, board_id, staff_id, staff_note, issued_at, checksum
FROM DBPREFIXfile_ban
@ -117,6 +159,44 @@ func CheckFileBan(checksum string, boardID int) (*FileBan, error) {
return &ban, err
}
func GetChecksumBans(boardID int, limit int) ([]FileBan, error) {
query := `SELECT
id, board_id, staff_id, staff_note, issued_at, checksum
FROM DBPREFIXfile_ban`
if boardID > 0 {
query += " WHERE board_id = ?"
}
query += " LIMIT " + strconv.Itoa(limit)
var rows *sql.Rows
var err error
if boardID > 0 {
rows, err = QuerySQL(query, boardID)
} else {
rows, err = QuerySQL(query)
}
if err != nil {
return nil, err
}
defer rows.Close()
var bans []FileBan
for rows.Next() {
var ban FileBan
if err = rows.Scan(
&ban.ID, &ban.BoardID, &ban.StaffID, &ban.StaffNote, &ban.IssuedAt, &ban.Checksum,
); err != nil {
return nil, err
}
bans = append(bans, ban)
}
return bans, nil
}
func (fb *FileBan) IsGlobalBan() bool {
return fb.BoardID == nil
}
// DeleteFileBanByID deletes the ban, given the id column value
func DeleteFileBanByID(id int) error {
_, err := ExecSQL("DELETE FROM DBPREFIXfile_ban WHERE id = ?", id)
return err
}

View file

@ -103,6 +103,25 @@ func GetBoardFromID(id int) (*Board, error) {
return board, err
}
// GetBoardURIs gets a list of all existing board URIs
func GetBoardURIs() (URIS []string, err error) {
const sql = `SELECT uri FROM DBPREFIXboards`
rows, err := QuerySQL(sql)
if err != nil {
return nil, err
}
defer rows.Close()
var uris []string
for rows.Next() {
var uri string
if err = rows.Scan(&uri); err != nil {
return nil, err
}
uris = append(uris, uri)
}
return uris, nil
}
// ResetBoardSectionArrays is run when the board list needs to be changed
// (board/section is added, deleted, etc)
func ResetBoardSectionArrays() error {

View file

@ -115,7 +115,7 @@ func DeactivateStaff(username string) error {
return s.SetActive(false)
}
func getStaffUsernameFromID(id int) (string, error) {
func GetStaffUsernameFromID(id int) (string, error) {
const query = `SELECT username FROM DBPREFIXstaff WHERE id = ?`
var username string
err := QueryRowSQL(query, interfaceSlice(id), interfaceSlice(&username))

View file

@ -102,7 +102,7 @@ func (wf *Wordfilter) OnBoard(dir string) bool {
}
func (wf *Wordfilter) StaffName() string {
staff, err := getStaffUsernameFromID(wf.StaffID)
staff, err := GetStaffUsernameFromID(wf.StaffID)
if err != nil {
return "?"
}

View file

@ -159,6 +159,17 @@ var funcMap = template.FuncMap{
"isBanned": func(ban *gcsql.IPBan, board string) bool {
return ban.IsActive && ban.BoardID != nil
},
"getBoardDirFromID": func(id int) string {
dir, _ := gcsql.GetBoardDir(id)
return dir
},
"getStaffNameFromID": func(id int) string {
username, err := gcsql.GetStaffUsernameFromID(id)
if err != nil {
return "?"
}
return username
},
"getCatalogThumbnail": func(img string) string {
return gcutil.GetThumbnailPath("catalog", img)
},

View file

@ -211,134 +211,82 @@ var actions = []Action{
return manageRecentsBuffer.String(), nil
},
},
/* {
ID: "filebans",
Title: "File bans",
{
ID: "checksumbans",
Title: "File checksum bans",
Permissions: ModPerms,
JSONoutput: OptionalJSON,
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool) (interface{}, error) {
var err error
fileBanType := request.PostForm.Get("bantype")
delFnbStr := request.Form.Get("delfnb")
if delFnbStr != "" {
var delFilenameBanID int
if delFilenameBanID, err = strconv.Atoi(delFnbStr); err != nil {
errorEv.Err(err).
Str("delfnb", delFnbStr).Caller().Send()
return "", err
}
if err = gcsql.DeleteFilenameBanByID(delFilenameBanID); err != nil {
errorEv.Err(err).
Int("delfnb", delFilenameBanID).Caller().Send()
return "", err
}
infoEv.Int("delFilenameBan", delFilenameBanID).Send()
}
delCsbStr := request.Form.Get("delcsb")
if delCsbStr != "" {
var delChecksumBanID int
if delChecksumBanID, err = strconv.Atoi(delCsbStr); err != nil {
errorEv.Err(err).
Str("delcsb", delCsbStr).Send()
return "", err
}
if err = gcsql.DeleteFileBanByID(delChecksumBanID); err != nil {
errorEv.Err(err).
Int("delcsb", delChecksumBanID).Send()
return "", err
}
InfoEv.Int("delChecksumBan", delChecksumBanID).Send()
}
switch fileBanType {
case "filename":
// filename form used
filename := request.PostForm.Get("filename")
isWildcard := request.PostForm.Get("iswildcard") == "on"
board := request.PostForm.Get("board")
staffNote := request.PostForm.Get("staffnote")
if filename == "" {
err = errors.New("missing filename field in filename ban creation")
errorEv.Err(err).Send()
return "", err
}
if err = gcsql.CreateFileNameBan(filename, isWildcard, staff.Username, staffNote, board); err != nil {
errorEv.Err(err).
Str("filename", filename).
Bool("iswildcard", isWildcard).
Str("board", board).
Str("staffnote", staffNote).Send()
return "", err
}
gcutil.LogInfo().
Str("action", "filebans").
Str("staff", staff.Username).
Str("newBanType", "filename").Send()
case "checksum":
// file checksum form used
checksum := request.PostForm.Get("checksum")
board := request.PostForm.Get("board")
staffNote := request.PostForm.Get("staffnote")
if checksum == "" {
err = errors.New("missing checksum field in filename ban creation")
errorEv.Err(err).Send()
return "", err
}
if err = gcsql.CreateFileBan(checksum, staff.Username, staffNote, board); err != nil {
errorEv.Err(err).
Str("checksum", checksum).
Str("board", board).
Str("staffnote", staffNote).Send()
return "", err
}
infoEv.Str("newBanType", "checksum").Send()
case "":
// no POST data sent
default:
err = fmt.Errorf(`invalid bantype value %q, valid values are "filename" and "checksum"`, fileBanType)
errorEv.Err(err).Caller().Send()
return "", err
}
filenameBans, err := gcsql.GetFilenameBans("", false)
if err != nil {
return "", err
}
checksumBans, err := gcsql.GetFileChecksumBans("")
if err != nil {
return "", err
}
if wantsJSON {
return map[string]interface{}{
"filenameBans": filenameBans,
"checksumBans": checksumBans,
}, nil
}
boardURIs, err := gcsql.GetBoardUris()
if err != nil {
return "", err
}
manageBansBuffer := bytes.NewBufferString("")
if err = serverutil.MinifyTemplate(gctemplates.ManageFileBans, map[string]interface{}{
"webroot": config.GetSystemCriticalConfig().WebRoot,
"filenameBans": filenameBans,
"checksumBans": checksumBans,
"currentStaff": staff.Username,
"boardURIs": boardURIs,
}, manageBansBuffer, "text/html"); err != nil {
errorEv.Err(err).Str("template", "manage_filebans.html").Caller().Send()
return "", errors.New("failed executing file ban management page template: " + err.Error())
}
return manageBansBuffer.String(), nil
Callback: func(writer http.ResponseWriter, request *http.Request, staff *gcsql.Staff, wantsJSON bool, infoEv, errEv *zerolog.Event) (output interface{}, err error) {
return "", gcutil.ErrNotImplemented
},
}, */
},
{
ID: "ipbans",
ID: "bans",
Title: "IP 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) {
return "", gcutil.ErrNotImplemented
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 {
errEv.Err(err).
Str("boardid", boardIDstr).Caller().Send()
return "", err
}
}
filterBoardIDstr := request.FormValue("filterboardid")
var filterBoardID int
if filterBoardIDstr != "" {
if filterBoardID, err = strconv.Atoi(filterBoardIDstr); err != nil {
errEv.Err(err).
Str("filterboardid", filterBoardIDstr).Caller().Send()
return "", err
}
}
limitStr := request.FormValue("limit")
limit := 200
if limitStr != "" {
if limit, err = strconv.Atoi(limitStr); err != nil {
errEv.Err(err).
Str("limit", limitStr).Caller().Send()
return "", err
}
}
banlist, err := gcsql.GetIPBans(filterBoardID, limit, true)
if err != nil {
errEv.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,
"allBoards": gcsql.AllBoards,
"boardid": boardid,
"ip": ip,
"postid": postid,
"filterboardid": filterBoardID,
}, 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
},
},
/* {

View file

@ -129,7 +129,7 @@ func checkFilenameBan(upload *gcsql.Upload, post *gcsql.Post, postBoard *gcsql.B
}
func checkChecksumBan(upload *gcsql.Upload, post *gcsql.Post, postBoard *gcsql.Board, writer http.ResponseWriter, request *http.Request) bool {
fileBan, err := gcsql.CheckFileBan(upload.Checksum, postBoard.ID)
fileBan, err := gcsql.CheckFileChecksumBan(upload.Checksum, postBoard.ID)
if err != nil {
gcutil.LogError(err).
Str("IP", post.IP).

View file

@ -1,32 +1,44 @@
<form method="POST" action="/manage?action=bans">
<input type="hidden" name="do" value="add" />
<b>User filter:</b><br />
<h2>Add IP ban</h2>
<table>
<tr><th>IP address</th><td><input type="text" name="ip" value="{{.post.IP}}" /></td></tr>
<tr><th></th><td>"192.168.1.36" will ban posts from that IP address<br />
"192.168" will block all IPs starting with 192.168<br /></td></tr>
<tr><th>Name!Tripcode</th><td><input type="text" name="name" value="{{if ne .post.Name ""}}{{.post.Name}}!{{.post.Tripcode}}{{end}}"/> <label>Regex<input type="checkbox" name="nameregex" /></label></td></tr>
<tr><th>Ban filename</th><td><input type="text" name="filename" value="{{.post.FilenameOriginal}}"/></td></tr>
<tr><th>Ban file checksum</th><td><input type="text" name="checksum" value="{{.post.FileChecksum}}" /></td></tr>
</table><br /><hr />
<b>Ban info</b><br />
<table>
<tr><th>Duration</th><td><input type="text" name="duration" /></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 /> or 'forever', '0', or '' for a permaban</td></tr>
<tr><th>Ban type</th><td><label>Image ban <input type="checkbox" name="imageban" /></label><br />
<label>Thread starting ban <input type="checkbox" name="threadban" /></label><br />
<label>Full ban (overrides the above) <input type="checkbox" name="fullban" /></label></tr>
<tr><th>Boards</th><td><input type="text" name="boards" /></td></tr>
<tr><th></th><td>Comma-separated list of boards (e.g. board1,board2,board3) or blank for all boards</td></tr>
<tr><th>Reason</th><td><textarea name="reason" rows="5" cols="30"></textarea></td></tr>
<tr><th>Staff note</th><td><input type="text" name="staffnote" /></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>
{{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>
{{- 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>
</table>
<input type="submit" value="Ban user" />
<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>
<table>
<tr><th>IP</th><th>Name!Tripcode</th><th>Reason</th><th>Staff note</th><th>Boards</th><th>Staff</th><th>Set</th><th>Expires</th><th>Permaban</th></tr>
{{range $b, $ban := $.banlist}} <tr><td>{{$ban.IP}}</td><td>{{$ban.Name}}</td><td>{{$ban.Reason}}</td><td>{{$ban.StaffNote}}</td><td>{{if eq $ban.Boards ""}}<i>all boards</i>{{else}}{{$ban.Boards}}{{end}}</td><td>{{$ban.Staff}}</td><td>{{$ban.Timestamp}}</td><td>{{if $ban.Permaban}}never{{else}}{{$ban.Expires}}{{end}}</td><td>{{$ban.Permaban}}</td></tr>
<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>
{{range $_, $ban := $.banlist -}}
<tr>
<td>{{$ban.IP}}</td>
<td>{{if not $ban.BoardID}}<i>all</i>{{else}}/{{getBoardDirFromID $ban.BoardID}}/{{end}}</td>
<td>{{$ban.Message}}</td>
<td>{{getStaffNameFromID $ban.StaffID}}</td>
<td>{{$ban.StaffNote}}</td>
<td>{{if not $ban.BannedForPostID}}<i>N/A</i>{{else}}{{$ban.CopyPostText}}{{end}}</td>
<td>{{formatTimestamp $ban.IssuedAt}}</td>
<td>
{{- if $ban.Permanent}}<i>Never</i>{{else}}{{formatTimestamp $ban.ExpiresAt}}{{end -}}
</td>
<td>
{{- if $ban.CanAppeal}}{{formatTimestamp $ban.AppealAt}}{{else}}<i>Never</i>{{end -}}
</td>
</tr>
{{end}}</table>