Filter UI requests through API

API is now the central control point for making authorized
requests. The current architecture is:

* db simply returns all data
* api uses tokens to filter data from db. It includes methods for
  returning objects, and HTTP endpoints for return JSON.
* ui calls the api functions to get filtered data.
This commit is contained in:
Anders Pitman 2020-10-14 09:17:04 -06:00
parent df6daa5bbd
commit cfcd781276
4 changed files with 55 additions and 29 deletions

51
api.go
View File

@ -10,20 +10,19 @@ import (
type Api struct {
config *BoringProxyConfig
db *Database
auth *Auth
tunMan *TunnelManager
mux *http.ServeMux
}
func NewApi(config *BoringProxyConfig, auth *Auth, tunMan *TunnelManager) *Api {
api := &Api{config, auth, tunMan, nil}
func NewApi(config *BoringProxyConfig, db *Database, auth *Auth, tunMan *TunnelManager) *Api {
mux := http.NewServeMux()
mux.Handle("/tunnels", http.StripPrefix("/tunnels", http.HandlerFunc(api.handleTunnels)))
api := &Api{config, db, auth, tunMan, mux}
api.mux = mux
mux.Handle("/tunnels", http.StripPrefix("/tunnels", http.HandlerFunc(api.handleTunnels)))
return api
}
@ -32,12 +31,48 @@ func (a *Api) ServeHTTP(w http.ResponseWriter, r *http.Request) {
a.mux.ServeHTTP(w, r)
}
func (a *Api) GetTunnels(tokenData TokenData) map[string]Tunnel {
user, _ := a.db.GetUser(tokenData.Owner)
var tunnels map[string]Tunnel
if user.IsAdmin {
tunnels = a.db.GetTunnels()
} else {
tunnels = make(map[string]Tunnel)
for domain, tun := range a.db.GetTunnels() {
if tokenData.Owner == tun.Owner {
tunnels[domain] = tun
}
}
}
return tunnels
}
func (a *Api) handleTunnels(w http.ResponseWriter, r *http.Request) {
token, err := extractToken("access_token", r)
if err != nil {
w.WriteHeader(401)
w.Write([]byte("No token provided"))
return
}
tokenData, exists := a.db.GetTokenData(token)
if !exists {
w.WriteHeader(403)
w.Write([]byte("Not authorized"))
return
}
switch r.Method {
case "GET":
query := r.URL.Query()
tunnels := a.tunMan.GetTunnels()
tunnels := a.GetTunnels(tokenData)
if len(query["client-name"]) == 1 {
clientName := query["client-name"][0]
@ -62,9 +97,9 @@ func (a *Api) handleTunnels(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(body))
case "POST":
a.validateToken(http.HandlerFunc(a.handleCreateTunnel)).ServeHTTP(w, r)
a.handleCreateTunnel(w, r)
case "DELETE":
a.validateToken(http.HandlerFunc(a.handleDeleteTunnel)).ServeHTTP(w, r)
a.handleDeleteTunnel(w, r)
default:
w.WriteHeader(405)
w.Write([]byte("Invalid method for /tunnels"))

View File

@ -59,6 +59,8 @@ func Listen() {
log.Println("Admin token: " + token)
}
auth := NewAuth(db)
certmagic.DefaultACME.DisableHTTPChallenge = true
//certmagic.DefaultACME.DisableTLSALPNChallenge = true
//certmagic.DefaultACME.CA = certmagic.LetsEncryptStagingCA
@ -72,17 +74,15 @@ func Listen() {
log.Println(err)
}
auth := NewAuth(db)
api := NewApi(config, db, auth, tunMan)
http.Handle("/api/", http.StripPrefix("/api", api))
webUiHandler := NewWebUiHandler(config, db, auth, tunMan)
webUiHandler := NewWebUiHandler(config, db, api, auth, tunMan)
httpClient := &http.Client{}
p := &BoringProxy{tunMan, httpClient}
api := NewApi(config, auth, tunMan)
http.Handle("/api/", http.StripPrefix("/api", api))
tlsConfig := &tls.Config{
GetCertificate: certConfig.GetCertificate,
NextProtos: []string{"h2", "acme-tls/1"},

View File

@ -6,3 +6,6 @@
after login and submitting a new tunnel too.
* Save next port in db
* On unknown page, redirect to referer if possible
* Properly pick unused ports for tunnels
* Apparently multiple tunnels can bind to a single server port. Looks like
maybe only the first one is used to actually tunnel to the clients?

View File

@ -15,6 +15,7 @@ import (
type WebUiHandler struct {
config *BoringProxyConfig
db *Database
api *Api
auth *Auth
tunMan *TunnelManager
box *rice.Box
@ -66,10 +67,11 @@ type TokensData struct {
Users map[string]User
}
func NewWebUiHandler(config *BoringProxyConfig, db *Database, auth *Auth, tunMan *TunnelManager) *WebUiHandler {
func NewWebUiHandler(config *BoringProxyConfig, db *Database, api *Api, auth *Auth, tunMan *TunnelManager) *WebUiHandler {
return &WebUiHandler{
config: config,
db: db,
api: api,
auth: auth,
tunMan: tunMan,
}
@ -185,24 +187,10 @@ func (h *WebUiHandler) handleWebUiRequest(w http.ResponseWriter, r *http.Request
return
}
var tunnels map[string]Tunnel
if user.IsAdmin {
tunnels = h.db.GetTunnels()
} else {
tunnels = make(map[string]Tunnel)
for domain, tun := range h.db.GetTunnels() {
if tokenData.Owner == tun.Owner {
tunnels[domain] = tun
}
}
}
indexData := IndexData{
Head: h.headHtml,
Menu: h.menuHtml,
Tunnels: tunnels,
Tunnels: h.api.GetTunnels(tokenData),
}
tmpl.Execute(w, indexData)