From de9977b7d27dc00c8e9b64a3072ac3b7bff5d686 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sat, 6 Nov 2021 13:57:35 +0200 Subject: [PATCH] Add support for automatic double puppeting from other servers --- config/bridge.go | 16 +++++++------ config/config.go | 22 ++++++++++-------- custompuppet.go | 56 ++++++++++++++++++++++++++++++++------------- example-config.yaml | 13 +++++++---- user.go | 2 +- 5 files changed, 71 insertions(+), 38 deletions(-) diff --git a/config/bridge.go b/config/bridge.go index 1b9abd5..446b4e2 100644 --- a/config/bridge.go +++ b/config/bridge.go @@ -46,11 +46,15 @@ type BridgeConfig struct { UserAvatarSync bool `yaml:"user_avatar_sync"` BridgeMatrixLeave bool `yaml:"bridge_matrix_leave"` - SyncWithCustomPuppets bool `yaml:"sync_with_custom_puppets"` - SyncDirectChatList bool `yaml:"sync_direct_chat_list"` - DefaultBridgeReceipts bool `yaml:"default_bridge_receipts"` - DefaultBridgePresence bool `yaml:"default_bridge_presence"` - LoginSharedSecret string `yaml:"login_shared_secret"` + SyncWithCustomPuppets bool `yaml:"sync_with_custom_puppets"` + SyncDirectChatList bool `yaml:"sync_direct_chat_list"` + DefaultBridgeReceipts bool `yaml:"default_bridge_receipts"` + DefaultBridgePresence bool `yaml:"default_bridge_presence"` + + DoublePuppetServerMap map[string]string `yaml:"double_puppet_server_map"` + DoublePuppetAllowDiscovery bool `yaml:"double_puppet_allow_discovery"` + LoginSharedSecretMap map[string]string `yaml:"login_shared_secret_map"` + LegacyLoginSharedSecret string `yaml:"login_shared_secret"` PrivateChatPortalMeta bool `yaml:"private_chat_portal_meta"` BridgeNotices bool `yaml:"bridge_notices"` @@ -92,8 +96,6 @@ type BridgeConfig struct { Relay RelaybotConfig `yaml:"relay"` - DoublePuppetServerMap map[string]string `yaml:"double_puppet_server_map"` - usernameTemplate *template.Template `yaml:"-"` displaynameTemplate *template.Template `yaml:"-"` } diff --git a/config/config.go b/config/config.go index 1b59ebf..d771856 100644 --- a/config/config.go +++ b/config/config.go @@ -21,6 +21,7 @@ import ( "os" "gopkg.in/yaml.v2" + "maunium.net/go/mautrix/id" "maunium.net/go/mautrix/appservice" @@ -80,15 +81,10 @@ type Config struct { Logging appservice.LogConfig `yaml:"logging"` } -func (config *Config) CanDoublePuppet(userID id.UserID) bool { - if len(config.Bridge.LoginSharedSecret) == 0 { - // Automatic login not enabled - return false - } else if _, homeserver, _ := userID.Parse(); homeserver != config.Homeserver.Domain { - // user is on another homeserver - return false - } - return true +func (config *Config) CanAutoDoublePuppet(userID id.UserID) bool { + _, homeserver, _ := userID.Parse() + _, hasSecret := config.Bridge.LoginSharedSecretMap[homeserver] + return hasSecret } func Load(path string) (*Config, error) { @@ -103,6 +99,14 @@ func Load(path string) (*Config, error) { return nil, fmt.Errorf("failed to unmarshal example config: %w", err) } err = yaml.Unmarshal(data, config) + if err != nil { + return nil, err + } + + if len(config.Bridge.LegacyLoginSharedSecret) > 0 { + config.Bridge.LoginSharedSecretMap[config.Homeserver.Domain] = config.Bridge.LegacyLoginSharedSecret + } + return config, err } diff --git a/custompuppet.go b/custompuppet.go index b806b3d..97fe8aa 100644 --- a/custompuppet.go +++ b/custompuppet.go @@ -21,6 +21,7 @@ import ( "crypto/sha512" "encoding/hex" "errors" + "fmt" "time" "go.mau.fi/whatsmeow/types" @@ -64,10 +65,15 @@ func (puppet *Puppet) SwitchCustomMXID(accessToken string, mxid id.UserID) error } func (puppet *Puppet) loginWithSharedSecret(mxid id.UserID) (string, error) { + _, homeserver, _ := mxid.Parse() puppet.log.Debugfln("Logging into %s with shared secret", mxid) - mac := hmac.New(sha512.New, []byte(puppet.bridge.Config.Bridge.LoginSharedSecret)) + mac := hmac.New(sha512.New, []byte(puppet.bridge.Config.Bridge.LoginSharedSecretMap[homeserver])) mac.Write([]byte(mxid)) - resp, err := puppet.bridge.AS.BotClient().Login(&mautrix.ReqLogin{ + client, err := puppet.bridge.newDoublePuppetClient(mxid, "") + if err != nil { + return "", fmt.Errorf("failed to create mautrix client to log in: %v", err) + } + resp, err := client.Login(&mautrix.ReqLogin{ Type: mautrix.AuthTypePassword, Identifier: mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: string(mxid)}, Password: hex.EncodeToString(mac.Sum(nil)), @@ -80,26 +86,44 @@ func (puppet *Puppet) loginWithSharedSecret(mxid id.UserID) (string, error) { return resp.AccessToken, nil } +func (bridge *Bridge) newDoublePuppetClient(mxid id.UserID, accessToken string) (*mautrix.Client, error) { + _, homeserver, err := mxid.Parse() + if err != nil { + return nil, err + } + homeserverURL, found := bridge.Config.Bridge.DoublePuppetServerMap[homeserver] + if !found { + if homeserver == bridge.AS.HomeserverDomain { + homeserverURL = bridge.AS.HomeserverURL + } else if bridge.Config.Bridge.DoublePuppetAllowDiscovery { + resp, err := mautrix.DiscoverClientAPI(homeserver) + if err != nil { + return nil, fmt.Errorf("failed to find homeserver URL for %s: %v", homeserver, err) + } + homeserverURL = resp.Homeserver.BaseURL + bridge.Log.Debugfln("Discovered URL %s for %s to enable double puppeting for %s", homeserverURL, homeserver, mxid) + } else { + return nil, fmt.Errorf("double puppeting from %s is not allowed", homeserver) + } + } + client, err := mautrix.NewClient(homeserverURL, mxid, accessToken) + if err != nil { + return nil, err + } + client.Logger = bridge.AS.Log.Sub(mxid.String()) + client.Client = bridge.AS.HTTPClient + client.DefaultHTTPRetries = bridge.AS.DefaultHTTPRetries + return client, nil +} + func (puppet *Puppet) newCustomIntent() (*appservice.IntentAPI, error) { if len(puppet.CustomMXID) == 0 { return nil, ErrNoCustomMXID } - _, homeserver, err := puppet.CustomMXID.Parse() + client, err := puppet.bridge.newDoublePuppetClient(puppet.CustomMXID, puppet.AccessToken) if err != nil { return nil, err } - homeserverUrl, found := puppet.bridge.Config.Bridge.DoublePuppetServerMap[homeserver] - if !found { - puppet.log.Debugfln("Homeserver not found in double puppet server map. Using local homeserver") - homeserverUrl = puppet.bridge.AS.HomeserverURL - } - client, err := mautrix.NewClient(homeserverUrl, puppet.CustomMXID, puppet.AccessToken) - if err != nil { - return nil, err - } - client.Logger = puppet.bridge.AS.Log.Sub(string(puppet.CustomMXID)) - client.Client = puppet.bridge.AS.HTTPClient - client.DefaultHTTPRetries = puppet.bridge.AS.DefaultHTTPRetries client.Syncer = puppet client.Store = puppet @@ -264,7 +288,7 @@ func (puppet *Puppet) handleTypingEvent(portal *Portal, evt *event.Event) { } func (puppet *Puppet) tryRelogin(cause error, action string) bool { - if !puppet.bridge.Config.CanDoublePuppet(puppet.CustomMXID) { + if !puppet.bridge.Config.CanAutoDoublePuppet(puppet.CustomMXID) { return false } puppet.log.Debugfln("Trying to relogin after '%v' while %s", cause, action) diff --git a/example-config.yaml b/example-config.yaml index 7ba4869..b287509 100644 --- a/example-config.yaml +++ b/example-config.yaml @@ -124,12 +124,18 @@ bridge: # Existing users won't be affected when these are changed. default_bridge_receipts: true default_bridge_presence: true - # Shared secret for https://github.com/devture/matrix-synapse-shared-secret-auth + # Servers to always allow double puppeting from + double_puppet_server_map: + example.com: https://example.com + # Allow using double puppeting from any server with a valid client .well-known file. + double_puppet_allow_discovery: false + # Shared secrets for https://github.com/devture/matrix-synapse-shared-secret-auth # # If set, double puppeting will be enabled automatically for local users # instead of users having to find an access token and run `login-matrix` # manually. - login_shared_secret: null + login_shared_secret_map: + example.com: foobar # Should the bridge explicitly set the avatar and room name for private chat portal rooms? private_chat_portal_meta: false @@ -216,9 +222,6 @@ bridge: "example.com": user "@admin:example.com": admin - double_puppet_server_map: - "matrix.org": https://matrix.org - relay: # Whether relay mode should be allowed. If allowed, `!wa set-relay` can be used to turn any # authenticated user into a relaybot for that chat. diff --git a/user.go b/user.go index 5b5617e..19028d4 100644 --- a/user.go +++ b/user.go @@ -299,7 +299,7 @@ func (user *User) IsLoggedIn() bool { } func (user *User) tryAutomaticDoublePuppeting() { - if !user.bridge.Config.CanDoublePuppet(user.MXID) { + if !user.bridge.Config.CanAutoDoublePuppet(user.MXID) { return } user.log.Debugln("Checking if double puppeting needs to be enabled")