Documentation ¶
Index ¶
- Constants
- Variables
- func AddBlueteams(w http.ResponseWriter, r *http.Request)
- func AddFlag(w http.ResponseWriter, r *http.Request)
- func AddFlags(w http.ResponseWriter, r *http.Request)
- func AddService(w http.ResponseWriter, r *http.Request)
- func AddTeam(w http.ResponseWriter, r *http.Request)
- func ApiCreate(w http.ResponseWriter, r *http.Request, v interface{})
- func ApiDelete(w http.ResponseWriter, r *http.Request, v models.Deleter)
- func ApiQuery(w http.ResponseWriter, r *http.Request, v interface{}, err error)
- func ApiUpdate(w http.ResponseWriter, r *http.Request, v models.Updater)
- func CalcPointsPerCheck(srv *models.Service, event *EventSettings, checkInterval time.Duration) float32
- func CheckCreds(w http.ResponseWriter, r *http.Request) bool
- func CheckSessionID(next http.Handler) http.Handler
- func ChecksRun(checkCfg *Configuration)
- func Compress() func(http.Handler) http.Handler
- func CreateStore(secure bool)
- func CreateWebRouter(teamScoreUpdater, servicesUpdater *broadcastHub) chi.Router
- func DeleteFlag(w http.ResponseWriter, r *http.Request)
- func DeleteService(w http.ResponseWriter, r *http.Request)
- func DeleteTeam(w http.ResponseWriter, r *http.Request)
- func EnableCTFChallenge(w http.ResponseWriter, r *http.Request)
- func EnsureAdmin(db models.DB)
- func ErrForbiddenBecause(reason string) render.Renderer
- func ErrInternal(err error) render.Renderer
- func ErrInvalidBecause(reason string) render.Renderer
- func ErrInvalidRequest(err error) render.Renderer
- func ErrRendering(err error) render.Renderer
- func GetAllFlags(w http.ResponseWriter, r *http.Request)
- func GetAllServices(w http.ResponseWriter, r *http.Request)
- func GetAllTeams(w http.ResponseWriter, r *http.Request)
- func GetBlueteams(w http.ResponseWriter, r *http.Request)
- func GetBonusPoints(w http.ResponseWriter, r *http.Request)
- func GetBreakdownOfSubmissionsPerFlag(w http.ResponseWriter, r *http.Request)
- func GetChallengeCapturesByTime(w http.ResponseWriter, r *http.Request)
- func GetChallengeDescription(w http.ResponseWriter, r *http.Request)
- func GetEachTeamsCapturedFlags(w http.ResponseWriter, r *http.Request)
- func GetEventConfig(w http.ResponseWriter, r *http.Request)
- func GetFlagByID(w http.ResponseWriter, r *http.Request)
- func GetFlagByName(w http.ResponseWriter, r *http.Request)
- func GetPublicChallenges(w http.ResponseWriter, r *http.Request)
- func GetScores(w http.ResponseWriter, r *http.Request)
- func GetServiceByID(w http.ResponseWriter, r *http.Request)
- func GetServicesStatuses(w http.ResponseWriter, r *http.Request)
- func GetTeamByID(w http.ResponseWriter, r *http.Request)
- func GetTeamByName(w http.ResponseWriter, r *http.Request)
- func GrantBonusPoints(w http.ResponseWriter, r *http.Request)
- func IPish(ip_prefix string) bool
- func Int64Max(x, y int64) int64
- func Logout(w http.ResponseWriter, r *http.Request)
- func MaybeRateLimit(r chi.Router, ratePerSec float64) chi.Router
- func NegroniResponseWriterMiddleware(next http.Handler) http.Handler
- func NewBroadcastHub(logID string, timeCheck timeCheckFn, getPayload payloadFn) *broadcastHub
- func NewRateLimiter(rate float64) *limiter.Limiter
- func PgConfigAsString(c *pgx.ConnConfig) string
- func PingDB(ctx context.Context) error
- func PingHandler(w http.ResponseWriter, r *http.Request)
- func ReadStdinLine() ([]byte, error)
- func RenderQueryErr(w http.ResponseWriter, r *http.Request, err error)
- func RequireAdmin(next http.Handler) http.Handler
- func RequireCtfStaff(next http.Handler) http.Handler
- func RequireEventNotOver(next http.Handler) http.Handler
- func RequireEventStarted(next http.Handler) http.Handler
- func RequireGroupIsAnyOf(whitelistedGroups []models.TeamRole) func(http.Handler) http.Handler
- func RequireLogin(next http.Handler) http.Handler
- func RequireNoSpeeding(lmt *limiter.Limiter) func(http.Handler) http.Handler
- func RequireNotOnBreak() func(http.Handler) http.Handler
- func RequireUrlParamInt(name string) func(http.Handler) http.Handler
- func Run(cfg *Configuration)
- func RunScriptTest(w http.ResponseWriter, r *http.Request)
- func ServiceStatusWsServer() *broadcastHub
- func SetGlobalPostgresDBs(pool *pgx.ConnPool)
- func SetupCheckServiceLogger(lc *LogSettings)
- func SetupPostgres(uri string)
- func SetupScoringLoggers(lc *LogSettings)
- func ShowBonusPage(w http.ResponseWriter, r *http.Request)
- func ShowChallenges(w http.ResponseWriter, r *http.Request)
- func ShowCtfConfig(w http.ResponseWriter, r *http.Request)
- func ShowCtfDashboard(w http.ResponseWriter, r *http.Request)
- func ShowHome(w http.ResponseWriter, r *http.Request)
- func ShowLogViewer(w http.ResponseWriter, r *http.Request)
- func ShowLogin(w http.ResponseWriter, r *http.Request)
- func ShowScoreboard(w http.ResponseWriter, r *http.Request)
- func ShowServiceScriptsConfig(w http.ResponseWriter, r *http.Request)
- func ShowServices(w http.ResponseWriter, r *http.Request)
- func ShowServicesConfig(w http.ResponseWriter, r *http.Request)
- func ShowTeamDashboard(w http.ResponseWriter, r *http.Request)
- func ShowTeamsConfig(w http.ResponseWriter, r *http.Request)
- func SubmitFlag(w http.ResponseWriter, r *http.Request)
- func SubmitLogin(w http.ResponseWriter, r *http.Request)
- func TeamScoreWsServer() *broadcastHub
- func UnwrapNegroniMiddleware(nh negroni.Handler) func(http.Handler) http.Handler
- func UpdateFlag(w http.ResponseWriter, r *http.Request)
- func UpdateService(w http.ResponseWriter, r *http.Request)
- func UpdateTeam(w http.ResponseWriter, r *http.Request)
- func WsTailFile(w http.ResponseWriter, r *http.Request)
- type BlueTeamInsertRequest
- type BlueTeamInsertRequestSlice
- type BonusPointsRequest
- type Check
- type Configuration
- type CtxKey
- type DBSettings
- type ErrResponse
- type EventSettings
- type FSContentManager
- func (cm FSContentManager) DeleteFile(w http.ResponseWriter, r *http.Request)
- func (cm FSContentManager) GetFile(w http.ResponseWriter, r *http.Request)
- func (cm FSContentManager) GetFileList(w http.ResponseWriter, r *http.Request)
- func (cm FSContentManager) SaveFile(w http.ResponseWriter, r *http.Request)
- type FileInfo
- type LogSettings
- type LoggerManager
- type M
- type Monitor
- func (m *Monitor) BreaktimeScheduler(breaks []ScheduledBreak)
- func (m *Monitor) ListenForConfigUpdatesFromPG(ctx context.Context, checksDir, baseIP string)
- func (m *Monitor) ReloadServicesAndTeams(checksDir, baseIP string)
- func (m *Monitor) Run(event *EventSettings, srvmon *ServiceMonitorSettings)
- func (m *Monitor) Stop()
- type Page
- type ScheduledBreak
- type ServerSettings
- type ServiceMonitorSettings
- type ServiceRequest
- type TeamModRequest
Constants ¶
const ( DefaultRotateSize = 100 // MB DefaultMaxBackups = 10 LogDir = "data/log" )
const MaxReqsPerSec = 1
MaxReqsPerSec caps the requests per IP for rate-limited endpoints (if limiting is enabled w/ the config `service_monitor.rate_limit = "true"`).
Currently, rate limiting is fixed to just a few endpoints where a blueteam can hit that endpoint at most once per second, full-stop.
const PGListenNotifyChannel = "cyboard.server.checks"
Variables ¶
var ( LogManager *LoggerManager Logger *logrus.Logger // Service logger (either for Checks, or for Scoring) CaptFlagsLogger *logrus.Logger // Just logs flags as they are captured RequestLogger *negronilogrus.Middleware // Sits in the server mw stack, not called directly )
var ( ErrNotFound = &ErrResponse{HTTPStatusCode: 404, StatusText: "Resource not found"} ErrForbidden = &ErrResponse{HTTPStatusCode: 403, StatusText: "Forbidden"} )
var CtfFileMgr = FSContentManager{ // contains filtered or unexported fields }
var LogReadOnlyMgr = FSContentManager{ // contains filtered or unexported fields }
var RequireIdParam = RequireUrlParamInt("id")
var ScriptMgr = FSContentManager{ // contains filtered or unexported fields }
Functions ¶
func AddBlueteams ¶ added in v0.2018.10
func AddBlueteams(w http.ResponseWriter, r *http.Request)
func AddService ¶ added in v0.2018.10
func AddService(w http.ResponseWriter, r *http.Request)
func ApiCreate ¶ added in v0.2018.10
func ApiCreate(w http.ResponseWriter, r *http.Request, v interface{})
ApiCreate is a helper for HTTP POST operations for ~a few~ models.
model `v` is expected to implement either `models.Inserter` or `models.ManyInserter`, or else it will error.
This helper less reusable than the other helpers, due to the extra type gymnastics which are difficult to abstract away (transforming input json into completely different types, arranging slice layouts, etc.)
func ApiDelete ¶ added in v0.2018.10
ApiDelete is a helper for HTTP DELETE operations for most models. The model is expected to have an integer `ID` field, or it will panic.
func ApiQuery ¶ added in v0.2018.10
func ApiQuery(w http.ResponseWriter, r *http.Request, v interface{}, err error)
ApiQuery is a helper for HTTP GET operations for most models. It handles responding to the API user with the results of a db query, and handling of non-nil errors if they arise.
func ApiUpdate ¶ added in v0.2018.10
ApiUpdate is a helper for HTTP UPDATE operations for most models. The model is expected to have an integer `ID` field, or it will panic.
func CalcPointsPerCheck ¶ added in v0.2018.10
func CalcPointsPerCheck( srv *models.Service, event *EventSettings, checkInterval time.Duration, ) float32
CalcPointsPerCheck determines the fraction of points to award when a service check passes. Inputs `event` and `checkInterval` will come from `config.toml`. Returns a value suitable for the Service.Points field (presumably to be assigned to the parameter Service `srv`).
The algorithm works as follows:
Span (difference) of time from service.StartsAt to event.End Minus the time for each break that occurs within that timeframe Then the number of checks that occur is the divison of that span by checkInterval Finally, return the service.TotalPoints divided by the num of checks, which is the points to award per check.
Caveats: This doesn't handle the case a service is set to start during a break (shouldn't happen), and also won't account for overlapping breaks (also shouldn't happen.) Both of those cases are misconfigurations, that validation will prevent.
func CheckCreds ¶
func CheckCreds(w http.ResponseWriter, r *http.Request) bool
CheckCreds authenticates users based on username/password form values contained in the request. If the credentials all match, the team's ID will be saved to a cookie in the browser. If there are any errors, they will get logged and this will return false.
func CheckSessionID ¶
CheckSessionID is a middleware that authenticates users based on a cookie their browser supplies with each request that has their team's ID. This does a query against the database and sticks the matching models.Team values onto the request context, under the "team" key.
If the user hasn't logged in, has tampered with their cookie, or there's some internal server error, the "team" key in the context will be nil.
func ChecksRun ¶
func ChecksRun(checkCfg *Configuration)
func Compress ¶ added in v0.2019.11
Compress fetches a singleton gzip compressor middleware.
If compression is disable in the app config, a dummy middleware is returned.
Gzip encoders have a lot of overhead, so they get reused. To ensure just one pool of encoders is created, a singleton is used.
func CreateStore ¶
func CreateStore(secure bool)
CreateStore initializes the global Session Manager, used to authenticate users across requests. If secure is true, the generated browser cookies will only be shared over HTTPS.
func CreateWebRouter ¶
func DeleteFlag ¶
func DeleteFlag(w http.ResponseWriter, r *http.Request)
func DeleteService ¶ added in v0.2018.10
func DeleteService(w http.ResponseWriter, r *http.Request)
func DeleteTeam ¶
func DeleteTeam(w http.ResponseWriter, r *http.Request)
func EnableCTFChallenge ¶ added in v0.2019.11
func EnableCTFChallenge(w http.ResponseWriter, r *http.Request)
func EnsureAdmin ¶
EnsureAdmin helps bootstrap the app configuration by prompting & setting up an admin account if there is not one already configured.
func ErrForbiddenBecause ¶ added in v0.2018.10
func ErrInternal ¶ added in v0.2018.10
func ErrInvalidBecause ¶ added in v0.2018.10
ErrInvalidBecause is a public error where the reason text is always displayed, even to non-staff members. Handlers for login and flag submission will give more friendly errors with this. However, things like Internal Server Errors should never be exposed to non-staff.
func ErrInvalidRequest ¶ added in v0.2018.10
func ErrRendering ¶ added in v0.2018.10
func GetAllFlags ¶ added in v0.2018.10
func GetAllFlags(w http.ResponseWriter, r *http.Request)
func GetAllServices ¶ added in v0.2018.10
func GetAllServices(w http.ResponseWriter, r *http.Request)
func GetAllTeams ¶ added in v0.2018.10
func GetAllTeams(w http.ResponseWriter, r *http.Request)
func GetBlueteams ¶ added in v0.2019.11
func GetBlueteams(w http.ResponseWriter, r *http.Request)
func GetBonusPoints ¶ added in v0.2019.11
func GetBonusPoints(w http.ResponseWriter, r *http.Request)
func GetBreakdownOfSubmissionsPerFlag ¶
func GetBreakdownOfSubmissionsPerFlag(w http.ResponseWriter, r *http.Request)
func GetChallengeCapturesByTime ¶ added in v0.2019.11
func GetChallengeCapturesByTime(w http.ResponseWriter, r *http.Request)
func GetChallengeDescription ¶ added in v0.2018.10
func GetChallengeDescription(w http.ResponseWriter, r *http.Request)
func GetEachTeamsCapturedFlags ¶
func GetEachTeamsCapturedFlags(w http.ResponseWriter, r *http.Request)
func GetEventConfig ¶ added in v0.2019.11
func GetEventConfig(w http.ResponseWriter, r *http.Request)
func GetFlagByID ¶ added in v0.2018.10
func GetFlagByID(w http.ResponseWriter, r *http.Request)
func GetFlagByName ¶
func GetFlagByName(w http.ResponseWriter, r *http.Request)
func GetPublicChallenges ¶
func GetPublicChallenges(w http.ResponseWriter, r *http.Request)
func GetServiceByID ¶ added in v0.2018.10
func GetServiceByID(w http.ResponseWriter, r *http.Request)
func GetServicesStatuses ¶ added in v0.2018.10
func GetServicesStatuses(w http.ResponseWriter, r *http.Request)
func GetTeamByID ¶ added in v0.2018.10
func GetTeamByID(w http.ResponseWriter, r *http.Request)
func GetTeamByName ¶ added in v0.2019.11
func GetTeamByName(w http.ResponseWriter, r *http.Request)
func GrantBonusPoints ¶
func GrantBonusPoints(w http.ResponseWriter, r *http.Request)
func IPish ¶ added in v0.2018.10
IPish tests whether a string looks like the first 3 octets of an IPv4 address.
func MaybeRateLimit ¶ added in v0.2018.10
func NegroniResponseWriterMiddleware ¶ added in v0.2018.10
func NewBroadcastHub ¶
func NewBroadcastHub(logID string, timeCheck timeCheckFn, getPayload payloadFn) *broadcastHub
func NewRateLimiter ¶ added in v0.2018.10
func PgConfigAsString ¶ added in v0.2018.10
func PgConfigAsString(c *pgx.ConnConfig) string
func PingHandler ¶ added in v0.2018.10
func PingHandler(w http.ResponseWriter, r *http.Request)
func ReadStdinLine ¶
func RenderQueryErr ¶ added in v0.2018.10
func RenderQueryErr(w http.ResponseWriter, r *http.Request, err error)
RenderQueryErr logs a SQL related error and displays an appropriate message to the user. Additional logging context may be added by setting fields in the request context, see `getCtxErrMsgFields()`
func RequireEventNotOver ¶ added in v0.2018.10
func RequireEventStarted ¶ added in v0.2018.10
func RequireGroupIsAnyOf ¶
func RequireNoSpeeding ¶ added in v0.2018.10
func RequireNotOnBreak ¶ added in v0.2019.11
func RequireUrlParamInt ¶ added in v0.2018.10
func Run ¶
func Run(cfg *Configuration)
func RunScriptTest ¶ added in v0.2018.10
func RunScriptTest(w http.ResponseWriter, r *http.Request)
func ServiceStatusWsServer ¶
func ServiceStatusWsServer() *broadcastHub
ServiceStatusWsServer is a hub suitable for updating the Service Monitor.
func SetGlobalPostgresDBs ¶ added in v0.2018.10
func SetupCheckServiceLogger ¶
func SetupCheckServiceLogger(lc *LogSettings)
SetupCheckServiceLogger uses the log configuration to instantiate a global logger used by the Service Checker component
func SetupPostgres ¶ added in v0.2018.10
func SetupPostgres(uri string)
func SetupScoringLoggers ¶
func SetupScoringLoggers(lc *LogSettings)
SetupScoringLoggers uses the log configuration to instantiate global loggers used by the CTF + Web server component
func ShowBonusPage ¶ added in v0.2018.10
func ShowBonusPage(w http.ResponseWriter, r *http.Request)
func ShowChallenges ¶
func ShowChallenges(w http.ResponseWriter, r *http.Request)
func ShowCtfConfig ¶ added in v0.2018.10
func ShowCtfConfig(w http.ResponseWriter, r *http.Request)
func ShowCtfDashboard ¶ added in v0.2018.10
func ShowCtfDashboard(w http.ResponseWriter, r *http.Request)
func ShowLogViewer ¶ added in v0.2019.11
func ShowLogViewer(w http.ResponseWriter, r *http.Request)
func ShowScoreboard ¶
func ShowScoreboard(w http.ResponseWriter, r *http.Request)
func ShowServiceScriptsConfig ¶ added in v0.2018.10
func ShowServiceScriptsConfig(w http.ResponseWriter, r *http.Request)
func ShowServices ¶
func ShowServices(w http.ResponseWriter, r *http.Request)
func ShowServicesConfig ¶ added in v0.2018.10
func ShowServicesConfig(w http.ResponseWriter, r *http.Request)
func ShowTeamDashboard ¶
func ShowTeamDashboard(w http.ResponseWriter, r *http.Request)
func ShowTeamsConfig ¶ added in v0.2018.10
func ShowTeamsConfig(w http.ResponseWriter, r *http.Request)
func SubmitFlag ¶ added in v0.2018.10
func SubmitFlag(w http.ResponseWriter, r *http.Request)
func SubmitLogin ¶
func SubmitLogin(w http.ResponseWriter, r *http.Request)
func TeamScoreWsServer ¶
func TeamScoreWsServer() *broadcastHub
TeamScoreWsServer is a hub suitable for updating the Scoreboard charts & tables.
func UnwrapNegroniMiddleware ¶ added in v0.2018.10
func UpdateFlag ¶
func UpdateFlag(w http.ResponseWriter, r *http.Request)
func UpdateService ¶ added in v0.2018.10
func UpdateService(w http.ResponseWriter, r *http.Request)
func UpdateTeam ¶
func UpdateTeam(w http.ResponseWriter, r *http.Request)
func WsTailFile ¶ added in v0.2019.11
func WsTailFile(w http.ResponseWriter, r *http.Request)
Types ¶
type BlueTeamInsertRequest ¶ added in v0.2018.10
type BlueTeamInsertRequest struct { *models.BlueTeamStore Password string `json:"password,omitempty"` // Becomes the `Hash` column }
type BlueTeamInsertRequestSlice ¶ added in v0.2018.10
type BlueTeamInsertRequestSlice []BlueTeamInsertRequest
type BonusPointsRequest ¶ added in v0.2018.10
type BonusPointsRequest struct { *models.OtherPoints TeamIDs []int `json:"teams"` }
type Configuration ¶
type Configuration struct { Database DBSettings Log LogSettings Event EventSettings Server ServerSettings ServiceMonitor ServiceMonitorSettings `mapstructure:"service_monitor"` }
func (*Configuration) Validate ¶ added in v0.2018.10
func (cfg *Configuration) Validate() error
Validate checks for constraints on the config, including: Event start is after event end, negative times (interval, timeout), breaks out of order, overlapping breaks, break occurs before/after event starts/ends, and base_ip is a 3-octet IP prefix.
type DBSettings ¶
type DBSettings struct {
URI string `mapstructure:"postgres_uri"`
}
type ErrResponse ¶ added in v0.2018.10
type ErrResponse struct { Err error `json:"-"` // low-level runtime error HTTPStatusCode int `json:"-"` // http response status code StatusText string `json:"status"` // user-level status message ErrorText string `json:"error,omitempty"` // application-level error message, for debugging }
ErrResponse renderer type for handling all sorts of errors.
func (*ErrResponse) Render ¶ added in v0.2018.10
func (e *ErrResponse) Render(w http.ResponseWriter, r *http.Request) error
Render satisfies the go-chi/render.Renderer interface, making errors easy to reuse and print out as JSON/XML
type EventSettings ¶
type EventSettings struct { Start time.Time End time.Time Breaks []ScheduledBreak }
func (EventSettings) String ¶
func (es EventSettings) String() string
type FSContentManager ¶ added in v0.2018.10
type FSContentManager struct {
// contains filtered or unexported fields
}
func (FSContentManager) DeleteFile ¶ added in v0.2018.10
func (cm FSContentManager) DeleteFile(w http.ResponseWriter, r *http.Request)
func (FSContentManager) GetFile ¶ added in v0.2018.10
func (cm FSContentManager) GetFile(w http.ResponseWriter, r *http.Request)
func (FSContentManager) GetFileList ¶ added in v0.2018.10
func (cm FSContentManager) GetFileList(w http.ResponseWriter, r *http.Request)
func (FSContentManager) SaveFile ¶ added in v0.2018.10
func (cm FSContentManager) SaveFile(w http.ResponseWriter, r *http.Request)
type LogSettings ¶
type LoggerManager ¶
type LoggerManager struct { UseStdout bool Level logrus.Level Formatter *logrus.TextFormatter // contains filtered or unexported fields }
LoggerManager holds all the log settings together, which are used across the multiple loggers the server uses.
type Monitor ¶ added in v0.2018.10
type Monitor struct { Checks []Check Unstarted []Check *sync.Mutex // contains filtered or unexported fields }
func NewMonitor ¶ added in v0.2018.10
func NewMonitor() *Monitor
func (*Monitor) BreaktimeScheduler ¶ added in v0.2018.10
func (m *Monitor) BreaktimeScheduler(breaks []ScheduledBreak)
func (*Monitor) ListenForConfigUpdatesFromPG ¶ added in v0.2018.10
func (*Monitor) ReloadServicesAndTeams ¶ added in v0.2018.10
func (*Monitor) Run ¶ added in v0.2018.10
func (m *Monitor) Run(event *EventSettings, srvmon *ServiceMonitorSettings)
type ScheduledBreak ¶ added in v0.2018.10
type ScheduledBreak struct { StartsAt time.Time `mapstructure:"at"` GoesFor time.Duration `mapstructure:"for"` }
func (*ScheduledBreak) End ¶ added in v0.2018.10
func (sb *ScheduledBreak) End() time.Time
func (ScheduledBreak) String ¶ added in v0.2018.10
func (sb ScheduledBreak) String() string
type ServerSettings ¶
type ServerSettings struct { Appname string `mapstructure:"appname"` IP string HTTPPort string `mapstructure:"http_port"` HTTPSPort string `mapstructure:"https_port"` CertPath string `mapstructure:"cert"` CertKeyPath string `mapstructure:"key"` Compress bool RateLimit bool `mapstructure:"rate_limit"` CtfFileDir string `mapstructure:"ctf_file_dir"` }
type ServiceMonitorSettings ¶ added in v0.2018.10
type ServiceRequest ¶ added in v0.2018.10
type TeamModRequest ¶ added in v0.2018.10
type TeamModRequest struct { *models.Team Password *string `json:"password,omitempty"` // Becomes the `Hash` column }
func (*TeamModRequest) Bind ¶ added in v0.2018.10
func (tr *TeamModRequest) Bind(r *http.Request) error
Bind satisfies the go-chi/render#Binder interface, which does post-decode validation & transforms on JSON/XML request bodies.
For a TeamModRequest, "PUT" reqs are not required to update the password/hash of the team, because it is impossible to recover.