mirror of
https://github.com/boringproxy/boringproxy.git
synced 2025-02-25 18:55:29 -06:00
Make client selection a dropdown
Also implemented adding and deleting clients through the API.
This commit is contained in:
parent
1607d41e5c
commit
1abc141d13
80
api.go
80
api.go
@ -9,6 +9,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Api struct {
|
||||
@ -26,6 +27,7 @@ func NewApi(config *BoringProxyConfig, db *Database, auth *Auth, tunMan *TunnelM
|
||||
api := &Api{config, db, auth, tunMan, mux}
|
||||
|
||||
mux.Handle("/tunnels", http.StripPrefix("/tunnels", http.HandlerFunc(api.handleTunnels)))
|
||||
mux.Handle("/users/", http.StripPrefix("/users", http.HandlerFunc(api.handleUsers)))
|
||||
|
||||
return api
|
||||
}
|
||||
@ -302,6 +304,84 @@ func (a *Api) DeleteSshKey(tokenData TokenData, params url.Values) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Api) handleUsers(w http.ResponseWriter, r *http.Request) {
|
||||
token, err := extractToken("access_token", r)
|
||||
if err != nil {
|
||||
w.WriteHeader(401)
|
||||
io.WriteString(w, "Invalid token")
|
||||
return
|
||||
}
|
||||
|
||||
tokenData, exists := a.db.GetTokenData(token)
|
||||
if !exists {
|
||||
w.WriteHeader(401)
|
||||
io.WriteString(w, "Failed to get token")
|
||||
return
|
||||
}
|
||||
|
||||
path := r.URL.Path
|
||||
parts := strings.Split(path[1:], "/")
|
||||
|
||||
r.ParseForm()
|
||||
|
||||
if len(parts) == 3 && parts[1] == "clients" {
|
||||
ownerId := parts[0]
|
||||
clientId := parts[2]
|
||||
if r.Method == "PUT" {
|
||||
err := a.SetClient(tokenData, r.Form, ownerId, clientId)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
io.WriteString(w, err.Error())
|
||||
return
|
||||
}
|
||||
} else if r.Method == "DELETE" {
|
||||
err := a.DeleteClient(tokenData, ownerId, clientId)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
io.WriteString(w, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
w.WriteHeader(400)
|
||||
io.WriteString(w, "Invalid /users/<username>/clients request")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Api) SetClient(tokenData TokenData, params url.Values, ownerId, clientId string) error {
|
||||
|
||||
if tokenData.Owner != ownerId {
|
||||
user, _ := a.db.GetUser(tokenData.Owner)
|
||||
if !user.IsAdmin {
|
||||
return errors.New("Unauthorized")
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: what if two users try to get then set at the same time?
|
||||
owner, _ := a.db.GetUser(ownerId)
|
||||
owner.Clients[clientId] = Client{}
|
||||
a.db.SetUser(ownerId, owner)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Api) DeleteClient(tokenData TokenData, ownerId, clientId string) error {
|
||||
|
||||
if tokenData.Owner != ownerId {
|
||||
user, _ := a.db.GetUser(tokenData.Owner)
|
||||
if !user.IsAdmin {
|
||||
return errors.New("Unauthorized")
|
||||
}
|
||||
}
|
||||
|
||||
owner, _ := a.db.GetUser(ownerId)
|
||||
delete(owner.Clients, clientId)
|
||||
a.db.SetUser(ownerId, owner)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Api) validateToken(h http.Handler) http.Handler {
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
21
client.go
21
client.go
@ -24,6 +24,7 @@ type BoringProxyClient struct {
|
||||
server string
|
||||
token string
|
||||
clientName string
|
||||
user string
|
||||
cancelFuncs map[string]context.CancelFunc
|
||||
cancelFuncsMutex *sync.Mutex
|
||||
}
|
||||
@ -33,6 +34,7 @@ func NewBoringProxyClient() *BoringProxyClient {
|
||||
server := flagSet.String("server", "", "boringproxy server")
|
||||
token := flagSet.String("token", "", "Access token")
|
||||
name := flagSet.String("client-name", "", "Client name")
|
||||
user := flagSet.String("user", "admin", "user")
|
||||
flagSet.Parse(os.Args[2:])
|
||||
|
||||
httpClient := &http.Client{}
|
||||
@ -47,6 +49,7 @@ func NewBoringProxyClient() *BoringProxyClient {
|
||||
server: *server,
|
||||
token: *token,
|
||||
clientName: *name,
|
||||
user: *user,
|
||||
cancelFuncs: cancelFuncs,
|
||||
cancelFuncsMutex: cancelFuncsMutex,
|
||||
}
|
||||
@ -54,6 +57,24 @@ func NewBoringProxyClient() *BoringProxyClient {
|
||||
|
||||
func (c *BoringProxyClient) RunPuppetClient() {
|
||||
|
||||
url := fmt.Sprintf("https://%s/api/users/%s/clients/%s", c.server, c.user, c.clientName)
|
||||
clientReq, err := http.NewRequest("PUT", url, nil)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to PUT client")
|
||||
}
|
||||
if len(c.token) > 0 {
|
||||
clientReq.Header.Add("Authorization", "bearer "+c.token)
|
||||
}
|
||||
resp, err := c.httpClient.Do(clientReq)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to PUT client")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
log.Fatal("Failed to PUT client")
|
||||
}
|
||||
|
||||
for {
|
||||
err := c.PollTunnels()
|
||||
if err != nil {
|
||||
|
17
database.go
17
database.go
@ -22,6 +22,7 @@ type TokenData struct {
|
||||
|
||||
type User struct {
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
Clients map[string]Client `json:"clients"`
|
||||
}
|
||||
|
||||
type SshKey struct {
|
||||
@ -29,6 +30,9 @@ type SshKey struct {
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
}
|
||||
|
||||
type Tunnel struct {
|
||||
Owner string `json:"owner"`
|
||||
Domain string `json:"domain"`
|
||||
@ -222,6 +226,16 @@ func (d *Database) GetUser(username string) (User, bool) {
|
||||
return user, true
|
||||
}
|
||||
|
||||
func (d *Database) SetUser(username string, user User) error {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
|
||||
d.Users[username] = user
|
||||
d.persist()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Database) AddUser(username string, isAdmin bool) error {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
@ -233,7 +247,8 @@ func (d *Database) AddUser(username string, isAdmin bool) error {
|
||||
}
|
||||
|
||||
d.Users[username] = User{
|
||||
isAdmin,
|
||||
IsAdmin: isAdmin,
|
||||
Clients: make(map[string]Client),
|
||||
}
|
||||
|
||||
d.persist()
|
||||
|
3
todo.md
3
todo.md
@ -18,3 +18,6 @@
|
||||
to manually combine them for custom keys.
|
||||
* Send public key back to clients, so they can automatically try to find the
|
||||
matching private key.
|
||||
* We might need some sort of a transaction or atomicity system on the db to
|
||||
prevent things like 2 people setting the user at the same time and one losing
|
||||
their changes.
|
||||
|
@ -36,6 +36,7 @@ type IndexData struct {
|
||||
Tokens map[string]TokenData
|
||||
SshKeys map[string]SshKey
|
||||
Users map[string]User
|
||||
UserId string
|
||||
IsAdmin bool
|
||||
QrCodes map[string]template.URL
|
||||
}
|
||||
@ -263,6 +264,7 @@ func (h *WebUiHandler) handleWebUiRequest(w http.ResponseWriter, r *http.Request
|
||||
Tokens: tokens,
|
||||
SshKeys: h.api.GetSshKeys(tokenData),
|
||||
Users: users,
|
||||
UserId: tokenData.Owner,
|
||||
IsAdmin: user.IsAdmin,
|
||||
QrCodes: qrCodes,
|
||||
}
|
||||
|
@ -80,7 +80,12 @@
|
||||
|
||||
<div class='input'>
|
||||
<label for="client-name">Client Name:</label>
|
||||
<input type="text" id="client-name" name="client-name">
|
||||
<select id="client-name" name="client-name">
|
||||
<option value="any">Any</option>
|
||||
{{range $id, $client := (index $.Users $.UserId).Clients}}
|
||||
<option value="{{$id}}">{{$id}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</div>
|
||||
<div class='input'>
|
||||
<label for="client-addr">Client Address:</label>
|
||||
|
Loading…
Reference in New Issue
Block a user