196 lines
4.3 KiB
Go
196 lines
4.3 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"html/template"
|
|
"log"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/gorilla/mux"
|
|
"github.com/gorilla/sessions"
|
|
"golang.org/x/crypto/argon2"
|
|
|
|
"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)
|
|
n, err := rand.Read(session_key)
|
|
if err != nil || n != 32 {
|
|
log.Fatal(err)
|
|
}
|
|
sessionsStore = sessions.NewCookieStore(session_key)
|
|
|
|
r := mux.NewRouter()
|
|
r.HandleFunc("/", handleHome)
|
|
r.HandleFunc("/logout", handleLogout)
|
|
|
|
staticfiles := http.FileServer(http.Dir("static"))
|
|
r.Handle("/static/{file:.*}", http.StripPrefix("/static/", staticfiles))
|
|
|
|
log.Printf("Starting web UI HTTP server on %s", config.WebBindAddr)
|
|
go func() {
|
|
err = http.ListenAndServe(config.WebBindAddr, logRequest(r))
|
|
if err != nil {
|
|
log.Fatal("Cannot start http server: ", err)
|
|
}
|
|
}()
|
|
}
|
|
|
|
func logRequest(handler http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
log.Printf("%s %s %s\n", r.RemoteAddr, r.Method, r.URL)
|
|
handler.ServeHTTP(w, r)
|
|
})
|
|
}
|
|
|
|
// ----
|
|
|
|
type LoginInfo struct {
|
|
MxId string
|
|
}
|
|
|
|
func checkLogin(w http.ResponseWriter, r *http.Request) *LoginInfo {
|
|
var login_info *LoginInfo
|
|
|
|
session, err := sessionsStore.Get(r, SESSION_NAME)
|
|
if err == nil {
|
|
mxid, ok := session.Values["login_mxid"]
|
|
if ok {
|
|
login_info = &LoginInfo{
|
|
MxId: mxid.(string),
|
|
}
|
|
}
|
|
}
|
|
|
|
if login_info == nil {
|
|
login_info = handleLogin(w, r)
|
|
}
|
|
|
|
return login_info
|
|
}
|
|
|
|
// ----
|
|
|
|
type HomeData struct {
|
|
Login *LoginInfo
|
|
Accounts []*Account
|
|
}
|
|
|
|
func handleHome(w http.ResponseWriter, r *http.Request) {
|
|
templateHome := template.Must(template.ParseFiles("templates/layout.html", "templates/home.html"))
|
|
|
|
login := checkLogin(w, r)
|
|
if login == nil {
|
|
return
|
|
}
|
|
|
|
templateHome.Execute(w, &HomeData{
|
|
Login: login,
|
|
Accounts: ListAccounts(login.MxId),
|
|
})
|
|
}
|
|
|
|
func handleLogout(w http.ResponseWriter, r *http.Request) {
|
|
session, err := sessionsStore.Get(r, SESSION_NAME)
|
|
if err != nil {
|
|
session, _ = sessionsStore.New(r, SESSION_NAME)
|
|
}
|
|
|
|
delete(session.Values, "login_mxid")
|
|
|
|
err = session.Save(r, w)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
http.Redirect(w, r, "/", http.StatusFound)
|
|
}
|
|
|
|
type LoginFormData struct {
|
|
Username string
|
|
WrongPass bool
|
|
ErrorMessage string
|
|
MatrixDomain string
|
|
}
|
|
|
|
func handleLogin(w http.ResponseWriter, r *http.Request) *LoginInfo {
|
|
templateLogin := template.Must(template.ParseFiles("templates/layout.html", "templates/login.html"))
|
|
|
|
data := &LoginFormData{
|
|
MatrixDomain: config.MatrixDomain,
|
|
}
|
|
|
|
if r.Method == "GET" {
|
|
templateLogin.Execute(w, data)
|
|
return nil
|
|
} else if r.Method == "POST" {
|
|
r.ParseForm()
|
|
|
|
username := strings.Join(r.Form["username"], "")
|
|
password := strings.Join(r.Form["password"], "")
|
|
|
|
cli := mxlib.NewClient(config.Server, "")
|
|
mxid, err := cli.PasswordLogin(username, password, "EZBRIDGE", "Easybridge")
|
|
|
|
if err != nil {
|
|
data.Username = username
|
|
data.ErrorMessage = err.Error()
|
|
templateLogin.Execute(w, data)
|
|
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
|
|
|
|
SaveDbAccounts(mxid, key)
|
|
LoadDbAccounts(mxid, key)
|
|
|
|
// Successfully logged in, save it to session
|
|
session, err := sessionsStore.Get(r, SESSION_NAME)
|
|
if err != nil {
|
|
session, _ = sessionsStore.New(r, SESSION_NAME)
|
|
}
|
|
|
|
session.Values["login_mxid"] = mxid
|
|
|
|
err = session.Save(r, w)
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
return nil
|
|
}
|
|
|
|
return &LoginInfo{
|
|
MxId: mxid,
|
|
}
|
|
} else {
|
|
http.Error(w, "Unsupported method", http.StatusBadRequest)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
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())
|
|
}
|
|
}
|
|
}
|