349 lines
8.1 KiB
Go
349 lines
8.1 KiB
Go
package hub
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"log"
|
|
"net/http"
|
|
"vppn/hub/api"
|
|
"vppn/m"
|
|
|
|
"git.crumpington.com/lib/go/webutil"
|
|
"golang.org/x/crypto/bcrypt"
|
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
|
)
|
|
|
|
func (a *App) _root(s *api.Session, w http.ResponseWriter, r *http.Request) error {
|
|
if s.SignedIn {
|
|
return a.redirect(w, r, "/admin/network/list/")
|
|
} else {
|
|
return a.redirect(w, r, "/sign-in/")
|
|
}
|
|
}
|
|
|
|
func (a *App) _signin(s *api.Session, w http.ResponseWriter, r *http.Request) error {
|
|
return a.render("/sign-in.html", w, struct{ Session *api.Session }{s})
|
|
}
|
|
|
|
func (a *App) _signinSubmit(s *api.Session, w http.ResponseWriter, r *http.Request) error {
|
|
var pwd string
|
|
err := webutil.NewFormScanner(r.Form).
|
|
Scan("Password", &pwd).
|
|
Error()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
sess, err := a.api.Session_SignIn(pwd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
a.setCookie(w, sessionIDCookieName, sess.SessionID)
|
|
|
|
return a.redirect(w, r, "/")
|
|
}
|
|
|
|
func (a *App) _adminSignOut(s *api.Session, w http.ResponseWriter, r *http.Request) error {
|
|
return a.render("/admin-sign-out.html", w, struct{ Session *api.Session }{s})
|
|
}
|
|
|
|
func (a *App) _adminSignOutSubmit(s *api.Session, w http.ResponseWriter, r *http.Request) error {
|
|
if err := a.api.Session_Delete(s.SessionID); err != nil {
|
|
log.Printf("Failed to delete session cookie %s: %v", s.SessionID, err)
|
|
}
|
|
a.deleteCookie(w, sessionIDCookieName)
|
|
return a.redirect(w, r, "/")
|
|
}
|
|
|
|
func (a *App) _adminNetworkList(s *api.Session, w http.ResponseWriter, r *http.Request) error {
|
|
l, err := a.api.Network_List()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return a.render("/admin-network-list.html", w, struct {
|
|
Session *api.Session
|
|
Networks []*api.Network
|
|
}{s, l})
|
|
}
|
|
|
|
func (a *App) _adminNetworkCreate(s *api.Session, w http.ResponseWriter, r *http.Request) error {
|
|
return a.render("/admin-network-create.html", w, struct{ Session *api.Session }{s})
|
|
}
|
|
|
|
func (a *App) _adminNetworkCreateSubmit(s *api.Session, w http.ResponseWriter, r *http.Request) error {
|
|
n := &api.Network{}
|
|
var netStr string
|
|
|
|
err := webutil.NewFormScanner(r.Form).
|
|
Scan("LocalDomain", &n.LocalDomain).
|
|
Scan("Network", &netStr).
|
|
Error()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
n.Network, err = stringToIP(netStr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := a.api.Network_Create(n); err != nil {
|
|
return err
|
|
}
|
|
|
|
return a.redirect(w, r, "/admin/network/view/?NetworkID=%d", n.NetworkID)
|
|
}
|
|
|
|
func (a *App) _adminNetworkView(s *api.Session, w http.ResponseWriter, r *http.Request) error {
|
|
n, peers, err := a.formGetNetworkPeers(r.Form)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return a.render("/network/network-view.html", w, struct {
|
|
Session *api.Session
|
|
Network *api.Network
|
|
Peers []*api.Peer
|
|
}{s, n, peers})
|
|
}
|
|
|
|
func (a *App) _adminNetworkDelete(s *api.Session, w http.ResponseWriter, r *http.Request) error {
|
|
n, peers, err := a.formGetNetworkPeers(r.Form)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return a.render("/network/network-delete.html", w, struct {
|
|
Session *api.Session
|
|
Network *api.Network
|
|
Peers []*api.Peer
|
|
}{s, n, peers})
|
|
}
|
|
|
|
func (a *App) _adminNetworkDeleteSubmit(s *api.Session, w http.ResponseWriter, r *http.Request) error {
|
|
n, err := a.formGetNetwork(r.Form)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = a.api.Network_Delete(n); err != nil {
|
|
return err
|
|
}
|
|
return a.redirect(w, r, "/admin/network/list/")
|
|
}
|
|
|
|
func (a *App) _adminPeerCreate(s *api.Session, w http.ResponseWriter, r *http.Request) error {
|
|
n, err := a.formGetNetwork(r.Form)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return a.render("/network/peer-create.html", w, struct {
|
|
Session *api.Session
|
|
Network *api.Network
|
|
}{s, n})
|
|
}
|
|
|
|
func (a *App) _adminPeerCreateSubmit(s *api.Session, w http.ResponseWriter, r *http.Request) error {
|
|
var addr4Str, addr6Str string
|
|
|
|
p := &api.Peer{}
|
|
err := webutil.NewFormScanner(r.Form).
|
|
Scan("NetworkID", &p.NetworkID).
|
|
Scan("IP", &p.PeerIP).
|
|
Scan("Name", &p.Name).
|
|
Scan("Addr4", &addr4Str).
|
|
Scan("Addr6", &addr6Str).
|
|
Scan("Port", &p.Port).
|
|
Scan("Relay", &p.Relay).
|
|
Error()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if p.Addr4, err = stringToIP(addr4Str); err != nil {
|
|
return err
|
|
}
|
|
if p.Addr6, err = stringToIP(addr6Str); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := a.api.Peer_CreateNew(p); err != nil {
|
|
return err
|
|
}
|
|
return a.redirect(w, r, "/admin/peer/view/?NetworkID=%d&PeerIP=%d", p.NetworkID, p.PeerIP)
|
|
}
|
|
|
|
func (a *App) _adminPeerView(s *api.Session, w http.ResponseWriter, r *http.Request) error {
|
|
net, peer, err := a.formGetPeer(r.Form)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return a.render("/network/peer-view.html", w, struct {
|
|
Session *api.Session
|
|
Network *api.Network
|
|
Peer *api.Peer
|
|
}{s, net, peer})
|
|
}
|
|
|
|
func (a *App) _adminPeerDelete(s *api.Session, w http.ResponseWriter, r *http.Request) error {
|
|
n, peer, err := a.formGetPeer(r.Form)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return a.render("/network/peer-delete.html", w, struct {
|
|
Session *api.Session
|
|
Network *api.Network
|
|
Peer *api.Peer
|
|
}{s, n, peer})
|
|
}
|
|
|
|
func (a *App) _adminPeerDeleteSubmit(s *api.Session, w http.ResponseWriter, r *http.Request) error {
|
|
n, peer, err := a.formGetPeer(r.Form)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := a.api.Peer_Delete(n.NetworkID, peer.PeerIP); err != nil {
|
|
return err
|
|
}
|
|
return a.redirect(w, r, "/admin/network/view/?NetworkID=%d", n.NetworkID)
|
|
}
|
|
|
|
func (a *App) _adminPasswordEdit(s *api.Session, w http.ResponseWriter, r *http.Request) error {
|
|
return a.render("/admin-password-edit.html", w, struct{ Session *api.Session }{s})
|
|
}
|
|
|
|
func (a *App) _adminPasswordSubmit(s *api.Session, w http.ResponseWriter, r *http.Request) error {
|
|
var (
|
|
curPwd string
|
|
newPwd string
|
|
newPwd2 string
|
|
)
|
|
|
|
conf, err := a.api.Config_Get()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = webutil.NewFormScanner(r.Form).
|
|
Scan("CurrentPassword", &curPwd).
|
|
Scan("NewPassword", &newPwd).
|
|
Scan("NewPassword2", &newPwd2).
|
|
Error()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(newPwd) < 8 {
|
|
return errors.New("password is too short")
|
|
}
|
|
|
|
if newPwd != newPwd2 {
|
|
return errors.New("passwords don't match")
|
|
}
|
|
|
|
err = bcrypt.CompareHashAndPassword(conf.Password, []byte(curPwd))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
hash, err := bcrypt.GenerateFromPassword([]byte(newPwd), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
conf.Password = hash
|
|
|
|
if err := a.api.Config_Update(conf); err != nil {
|
|
return err
|
|
}
|
|
|
|
return a.redirect(w, r, "/admin/network/list/")
|
|
}
|
|
|
|
func (a *App) _peerInit(peer *api.Peer, w http.ResponseWriter, r *http.Request) error {
|
|
if len(peer.WGPubKey) != 0 {
|
|
http.Error(w, "Already initialized", http.StatusConflict)
|
|
return nil
|
|
}
|
|
|
|
args := m.PeerInitArgs{}
|
|
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(args.WGPubKey) != 32 {
|
|
http.Error(w, "invalid WGPubKey", http.StatusBadRequest)
|
|
return nil
|
|
}
|
|
if len(args.SignPubKey) != 32 {
|
|
http.Error(w, "invalid SignPubKey", http.StatusBadRequest)
|
|
return nil
|
|
}
|
|
|
|
net, err := a.api.Network_Get(peer.NetworkID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := a.api.Peer_Init(peer, args); err != nil {
|
|
return err
|
|
}
|
|
|
|
resp := m.PeerInitResp{
|
|
PeerIP: peer.PeerIP,
|
|
Network: net.Network,
|
|
LocalDomain: net.LocalDomain,
|
|
}
|
|
|
|
resp.NetworkState.Peers, err = a.peersList(net.NetworkID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return a.sendJSON(w, resp)
|
|
}
|
|
|
|
func (a *App) _peerFetchState(peer *api.Peer, w http.ResponseWriter, r *http.Request) error {
|
|
peers, err := a.peersList(peer.NetworkID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return a.sendJSON(w, m.NetworkState{Peers: peers})
|
|
}
|
|
|
|
func (a *App) peersList(networkID int64) (peers []m.Peer, err error) {
|
|
l, err := a.api.Peer_List(networkID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
peers = make([]m.Peer, 0, len(l))
|
|
|
|
for _, p := range l {
|
|
if len(p.WGPubKey) == 0 {
|
|
continue
|
|
}
|
|
wgKey, err := wgtypes.NewKey(p.WGPubKey)
|
|
if err != nil {
|
|
continue // malformed key; skip rather than serve garbage
|
|
}
|
|
var signKey [32]byte
|
|
copy(signKey[:], p.SignPubKey)
|
|
peers = append(peers, m.Peer{
|
|
PeerIP: p.PeerIP,
|
|
Name: p.Name,
|
|
Addr4: addrFromBytes(p.Addr4),
|
|
Addr6: addrFromBytes(p.Addr6),
|
|
Port: p.Port,
|
|
Relay: p.Relay,
|
|
WGPubKey: wgKey,
|
|
SignPubKey: signKey,
|
|
})
|
|
}
|
|
|
|
return peers, nil
|
|
}
|