diff --git a/README.md b/README.md index 1a74193..c0c28d7 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/bot/access.go b/bot/access.go new file mode 100644 index 0000000..297cf1c --- /dev/null +++ b/bot/access.go @@ -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) +} diff --git a/bot/bot.go b/bot/bot.go index d731b7e..c36373d 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -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) diff --git a/bot/command.go b/bot/command.go index fdc7068..606436e 100644 --- a/bot/command.go +++ b/bot/command.go @@ -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()) } diff --git a/bot/command_admin.go b/bot/command_admin.go new file mode 100644 index 0000000..8513d92 --- /dev/null +++ b/bot/command_admin.go @@ -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()) +} diff --git a/bot/command_owner.go b/bot/command_owner.go new file mode 100644 index 0000000..84f26cd --- /dev/null +++ b/bot/command_owner.go @@ -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)) +} diff --git a/bot/settings.go b/bot/settings.go index 0f93d37..74886c3 100644 --- a/bot/settings.go +++ b/bot/settings.go @@ -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() -} diff --git a/bot/sync.go b/bot/sync.go index 7b1deba..b46a7d7 100644 --- a/bot/sync.go +++ b/bot/sync.go @@ -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) diff --git a/cmd/cmd.go b/cmd/cmd.go index 4ccf225..dc7d455 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -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") } diff --git a/config/config.go b/config/config.go index 6c4804f..c9e297f 100644 --- a/config/config.go +++ b/config/config.go @@ -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 +} diff --git a/config/types.go b/config/types.go index 4920919..ba99036 100644 --- a/config/types.go +++ b/config/types.go @@ -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 diff --git a/go.mod b/go.mod index f170745..a69b4b8 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 316dd09..aeb359c 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/utils/user.go b/utils/user.go index 0e8c59e..3b93c49 100644 --- a/utils/user.go +++ b/utils/user.go @@ -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 ®exPatterns, 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 diff --git a/utils/user_test.go b/utils/user_test.go index 5068204..28e87c9 100644 --- a/utils/user_test.go +++ b/utils/user_test.go @@ -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