Implement account configuration save/load from db

This commit is contained in:
Alex 2020-02-26 20:21:32 +01:00
parent d1b66d3088
commit f675ba57e4
6 changed files with 141 additions and 18 deletions

View file

@ -14,6 +14,7 @@ type Account struct {
MatrixUser string
AccountName string
Protocol string
Config map[string]string
Conn Connector
JoinedRooms map[RoomID]bool
@ -77,7 +78,7 @@ func (a *Account) ezbrMessagef(format string, args ...interface{}) {
ezbrSystemSend(a.MatrixUser, msg)
}
func (a *Account) connect(config map[string]string, join_rooms []string) {
func (a *Account) connect(config map[string]string) {
ezbrSystemSendf(a.MatrixUser, "Connecting to account %s (%s)", a.AccountName, a.Protocol)
err := a.Conn.Configure(config)
@ -86,10 +87,6 @@ func (a *Account) connect(config map[string]string, join_rooms []string) {
return
}
for _, room := range join_rooms {
a.addAutojoin(RoomID(room))
}
var autojoin []DbJoinedRoom
db.Where(&DbJoinedRoom{
MxUserID: a.MatrixUser,

14
db.go
View file

@ -25,6 +25,8 @@ func InitDb() error {
return err
}
db.AutoMigrate(&DbAccountConfig{})
db.AutoMigrate(&DbCache{})
db.AutoMigrate(&DbUserMap{})
@ -42,6 +44,16 @@ func InitDb() error {
return nil
}
// Account configuration
type DbAccountConfig struct {
gorm.Model
MxUserID string `gorm:"index:account_mxuserid"`
Name string
Protocol string
Config string
}
// Long-term cache entries
type DbCache struct {
gorm.Model
@ -56,7 +68,7 @@ type DbUserMap struct {
Protocol string
UserID connector.UserID
MxUserID string `gorm:"index:mxuserid"`
MxUserID string `gorm:"index:usermap_mxuserid"`
}
// Room mapping between Matrix rooms and outside rooms

2
go.sum
View file

@ -106,6 +106,7 @@ github.com/keybase/go-keybase-chat-bot v0.0.0-20190816161829-561f10822eb2/go.mod
github.com/keybase/go-ps v0.0.0-20161005175911-668c8856d999/go.mod h1:hY+WOq6m2FpbvyrI93sMaypsttvaIL5nhVR92dTMUcQ=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@ -274,6 +275,7 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=

30
main.go
View file

@ -24,7 +24,6 @@ import (
type ConfigAccount struct {
Protocol string `json:"protocol"`
Rooms []string `json:"rooms"`
Config map[string]string `json:"config"`
}
@ -176,27 +175,21 @@ func main() {
for user, accounts := range config.Accounts {
for name, params := range accounts {
var conn connector.Connector
switch params.Protocol {
case "irc":
conn = &irc.IRC{}
case "xmpp":
conn = &xmpp.XMPP{}
case "mattermost":
conn = &mattermost.Mattermost{}
default:
log.Fatalf("Invalid protocol %s", params.Protocol)
conn := createConnector(params.Protocol)
if conn == nil {
log.Fatalf("Could not create connector for protocol %s", params.Protocol)
}
account := &Account{
MatrixUser: fmt.Sprintf("@%s:%s", user, config.MatrixDomain),
AccountName: name,
Protocol: params.Protocol,
Config: params.Config,
Conn: conn,
JoinedRooms: map[connector.RoomID]bool{},
}
conn.SetHandler(account)
AddAccount(account)
go account.connect(params.Config, params.Rooms)
go account.connect(params.Config)
}
}
@ -205,3 +198,16 @@ func main() {
log.Fatal(err)
}
}
func createConnector(protocol string) connector.Connector {
switch protocol {
case "irc":
return &irc.IRC{}
case "xmpp":
return &xmpp.XMPP{}
case "mattermost":
return &mattermost.Mattermost{}
default:
return nil
}
}

42
util.go
View file

@ -1,10 +1,14 @@
package main
import (
"crypto/rand"
"encoding/base64"
"encoding/json"
"fmt"
"unicode"
log "github.com/sirupsen/logrus"
"golang.org/x/crypto/nacl/secretbox"
. "git.deuxfleurs.fr/Deuxfleurs/easybridge/connector"
)
@ -56,3 +60,41 @@ func safeStringForId(in string) string {
}
return id2
}
// ---- Encoding and encryption of account config
func encryptAccountConfig(config map[string]string, key *[32]byte) string {
bytes, err := json.Marshal(config)
if err != nil {
log.Fatal(err)
}
var nonce [24]byte
_, err = rand.Read(nonce[:])
if err != nil {
log.Fatal(err)
}
crypto := secretbox.Seal([]byte{}, bytes, &nonce, key)
all := append(nonce[:], crypto...)
return base64.StdEncoding.EncodeToString(all)
}
func decryptAccountConfig(data string, key *[32]byte) (map[string]string, error) {
bytes, err := base64.StdEncoding.DecodeString(data)
if err != nil {
return nil, err
}
var nonce [24]byte
copy(nonce[:], bytes[:24])
decoded, ok := secretbox.Open([]byte{}, bytes[24:], &nonce, key)
if !ok {
return nil, fmt.Errorf("Invalid key")
}
var config map[string]string
err = json.Unmarshal(decoded, &config)
return config, err
}

64
web.go
View file

@ -9,13 +9,16 @@ import (
"github.com/gorilla/mux"
"github.com/gorilla/sessions"
"golang.org/x/crypto/argon2"
"git.deuxfleurs.fr/Deuxfleurs/easybridge/connector"
"git.deuxfleurs.fr/Deuxfleurs/easybridge/mxlib"
)
const SESSION_NAME = "easybridge_session"
var sessionsStore sessions.Store = nil
var userKeys = map[string]*[32]byte{}
func StartWeb() {
session_key := make([]byte, 32)
@ -147,6 +150,12 @@ func handleLogin(w http.ResponseWriter, r *http.Request) *LoginInfo {
return nil
}
key := new([32]byte)
key_slice := argon2.IDKey([]byte(password), []byte("EZBRIDGE account store"), 3, 64*1024, 4, 32)
copy(key[:], key_slice[:])
userKeys[mxid] = key
syncDbAccounts(mxid, key)
// Successfully logged in, save it to session
session, err := sessionsStore.Get(r, SESSION_NAME)
if err != nil {
@ -169,3 +178,58 @@ func handleLogin(w http.ResponseWriter, r *http.Request) *LoginInfo {
return nil
}
}
func syncDbAccounts(mxid string, key *[32]byte) {
accountsLock.Lock()
defer accountsLock.Unlock()
// 1. Save all accounts that we have
var accounts map[string]*Account
if accts, ok := registeredAccounts[mxid]; ok {
accounts = accts
for name, acct := range accts {
var entry DbAccountConfig
db.Where(&DbAccountConfig{
MxUserID: mxid,
Name: name,
}).Assign(&DbAccountConfig{
Protocol: acct.Protocol,
Config: encryptAccountConfig(acct.Config, key),
}).FirstOrCreate(&entry)
}
} else {
accounts = make(map[string]*Account)
registeredAccounts[mxid] = accounts
}
// 2. Load and start missing accounts
var allAccounts []DbAccountConfig
db.Where(&DbAccountConfig{MxUserID: mxid}).Find(&allAccounts)
for _, acct := range allAccounts {
if _, ok := accounts[acct.Name]; !ok {
config, err := decryptAccountConfig(acct.Config, key)
if err != nil {
ezbrSystemSendf("Could not decrypt stored configuration for account %s", acct.Name)
continue
}
conn := createConnector(acct.Protocol)
if conn == nil {
ezbrSystemSendf("Could not create connector for protocol %s", acct.Protocol)
continue
}
account := &Account{
MatrixUser: mxid,
AccountName: acct.Name,
Protocol: acct.Protocol,
Config: config,
Conn: conn,
JoinedRooms: map[connector.RoomID]bool{},
}
conn.SetHandler(account)
accounts[acct.Name] = account
go account.connect(config)
}
}
}