easybridge/web.go
2020-02-26 21:36:35 +01:00

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())
}
}
}