package hub import ( "encoding/json" "log" "net/http" "vppn/hub/api" "vppn/hub/errs" "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 { a.api.Session_Delete(s.SessionID) 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 errs.ErrInvalidPassword } if newPwd != newPwd2 { return errs.ErrPasswordMismatch } err = bcrypt.CompareHashAndPassword(conf.Password, []byte(curPwd)) if err != nil { return errs.ErrNotAuthorized } hash, err := bcrypt.GenerateFromPassword([]byte(newPwd), bcrypt.DefaultCost) if err != nil { log.Printf("Failed to hash password with bcrypt: %v", err) return errs.ErrUnexpected } 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 } r.Body = http.MaxBytesReader(w, r.Body, 2048) args := m.PeerInitArgs{} if err := json.NewDecoder(r.Body).Decode(&args); err != nil { return errs.BadRequest.WithMsg("Invalid request body.") } 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 }