Move double puppeting login code to mautrix-go
This commit is contained in:
parent
fdce256a03
commit
4229f85d09
8 changed files with 45 additions and 148 deletions
|
@ -44,9 +44,7 @@ type BridgeConfig struct {
|
||||||
MissedLimit int `yaml:"missed_limit"`
|
MissedLimit int `yaml:"missed_limit"`
|
||||||
} `yaml:"backfill"`
|
} `yaml:"backfill"`
|
||||||
|
|
||||||
DoublePuppetServerMap map[string]string `yaml:"double_puppet_server_map"`
|
DoublePuppetConfig bridgeconfig.DoublePuppetConfig `yaml:",inline"`
|
||||||
DoublePuppetAllowDiscovery bool `yaml:"double_puppet_allow_discovery"`
|
|
||||||
LoginSharedSecretMap map[string]string `yaml:"login_shared_secret_map"`
|
|
||||||
|
|
||||||
PrivateChatPortalMeta string `yaml:"private_chat_portal_meta"`
|
PrivateChatPortalMeta string `yaml:"private_chat_portal_meta"`
|
||||||
BridgeNotices bool `yaml:"bridge_notices"`
|
BridgeNotices bool `yaml:"bridge_notices"`
|
||||||
|
@ -77,6 +75,10 @@ type BridgeConfig struct {
|
||||||
displaynameTemplate *template.Template `yaml:"-"`
|
displaynameTemplate *template.Template `yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bc BridgeConfig) GetDoublePuppetConfig() bridgeconfig.DoublePuppetConfig {
|
||||||
|
return bc.DoublePuppetConfig
|
||||||
|
}
|
||||||
|
|
||||||
func (bc BridgeConfig) GetEncryptionConfig() bridgeconfig.EncryptionConfig {
|
func (bc BridgeConfig) GetEncryptionConfig() bridgeconfig.EncryptionConfig {
|
||||||
return bc.Encryption
|
return bc.Encryption
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,6 @@ type Config struct {
|
||||||
|
|
||||||
func (config *Config) CanAutoDoublePuppet(userID id.UserID) bool {
|
func (config *Config) CanAutoDoublePuppet(userID id.UserID) bool {
|
||||||
_, homeserver, _ := userID.Parse()
|
_, homeserver, _ := userID.Parse()
|
||||||
_, hasSecret := config.Bridge.LoginSharedSecretMap[homeserver]
|
_, hasSecret := config.Bridge.DoublePuppetConfig.SharedSecretMap[homeserver]
|
||||||
return hasSecret
|
return hasSecret
|
||||||
}
|
}
|
||||||
|
|
155
custompuppet.go
155
custompuppet.go
|
@ -18,22 +18,14 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/sha512"
|
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"maunium.net/go/mautrix"
|
|
||||||
"maunium.net/go/mautrix/appservice"
|
"maunium.net/go/mautrix/appservice"
|
||||||
"maunium.net/go/mautrix/bridge"
|
"maunium.net/go/mautrix/bridge"
|
||||||
"maunium.net/go/mautrix/id"
|
"maunium.net/go/mautrix/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
ErrMismatchingMXID = errors.New("whoami result does not match custom mxid")
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ bridge.DoublePuppet = (*User)(nil)
|
var _ bridge.DoublePuppet = (*User)(nil)
|
||||||
|
|
||||||
func (user *User) SwitchCustomMXID(accessToken string, mxid id.UserID) error {
|
func (user *User) SwitchCustomMXID(accessToken string, mxid id.UserID) error {
|
||||||
|
@ -42,14 +34,36 @@ func (user *User) SwitchCustomMXID(accessToken string, mxid id.UserID) error {
|
||||||
}
|
}
|
||||||
user.DoublePuppetIntent = nil
|
user.DoublePuppetIntent = nil
|
||||||
user.AccessToken = accessToken
|
user.AccessToken = accessToken
|
||||||
err := user.startCustomMXID(false)
|
err := user.StartCustomMXID(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (user *User) ClearCustomMXID() {
|
||||||
|
user.DoublePuppetIntent = nil
|
||||||
|
user.AccessToken = ""
|
||||||
|
err := user.Update(context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
user.zlog.Warn().Err(err).Msg("Failed to clear access token from database")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (user *User) StartCustomMXID(reloginOnFail bool) error {
|
||||||
|
newIntent, newAccessToken, err := user.bridge.DoublePuppet.Setup(user.MXID, user.AccessToken, reloginOnFail)
|
||||||
|
if err != nil {
|
||||||
|
user.ClearCustomMXID()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if user.AccessToken != newAccessToken {
|
||||||
|
user.AccessToken = newAccessToken
|
||||||
err = user.Update(context.TODO())
|
err = user.Update(context.TODO())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to save access token to database: %w", err)
|
return fmt.Errorf("failed to save access token: %w", err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
user.DoublePuppetIntent = newIntent
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,121 +71,14 @@ func (user *User) CustomIntent() *appservice.IntentAPI {
|
||||||
return user.DoublePuppetIntent
|
return user.DoublePuppetIntent
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) loginWithSharedSecret() error {
|
func (user *User) tryAutomaticDoublePuppeting() {
|
||||||
_, homeserver, _ := user.MXID.Parse()
|
if !user.bridge.Config.CanAutoDoublePuppet(user.MXID) || user.DoublePuppetIntent != nil {
|
||||||
user.zlog.Debug().Msg("Logging into double puppet with shared secret")
|
return
|
||||||
loginSecret := user.bridge.Config.Bridge.LoginSharedSecretMap[homeserver]
|
}
|
||||||
client, err := user.bridge.newDoublePuppetClient(user.MXID, "")
|
err := user.StartCustomMXID(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
user.zlog.Warn().Err(err).Msg("Failed to login with shared secret for double puppeting")
|
||||||
}
|
|
||||||
req := mautrix.ReqLogin{
|
|
||||||
Identifier: mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: string(user.MXID)},
|
|
||||||
DeviceID: "Google Messages Bridge",
|
|
||||||
InitialDeviceDisplayName: "Google Messages Bridge",
|
|
||||||
}
|
|
||||||
if loginSecret == "appservice" {
|
|
||||||
client.AccessToken = user.bridge.AS.Registration.AppToken
|
|
||||||
req.Type = mautrix.AuthTypeAppservice
|
|
||||||
} else {
|
} else {
|
||||||
mac := hmac.New(sha512.New, []byte(loginSecret))
|
user.zlog.Info().Msg("Successfully automatically enabled double puppet")
|
||||||
mac.Write([]byte(user.MXID))
|
|
||||||
req.Password = hex.EncodeToString(mac.Sum(nil))
|
|
||||||
req.Type = mautrix.AuthTypePassword
|
|
||||||
}
|
|
||||||
resp, err := client.Login(&req)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to log in with shared secret: %w", err)
|
|
||||||
}
|
|
||||||
user.AccessToken = resp.AccessToken
|
|
||||||
err = user.Update(context.TODO())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to save access token: %w", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (br *GMBridge) 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.ZLog.Debug().
|
|
||||||
Str("server_name", homeserver).
|
|
||||||
Str("base_url", homeserverURL).
|
|
||||||
Str("user_id", mxid.String()).
|
|
||||||
Msg("Discovered homeserver URL to enable double puppeting for external user")
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("double puppeting from %s is not allowed", homeserver)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return br.AS.NewExternalMautrixClient(mxid, accessToken, homeserverURL)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (user *User) newDoublePuppetIntent() (*appservice.IntentAPI, error) {
|
|
||||||
client, err := user.bridge.newDoublePuppetClient(user.MXID, user.AccessToken)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ia := user.bridge.AS.NewIntentAPI("custom")
|
|
||||||
ia.Client = client
|
|
||||||
ia.Localpart, _, _ = user.MXID.Parse()
|
|
||||||
ia.UserID = user.MXID
|
|
||||||
ia.IsCustomPuppet = true
|
|
||||||
return ia, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (user *User) clearCustomMXID() {
|
|
||||||
user.AccessToken = ""
|
|
||||||
user.DoublePuppetIntent = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (user *User) startCustomMXID(reloginOnFail bool) error {
|
|
||||||
if len(user.AccessToken) == 0 || user.DoublePuppetIntent != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
intent, err := user.newDoublePuppetIntent()
|
|
||||||
if err != nil {
|
|
||||||
user.clearCustomMXID()
|
|
||||||
return fmt.Errorf("failed to create double puppet intent: %w", err)
|
|
||||||
}
|
|
||||||
resp, err := intent.Whoami()
|
|
||||||
if err != nil {
|
|
||||||
if !reloginOnFail || (errors.Is(err, mautrix.MUnknownToken) && !user.tryRelogin(err)) {
|
|
||||||
user.clearCustomMXID()
|
|
||||||
return fmt.Errorf("failed to ensure double puppet token is valid: %w", err)
|
|
||||||
}
|
|
||||||
intent.AccessToken = user.AccessToken
|
|
||||||
}
|
|
||||||
if resp.UserID != user.MXID {
|
|
||||||
user.clearCustomMXID()
|
|
||||||
return ErrMismatchingMXID
|
|
||||||
}
|
|
||||||
user.DoublePuppetIntent = intent
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (user *User) tryRelogin(err error) bool {
|
|
||||||
if !user.bridge.Config.CanAutoDoublePuppet(user.MXID) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
user.zlog.Debug().Err(err).Msg("Trying to relogin after error in double puppet")
|
|
||||||
err = user.loginWithSharedSecret()
|
|
||||||
if err != nil {
|
|
||||||
user.zlog.Err(err).Msg("Failed to relogin after error in double puppet")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
user.zlog.Info().Msg("Successfully relogined after error in double puppet")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
4
go.mod
4
go.mod
|
@ -4,6 +4,7 @@ go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2
|
github.com/gabriel-vasile/mimetype v1.4.2
|
||||||
|
github.com/lib/pq v1.10.9
|
||||||
github.com/mattn/go-sqlite3 v1.14.17
|
github.com/mattn/go-sqlite3 v1.14.17
|
||||||
github.com/rs/zerolog v1.30.0
|
github.com/rs/zerolog v1.30.0
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||||
|
@ -12,7 +13,7 @@ require (
|
||||||
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb
|
golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb
|
||||||
google.golang.org/protobuf v1.31.0
|
google.golang.org/protobuf v1.31.0
|
||||||
maunium.net/go/maulogger/v2 v2.4.1
|
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 (
|
require (
|
||||||
|
@ -22,7 +23,6 @@ require (
|
||||||
github.com/gorilla/mux v1.8.0 // indirect
|
github.com/gorilla/mux v1.8.0 // indirect
|
||||||
github.com/gorilla/websocket v1.5.0 // indirect
|
github.com/gorilla/websocket v1.5.0 // indirect
|
||||||
github.com/kr/pretty v0.3.1 // indirect
|
github.com/kr/pretty v0.3.1 // indirect
|
||||||
github.com/lib/pq v1.10.9 // indirect
|
|
||||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.10.0 // indirect
|
github.com/rogpeppe/go-internal v1.10.0 // indirect
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -83,5 +83,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/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 h1:N7zSdd0mZkB2m2JtFUsiGTQQAdP0YeFWT7YMc80yAL8=
|
||||||
maunium.net/go/maulogger/v2 v2.4.1/go.mod h1:omPuYwYBILeVQobz8uO3XC8DIRuEb5rXYlQSuqrbCho=
|
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.1-0.20230821105106-ac5c2c22102c h1:oRIaFbS4ds9biwJVguT+9Zu7n5zDbKQeuGklXHQxvCU=
|
||||||
maunium.net/go/mautrix v0.16.0/go.mod h1:XAjE9pTSGcr6vXaiNgQGiip7tddJ8FQV1a29u2QdBG4=
|
maunium.net/go/mautrix v0.16.1-0.20230821105106-ac5c2c22102c/go.mod h1:XAjE9pTSGcr6vXaiNgQGiip7tddJ8FQV1a29u2QdBG4=
|
||||||
|
|
2
main.go
2
main.go
|
@ -115,7 +115,7 @@ func (br *GMBridge) StartUsers() {
|
||||||
for _, loopuser := range br.GetAllUsersWithDoublePuppet() {
|
for _, loopuser := range br.GetAllUsersWithDoublePuppet() {
|
||||||
go func(user *User) {
|
go func(user *User) {
|
||||||
user.zlog.Debug().Msg("Starting double puppet")
|
user.zlog.Debug().Msg("Starting double puppet")
|
||||||
err := user.startCustomMXID(true)
|
err := user.StartCustomMXID(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
user.zlog.Err(err).Msg("Failed to start double puppet")
|
user.zlog.Err(err).Msg("Failed to start double puppet")
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,6 +184,8 @@ func (puppet *Puppet) SwitchCustomMXID(_ string, _ id.UserID) error {
|
||||||
return fmt.Errorf("puppets don't support custom MXIDs here")
|
return fmt.Errorf("puppets don't support custom MXIDs here")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (puppet *Puppet) ClearCustomMXID() {}
|
||||||
|
|
||||||
func (puppet *Puppet) IntentFor(_ *Portal) *appservice.IntentAPI {
|
func (puppet *Puppet) IntentFor(_ *Portal) *appservice.IntentAPI {
|
||||||
return puppet.DefaultIntent()
|
return puppet.DefaultIntent()
|
||||||
}
|
}
|
||||||
|
|
14
user.go
14
user.go
|
@ -521,20 +521,6 @@ func (user *User) IsLoggedIn() bool {
|
||||||
return user.IsConnected() && user.Client.IsLoggedIn()
|
return user.IsConnected() && user.Client.IsLoggedIn()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) tryAutomaticDoublePuppeting() {
|
|
||||||
if !user.bridge.Config.CanAutoDoublePuppet(user.MXID) || user.DoublePuppetIntent != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := user.loginWithSharedSecret(); err != nil {
|
|
||||||
user.zlog.Warn().Err(err).Msg("Failed to login with shared secret for double puppeting")
|
|
||||||
} else if err = user.startCustomMXID(false); err != nil {
|
|
||||||
user.zlog.Warn().Err(err).Msg("Failed to start double puppet after logging in with shared secret")
|
|
||||||
} else {
|
|
||||||
user.zlog.Info().Msg("Successfully automatically enabled double puppet")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (user *User) sendMarkdownBridgeAlert(important bool, formatString string, args ...interface{}) {
|
func (user *User) sendMarkdownBridgeAlert(important bool, formatString string, args ...interface{}) {
|
||||||
if user.bridge.Config.Bridge.DisableBridgeAlerts {
|
if user.bridge.Config.Bridge.DisableBridgeAlerts {
|
||||||
return
|
return
|
||||||
|
|
Loading…
Reference in a new issue