From 185f9a8963f02bb1fbf1beedd3e4d9db3374d47c Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Tue, 22 Aug 2023 18:55:09 +0300 Subject: [PATCH] Move double puppeting login code to mautrix-go --- config/bridge.go | 8 +- config/config.go | 2 +- custompuppet.go | 210 +++++++++++++---------------------------------- go.mod | 6 +- go.sum | 12 +-- user.go | 31 ------- 6 files changed, 71 insertions(+), 198 deletions(-) diff --git a/config/bridge.go b/config/bridge.go index a0a3d50..98b7b14 100644 --- a/config/bridge.go +++ b/config/bridge.go @@ -67,9 +67,7 @@ type BridgeConfig struct { } `yaml:"args"` } `yaml:"animated_sticker"` - 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"` + DoublePuppetConfig bridgeconfig.DoublePuppetConfig `yaml:",inline"` CommandPrefix string `yaml:"command_prefix"` ManagementRoomText bridgeconfig.ManagementRoomTexts `yaml:"management_room_text"` @@ -272,6 +270,10 @@ func (bc *BridgeConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { var _ bridgeconfig.BridgeConfig = (*BridgeConfig)(nil) +func (bc BridgeConfig) GetDoublePuppetConfig() bridgeconfig.DoublePuppetConfig { + return bc.DoublePuppetConfig +} + func (bc BridgeConfig) GetEncryptionConfig() bridgeconfig.EncryptionConfig { return bc.Encryption } diff --git a/config/config.go b/config/config.go index b3f6692..d704651 100644 --- a/config/config.go +++ b/config/config.go @@ -29,7 +29,7 @@ type Config struct { func (config *Config) CanAutoDoublePuppet(userID id.UserID) bool { _, homeserver, _ := userID.Parse() - _, hasSecret := config.Bridge.LoginSharedSecretMap[homeserver] + _, hasSecret := config.Bridge.DoublePuppetConfig.SharedSecretMap[homeserver] return hasSecret } diff --git a/custompuppet.go b/custompuppet.go index e7fb2c2..f1c1f05 100644 --- a/custompuppet.go +++ b/custompuppet.go @@ -1,170 +1,72 @@ package main import ( - "crypto/hmac" - "crypto/sha512" - "encoding/hex" - "errors" - "fmt" - - "maunium.net/go/mautrix" - "maunium.net/go/mautrix/appservice" "maunium.net/go/mautrix/id" ) -var ( - ErrNoCustomMXID = errors.New("no custom mxid set") - ErrMismatchingMXID = errors.New("whoami result does not match custom mxid") -) - -func (br *DiscordBridge) newDoublePuppetClient(mxid id.UserID, accessToken string) (*mautrix.Client, error) { - _, homeserver, err := mxid.Parse() - if err != nil { - return nil, err - } - - homeserverURL, found := br.Config.Bridge.DoublePuppetServerMap[homeserver] - if !found { - if homeserver == br.AS.HomeserverDomain { - homeserverURL = "" - } else if br.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 - br.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) - } - } - - return br.AS.NewExternalMautrixClient(mxid, accessToken, homeserverURL) -} - -func (puppet *Puppet) clearCustomMXID() { - puppet.CustomMXID = "" - puppet.AccessToken = "" - puppet.customIntent = nil - puppet.customUser = nil -} - -func (puppet *Puppet) newCustomIntent() (*appservice.IntentAPI, error) { - if puppet.CustomMXID == "" { - return nil, ErrNoCustomMXID - } - - client, err := puppet.bridge.newDoublePuppetClient(puppet.CustomMXID, puppet.AccessToken) - if err != nil { - return nil, err - } - - ia := puppet.bridge.AS.NewIntentAPI("custom") - ia.Client = client - ia.Localpart, _, _ = puppet.CustomMXID.Parse() - ia.UserID = puppet.CustomMXID - ia.IsCustomPuppet = true - return ia, nil -} - -func (puppet *Puppet) StartCustomMXID(reloginOnFail bool) error { - if puppet.CustomMXID == "" { - puppet.clearCustomMXID() - return nil - } - - intent, err := puppet.newCustomIntent() - if err != nil { - puppet.clearCustomMXID() - return err - } - - resp, err := intent.Whoami() - if err != nil { - if !reloginOnFail || (errors.Is(err, mautrix.MUnknownToken) && !puppet.tryRelogin(err, "initializing double puppeting")) { - puppet.clearCustomMXID() - return err - } - - intent.AccessToken = puppet.AccessToken - } else if resp.UserID != puppet.CustomMXID { - puppet.clearCustomMXID() - return ErrMismatchingMXID - } - - puppet.customIntent = intent - puppet.customUser = puppet.bridge.GetUserByMXID(puppet.CustomMXID) - return nil -} - -func (puppet *Puppet) tryRelogin(cause error, action string) bool { - if !puppet.bridge.Config.CanAutoDoublePuppet(puppet.CustomMXID) { - return false - } - log := puppet.log.With(). - AnErr("cause_error", cause). - Str("while_action", action). - Logger() - log.Debug().Msg("Trying to relogin") - accessToken, err := puppet.loginWithSharedSecret(puppet.CustomMXID) - if err != nil { - log.Error().Err(err).Msg("Failed to relogin") - return false - } - log.Info().Msg("Successfully relogined") - puppet.AccessToken = accessToken - puppet.Update() - return true -} - -func (puppet *Puppet) loginWithSharedSecret(mxid id.UserID) (string, error) { - _, homeserver, _ := mxid.Parse() - puppet.log.Debug().Str("user_id", mxid.String()).Msg("Logging into double puppet target with shared secret") - loginSecret := puppet.bridge.Config.Bridge.LoginSharedSecretMap[homeserver] - client, err := puppet.bridge.newDoublePuppetClient(mxid, "") - if err != nil { - return "", fmt.Errorf("failed to create mautrix client to log in: %v", err) - } - req := mautrix.ReqLogin{ - Identifier: mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: string(mxid)}, - DeviceID: "Discord Bridge", - InitialDeviceDisplayName: "Discord Bridge", - } - if loginSecret == "appservice" { - client.AccessToken = puppet.bridge.AS.Registration.AppToken - req.Type = mautrix.AuthTypeAppservice - } else { - mac := hmac.New(sha512.New, []byte(loginSecret)) - mac.Write([]byte(mxid)) - req.Password = hex.EncodeToString(mac.Sum(nil)) - req.Type = mautrix.AuthTypePassword - } - resp, err := client.Login(&req) - if err != nil { - return "", err - } - return resp.AccessToken, nil -} - func (puppet *Puppet) SwitchCustomMXID(accessToken string, mxid id.UserID) error { - prevCustomMXID := puppet.CustomMXID puppet.CustomMXID = mxid puppet.AccessToken = accessToken - + puppet.Update() err := puppet.StartCustomMXID(false) if err != nil { return err } - - if prevCustomMXID != "" { - delete(puppet.bridge.puppetsByCustomMXID, prevCustomMXID) - } - if puppet.CustomMXID != "" { - puppet.bridge.puppetsByCustomMXID[puppet.CustomMXID] = puppet - } - puppet.bridge.AS.StateStore.MarkRegistered(puppet.CustomMXID) - puppet.Update() // TODO leave rooms with default puppet return nil } + +func (puppet *Puppet) ClearCustomMXID() { + save := puppet.CustomMXID != "" || puppet.AccessToken != "" + puppet.bridge.puppetsLock.Lock() + if puppet.CustomMXID != "" && puppet.bridge.puppetsByCustomMXID[puppet.CustomMXID] == puppet { + delete(puppet.bridge.puppetsByCustomMXID, puppet.CustomMXID) + } + puppet.bridge.puppetsLock.Unlock() + puppet.CustomMXID = "" + puppet.AccessToken = "" + puppet.customIntent = nil + puppet.customUser = nil + if save { + puppet.Update() + } +} + +func (puppet *Puppet) StartCustomMXID(reloginOnFail bool) error { + newIntent, newAccessToken, err := puppet.bridge.DoublePuppet.Setup(puppet.CustomMXID, puppet.AccessToken, reloginOnFail) + if err != nil { + puppet.ClearCustomMXID() + return err + } + puppet.bridge.puppetsLock.Lock() + puppet.bridge.puppetsByCustomMXID[puppet.CustomMXID] = puppet + puppet.bridge.puppetsLock.Unlock() + if puppet.AccessToken != newAccessToken { + puppet.AccessToken = newAccessToken + puppet.Update() + } + puppet.customIntent = newIntent + puppet.customUser = puppet.bridge.GetUserByMXID(puppet.CustomMXID) + return nil +} + +func (user *User) tryAutomaticDoublePuppeting() { + if !user.bridge.Config.CanAutoDoublePuppet(user.MXID) { + return + } + user.log.Debug().Msg("Checking if double puppeting needs to be enabled") + puppet := user.bridge.GetPuppetByID(user.DiscordID) + if len(puppet.CustomMXID) > 0 { + user.log.Debug().Msg("User already has double-puppeting enabled") + // Custom puppet already enabled + return + } + puppet.CustomMXID = user.MXID + err := puppet.StartCustomMXID(true) + if err != nil { + user.log.Warn().Err(err).Msg("Failed to login with shared secret") + } else { + // TODO leave rooms with default puppet + user.log.Debug().Msg("Successfully automatically enabled custom puppet") + } +} diff --git a/go.mod b/go.mod index c229c50..21dd529 100644 --- a/go.mod +++ b/go.mod @@ -13,12 +13,12 @@ require ( github.com/rs/zerolog v1.30.0 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/stretchr/testify v1.8.4 - github.com/yuin/goldmark v1.5.5 + github.com/yuin/goldmark v1.5.6 go.mau.fi/util v0.0.0-20230805171708-199bf3eec776 - golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb + golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 golang.org/x/sync v0.3.0 maunium.net/go/maulogger/v2 v2.4.1 - maunium.net/go/mautrix v0.16.0 + maunium.net/go/mautrix v0.16.1-0.20230821105106-ac5c2c22102c ) require ( diff --git a/go.sum b/go.sum index 55434e7..11d9d17 100644 --- a/go.sum +++ b/go.sum @@ -41,16 +41,16 @@ github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= -github.com/yuin/goldmark v1.5.5 h1:IJznPe8wOzfIKETmMkd06F8nXkmlhaHqFRM9l1hAGsU= -github.com/yuin/goldmark v1.5.5/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark v1.5.6 h1:COmQAWTCcGetChm3Ig7G/t8AFAN00t+o8Mt4cf7JpwA= +github.com/yuin/goldmark v1.5.6/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.mau.fi/util v0.0.0-20230805171708-199bf3eec776 h1:VrxDCO/gLFHLQywGUsJzertrvt2mUEMrZPf4hEL/s18= go.mau.fi/util v0.0.0-20230805171708-199bf3eec776/go.mod h1:AxuJUMCxpzgJ5eV9JbPWKRH8aAJJidxetNdUj7qcb84= go.mau.fi/zeroconfig v0.1.2 h1:DKOydWnhPMn65GbXZOafgkPm11BvFashZWLct0dGFto= go.mau.fi/zeroconfig v0.1.2/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70= golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb h1:mIKbk8weKhSeLH2GmUTrvx8CjkyJmnU1wFmg59CUjFA= -golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ= +golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8= golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= @@ -69,5 +69,5 @@ maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M= maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA= maunium.net/go/maulogger/v2 v2.4.1 h1:N7zSdd0mZkB2m2JtFUsiGTQQAdP0YeFWT7YMc80yAL8= maunium.net/go/maulogger/v2 v2.4.1/go.mod h1:omPuYwYBILeVQobz8uO3XC8DIRuEb5rXYlQSuqrbCho= -maunium.net/go/mautrix v0.16.0 h1:iUqCzJE2yqBC1ddAK6eAn159My8rLb4X8g4SFtQh2Dk= -maunium.net/go/mautrix v0.16.0/go.mod h1:XAjE9pTSGcr6vXaiNgQGiip7tddJ8FQV1a29u2QdBG4= +maunium.net/go/mautrix v0.16.1-0.20230821105106-ac5c2c22102c h1:oRIaFbS4ds9biwJVguT+9Zu7n5zDbKQeuGklXHQxvCU= +maunium.net/go/mautrix v0.16.1-0.20230821105106-ac5c2c22102c/go.mod h1:XAjE9pTSGcr6vXaiNgQGiip7tddJ8FQV1a29u2QdBG4= diff --git a/user.go b/user.go index efa7610..f82a0b4 100644 --- a/user.go +++ b/user.go @@ -368,37 +368,6 @@ func (user *User) GetDMSpaceRoom() id.RoomID { return user.getSpaceRoom(&user.DMSpaceRoom, "Direct Messages", "Your Discord direct messages", user.GetSpaceRoom()) } -func (user *User) tryAutomaticDoublePuppeting() { - user.Lock() - defer user.Unlock() - - if !user.bridge.Config.CanAutoDoublePuppet(user.MXID) { - return - } - - user.log.Debug().Msg("Checking if double puppeting needs to be enabled") - - puppet := user.bridge.GetPuppetByID(user.DiscordID) - if puppet.CustomMXID != "" { - user.log.Debug().Msg("User already has double-puppeting enabled") - return - } - - accessToken, err := puppet.loginWithSharedSecret(user.MXID) - if err != nil { - user.log.Warn().Err(err).Msg("Failed to login with shared secret") - return - } - - err = puppet.SwitchCustomMXID(accessToken, user.MXID) - if err != nil { - puppet.log.Warn().Err(err).Msg("Failed to switch to auto-logined custom puppet") - return - } - - user.log.Info().Msg("Successfully automatically enabled custom puppet") -} - func (user *User) ViewingChannel(portal *Portal) bool { if portal.GuildID != "" || !user.Session.IsUser { return false