From f97ebb604ada6196ffbcbf83870f05ba059cf4d2 Mon Sep 17 00:00:00 2001 From: Aine Date: Mon, 29 Aug 2022 21:41:14 +0300 Subject: [PATCH] manage users in runtime, closes #16 --- README.md | 3 ++- bot/bot.go | 31 ++++++++++++++------------- bot/command.go | 8 +++++++ bot/command_admin.go | 38 +++++++++++++++++++++++++++++++++ bot/data.go | 50 ++++++++++++++++++++++++++++++++++++++++++-- bot/settings.go | 5 +++++ config/types.go | 2 +- 7 files changed, 119 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index b5913e7..631e933 100644 --- a/README.md +++ b/README.md @@ -44,8 +44,8 @@ env vars * **POSTMOOGLE_DB_DSN** - database connection string * **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 on the homeserver are 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 +* **POSTMOOGLE_USERS** - deprecated and ignored, use `!pm users` instead You can find default values in [config/defaults.go](config/defaults.go) @@ -83,6 +83,7 @@ If you want to change them - check available options in the help message (`!pm h --- * **!pm mailboxes** - Show the list of all mailboxes +* **!pm users** - Get or set allowed users patterns * **!pm delete** <mailbox> - Delete specific mailbox diff --git a/bot/bot.go b/bot/bot.go index de25ddb..43e9018 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -39,12 +39,25 @@ func New( users []string, admins []string, ) (*Bot, error) { + b := &Bot{ + prefix: prefix, + domain: domain, + rooms: sync.Map{}, + cfg: cache.NewLRU[settings](1000), + log: log, + lp: lp, + mu: map[id.RoomID]*sync.Mutex{}, + } + err := b.migrateBotSettings(users) + if err != nil { + return nil, err + } + _, homeserver, err := lp.GetClient().UserID.Parse() if err != nil { return nil, err } - var allowedUsers []*regexp.Regexp - allowedUsers, uerr := parseMXIDpatterns(users, "@*:"+homeserver) + allowedUsers, uerr := parseMXIDpatterns(b.getBotSettings().Users(), "@*:"+homeserver) if uerr != nil { return nil, uerr } @@ -52,18 +65,8 @@ func New( if aerr != nil { return nil, aerr } - - b := &Bot{ - 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.allowedUsers = allowedUsers + b.allowedAdmins = allowedAdmins b.commands = b.buildCommandList() return b, nil diff --git a/bot/command.go b/bot/command.go index 966662f..926eadd 100644 --- a/bot/command.go +++ b/bot/command.go @@ -14,6 +14,7 @@ import ( const ( commandHelp = "help" commandStop = "stop" + commandUsers = botOptionUsers commandDelete = "delete" commandMailboxes = "mailboxes" ) @@ -111,6 +112,11 @@ func (b *Bot) buildCommandList() commandList { allowed: b.allowOwner, }, {allowed: b.allowAdmin}, // delimiter + { + key: botOptionUsers, + description: "Get or set allowed users", + allowed: b.allowAdmin, + }, { key: commandMailboxes, description: "Show the list of all mailboxes", @@ -140,6 +146,8 @@ func (b *Bot) handleCommand(ctx context.Context, evt *event.Event, commandSlice b.sendHelp(ctx) case commandStop: b.runStop(ctx) + case commandUsers: + b.runUsers(ctx, commandSlice) case commandDelete: b.runDelete(ctx, commandSlice) case commandMailboxes: diff --git a/bot/command_admin.go b/bot/command_admin.go index b51b1de..ebec11b 100644 --- a/bot/command_admin.go +++ b/bot/command_admin.go @@ -87,3 +87,41 @@ func (b *Bot) runDelete(ctx context.Context, commandSlice []string) { b.SendNotice(ctx, evt.RoomID, "mailbox has been deleted") } + +func (b *Bot) runUsers(ctx context.Context, commandSlice []string) { + evt := eventFromContext(ctx) + cfg := b.getBotSettings() + if len(commandSlice) < 2 { + var msg strings.Builder + users := cfg.Users() + if len(users) > 0 { + msg.WriteString("Currently: `") + msg.WriteString(strings.Join(users, " ")) + msg.WriteString("`\n\n") + } + msg.WriteString("Usage: `") + msg.WriteString(b.prefix) + msg.WriteString(" users PATTERN1 PATTERN2 PATTERN3...`") + msg.WriteString("where patterns like `@someone:example.com ") + msg.WriteString(" @bot.*:example.com @*:another.com @*:*`\n") + + b.SendNotice(ctx, evt.RoomID, msg.String()) + return + } + + patterns := commandSlice[1:] + allowedUsers, err := parseMXIDpatterns(patterns, "") + if err != nil { + b.SendError(ctx, evt.RoomID, fmt.Sprintf("invalid patterns: %v", err)) + return + } + + cfg.Set(botOptionUsers, strings.Join(patterns, " ")) + + err = b.setBotSettings(cfg) + if err != nil { + b.Error(ctx, evt.RoomID, "cannot set bot config: %v", err) + } + b.allowedUsers = allowedUsers + b.SendNotice(ctx, evt.RoomID, "allowed users updated") +} diff --git a/bot/data.go b/bot/data.go index 31b8902..310704d 100644 --- a/bot/data.go +++ b/bot/data.go @@ -4,12 +4,15 @@ import ( "strings" "maunium.net/go/mautrix/id" + + "gitlab.com/etke.cc/postmoogle/utils" ) // account data keys const ( - messagekey = "cc.etke.postmoogle.message" - settingskey = "cc.etke.postmoogle.settings" + messagekey = "cc.etke.postmoogle.message" + settingskey = "cc.etke.postmoogle.settings" + botconfigkey = "cc.etke.postmoogle.config" ) // event keys @@ -27,6 +30,8 @@ const ( optionNoHTML = "nohtml" optionNoThreads = "nothreads" optionNoFiles = "nofiles" + + botOptionUsers = "users" ) var migrations = []string{} @@ -108,3 +113,44 @@ func (b *Bot) setThreadID(roomID id.RoomID, messageID string, eventID id.EventID } } } + +// TODO: remove after migration +func (b *Bot) migrateBotSettings(users []string) error { + config := b.getBotSettings() + cfgUsers := config.Users() + if len(users) > 0 && len(cfgUsers) == 0 { + _, err := parseMXIDpatterns(users, "") + if err != nil { + return err + } + config.Set(botOptionUsers, strings.Join(users, " ")) + return b.setBotSettings(config) + } + + return nil +} + +func (b *Bot) getBotSettings() settings { + cfg := b.cfg.Get(botconfigkey) + if cfg != nil { + return cfg + } + + config := settings{} + err := b.lp.GetClient().GetAccountData(botconfigkey, &config) + if err != nil { + if strings.Contains(err.Error(), "M_NOT_FOUND") { + err = nil + } + b.log.Error("cannot get bot settings: %v", utils.UnwrapError(err)) + } else { + b.cfg.Set(botconfigkey, config) + } + + return config +} + +func (b *Bot) setBotSettings(cfg settings) error { + b.cfg.Set(botconfigkey, cfg) + return utils.UnwrapError(b.lp.GetClient().SetAccountData(botconfigkey, cfg)) +} diff --git a/bot/settings.go b/bot/settings.go index 74886c3..66bb29a 100644 --- a/bot/settings.go +++ b/bot/settings.go @@ -52,6 +52,11 @@ func (s settings) NoFiles() bool { return utils.Bool(s.Get(optionNoFiles)) } +// Users is bot/admin option +func (s settings) Users() []string { + return strings.Split(s.Get(botOptionUsers), " ") +} + // Set option func (s settings) Set(key, value string) { s[strings.ToLower(strings.TrimSpace(key))] = value diff --git a/config/types.go b/config/types.go index 531ebfb..605d8a5 100644 --- a/config/types.go +++ b/config/types.go @@ -22,7 +22,7 @@ type Config struct { MaxSize int // StatusMsg of the bot StatusMsg string - // Users holds list of allowed users (wildcards supported), e.g.: @*:example.com, @bot.*:example.com, @admin:*. Empty = homeserver only + // Users DEPRECATED holds list of allowed users (wildcards supported), e.g.: @*:example.com, @bot.*:example.com, @admin:*. Empty = homeserver only Users []string // Admins holds list of admin users (wildcards supported), e.g.: @*:example.com, @bot.*:example.com, @admin:*. Empty = no admins Admins []string