Files
boringproxy/api.go

220 lines
4.3 KiB
Go
Raw Normal View History

package main
import (
2020-10-10 08:20:17 -06:00
"crypto/md5"
2020-10-02 17:09:14 -06:00
"encoding/json"
2020-10-14 10:29:19 -06:00
"errors"
2020-10-10 08:20:17 -06:00
"fmt"
2020-10-02 17:09:14 -06:00
"io"
"net/http"
2020-10-14 10:29:19 -06:00
"net/url"
"strconv"
)
type Api struct {
2020-10-02 17:09:14 -06:00
config *BoringProxyConfig
db *Database
2020-10-02 17:09:14 -06:00
auth *Auth
tunMan *TunnelManager
mux *http.ServeMux
}
func NewApi(config *BoringProxyConfig, db *Database, auth *Auth, tunMan *TunnelManager) *Api {
2020-10-02 17:09:14 -06:00
mux := http.NewServeMux()
api := &Api{config, db, auth, tunMan, mux}
mux.Handle("/tunnels", http.StripPrefix("/tunnels", http.HandlerFunc(api.handleTunnels)))
2020-10-02 17:09:14 -06:00
return api
}
func (a *Api) ServeHTTP(w http.ResponseWriter, r *http.Request) {
2020-10-02 17:09:14 -06:00
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
}
2020-10-14 10:29:19 -06:00
func (a *Api) CreateTunnel(tokenData TokenData, params url.Values) (*Tunnel, error) {
2020-10-14 10:33:06 -06:00
domain := params.Get("domain")
if domain == "" {
2020-10-14 10:29:19 -06:00
return nil, errors.New("Invalid domain parameter")
}
2020-10-14 10:33:06 -06:00
clientName := params.Get("client-name")
if clientName == "" {
2020-10-14 10:29:19 -06:00
return nil, errors.New("Invalid client-name parameter")
}
2020-10-14 10:33:06 -06:00
clientPort, err := strconv.Atoi(params.Get("client-port"))
2020-10-14 10:29:19 -06:00
if err != nil {
return nil, errors.New("Invalid client-port parameter")
}
tunnel, err := a.tunMan.CreateTunnelForClient(domain, tokenData.Owner, clientName, clientPort)
if err != nil {
return nil, err
}
return &tunnel, nil
}
2020-10-14 10:42:54 -06:00
func (a *Api) DeleteTunnel(tokenData TokenData, params url.Values) error {
domain := params.Get("domain")
if domain == "" {
return errors.New("Invalid domain parameter")
}
a.tunMan.DeleteTunnel(domain)
return nil
}
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
}
2020-10-02 17:09:14 -06:00
switch r.Method {
2020-10-06 10:22:03 -06:00
case "GET":
query := r.URL.Query()
tunnels := a.GetTunnels(tokenData)
if len(query["client-name"]) == 1 {
clientName := query["client-name"][0]
for k, tun := range tunnels {
if tun.ClientName != clientName {
delete(tunnels, k)
}
}
}
body, err := json.Marshal(tunnels)
2020-10-05 23:37:03 -06:00
if err != nil {
w.WriteHeader(500)
w.Write([]byte("Error encoding tunnels"))
return
}
2020-10-10 08:20:17 -06:00
hash := md5.Sum(body)
hashStr := fmt.Sprintf("%x", hash)
w.Header()["ETag"] = []string{hashStr}
2020-10-05 23:37:03 -06:00
w.Write([]byte(body))
2020-10-02 17:09:14 -06:00
case "POST":
a.handleCreateTunnel(w, r)
2020-10-06 19:00:20 -06:00
case "DELETE":
a.handleDeleteTunnel(w, r)
2020-10-02 17:09:14 -06:00
default:
w.WriteHeader(405)
w.Write([]byte("Invalid method for /tunnels"))
2020-10-02 17:09:14 -06:00
}
}
func (a *Api) handleCreateTunnel(w http.ResponseWriter, r *http.Request) {
2020-10-02 17:09:14 -06:00
query := r.URL.Query()
if len(query["domain"]) != 1 {
w.WriteHeader(400)
w.Write([]byte("Invalid domain parameter"))
return
}
domain := query["domain"][0]
if len(query["owner"]) != 1 {
w.WriteHeader(400)
w.Write([]byte("Invalid owner parameter"))
return
}
owner := query["owner"][0]
tunnel, err := a.tunMan.CreateTunnel(domain, owner)
2020-10-02 17:09:14 -06:00
if err != nil {
w.WriteHeader(400)
2020-10-02 17:09:14 -06:00
io.WriteString(w, err.Error())
return
2020-10-02 17:09:14 -06:00
}
tunnelJson, err := json.MarshalIndent(tunnel, "", " ")
2020-10-02 17:09:14 -06:00
if err != nil {
w.WriteHeader(500)
io.WriteString(w, "Error encoding tunnel")
return
2020-10-02 17:09:14 -06:00
}
w.Write(tunnelJson)
}
2020-10-06 19:00:20 -06:00
func (a *Api) handleDeleteTunnel(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
if len(query["domain"]) != 1 {
w.WriteHeader(400)
w.Write([]byte("Invalid domain parameter"))
return
}
domain := query["domain"][0]
err := a.tunMan.DeleteTunnel(domain)
if err != nil {
2020-10-06 19:00:20 -06:00
w.WriteHeader(500)
io.WriteString(w, "Failed to delete tunnel")
return
}
2020-10-06 19:00:20 -06:00
}
2020-10-11 14:27:32 -06:00
func (a *Api) validateToken(h http.Handler) http.Handler {
2020-10-02 17:09:14 -06:00
return http.HandlerFunc(func(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
}
if !a.auth.Authorized(token) {
w.WriteHeader(403)
w.Write([]byte("Not authorized"))
return
}
h.ServeHTTP(w, r)
})
}