mirror of
https://github.com/boringproxy/boringproxy.git
synced 2025-02-25 18:55:29 -06:00
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:
parent
df6daa5bbd
commit
cfcd781276
51
api.go
51
api.go
@ -10,20 +10,19 @@ import (
|
|||||||
|
|
||||||
type Api struct {
|
type Api struct {
|
||||||
config *BoringProxyConfig
|
config *BoringProxyConfig
|
||||||
|
db *Database
|
||||||
auth *Auth
|
auth *Auth
|
||||||
tunMan *TunnelManager
|
tunMan *TunnelManager
|
||||||
mux *http.ServeMux
|
mux *http.ServeMux
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewApi(config *BoringProxyConfig, auth *Auth, tunMan *TunnelManager) *Api {
|
func NewApi(config *BoringProxyConfig, db *Database, auth *Auth, tunMan *TunnelManager) *Api {
|
||||||
|
|
||||||
api := &Api{config, auth, tunMan, nil}
|
|
||||||
|
|
||||||
mux := http.NewServeMux()
|
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
|
return api
|
||||||
}
|
}
|
||||||
@ -32,12 +31,48 @@ func (a *Api) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
a.mux.ServeHTTP(w, r)
|
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) {
|
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 {
|
switch r.Method {
|
||||||
case "GET":
|
case "GET":
|
||||||
query := r.URL.Query()
|
query := r.URL.Query()
|
||||||
|
|
||||||
tunnels := a.tunMan.GetTunnels()
|
tunnels := a.GetTunnels(tokenData)
|
||||||
|
|
||||||
if len(query["client-name"]) == 1 {
|
if len(query["client-name"]) == 1 {
|
||||||
clientName := query["client-name"][0]
|
clientName := query["client-name"][0]
|
||||||
@ -62,9 +97,9 @@ func (a *Api) handleTunnels(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
w.Write([]byte(body))
|
w.Write([]byte(body))
|
||||||
case "POST":
|
case "POST":
|
||||||
a.validateToken(http.HandlerFunc(a.handleCreateTunnel)).ServeHTTP(w, r)
|
a.handleCreateTunnel(w, r)
|
||||||
case "DELETE":
|
case "DELETE":
|
||||||
a.validateToken(http.HandlerFunc(a.handleDeleteTunnel)).ServeHTTP(w, r)
|
a.handleDeleteTunnel(w, r)
|
||||||
default:
|
default:
|
||||||
w.WriteHeader(405)
|
w.WriteHeader(405)
|
||||||
w.Write([]byte("Invalid method for /tunnels"))
|
w.Write([]byte("Invalid method for /tunnels"))
|
||||||
|
@ -59,6 +59,8 @@ func Listen() {
|
|||||||
log.Println("Admin token: " + token)
|
log.Println("Admin token: " + token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auth := NewAuth(db)
|
||||||
|
|
||||||
certmagic.DefaultACME.DisableHTTPChallenge = true
|
certmagic.DefaultACME.DisableHTTPChallenge = true
|
||||||
//certmagic.DefaultACME.DisableTLSALPNChallenge = true
|
//certmagic.DefaultACME.DisableTLSALPNChallenge = true
|
||||||
//certmagic.DefaultACME.CA = certmagic.LetsEncryptStagingCA
|
//certmagic.DefaultACME.CA = certmagic.LetsEncryptStagingCA
|
||||||
@ -72,17 +74,15 @@ func Listen() {
|
|||||||
log.Println(err)
|
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{}
|
httpClient := &http.Client{}
|
||||||
|
|
||||||
p := &BoringProxy{tunMan, httpClient}
|
p := &BoringProxy{tunMan, httpClient}
|
||||||
|
|
||||||
api := NewApi(config, auth, tunMan)
|
|
||||||
http.Handle("/api/", http.StripPrefix("/api", api))
|
|
||||||
|
|
||||||
tlsConfig := &tls.Config{
|
tlsConfig := &tls.Config{
|
||||||
GetCertificate: certConfig.GetCertificate,
|
GetCertificate: certConfig.GetCertificate,
|
||||||
NextProtos: []string{"h2", "acme-tls/1"},
|
NextProtos: []string{"h2", "acme-tls/1"},
|
||||||
|
3
todo.md
3
todo.md
@ -6,3 +6,6 @@
|
|||||||
after login and submitting a new tunnel too.
|
after login and submitting a new tunnel too.
|
||||||
* Save next port in db
|
* Save next port in db
|
||||||
* On unknown page, redirect to referer if possible
|
* 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?
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
type WebUiHandler struct {
|
type WebUiHandler struct {
|
||||||
config *BoringProxyConfig
|
config *BoringProxyConfig
|
||||||
db *Database
|
db *Database
|
||||||
|
api *Api
|
||||||
auth *Auth
|
auth *Auth
|
||||||
tunMan *TunnelManager
|
tunMan *TunnelManager
|
||||||
box *rice.Box
|
box *rice.Box
|
||||||
@ -66,10 +67,11 @@ type TokensData struct {
|
|||||||
Users map[string]User
|
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{
|
return &WebUiHandler{
|
||||||
config: config,
|
config: config,
|
||||||
db: db,
|
db: db,
|
||||||
|
api: api,
|
||||||
auth: auth,
|
auth: auth,
|
||||||
tunMan: tunMan,
|
tunMan: tunMan,
|
||||||
}
|
}
|
||||||
@ -185,24 +187,10 @@ func (h *WebUiHandler) handleWebUiRequest(w http.ResponseWriter, r *http.Request
|
|||||||
return
|
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{
|
indexData := IndexData{
|
||||||
Head: h.headHtml,
|
Head: h.headHtml,
|
||||||
Menu: h.menuHtml,
|
Menu: h.menuHtml,
|
||||||
Tunnels: tunnels,
|
Tunnels: h.api.GetTunnels(tokenData),
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpl.Execute(w, indexData)
|
tmpl.Execute(w, indexData)
|
||||||
|
Loading…
Reference in New Issue
Block a user