|
|
|
|
@@ -6,12 +6,12 @@ import (
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// WildcardUserPatternsToRegexPatterns converts a list of wildcard patterns to a list of regular expressions
|
|
|
|
|
func WildcardUserPatternsToRegexPatterns(wildCardPatterns []string) (*[]*regexp.Regexp, error) {
|
|
|
|
|
// WildcardMXIDsToRegexes converts a list of wildcard patterns to a list of regular expressions
|
|
|
|
|
func WildcardMXIDsToRegexes(wildCardPatterns []string) (*[]*regexp.Regexp, error) {
|
|
|
|
|
regexPatterns := make([]*regexp.Regexp, len(wildCardPatterns))
|
|
|
|
|
|
|
|
|
|
for idx, wildCardPattern := range wildCardPatterns {
|
|
|
|
|
regex, err := parseAllowedUserRule(wildCardPattern)
|
|
|
|
|
regex, err := parseMXIDWildcard(wildCardPattern)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to parse allowed user rule `%s`: %s", wildCardPattern, err)
|
|
|
|
|
}
|
|
|
|
|
@@ -21,9 +21,8 @@ func WildcardUserPatternsToRegexPatterns(wildCardPatterns []string) (*[]*regexp.
|
|
|
|
|
return ®exPatterns, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MatchUserWithAllowedRegexes tells if the given user id is allowed to use the bot, according to the given whitelist
|
|
|
|
|
// An empty whitelist means "everyone is allowed"
|
|
|
|
|
func MatchUserWithAllowedRegexes(userID string, allowed []*regexp.Regexp) bool {
|
|
|
|
|
// Match tells if the given user id is allowed to use the bot, according to the given whitelist
|
|
|
|
|
func Match(userID string, allowed []*regexp.Regexp) bool {
|
|
|
|
|
// No whitelisted users means everyone is whitelisted
|
|
|
|
|
if len(allowed) == 0 {
|
|
|
|
|
return true
|
|
|
|
|
@@ -38,10 +37,18 @@ func MatchUserWithAllowedRegexes(userID string, allowed []*regexp.Regexp) bool {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parseAllowedUserRule parses a user whitelisting rule and returns a regular expression which corresponds to it
|
|
|
|
|
// parseMXIDWildcard parses a user whitelisting wildcard rule and returns a regular expression which corresponds to it
|
|
|
|
|
//
|
|
|
|
|
// Example conversion: `@bot.*.something:*.example.com` -> `^bot\.([^:@]*)\.something:([^:@]*)\.example.com$`
|
|
|
|
|
// Example of recognized wildcard patterns: `@someone:example.com`, `@*:example.com`, `@bot.*:example.com`, `@someone:*`, `@someone:*.example.com`
|
|
|
|
|
func parseAllowedUserRule(wildCardRule string) (*regexp.Regexp, error) {
|
|
|
|
|
//
|
|
|
|
|
// The `*` wildcard character is normally interpretted as "a number of literal characters or an empty string".
|
|
|
|
|
// Our implementation below matches this (yielding `([^:@])*`), which could provide a slightly suboptimal regex in these cases:
|
|
|
|
|
// - `@*:example.com` -> `^@([^:@])*:example\.com$`, although `^@([^:@])+:example\.com$` would be preferable
|
|
|
|
|
// - `@someone:*` -> `@someone:([^:@])*$`, although `@someone:([^:@])+$` would be preferable
|
|
|
|
|
// When it's a bare wildcard (`*`, instead of `*.example.com`) we likely prefer to yield a regex that matches **at least one character**.
|
|
|
|
|
// This probably doesn't matter because mxids that we'll match against are all valid and fully complete.
|
|
|
|
|
func parseMXIDWildcard(wildCardRule string) (*regexp.Regexp, error) {
|
|
|
|
|
if !strings.HasPrefix(wildCardRule, "@") {
|
|
|
|
|
return nil, fmt.Errorf("rules need to be fully-qualified, starting with a @")
|
|
|
|
|
}
|
|
|
|
|
@@ -56,25 +63,6 @@ func parseAllowedUserRule(wildCardRule string) (*regexp.Regexp, error) {
|
|
|
|
|
return nil, fmt.Errorf("expected exactly 2 parts in the rule, separated by `:`")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getRegexPatternForPart := func(part string) (string, error) {
|
|
|
|
|
if part == "" {
|
|
|
|
|
return "", fmt.Errorf("rejecting empty part")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var pattern strings.Builder
|
|
|
|
|
for _, rune := range part {
|
|
|
|
|
if rune == '*' {
|
|
|
|
|
// We match everything except for `:` and `@`, because that would be an invalid MXID anyway
|
|
|
|
|
pattern.WriteString("([^:@]*)")
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pattern.WriteString(regexp.QuoteMeta(string(rune)))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pattern.String(), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
localPart := parts[0]
|
|
|
|
|
localPartPattern, err := getRegexPatternForPart(localPart)
|
|
|
|
|
if err != nil {
|
|
|
|
|
@@ -96,3 +84,26 @@ func parseAllowedUserRule(wildCardRule string) (*regexp.Regexp, error) {
|
|
|
|
|
|
|
|
|
|
return regex, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getRegexPatternForPart(part string) (string, error) {
|
|
|
|
|
if part == "" {
|
|
|
|
|
return "", fmt.Errorf("rejecting empty part")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var pattern strings.Builder
|
|
|
|
|
for _, rune := range part {
|
|
|
|
|
if rune == '*' {
|
|
|
|
|
// We match everything except for `:` and `@`, because that would be an invalid MXID anyway.
|
|
|
|
|
//
|
|
|
|
|
// If the whole part is `*` (only) instead of merely containing `*` within it,
|
|
|
|
|
// we may also consider replacing it with `([^:@]+)` (+, instead of *).
|
|
|
|
|
// See parseMXIDWildcard for notes about this.
|
|
|
|
|
pattern.WriteString("([^:@]*)")
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pattern.WriteString(regexp.QuoteMeta(string(rune)))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pattern.String(), nil
|
|
|
|
|
}
|
|
|
|
|
|