Implement account configuration save/load from db
This commit is contained in:
parent
d1b66d3088
commit
f675ba57e4
6 changed files with 141 additions and 18 deletions
|
@ -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
14
db.go
|
@ -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
2
go.sum
|
@ -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
30
main.go
|
@ -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
42
util.go
|
@ -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
64
web.go
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue