Initial ability to configure accounts from web interface
This commit is contained in:
parent
775fc7b217
commit
8a5ed3f507
11 changed files with 423 additions and 40 deletions
22
account.go
22
account.go
|
@ -34,6 +34,9 @@ func SetAccount(mxid string, name string, protocol string, config map[string]str
|
||||||
accounts := registeredAccounts[mxid]
|
accounts := registeredAccounts[mxid]
|
||||||
|
|
||||||
if prev_acct, ok := accounts[name]; ok {
|
if prev_acct, ok := accounts[name]; ok {
|
||||||
|
prev_acct.Conn.Close()
|
||||||
|
prev_acct.JoinedRooms = map[RoomID]bool{}
|
||||||
|
|
||||||
if protocol != prev_acct.Protocol {
|
if protocol != prev_acct.Protocol {
|
||||||
return fmt.Errorf("Wrong protocol")
|
return fmt.Errorf("Wrong protocol")
|
||||||
}
|
}
|
||||||
|
@ -112,6 +115,8 @@ func RemoveAccount(mxUser string, name string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----
|
||||||
|
|
||||||
func SaveDbAccounts(mxid string, key *[32]byte) {
|
func SaveDbAccounts(mxid string, key *[32]byte) {
|
||||||
accountsLock.Lock()
|
accountsLock.Lock()
|
||||||
defer accountsLock.Unlock()
|
defer accountsLock.Unlock()
|
||||||
|
@ -130,6 +135,23 @@ func SaveDbAccounts(mxid string, key *[32]byte) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func LoadDbAccounts(mxid string, key *[32]byte) {
|
||||||
|
var allAccounts []DbAccountConfig
|
||||||
|
db.Where(&DbAccountConfig{MxUserID: mxid}).Find(&allAccounts)
|
||||||
|
for _, acct := range allAccounts {
|
||||||
|
config, err := decryptAccountConfig(acct.Config, key)
|
||||||
|
if err != nil {
|
||||||
|
ezbrSystemSendf("Could not decrypt stored configuration for account %s", acct.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err = SetAccount(mxid, acct.Name, acct.Protocol, config)
|
||||||
|
if err != nil {
|
||||||
|
ezbrSystemSendf("Could not setup account %s: %s", acct.Name, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ----
|
// ----
|
||||||
|
|
||||||
func (a *Account) ezbrMessagef(format string, args ...interface{}) {
|
func (a *Account) ezbrMessagef(format string, args ...interface{}) {
|
||||||
|
|
|
@ -43,3 +43,24 @@ func (c Configuration) GetBool(k string, deflt ...bool) (bool, error) {
|
||||||
}
|
}
|
||||||
return false, fmt.Errorf("Missing configuration key: %s", k)
|
return false, fmt.Errorf("Missing configuration key: %s", k)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----
|
||||||
|
|
||||||
|
type ConfigSchema []*ConfigEntry
|
||||||
|
|
||||||
|
type ConfigEntry struct {
|
||||||
|
Name string
|
||||||
|
Description string
|
||||||
|
Default string
|
||||||
|
FixedValue string
|
||||||
|
Required bool
|
||||||
|
IsPassword bool
|
||||||
|
IsNumeric bool
|
||||||
|
IsBoolean bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var Protocols = map[string]ConfigSchema{}
|
||||||
|
|
||||||
|
func Register(name string, schema ConfigSchema) {
|
||||||
|
Protocols[name] = schema
|
||||||
|
}
|
||||||
|
|
32
connector/irc/config.go
Normal file
32
connector/irc/config.go
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package irc
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "git.deuxfleurs.fr/Deuxfleurs/easybridge/connector"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Register("irc", ConfigSchema{
|
||||||
|
&ConfigEntry{
|
||||||
|
Name: "nick",
|
||||||
|
Description: "Nickname",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
&ConfigEntry{
|
||||||
|
Name: "server",
|
||||||
|
Description: "Server",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
&ConfigEntry{
|
||||||
|
Name: "port",
|
||||||
|
Description: "Port",
|
||||||
|
IsNumeric: true,
|
||||||
|
Default: "6667",
|
||||||
|
},
|
||||||
|
&ConfigEntry{
|
||||||
|
Name: "ssl",
|
||||||
|
Description: "Use SSL",
|
||||||
|
IsBoolean: true,
|
||||||
|
Default: "false",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
|
@ -127,6 +127,10 @@ func (irc *IRC) SetUserInfo(info *UserInfo) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (irc *IRC) SetRoomInfo(roomId RoomID, info *RoomInfo) error {
|
func (irc *IRC) SetRoomInfo(roomId RoomID, info *RoomInfo) error {
|
||||||
|
if irc.conn == nil {
|
||||||
|
return fmt.Errorf("Not connected")
|
||||||
|
}
|
||||||
|
|
||||||
ch, err := irc.checkRoomId(roomId)
|
ch, err := irc.checkRoomId(roomId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -145,6 +149,10 @@ func (irc *IRC) SetRoomInfo(roomId RoomID, info *RoomInfo) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (irc *IRC) Join(roomId RoomID) error {
|
func (irc *IRC) Join(roomId RoomID) error {
|
||||||
|
if irc.conn == nil {
|
||||||
|
return fmt.Errorf("Not connected")
|
||||||
|
}
|
||||||
|
|
||||||
ch, err := irc.checkRoomId(roomId)
|
ch, err := irc.checkRoomId(roomId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -155,6 +163,10 @@ func (irc *IRC) Join(roomId RoomID) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (irc *IRC) Invite(userId UserID, roomId RoomID) error {
|
func (irc *IRC) Invite(userId UserID, roomId RoomID) error {
|
||||||
|
if irc.conn == nil {
|
||||||
|
return fmt.Errorf("Not connected")
|
||||||
|
}
|
||||||
|
|
||||||
who, err := irc.checkUserId(userId)
|
who, err := irc.checkUserId(userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -174,6 +186,10 @@ func (irc *IRC) Invite(userId UserID, roomId RoomID) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (irc *IRC) Leave(roomId RoomID) {
|
func (irc *IRC) Leave(roomId RoomID) {
|
||||||
|
if irc.conn == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
ch, err := irc.checkRoomId(roomId)
|
ch, err := irc.checkRoomId(roomId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
@ -183,6 +199,10 @@ func (irc *IRC) Leave(roomId RoomID) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (irc *IRC) Send(event *Event) error {
|
func (irc *IRC) Send(event *Event) error {
|
||||||
|
if irc.conn == nil {
|
||||||
|
return fmt.Errorf("Not connected")
|
||||||
|
}
|
||||||
|
|
||||||
// Workaround girc bug
|
// Workaround girc bug
|
||||||
if event.Text[0] == ':' {
|
if event.Text[0] == ':' {
|
||||||
event.Text = " " + event.Text
|
event.Text = " " + event.Text
|
||||||
|
@ -231,7 +251,9 @@ func (irc *IRC) Send(event *Event) error {
|
||||||
func (irc *IRC) Close() {
|
func (irc *IRC) Close() {
|
||||||
conn := irc.conn
|
conn := irc.conn
|
||||||
irc.conn = nil
|
irc.conn = nil
|
||||||
|
if conn != nil {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (irc *IRC) connectLoop(c *girc.Client) {
|
func (irc *IRC) connectLoop(c *girc.Client) {
|
||||||
|
|
52
connector/mattermost/config.go
Normal file
52
connector/mattermost/config.go
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
package mattermost
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "git.deuxfleurs.fr/Deuxfleurs/easybridge/connector"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Register("mattermost", ConfigSchema{
|
||||||
|
&ConfigEntry{
|
||||||
|
Name: "server",
|
||||||
|
Description: "Server",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
&ConfigEntry{
|
||||||
|
Name: "username",
|
||||||
|
Description: "Username",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
&ConfigEntry{
|
||||||
|
Name: "password",
|
||||||
|
Description: "Password",
|
||||||
|
IsPassword: true,
|
||||||
|
},
|
||||||
|
&ConfigEntry{
|
||||||
|
Name: "token",
|
||||||
|
Description: "Authentification token (replaces password if set)",
|
||||||
|
},
|
||||||
|
&ConfigEntry{
|
||||||
|
Name: "teams",
|
||||||
|
Description: "Comma-separated list of teams to follow",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
&ConfigEntry{
|
||||||
|
Name: "no_tls",
|
||||||
|
Description: "Disable SSL/TLS",
|
||||||
|
IsBoolean: true,
|
||||||
|
Default: "false",
|
||||||
|
},
|
||||||
|
&ConfigEntry{
|
||||||
|
Name: "initial_backlog",
|
||||||
|
Description: "Maximum number of messages to load when joining a channel",
|
||||||
|
IsNumeric: true,
|
||||||
|
Default: "1000",
|
||||||
|
},
|
||||||
|
&ConfigEntry{
|
||||||
|
Name: "initial_members",
|
||||||
|
Description: "Maximum number of members to load when joining a channel",
|
||||||
|
IsNumeric: true,
|
||||||
|
Default: "100",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
|
@ -69,7 +69,7 @@ func (mm *Mattermost) Configure(c Configuration) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
mm.initial_members, err = c.GetInt("initial_members", 1000)
|
mm.initial_members, err = c.GetInt("initial_members", 100)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -312,7 +312,9 @@ func (mm *Mattermost) Send(event *Event) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mm *Mattermost) Close() {
|
func (mm *Mattermost) Close() {
|
||||||
|
if mm.conn != nil {
|
||||||
mm.conn.WsQuit = true
|
mm.conn.WsQuit = true
|
||||||
|
}
|
||||||
if mm.handlerStopChan != nil {
|
if mm.handlerStopChan != nil {
|
||||||
mm.handlerStopChan <- true
|
mm.handlerStopChan <- true
|
||||||
mm.handlerStopChan = nil
|
mm.handlerStopChan = nil
|
||||||
|
|
38
connector/xmpp/config.go
Normal file
38
connector/xmpp/config.go
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package xmpp
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "git.deuxfleurs.fr/Deuxfleurs/easybridge/connector"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Register("xmpp", ConfigSchema{
|
||||||
|
&ConfigEntry{
|
||||||
|
Name: "jid",
|
||||||
|
Description: "JID",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
&ConfigEntry{
|
||||||
|
Name: "password",
|
||||||
|
Description: "Password",
|
||||||
|
Required: true,
|
||||||
|
IsPassword: true,
|
||||||
|
},
|
||||||
|
&ConfigEntry{
|
||||||
|
Name: "nickname",
|
||||||
|
Description: "Nickname in MUCs",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
&ConfigEntry{
|
||||||
|
Name: "port",
|
||||||
|
Description: "Port",
|
||||||
|
IsNumeric: true,
|
||||||
|
Default: "6667",
|
||||||
|
},
|
||||||
|
&ConfigEntry{
|
||||||
|
Name: "ssl",
|
||||||
|
Description: "Use SSL",
|
||||||
|
IsBoolean: true,
|
||||||
|
Default: "true",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
|
@ -55,11 +55,6 @@ func (xm *XMPP) Configure(c Configuration) error {
|
||||||
// Parse and validate configuration
|
// Parse and validate configuration
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
xm.server, err = c.GetString("server")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
xm.port, err = c.GetInt("port", 5222)
|
xm.port, err = c.GetInt("port", 5222)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -78,9 +73,7 @@ func (xm *XMPP) Configure(c Configuration) error {
|
||||||
if len(jid_parts) != 2 {
|
if len(jid_parts) != 2 {
|
||||||
return fmt.Errorf("Invalid JID: %s", xm.jid)
|
return fmt.Errorf("Invalid JID: %s", xm.jid)
|
||||||
}
|
}
|
||||||
if jid_parts[1] != xm.server {
|
xm.server = jid_parts[1]
|
||||||
return fmt.Errorf("JID %s not on server %s", xm.jid, xm.server)
|
|
||||||
}
|
|
||||||
xm.jid_localpart = jid_parts[0]
|
xm.jid_localpart = jid_parts[0]
|
||||||
|
|
||||||
xm.nickname, _ = c.GetString("nickname", xm.jid_localpart)
|
xm.nickname, _ = c.GetString("nickname", xm.jid_localpart)
|
||||||
|
@ -353,7 +346,9 @@ func (xm *XMPP) Send(event *Event) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (xm *XMPP) Close() {
|
func (xm *XMPP) Close() {
|
||||||
|
if xm.conn != nil {
|
||||||
xm.conn.Close()
|
xm.conn.Close()
|
||||||
|
}
|
||||||
xm.conn = nil
|
xm.conn = nil
|
||||||
xm.connectorLoopNum += 1
|
xm.connectorLoopNum += 1
|
||||||
}
|
}
|
||||||
|
|
71
templates/config.html
Normal file
71
templates/config.html
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
{{define "title"}}Account configuration |{{end}}
|
||||||
|
|
||||||
|
{{define "body"}}
|
||||||
|
<div class="d-flex">
|
||||||
|
<h4>Configure account</h4>
|
||||||
|
<a class="ml-auto btn btn-info" href="/">Go back</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{if .ErrorMessage}}
|
||||||
|
<div class="alert alert-danger mt-4">An error occurred.
|
||||||
|
<div style="font-size: 0.8em">{{ .ErrorMessage }}</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
<form method="POST" class="mt-4">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="name">Account name:</label>
|
||||||
|
<input type="text" {{if .NameEditable}}{{else}}disabled="disabled"{{end}} id="name" name="name" class="form-control" value="{{ .Name }}" />
|
||||||
|
{{if .InvalidName}}
|
||||||
|
<div class="alert alert-warning">Invalid name (must not be empty)</div>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Protocol:</label>
|
||||||
|
<input type="text" disabled="disabled" class="form-control" value="{{ .Protocol }}" />
|
||||||
|
</div>
|
||||||
|
{{$config := .Config}}
|
||||||
|
{{$errors := .Errors}}
|
||||||
|
{{range $i, $schema := .Schema}}
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="{{$schema.Name}}">{{$schema.Description}}</label>
|
||||||
|
{{if $schema.FixedValue}}
|
||||||
|
<input type="text"
|
||||||
|
disabled="disabled"
|
||||||
|
class="form-control"
|
||||||
|
name="{{$schema.Name}}"
|
||||||
|
id="{{$schema.Name}}"
|
||||||
|
value="{{index $config $schema.Name}}" />
|
||||||
|
{{else if $schema.IsBoolean}}
|
||||||
|
{{$value := index $config $schema.Name}}
|
||||||
|
<label for="{{$schema.Name}}-true">
|
||||||
|
<input type="radio" name="{{$schema.Name}}" id="{{$schema.Name}}-true" value="true" {{if eq $value "true"}}checked="checked"{{end}} />
|
||||||
|
Yes
|
||||||
|
</label>
|
||||||
|
<label for="{{$schema.Name}}-false">
|
||||||
|
<input type="radio" name="{{$schema.Name}}" id="{{$schema.Name}}-false" value="false" {{if eq $value "false"}}checked="checked"{{end}} />
|
||||||
|
No
|
||||||
|
</label>
|
||||||
|
{{else if $schema.IsPassword}}
|
||||||
|
<input type="password"
|
||||||
|
class="form-control"
|
||||||
|
name="{{$schema.Name}}"
|
||||||
|
id="{{$schema.Name}}"
|
||||||
|
value="{{index $config $schema.Name}}" />
|
||||||
|
{{else}}
|
||||||
|
<input type="text"
|
||||||
|
class="form-control"
|
||||||
|
name="{{$schema.Name}}"
|
||||||
|
id="{{$schema.Name}}"
|
||||||
|
value="{{index $config $schema.Name}}" />
|
||||||
|
{{end}}
|
||||||
|
{{$error := index $errors $schema.Name}}
|
||||||
|
{{if $error}}
|
||||||
|
<div class="alert alert-warning mt-2">{{$error}}</div>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
<button type="submit" class="btn btn-primary">Save configuration</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{{end}}
|
|
@ -8,7 +8,8 @@
|
||||||
<a class="ml-auto btn btn-sm btn-dark" href="/logout">Log out</a>
|
<a class="ml-auto btn btn-sm btn-dark" href="/logout">Log out</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table class="table mt-4">
|
{{ if .Accounts }}
|
||||||
|
<table class="table mt-4">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Account name</th>
|
<th>Account name</th>
|
||||||
|
@ -17,14 +18,24 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{{range $name, $acc := .Accounts}}
|
{{range $i, $acc := .Accounts}}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ $name }}</td>
|
<td>{{ $acc.AccountName }}</td>
|
||||||
<td>{{ $acc.Protocol }}</td>
|
<td>{{ $acc.Protocol }}</td>
|
||||||
<td>Modifier etc</td>
|
<td>
|
||||||
|
<a class="btn btn-sm btn-primary" href="/edit/{{ $acc.AccountName }}">Modify</a>
|
||||||
|
<a class="btn btn-sm btn-danger ml-4" href="/delete/{{ $acc.AccountName }}">Delete</a>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
<h5 class="mt-4">Add account</h5>
|
||||||
|
|
||||||
|
<a class="btn btn-sm btn-dark" href="/add/irc">IRC</a>
|
||||||
|
<a class="btn btn-sm btn-warning ml-4" href="/add/xmpp">XMPP</a>
|
||||||
|
<a class="btn btn-sm btn-info ml-4" href="/add/mattermost">Mattermost</a>
|
||||||
|
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
143
web.go
143
web.go
|
@ -5,12 +5,14 @@ import (
|
||||||
"html/template"
|
"html/template"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
"golang.org/x/crypto/argon2"
|
"golang.org/x/crypto/argon2"
|
||||||
|
|
||||||
|
"git.deuxfleurs.fr/Deuxfleurs/easybridge/connector"
|
||||||
"git.deuxfleurs.fr/Deuxfleurs/easybridge/mxlib"
|
"git.deuxfleurs.fr/Deuxfleurs/easybridge/mxlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -30,6 +32,9 @@ func StartWeb() {
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
r.HandleFunc("/", handleHome)
|
r.HandleFunc("/", handleHome)
|
||||||
r.HandleFunc("/logout", handleLogout)
|
r.HandleFunc("/logout", handleLogout)
|
||||||
|
r.HandleFunc("/add/{protocol}", handleAdd)
|
||||||
|
r.HandleFunc("/edit/{account}", handleEdit)
|
||||||
|
r.HandleFunc("/delete/{account}", handleDelete)
|
||||||
|
|
||||||
staticfiles := http.FileServer(http.Dir("static"))
|
staticfiles := http.FileServer(http.Dir("static"))
|
||||||
r.Handle("/static/{file:.*}", http.StripPrefix("/static/", staticfiles))
|
r.Handle("/static/{file:.*}", http.StripPrefix("/static/", staticfiles))
|
||||||
|
@ -178,19 +183,131 @@ func handleLogin(w http.ResponseWriter, r *http.Request) *LoginInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadDbAccounts(mxid string, key *[32]byte) {
|
// ----
|
||||||
var allAccounts []DbAccountConfig
|
|
||||||
db.Where(&DbAccountConfig{MxUserID: mxid}).Find(&allAccounts)
|
func handleAdd(w http.ResponseWriter, r *http.Request) {
|
||||||
for _, acct := range allAccounts {
|
login := checkLogin(w, r)
|
||||||
config, err := decryptAccountConfig(acct.Config, key)
|
if login == nil {
|
||||||
if err != nil {
|
return
|
||||||
ezbrSystemSendf("Could not decrypt stored configuration for account %s", acct.Name)
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = SetAccount(mxid, acct.Name, acct.Protocol, config)
|
protocol := mux.Vars(r)["protocol"]
|
||||||
if err != nil {
|
|
||||||
ezbrSystemSendf("Could not setup account %s: %s", acct.Name, err.Error())
|
configForm(w, r, login, "", protocol, map[string]string{})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
func handleEdit(w http.ResponseWriter, r *http.Request) {
|
||||||
|
login := checkLogin(w, r)
|
||||||
|
if login == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
account := mux.Vars(r)["account"]
|
||||||
|
acct := FindAccount(login.MxId, account)
|
||||||
|
if acct == nil {
|
||||||
|
http.Error(w, "No such account", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
configForm(w, r, login, account, acct.Protocol, acct.Config)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfigFormData struct {
|
||||||
|
ErrorMessage string
|
||||||
|
|
||||||
|
Name string
|
||||||
|
NameEditable bool
|
||||||
|
InvalidName bool
|
||||||
|
|
||||||
|
Protocol string
|
||||||
|
|
||||||
|
Config map[string]string
|
||||||
|
Errors map[string]string
|
||||||
|
Schema connector.ConfigSchema
|
||||||
|
}
|
||||||
|
|
||||||
|
func configForm(w http.ResponseWriter, r *http.Request,
|
||||||
|
login *LoginInfo, name string, protocol string,
|
||||||
|
prevConfig map[string]string) {
|
||||||
|
templateConfig := template.Must(template.ParseFiles("templates/layout.html", "templates/config.html"))
|
||||||
|
|
||||||
|
data := &ConfigFormData{
|
||||||
|
Name: name,
|
||||||
|
NameEditable: (name == ""),
|
||||||
|
Protocol: protocol,
|
||||||
|
Config: map[string]string{},
|
||||||
|
Errors: map[string]string{},
|
||||||
|
Schema: connector.Protocols[protocol],
|
||||||
|
}
|
||||||
|
for k, v := range prevConfig {
|
||||||
|
data.Config[k] = v
|
||||||
|
}
|
||||||
|
for _, sch := range data.Schema {
|
||||||
|
if _, ok := data.Config[sch.Name]; !ok && sch.Default != "" {
|
||||||
|
data.Config[sch.Name] = sch.Default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Method == "POST" {
|
||||||
|
ok := true
|
||||||
|
r.ParseForm()
|
||||||
|
|
||||||
|
if data.NameEditable {
|
||||||
|
data.Name = strings.Join(r.Form["name"], "")
|
||||||
|
if data.Name == "" {
|
||||||
|
ok = false
|
||||||
|
data.InvalidName = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, schema := range data.Schema {
|
||||||
|
field := schema.Name
|
||||||
|
data.Config[field] = strings.Join(r.Form[field], "")
|
||||||
|
if data.Config[field] == "" {
|
||||||
|
if schema.Required {
|
||||||
|
ok = false
|
||||||
|
data.Errors[field] = "This field is required"
|
||||||
|
}
|
||||||
|
} else if schema.FixedValue != "" {
|
||||||
|
if data.Config[field] != schema.FixedValue {
|
||||||
|
ok = false
|
||||||
|
data.Errors[field] = "This field must be equal to " + schema.FixedValue
|
||||||
|
}
|
||||||
|
} else if schema.IsBoolean {
|
||||||
|
if data.Config[field] != "false" && data.Config[field] != "true" {
|
||||||
|
ok = false
|
||||||
|
data.Errors[field] = "This field must be 'true' or 'false'"
|
||||||
|
}
|
||||||
|
} else if schema.IsNumeric {
|
||||||
|
_, err := strconv.Atoi(data.Config[field])
|
||||||
|
if err != nil {
|
||||||
|
ok = false
|
||||||
|
data.Errors[field] = "This field must be a valid number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
var entry DbAccountConfig
|
||||||
|
db.Where(&DbAccountConfig{
|
||||||
|
MxUserID: login.MxId,
|
||||||
|
Name: data.Name,
|
||||||
|
}).Assign(&DbAccountConfig{
|
||||||
|
Protocol: protocol,
|
||||||
|
Config: encryptAccountConfig(data.Config, userKeys[login.MxId]),
|
||||||
|
}).FirstOrCreate(&entry)
|
||||||
|
|
||||||
|
err := SetAccount(login.MxId, data.Name, protocol, data.Config)
|
||||||
|
if err == nil {
|
||||||
|
http.Redirect(w, r, "/", http.StatusFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data.ErrorMessage = err.Error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
templateConfig.Execute(w, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleDelete(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue