mirror of
https://github.com/boringproxy/boringproxy.git
synced 2025-02-25 18:55:29 -06:00
Implement creating users and tokens from REST API
This commit is contained in:
72
api.go
72
api.go
@@ -28,6 +28,7 @@ func NewApi(config *BoringProxyConfig, db *Database, auth *Auth, tunMan *TunnelM
|
||||
|
||||
mux.Handle("/tunnels", http.StripPrefix("/tunnels", http.HandlerFunc(api.handleTunnels)))
|
||||
mux.Handle("/users/", http.StripPrefix("/users", http.HandlerFunc(api.handleUsers)))
|
||||
mux.Handle("/tokens/", http.StripPrefix("/tokens", http.HandlerFunc(api.handleTokens)))
|
||||
|
||||
return api
|
||||
}
|
||||
@@ -184,6 +185,37 @@ func (a *Api) DeleteTunnel(tokenData TokenData, params url.Values) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Api) handleTokens(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 "POST":
|
||||
r.ParseForm()
|
||||
token, err := a.CreateToken(tokenData, r.Form)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
w.Write([]byte(err.Error()))
|
||||
}
|
||||
|
||||
io.WriteString(w, token)
|
||||
default:
|
||||
w.WriteHeader(405)
|
||||
w.Write([]byte(err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Api) CreateToken(tokenData TokenData, params url.Values) (string, error) {
|
||||
|
||||
owner := params.Get("owner")
|
||||
@@ -346,7 +378,21 @@ func (a *Api) handleUsers(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
r.ParseForm()
|
||||
|
||||
if len(parts) == 3 && parts[1] == "clients" {
|
||||
if path == "/" {
|
||||
switch r.Method {
|
||||
case "POST":
|
||||
err := a.CreateUser(tokenData, r.Form)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
io.WriteString(w, err.Error())
|
||||
return
|
||||
}
|
||||
default:
|
||||
w.WriteHeader(406)
|
||||
io.WriteString(w, "Invalid method for /users")
|
||||
return
|
||||
}
|
||||
} else if len(parts) == 3 && parts[1] == "clients" {
|
||||
ownerId := parts[0]
|
||||
clientId := parts[2]
|
||||
if r.Method == "PUT" {
|
||||
@@ -371,6 +417,30 @@ func (a *Api) handleUsers(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Api) CreateUser(tokenData TokenData, params url.Values) error {
|
||||
|
||||
user, _ := a.db.GetUser(tokenData.Owner)
|
||||
if !user.IsAdmin {
|
||||
return errors.New("Unauthorized")
|
||||
}
|
||||
|
||||
username := params.Get("username")
|
||||
minUsernameLen := 6
|
||||
if len(username) < minUsernameLen {
|
||||
errStr := fmt.Sprintf("Username must be at least %d characters", minUsernameLen)
|
||||
return errors.New(errStr)
|
||||
}
|
||||
|
||||
isAdmin := params.Get("is-admin") == "on"
|
||||
|
||||
err := a.db.AddUser(username, isAdmin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Api) SetClient(tokenData TokenData, params url.Values, ownerId, clientId string) error {
|
||||
|
||||
if tokenData.Owner != ownerId {
|
||||
|
||||
1
todo.md
1
todo.md
@@ -8,6 +8,7 @@
|
||||
- [ ] Improve SSH key download UI.
|
||||
- [ ] Improve token list UI.
|
||||
- [ ] Invalid database is wiping out tunnels
|
||||
- [ ] Delete tokens when user is deleted
|
||||
- [x] Head can be rendered before h.headHtml is ever set, ie if login page is visited before any other page
|
||||
- [x] Responses to unauthorized requests are leaking information about the current tunnels through the generated CSS.
|
||||
- [x] I think it's possible to create tokens for arbitrary user, even if you're not that user.
|
||||
|
||||
@@ -162,13 +162,7 @@ func (h *WebUiHandler) handleWebUiRequest(w http.ResponseWriter, r *http.Request
|
||||
case "/login":
|
||||
h.handleLogin(w, r)
|
||||
case "/users":
|
||||
if user.IsAdmin {
|
||||
h.handleUsers(w, r)
|
||||
} else {
|
||||
w.WriteHeader(403)
|
||||
h.alertDialog(w, r, "Not authorized", "/#/tunnels")
|
||||
return
|
||||
}
|
||||
h.handleUsers(w, r, tokenData)
|
||||
|
||||
case "/confirm-delete-user":
|
||||
h.confirmDeleteUser(w, r)
|
||||
@@ -565,7 +559,7 @@ func (h *WebUiHandler) sendLoginPage(w http.ResponseWriter, r *http.Request, cod
|
||||
loginTemplate.Execute(w, loginData)
|
||||
}
|
||||
|
||||
func (h *WebUiHandler) handleUsers(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *WebUiHandler) handleUsers(w http.ResponseWriter, r *http.Request, tokenData TokenData) {
|
||||
|
||||
if r.Method != "POST" {
|
||||
w.WriteHeader(405)
|
||||
@@ -575,24 +569,7 @@ func (h *WebUiHandler) handleUsers(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
r.ParseForm()
|
||||
|
||||
if len(r.Form["username"]) != 1 {
|
||||
w.WriteHeader(400)
|
||||
w.Write([]byte("Invalid username parameter"))
|
||||
return
|
||||
}
|
||||
username := r.Form["username"][0]
|
||||
|
||||
minUsernameLen := 6
|
||||
if len(username) < minUsernameLen {
|
||||
w.WriteHeader(400)
|
||||
errStr := fmt.Sprintf("Username must be at least %d characters", minUsernameLen)
|
||||
h.alertDialog(w, r, errStr, "/#/users")
|
||||
return
|
||||
}
|
||||
|
||||
isAdmin := len(r.Form["is-admin"]) == 1 && r.Form["is-admin"][0] == "on"
|
||||
|
||||
err := h.db.AddUser(username, isAdmin)
|
||||
err := h.api.CreateUser(tokenData, r.Form)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
h.alertDialog(w, r, err.Error(), "/#/users")
|
||||
|
||||
Reference in New Issue
Block a user