Merge branch 'admin-support' into 'main'

Initial work on admin commands support

See merge request etke.cc/postmoogle!22
This commit is contained in:
Aine
2022-08-29 14:45:27 +00:00
15 changed files with 392 additions and 236 deletions

View File

@@ -44,6 +44,7 @@ env vars
* **POSTMOOGLE_DB_DIALECT** - database dialect (postgres, sqlite3)
* **POSTMOOGLE_MAXSIZE** - max email size (including attachments) in megabytes
* **POSTMOOGLE_USERS** - a space-separated list of whitelisted users allowed to use the bridge. If not defined, everyone is allowed. Example rule: `@someone:example.com @another:example.com @bot.*:example.com @*:another.com`
* **POSTMOOGLE_ADMINS** - a space-separated list of admin users. See `POSTMOOGLE_USERS` for syntax examples
You can find default values in [config/defaults.go](config/defaults.go)

42
bot/access.go Normal file
View File

@@ -0,0 +1,42 @@
package bot
import (
"context"
"maunium.net/go/mautrix/id"
"gitlab.com/etke.cc/postmoogle/utils"
)
func (b *Bot) allowAnyone(actorID id.UserID, targetRoomID id.RoomID) bool {
return true
}
func (b *Bot) allowOwner(actorID id.UserID, targetRoomID id.RoomID) bool {
if len(b.allowedUsers) != 0 {
if !utils.Match(actorID.String(), b.allowedUsers) {
return false
}
}
if b.noowner {
return true
}
cfg, err := b.getSettings(targetRoomID)
if err != nil {
b.Error(context.Background(), targetRoomID, "failed to retrieve settings: %v", err)
return false
}
owner := cfg.Owner()
if owner == "" {
return true
}
return owner == actorID.String()
}
func (b *Bot) allowAdmin(actorID id.UserID, targetRoomID id.RoomID) bool {
return utils.Match(actorID.String(), b.allowedAdmins)
}

View File

@@ -6,6 +6,7 @@ import (
"regexp"
"sync"
"git.sr.ht/~xn/cache/v2"
"github.com/getsentry/sentry-go"
"gitlab.com/etke.cc/go/logger"
"gitlab.com/etke.cc/linkpearl"
@@ -21,7 +22,10 @@ type Bot struct {
prefix string
domain string
allowedUsers []*regexp.Regexp
allowedAdmins []*regexp.Regexp
commands commandList
rooms sync.Map
cfg cache.Cache[settings]
log *logger.Logger
lp *linkpearl.Linkpearl
mu map[id.RoomID]*sync.Mutex
@@ -29,36 +33,51 @@ type Bot struct {
}
// New creates a new matrix bot
func New(lp *linkpearl.Linkpearl, log *logger.Logger, prefix, domain string, noowner, federation bool, allowedUsers []*regexp.Regexp) *Bot {
return &Bot{
noowner: noowner,
federation: federation,
prefix: prefix,
domain: domain,
allowedUsers: allowedUsers,
rooms: sync.Map{},
log: log,
lp: lp,
mu: map[id.RoomID]*sync.Mutex{},
func New(
lp *linkpearl.Linkpearl,
log *logger.Logger,
prefix, domain string,
noowner, federation bool,
allowedUsers []*regexp.Regexp,
allowedAdmins []*regexp.Regexp,
) *Bot {
b := &Bot{
noowner: noowner,
federation: federation,
prefix: prefix,
domain: domain,
allowedUsers: allowedUsers,
allowedAdmins: allowedAdmins,
rooms: sync.Map{},
cfg: cache.NewLRU[settings](1000),
log: log,
lp: lp,
mu: map[id.RoomID]*sync.Mutex{},
}
b.commands = b.buildCommandList()
return b
}
// Error message to the log and matrix room
func (b *Bot) Error(ctx context.Context, roomID id.RoomID, message string, args ...interface{}) {
b.log.Error(message, args...)
err := fmt.Errorf(message, args...)
sentry.GetHubFromContext(ctx).CaptureException(fmt.Errorf(message, args...))
sentry.GetHubFromContext(ctx).CaptureException(err)
if roomID != "" {
// nolint // if something goes wrong here nobody can help...
b.lp.Send(roomID, &event.MessageEventContent{
MsgType: event.MsgNotice,
Body: "ERROR: " + fmt.Sprintf(message, args...),
})
b.SendError(ctx, roomID, message)
}
}
// Notice sends a notice message to the matrix room
func (b *Bot) Notice(ctx context.Context, roomID id.RoomID, message string) {
// SendError sends an error message to the matrix room
func (b *Bot) SendError(ctx context.Context, roomID id.RoomID, message string) {
b.SendNotice(ctx, roomID, "ERROR: "+message)
}
// SendNotice sends a notice message to the matrix room
func (b *Bot) SendNotice(ctx context.Context, roomID id.RoomID, message string) {
content := format.RenderMarkdown(message, true, true)
content.MsgType = event.MsgNotice
_, err := b.lp.Send(roomID, &content)

View File

@@ -11,11 +11,18 @@ import (
"gitlab.com/etke.cc/postmoogle/utils"
)
const (
commandHelp = "help"
commandStop = "stop"
commandMailboxes = "mailboxes"
)
type (
command struct {
key string
description string
sanitizer func(string) string
allowed func(id.UserID, id.RoomID) bool
}
commandList []command
)
@@ -29,73 +36,91 @@ func (c commandList) get(key string) *command {
return nil
}
var commands = commandList{
// special commands
{
key: "help",
description: "Show this help message",
},
{
key: "stop",
description: "Disable bridge for the room and clear all configuration",
},
{}, // delimiter
// options commands
{
key: optionMailbox,
description: "Get or set mailbox of the room",
sanitizer: utils.Mailbox,
},
{
key: optionOwner,
description: "Get or set owner of the room",
sanitizer: func(s string) string { return s },
},
{}, // delimiter
{
key: optionNoSender,
description: fmt.Sprintf(
"Get or set `%s` of the room (`true` - hide email sender; `false` - show email sender)",
optionNoSender,
),
sanitizer: utils.SanitizeBoolString,
},
{
key: optionNoSubject,
description: fmt.Sprintf(
"Get or set `%s` of the room (`true` - hide email subject; `false` - show email subject)",
optionNoSubject,
),
sanitizer: utils.SanitizeBoolString,
},
{
key: optionNoHTML,
description: fmt.Sprintf(
"Get or set `%s` of the room (`true` - ignore HTML in email; `false` - parse HTML in emails)",
optionNoHTML,
),
sanitizer: utils.SanitizeBoolString,
},
{
key: optionNoThreads,
description: fmt.Sprintf(
"Get or set `%s` of the room (`true` - ignore email threads; `false` - convert email threads into matrix threads)",
optionNoThreads,
),
sanitizer: utils.SanitizeBoolString,
},
{
key: optionNoFiles,
description: fmt.Sprintf(
"Get or set `%s` of the room (`true` - ignore email attachments; `false` - upload email attachments)",
optionNoFiles,
),
sanitizer: utils.SanitizeBoolString,
},
func (b *Bot) buildCommandList() commandList {
return commandList{
// special commands
{
key: commandHelp,
description: "Show this help message",
allowed: b.allowAnyone,
},
{
key: commandStop,
description: "Disable bridge for the room and clear all configuration",
allowed: b.allowOwner,
},
{allowed: b.allowOwner}, // delimiter
// options commands
{
key: optionMailbox,
description: "Get or set mailbox of the room",
sanitizer: utils.Mailbox,
allowed: b.allowOwner,
},
{
key: optionOwner,
description: "Get or set owner of the room",
sanitizer: func(s string) string { return s },
allowed: b.allowOwner,
},
{allowed: b.allowOwner}, // delimiter
{
key: optionNoSender,
description: fmt.Sprintf(
"Get or set `%s` of the room (`true` - hide email sender; `false` - show email sender)",
optionNoSender,
),
sanitizer: utils.SanitizeBoolString,
allowed: b.allowOwner,
},
{
key: optionNoSubject,
description: fmt.Sprintf(
"Get or set `%s` of the room (`true` - hide email subject; `false` - show email subject)",
optionNoSubject,
),
sanitizer: utils.SanitizeBoolString,
allowed: b.allowOwner,
},
{
key: optionNoHTML,
description: fmt.Sprintf(
"Get or set `%s` of the room (`true` - ignore HTML in email; `false` - parse HTML in emails)",
optionNoHTML,
),
sanitizer: utils.SanitizeBoolString,
allowed: b.allowOwner,
},
{
key: optionNoThreads,
description: fmt.Sprintf(
"Get or set `%s` of the room (`true` - ignore email threads; `false` - convert email threads into matrix threads)",
optionNoThreads,
),
sanitizer: utils.SanitizeBoolString,
allowed: b.allowOwner,
},
{
key: optionNoFiles,
description: fmt.Sprintf(
"Get or set `%s` of the room (`true` - ignore email attachments; `false` - upload email attachments)",
optionNoFiles,
),
sanitizer: utils.SanitizeBoolString,
allowed: b.allowOwner,
},
{allowed: b.allowAdmin}, // delimiter
{
key: commandMailboxes,
description: "Show the list of all mailboxes",
allowed: b.allowAdmin,
},
}
}
func (b *Bot) handleCommand(ctx context.Context, evt *event.Event, commandSlice []string) {
if cmd := commands.get(commandSlice[0]); cmd == nil {
cmd := b.commands.get(commandSlice[0])
if cmd == nil {
return
}
@@ -104,11 +129,18 @@ func (b *Bot) handleCommand(ctx context.Context, evt *event.Event, commandSlice
return
}
if !cmd.allowed(evt.Sender, evt.RoomID) {
b.SendNotice(ctx, evt.RoomID, "not allowed to do that, kupo")
return
}
switch commandSlice[0] {
case "help":
b.sendHelp(ctx, evt.RoomID)
case "stop":
b.runStop(ctx, true)
case commandHelp:
b.sendHelp(ctx)
case commandStop:
b.runStop(ctx)
case commandMailboxes:
b.sendMailboxes(ctx)
default:
b.handleOption(ctx, commandSlice)
}
@@ -144,10 +176,10 @@ func (b *Bot) sendIntroduction(ctx context.Context, roomID id.RoomID) {
msg.WriteString(b.domain)
msg.WriteString("` and have them appear in this room.")
b.Notice(ctx, roomID, msg.String())
b.SendNotice(ctx, roomID, msg.String())
}
func (b *Bot) sendHelp(ctx context.Context, roomID id.RoomID) {
func (b *Bot) sendHelp(ctx context.Context) {
evt := eventFromContext(ctx)
cfg, serr := b.getSettings(evt.RoomID)
@@ -157,7 +189,10 @@ func (b *Bot) sendHelp(ctx context.Context, roomID id.RoomID) {
var msg strings.Builder
msg.WriteString("The following commands are supported:\n\n")
for _, cmd := range commands {
for _, cmd := range b.commands {
if !cmd.allowed(evt.Sender, evt.RoomID) {
continue
}
if cmd.key == "" {
msg.WriteString("\n---\n")
continue
@@ -189,111 +224,5 @@ func (b *Bot) sendHelp(ctx context.Context, roomID id.RoomID) {
msg.WriteString("\n")
}
b.Notice(ctx, roomID, msg.String())
}
func (b *Bot) runStop(ctx context.Context, checkAllowed bool) {
evt := eventFromContext(ctx)
cfg, err := b.getSettings(evt.RoomID)
if err != nil {
b.Error(ctx, evt.RoomID, "failed to retrieve settings: %v", err)
return
}
if checkAllowed && !b.Allowed(evt.Sender, cfg) {
b.Notice(ctx, evt.RoomID, "you don't have permission to do that")
return
}
mailbox := cfg.Get(optionMailbox)
if mailbox == "" {
b.Notice(ctx, evt.RoomID, "that room is not configured yet")
return
}
b.rooms.Delete(mailbox)
err = b.setSettings(evt.RoomID, settings{})
if err != nil {
b.Error(ctx, evt.RoomID, "cannot update settings: %v", err)
return
}
b.Notice(ctx, evt.RoomID, "mailbox has been disabled")
}
func (b *Bot) handleOption(ctx context.Context, cmd []string) {
if len(cmd) == 1 {
b.getOption(ctx, cmd[0])
return
}
b.setOption(ctx, cmd[0], cmd[1])
}
func (b *Bot) getOption(ctx context.Context, name string) {
evt := eventFromContext(ctx)
cfg, err := b.getSettings(evt.RoomID)
if err != nil {
b.Error(ctx, evt.RoomID, "failed to retrieve settings: %v", err)
return
}
value := cfg.Get(name)
if value == "" {
b.Notice(ctx, evt.RoomID, fmt.Sprintf("`%s` is not set, kupo.", name))
return
}
if name == optionMailbox {
value = value + "@" + b.domain
}
b.Notice(ctx, evt.RoomID, fmt.Sprintf("`%s` of this room is `%s`", name, value))
}
func (b *Bot) setOption(ctx context.Context, name, value string) {
cmd := commands.get(name)
if cmd != nil {
value = cmd.sanitizer(value)
}
evt := eventFromContext(ctx)
if name == optionMailbox {
existingID, ok := b.GetMapping(value)
if ok && existingID != "" && existingID != evt.RoomID {
b.Notice(ctx, evt.RoomID, fmt.Sprintf("Mailbox `%s@%s` already taken, kupo", value, b.domain))
return
}
}
cfg, err := b.getSettings(evt.RoomID)
if err != nil {
b.Error(ctx, evt.RoomID, "failed to retrieve settings: %v", err)
return
}
if !b.Allowed(evt.Sender, cfg) {
b.Notice(ctx, evt.RoomID, "you don't have permission to do that, kupo")
return
}
old := cfg.Get(name)
cfg.Set(name, value)
if name == optionMailbox {
cfg.Set(optionOwner, evt.Sender.String())
if old != "" {
b.rooms.Delete(old)
}
b.rooms.Store(value, evt.RoomID)
value = fmt.Sprintf("%s@%s", value, b.domain)
}
err = b.setSettings(evt.RoomID, cfg)
if err != nil {
b.Error(ctx, evt.RoomID, "cannot update settings: %v", err)
return
}
b.Notice(ctx, evt.RoomID, fmt.Sprintf("`%s` of this room set to `%s`", name, value))
b.SendNotice(ctx, evt.RoomID, msg.String())
}

53
bot/command_admin.go Normal file
View File

@@ -0,0 +1,53 @@
package bot
import (
"context"
"strings"
"maunium.net/go/mautrix/id"
)
func (b *Bot) sendMailboxes(ctx context.Context) {
evt := eventFromContext(ctx)
mailboxes := map[string]id.RoomID{}
b.rooms.Range(func(key any, value any) bool {
if key == nil {
return true
}
if value == nil {
return true
}
mailbox, ok := key.(string)
if !ok {
return true
}
roomID, ok := value.(id.RoomID)
if !ok {
return true
}
mailboxes[mailbox] = roomID
return true
})
if len(mailboxes) == 0 {
b.SendNotice(ctx, evt.RoomID, "No mailboxes are managed by the bot so far, kupo!")
return
}
var msg strings.Builder
msg.WriteString("The following mailboxes are managed by the bot:\n")
for mailbox, roomID := range mailboxes {
msg.WriteString("* `")
msg.WriteString(mailbox)
msg.WriteString("@")
msg.WriteString(b.domain)
msg.WriteString("` - `")
msg.WriteString(roomID.String())
msg.WriteString("`")
msg.WriteString("\n")
}
b.SendNotice(ctx, evt.RoomID, msg.String())
}

106
bot/command_owner.go Normal file
View File

@@ -0,0 +1,106 @@
package bot
import (
"context"
"fmt"
)
func (b *Bot) runStop(ctx context.Context) {
evt := eventFromContext(ctx)
cfg, err := b.getSettings(evt.RoomID)
if err != nil {
b.Error(ctx, evt.RoomID, "failed to retrieve settings: %v", err)
return
}
mailbox := cfg.Get(optionMailbox)
if mailbox == "" {
b.SendNotice(ctx, evt.RoomID, "that room is not configured yet")
return
}
b.rooms.Delete(mailbox)
err = b.setSettings(evt.RoomID, settings{})
if err != nil {
b.Error(ctx, evt.RoomID, "cannot update settings: %v", err)
return
}
b.SendNotice(ctx, evt.RoomID, "mailbox has been disabled")
}
func (b *Bot) handleOption(ctx context.Context, cmd []string) {
if len(cmd) == 1 {
b.getOption(ctx, cmd[0])
return
}
b.setOption(ctx, cmd[0], cmd[1])
}
func (b *Bot) getOption(ctx context.Context, name string) {
evt := eventFromContext(ctx)
cfg, err := b.getSettings(evt.RoomID)
if err != nil {
b.Error(ctx, evt.RoomID, "failed to retrieve settings: %v", err)
return
}
value := cfg.Get(name)
if value == "" {
b.SendNotice(ctx, evt.RoomID, fmt.Sprintf("`%s` is not set, kupo.", name))
return
}
if name == optionMailbox {
value = value + "@" + b.domain
}
b.SendNotice(ctx, evt.RoomID, fmt.Sprintf("`%s` of this room is `%s`", name, value))
}
func (b *Bot) setOption(ctx context.Context, name, value string) {
cmd := b.commands.get(name)
if cmd != nil {
value = cmd.sanitizer(value)
}
evt := eventFromContext(ctx)
if name == optionMailbox {
existingID, ok := b.GetMapping(value)
if ok && existingID != "" && existingID != evt.RoomID {
b.SendNotice(ctx, evt.RoomID, fmt.Sprintf("Mailbox `%s@%s` already taken, kupo", value, b.domain))
return
}
}
cfg, err := b.getSettings(evt.RoomID)
if err != nil {
b.Error(ctx, evt.RoomID, "failed to retrieve settings: %v", err)
return
}
old := cfg.Get(name)
cfg.Set(name, value)
if name == optionMailbox {
cfg.Set(optionOwner, evt.Sender.String())
if old != "" {
b.rooms.Delete(old)
}
b.rooms.Store(value, evt.RoomID)
value = fmt.Sprintf("%s@%s", value, b.domain)
}
err = b.setSettings(evt.RoomID, cfg)
if err != nil {
b.Error(ctx, evt.RoomID, "cannot update settings: %v", err)
return
}
if name == optionMailbox {
value = value + "@" + b.domain
}
b.SendNotice(ctx, evt.RoomID, fmt.Sprintf("`%s` of this room set to `%s`", name, value))
}

View File

@@ -81,6 +81,11 @@ func (b *Bot) migrateSettings(roomID id.RoomID) {
}
func (b *Bot) getSettings(roomID id.RoomID) (settings, error) {
cfg := b.cfg.Get(roomID.String())
if cfg != nil {
return cfg, nil
}
config := settings{}
err := b.lp.GetClient().GetRoomAccountData(roomID, settingskey, &config)
if err != nil {
@@ -90,29 +95,14 @@ func (b *Bot) getSettings(roomID id.RoomID) (settings, error) {
// In such cases, just return a default (empty) settings object.
err = nil
}
} else {
b.cfg.Set(roomID.String(), config)
}
return config, utils.UnwrapError(err)
}
func (b *Bot) setSettings(roomID id.RoomID, cfg settings) error {
b.cfg.Set(roomID.String(), cfg)
return utils.UnwrapError(b.lp.GetClient().SetRoomAccountData(roomID, settingskey, cfg))
}
// Allowed checks if change is allowed
func (b *Bot) Allowed(userID id.UserID, cfg settings) bool {
if !utils.Match(userID.String(), b.allowedUsers) {
return false
}
if b.noowner {
return true
}
owner := cfg.Owner()
if owner == "" {
return true
}
return owner == userID.String()
}

View File

@@ -80,7 +80,7 @@ func (b *Bot) onBotJoin(ctx context.Context) {
}
b.sendIntroduction(ctx, evt.RoomID)
b.sendHelp(ctx, evt.RoomID)
b.sendHelp(ctx)
}
func (b *Bot) onLeave(ctx context.Context) {
@@ -94,7 +94,7 @@ func (b *Bot) onLeave(ctx context.Context) {
count := len(members)
if count == 1 && members[0] == b.lp.GetClient().UserID {
b.log.Info("no more users left in the %s room", evt.RoomID)
b.runStop(ctx, false)
b.runStop(ctx)
_, err := b.lp.GetClient().LeaveRoom(evt.RoomID)
if err != nil {
b.Error(ctx, evt.RoomID, "cannot leave empty room: %v", err)

View File

@@ -87,7 +87,7 @@ func initBot(cfg *config.Config) {
// nolint // Fatal = panic, not os.Exit()
log.Fatal("cannot initialize matrix bot: %v", err)
}
mxb = bot.New(lp, mxlog, cfg.Prefix, cfg.Domain, cfg.NoOwner, cfg.Federation, cfg.Users)
mxb = bot.New(lp, mxlog, cfg.Prefix, cfg.Domain, cfg.NoOwner, cfg.Federation, cfg.Users, cfg.Admins)
log.Debug("bot has been created")
}

View File

@@ -2,6 +2,7 @@ package config
import (
"fmt"
"regexp"
"gitlab.com/etke.cc/go/env"
@@ -14,14 +15,14 @@ const prefix = "postmoogle"
func New() (*Config, error) {
env.SetPrefix(prefix)
mxidPatterns := env.Slice("users")
regexPatterns, err := utils.WildcardMXIDsToRegexes(mxidPatterns)
userPatterns, err := getUserRegexPatterns("users")
if err != nil {
return nil, fmt.Errorf(
"failed to convert wildcard user patterns (`%s`) to regular expression: %s",
mxidPatterns,
err,
)
return nil, err
}
adminPatterns, err := getUserRegexPatterns("admins")
if err != nil {
return nil, err
}
cfg := &Config{
@@ -36,7 +37,8 @@ func New() (*Config, error) {
Federation: env.Bool("federation"),
MaxSize: env.Int("maxsize", defaultConfig.MaxSize),
StatusMsg: env.String("statusmsg", defaultConfig.StatusMsg),
Users: *regexPatterns,
Users: userPatterns,
Admins: adminPatterns,
Sentry: Sentry{
DSN: env.String("sentry.dsn", defaultConfig.Sentry.DSN),
},
@@ -49,3 +51,17 @@ func New() (*Config, error) {
return cfg, nil
}
func getUserRegexPatterns(key string) ([]*regexp.Regexp, error) {
mxidPatterns := env.Slice(key)
regexPatterns, err := utils.WildcardMXIDsToRegexes(mxidPatterns)
if err != nil {
return nil, fmt.Errorf(
"failed to convert wildcard %s patterns (`%s`) to regular expression: %s",
key,
mxidPatterns,
err,
)
}
return regexPatterns, nil
}

View File

@@ -30,6 +30,8 @@ type Config struct {
StatusMsg string
// Users holds list of allowed users (wildcards supported), e.g.: @*:example.com, @bot.*:example.com, @admin:*. Empty = *
Users []*regexp.Regexp
// Admins holds list of admin users (wildcards supported), e.g.: @*:example.com, @bot.*:example.com, @admin:*. Empty = *
Admins []*regexp.Regexp
// DB config
DB DB

1
go.mod
View File

@@ -3,6 +3,7 @@ module gitlab.com/etke.cc/postmoogle
go 1.18
require (
git.sr.ht/~xn/cache/v2 v2.0.0
github.com/emersion/go-smtp v0.15.0
github.com/getsentry/sentry-go v0.13.0
github.com/jhillyerd/enmime v0.10.0

2
go.sum
View File

@@ -1,3 +1,5 @@
git.sr.ht/~xn/cache/v2 v2.0.0 h1:aYzwGDyVIzjCl2yqcxZjprnu++Q3BmUQeK2agqvcQt8=
git.sr.ht/~xn/cache/v2 v2.0.0/go.mod h1:HIPSMiDudQ483tRDup586e0YZdwMySIZFWXMPwYMuV8=
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI=
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXOlEPAMFPfp014mK1SWq8G8BN8o7/dfYqJrVGn8=

View File

@@ -7,7 +7,7 @@ import (
)
// WildcardMXIDsToRegexes converts a list of wildcard patterns to a list of regular expressions
func WildcardMXIDsToRegexes(wildCardPatterns []string) (*[]*regexp.Regexp, error) {
func WildcardMXIDsToRegexes(wildCardPatterns []string) ([]*regexp.Regexp, error) {
regexPatterns := make([]*regexp.Regexp, len(wildCardPatterns))
for idx, wildCardPattern := range wildCardPatterns {
@@ -18,16 +18,11 @@ func WildcardMXIDsToRegexes(wildCardPatterns []string) (*[]*regexp.Regexp, error
regexPatterns[idx] = regex
}
return &regexPatterns, nil
return regexPatterns, nil
}
// 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
}
for _, regex := range allowed {
if regex.MatchString(userID) {
return true

View File

@@ -127,10 +127,10 @@ func TestMatch(t *testing.T) {
tests := []testDataDefinition{
{
name: "Empty allowed users allows anyone",
name: "Empty allowed users allows no one",
checkedValue: "@someone:example.com",
allowedUsers: []string{},
expectedResult: true,
expectedResult: false,
},
{
name: "Direct full mxid match is allowed",
@@ -202,7 +202,7 @@ func TestMatch(t *testing.T) {
t.Error(err)
}
actualResult := Match(testData.checkedValue, *allowedUserRegexes)
actualResult := Match(testData.checkedValue, allowedUserRegexes)
if actualResult == testData.expectedResult {
return