Implement managing domains

This commit is contained in:
Anders Pitman 2022-03-06 15:09:09 -07:00
parent 1832e99e22
commit 4d8b9aeaae
5 changed files with 235 additions and 0 deletions

57
api.go
View File

@ -580,3 +580,60 @@ func (a *Api) DeleteClient(tokenData TokenData, ownerId, clientId string) error
return nil
}
func (a *Api) GetDomains(tokenData TokenData) map[string]Domain {
user, _ := a.db.GetUser(tokenData.Owner)
var domains map[string]Domain
if user.IsAdmin {
domains = a.db.GetDomains()
} else {
domains = make(map[string]Domain)
for name, domain := range a.db.GetDomains() {
if tokenData.Owner == domain.Owner {
domains[name] = domain
}
}
}
return domains
}
func (a *Api) SetDomain(tokenData TokenData, params url.Values, domain, ownerId string) error {
if tokenData.Owner != ownerId {
user, _ := a.db.GetUser(tokenData.Owner)
if !user.IsAdmin {
return errors.New("Unauthorized")
}
}
err := a.db.AddDomain(domain, ownerId)
if err != nil {
return err
}
return nil
}
func (a *Api) DeleteDomain(tokenData TokenData, domainName string) error {
domains := a.db.GetDomains()
domain, exists := domains[domainName]
if !exists {
return errors.New("No such domain")
}
if tokenData.Owner != domain.Owner {
user, _ := a.db.GetUser(tokenData.Owner)
if !user.IsAdmin {
return errors.New("Unauthorized")
}
}
a.db.DeleteDomain(domainName)
return nil
}

View File

@ -18,6 +18,7 @@ type Database struct {
Tokens map[string]TokenData `json:"tokens"`
Tunnels map[string]Tunnel `json:"tunnels"`
Users map[string]User `json:"users"`
Domains map[string]Domain `json:"domains"`
dnsRequests map[string]namedrop.DNSRequest `json:"dns_requests"`
Waygates map[string]waygate.Waygate `json:"waygates"`
WaygateTokens map[string]waygate.TokenData `json:"waygate_tokens"`
@ -35,6 +36,10 @@ type User struct {
Clients map[string]DbClient `json:"clients"`
}
type Domain struct {
Owner string `json:"owner"`
}
type DbClient struct {
}
@ -336,6 +341,51 @@ func (d *Database) DeleteUser(username string) {
d.persist()
}
func (d *Database) GetDomains() map[string]Domain {
d.mutex.Lock()
defer d.mutex.Unlock()
domains := make(map[string]Domain)
for k, v := range d.Domains {
domains[k] = v
}
return domains
}
func (d *Database) AddDomain(domain, owner string) error {
d.mutex.Lock()
defer d.mutex.Unlock()
_, exists := d.Domains[domain]
if exists {
return errors.New("Domain already taken")
}
_, exists = d.Users[owner]
if !exists {
return errors.New("No such user")
}
d.Domains[domain] = Domain{
Owner: owner,
}
d.persist()
return nil
}
func (d *Database) DeleteDomain(domain string) {
d.mutex.Lock()
defer d.mutex.Unlock()
delete(d.Domains, domain)
d.persist()
}
func (d *Database) AddWaygateTunnel(domains []string) (string, error) {
d.mutex.Lock()
defer d.mutex.Unlock()

29
templates/domains.tmpl Normal file
View File

@ -0,0 +1,29 @@
{{ template "header.tmpl" . }}
<div class='list'>
{{range $name, $domain := $.Domains}}
<div class='list-item'>
<span class='domain'>{{$name}} (Owner: {{$domain.Owner}})</span>
<a href="/confirm-delete-domain?owner={{$domain.Owner}}&domain={{$name}}">
<button class='button'>Delete</button>
</a>
</div>
{{end}}
</div>
<div class='domain-adder'>
<form action="/domains" method="POST">
<label for="owner">Owner:</label>
<select id="owner" name="owner">
{{range $username, $user := $.Users}}
<option value="{{$username}}">{{$username}}</option>
{{end}}
</select>
<label for="domain">Domain:</label>
<input type="text" name="domain" required></input>
<button class='button' type="submit">Add Domain</button>
</form>
</div>
{{ template "footer.tmpl" . }}

View File

@ -27,6 +27,7 @@
<a class='menu-item' href='/edit-tunnel'>Add Tunnel</a>
<a class='menu-item' href='/tokens'>Tokens</a>
<a class='menu-item' href='/clients'>Clients</a>
<a class='menu-item' href='/domains'>Domains</a>
{{ if $.User.IsAdmin }}
<a class='menu-item' href='/users'>Users</a>
{{ end }}

View File

@ -352,6 +352,8 @@ func (h *WebUiHandler) handleWebUiRequest(w http.ResponseWriter, r *http.Request
h.handleTokens(w, r, user, tokenData)
case "/clients":
h.handleClients(w, r, user, tokenData)
case "/domains":
h.handleDomains(w, r, user, tokenData)
case "/confirm-delete-token":
h.confirmDeleteToken(w, r)
case "/delete-token":
@ -360,6 +362,10 @@ func (h *WebUiHandler) handleWebUiRequest(w http.ResponseWriter, r *http.Request
h.confirmDeleteClient(w, r)
case "/delete-client":
h.deleteClient(w, r, tokenData)
case "/confirm-delete-domain":
h.confirmDeleteDomain(w, r)
case "/delete-domain":
h.deleteDomain(w, r, tokenData)
case "/confirm-logout":
data := &ConfirmData{
@ -583,6 +589,62 @@ func (h *WebUiHandler) handleClients(w http.ResponseWriter, r *http.Request, use
return
}
}
func (h *WebUiHandler) handleDomains(w http.ResponseWriter, r *http.Request, user User, tokenData TokenData) {
r.ParseForm()
switch r.Method {
case "GET":
var users map[string]User
// TODO: handle security checks in api
if user.IsAdmin {
users = h.db.GetUsers()
} else {
user, _ := h.db.GetUser(tokenData.Owner)
users = make(map[string]User)
users[tokenData.Owner] = user
}
domains := h.api.GetDomains(tokenData)
templateData := struct {
User User
Users map[string]User
Domains map[string]Domain
}{
User: user,
Users: users,
Domains: domains,
}
err := h.tmpl.ExecuteTemplate(w, "domains.tmpl", templateData)
if err != nil {
w.WriteHeader(500)
io.WriteString(w, err.Error())
return
}
case "POST":
owner := r.Form.Get("owner")
domain := r.Form.Get("domain")
err := h.api.SetDomain(tokenData, r.Form, domain, owner)
if err != nil {
w.WriteHeader(500)
h.alertDialog(w, r, err.Error(), "/domains")
return
}
http.Redirect(w, r, "/domains", 303)
default:
w.WriteHeader(405)
h.alertDialog(w, r, "Invalid method for tokens", "/tokens")
return
}
}
func (h *WebUiHandler) handleLogin(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
@ -878,6 +940,42 @@ func (h *WebUiHandler) deleteClient(w http.ResponseWriter, r *http.Request, toke
http.Redirect(w, r, "/clients", 303)
}
func (h *WebUiHandler) confirmDeleteDomain(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
domain := r.Form.Get("domain")
data := &ConfirmData{
Head: h.headHtml,
Message: fmt.Sprintf("Are you sure you want to delete domain %s?", domain),
ConfirmUrl: fmt.Sprintf("/delete-domain?domain=%s", domain),
CancelUrl: "/domains",
}
err := h.tmpl.ExecuteTemplate(w, "confirm.tmpl", data)
if err != nil {
w.WriteHeader(500)
h.alertDialog(w, r, err.Error(), "/domains")
return
}
}
func (h *WebUiHandler) deleteDomain(w http.ResponseWriter, r *http.Request, tokenData TokenData) {
r.ParseForm()
domain := r.Form.Get("domain")
err := h.api.DeleteDomain(tokenData, domain)
if err != nil {
w.WriteHeader(500)
h.alertDialog(w, r, err.Error(), "/domains")
return
}
http.Redirect(w, r, "/domains", 303)
}
func (h *WebUiHandler) alertDialog(w http.ResponseWriter, r *http.Request, message, redirectUrl string) error {
err := h.tmpl.ExecuteTemplate(w, "alert.tmpl", &AlertData{
Head: h.headHtml,