package webui import ( "net" "net/http" ) // blacklistPage lists both currently/recently blacklisted IPs (auto or manual) and the // abuse-detection whitelist — global-admin only, since a blacklist entry isn't // attributable to one domain the way a mailbox or relay-whitelist row is. func (a *App) blacklistPage(w http.ResponseWriter, r *http.Request) { entries, err := a.DB.ListBlacklist() if err != nil { setFlash(w, "error", "Error loading blacklist") } whitelist, err := a.DB.ListAbuseWhitelist() if err != nil { setFlash(w, "error", "Error loading abuse whitelist") } a.render(w, r, "blacklist.html", M{"active": "blacklist", "entries": entries, "whitelist": whitelist}) } // addBlacklistEntry is an admin-initiated manual block: fixed duration, no escalation. func (a *App) addBlacklistEntry(w http.ResponseWriter, r *http.Request) { ip := r.FormValue("ip_address") reason := r.FormValue("reason") hours := atoi(r.FormValue("hours")) if net.ParseIP(ip) == nil || hours <= 0 { setFlash(w, "error", "A valid IP address and a positive duration in hours are required") http.Redirect(w, r, Prefix+"/blacklist", http.StatusFound) return } if err := a.DB.AddManualBlacklistEntry(ip, reason, hours); err != nil { setFlash(w, "error", "Error blacklisting IP") } else { setFlash(w, "success", "IP blacklisted") } http.Redirect(w, r, Prefix+"/blacklist", http.StatusFound) } func (a *App) removeBlacklistEntry(w http.ResponseWriter, r *http.Request) { if err := a.DB.RemoveBlacklistEntry(pathID(r)); err != nil { setFlash(w, "error", "Error removing blacklist entry") } else { setFlash(w, "success", "Blacklist entry removed") } http.Redirect(w, r, Prefix+"/blacklist", http.StatusFound) } // whitelistBlacklistedIP removes ip from the blacklist and adds it to the abuse // whitelist in one action, so an admin can undo a false-positive auto-block without // two separate trips. func (a *App) whitelistBlacklistedIP(w http.ResponseWriter, r *http.Request) { id := pathID(r) entries, err := a.DB.ListBlacklist() if err != nil { setFlash(w, "error", "Error loading blacklist") http.Redirect(w, r, Prefix+"/blacklist", http.StatusFound) return } var ip string for _, e := range entries { if e.ID == id { ip = e.IPAddress break } } if ip == "" { http.NotFound(w, r) return } if err := a.DB.AddAbuseWhitelist(ip, "whitelisted from a blacklist entry"); err != nil { setFlash(w, "error", "Error whitelisting IP") http.Redirect(w, r, Prefix+"/blacklist", http.StatusFound) return } _ = a.DB.RemoveBlacklistEntry(id) setFlash(w, "success", ip+" whitelisted and removed from the blacklist") http.Redirect(w, r, Prefix+"/blacklist", http.StatusFound) } func (a *App) addAbuseWhitelistEntry(w http.ResponseWriter, r *http.Request) { ip := r.FormValue("ip_address") note := r.FormValue("note") if net.ParseIP(ip) == nil { setFlash(w, "error", "A valid IP address is required") http.Redirect(w, r, Prefix+"/blacklist", http.StatusFound) return } if err := a.DB.AddAbuseWhitelist(ip, note); err != nil { setFlash(w, "error", "Error adding to abuse whitelist") } else { setFlash(w, "success", "IP added to the abuse-detection whitelist") } http.Redirect(w, r, Prefix+"/blacklist", http.StatusFound) } func (a *App) removeAbuseWhitelistEntry(w http.ResponseWriter, r *http.Request) { if err := a.DB.RemoveAbuseWhitelist(pathID(r)); err != nil { setFlash(w, "error", "Error removing abuse whitelist entry") } else { setFlash(w, "success", "Removed from the abuse-detection whitelist") } http.Redirect(w, r, Prefix+"/blacklist", http.StatusFound) }