99 lines
2.3 KiB
Go
99 lines
2.3 KiB
Go
package config
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"regexp"
|
|
)
|
|
|
|
type Rule string
|
|
|
|
const (
|
|
RuleUnknown = ""
|
|
RuleAllow = "ALLOW"
|
|
RuleDeny = "DENY"
|
|
RuleChallenge = "CHALLENGE"
|
|
)
|
|
|
|
type Bot struct {
|
|
Name string `json:"name"`
|
|
UserAgentRegex *string `json:"user_agent_regex"`
|
|
PathRegex *string `json:"path_regex"`
|
|
Action Rule `json:"action"`
|
|
}
|
|
|
|
var (
|
|
ErrNoBotRulesDefined = errors.New("config: must define at least one (1) bot rule")
|
|
ErrBotMustHaveName = errors.New("config.Bot: must set name")
|
|
ErrBotMustHaveUserAgentOrPath = errors.New("config.Bot: must set either user_agent_regex, path_regex")
|
|
ErrBotMustHaveUserAgentOrPathNotBoth = errors.New("config.Bot: must set either user_agent_regex, path_regex, and not both")
|
|
ErrUnknownAction = errors.New("config.Bot: unknown action")
|
|
ErrInvalidUserAgentRegex = errors.New("config.Bot: invalid user agent regex")
|
|
ErrInvalidPathRegex = errors.New("config.Bot: invalid path regex")
|
|
)
|
|
|
|
func (b Bot) Valid() error {
|
|
var errs []error
|
|
|
|
if b.Name == "" {
|
|
errs = append(errs, ErrBotMustHaveName)
|
|
}
|
|
|
|
if b.UserAgentRegex == nil && b.PathRegex == nil {
|
|
errs = append(errs, ErrBotMustHaveUserAgentOrPath)
|
|
}
|
|
|
|
if b.UserAgentRegex != nil && b.PathRegex != nil {
|
|
errs = append(errs, ErrBotMustHaveUserAgentOrPathNotBoth)
|
|
}
|
|
|
|
if b.UserAgentRegex != nil {
|
|
if _, err := regexp.Compile(*b.UserAgentRegex); err != nil {
|
|
errs = append(errs, ErrInvalidUserAgentRegex, err)
|
|
}
|
|
}
|
|
|
|
if b.PathRegex != nil {
|
|
if _, err := regexp.Compile(*b.PathRegex); err != nil {
|
|
errs = append(errs, ErrInvalidPathRegex, err)
|
|
}
|
|
}
|
|
|
|
switch b.Action {
|
|
case RuleAllow, RuleChallenge, RuleDeny:
|
|
// okay
|
|
default:
|
|
errs = append(errs, fmt.Errorf("%w: %q", ErrUnknownAction, b.Action))
|
|
}
|
|
|
|
if len(errs) != 0 {
|
|
return fmt.Errorf("config: bot entry for %q is not valid:\n%w", b.Name, errors.Join(errs...))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type Config struct {
|
|
Bots []Bot `json:"bots"`
|
|
DNSBL bool `json:"dnsbl"`
|
|
}
|
|
|
|
func (c Config) Valid() error {
|
|
var errs []error
|
|
|
|
if len(c.Bots) == 0 {
|
|
errs = append(errs, ErrNoBotRulesDefined)
|
|
}
|
|
|
|
for _, b := range c.Bots {
|
|
if err := b.Valid(); err != nil {
|
|
errs = append(errs, err)
|
|
}
|
|
}
|
|
|
|
if len(errs) != 0 {
|
|
return fmt.Errorf("config is not valid:\n%w", errors.Join(errs...))
|
|
}
|
|
|
|
return nil
|
|
}
|