✨ Implemented admin authentication
This commit is contained in:
parent
5034d1a1f8
commit
180ee02582
|
@ -7,17 +7,20 @@
|
||||||
thead {
|
thead {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
#content {
|
#content {
|
||||||
float: right;
|
float: right;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: #F0F0F0;
|
background-color: #F0F0F0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu {
|
#menu {
|
||||||
float: left;
|
float: left;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
margin-left: -200px;
|
margin-left: -200px;
|
||||||
background-color: #CCCCCC;
|
background-color: #CCCCCC;
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu span {
|
#menu span {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
@ -29,14 +32,19 @@
|
||||||
{{ block "page-styles" . }}{{ end }}
|
{{ block "page-styles" . }}{{ end }}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="content">
|
<div id="content">
|
||||||
{{ block "page-content" . }}{{ end }}
|
{{ block "page-content" . }}{{ end }}
|
||||||
</div>
|
</div>
|
||||||
<div id="menu">
|
<div id="menu">
|
||||||
<span><a href="./">ShareDAV</a></span>
|
<span><a href="./">ShareDAV</a></span>
|
||||||
<span><a href="users">Users</a></span>
|
{{ if .SessionUser }}
|
||||||
<span><a href="shares">Shares</a></span>
|
{{ if eq .SessionUser.Role "admin" }}
|
||||||
</div>
|
<span><a href="users">Users</a></span>
|
||||||
<div style="clear: both;"></div>
|
<span><a href="shares">Shares</a></span>
|
||||||
|
{{ end }}
|
||||||
|
<span><a href="logout">Logout</a></span>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
<div style="clear: both;"></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -1,3 +1,3 @@
|
||||||
{{ define "page-content" }}
|
{{ define "page-content" }}
|
||||||
Test {{.foo}}
|
Startpage.
|
||||||
{{ end }}
|
{{ end }}
|
|
@ -0,0 +1,24 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>ShareDAV Login</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<form method="post">
|
||||||
|
<label>
|
||||||
|
Username: <input name="username"/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Password: <input type="password" name="password">
|
||||||
|
</label>
|
||||||
|
<div>
|
||||||
|
Beware! You need to have cookies enabled for the login to work.<br/>
|
||||||
|
The cookie will be used solely for keeping the session alive.
|
||||||
|
</div>
|
||||||
|
<input type="submit" value="Login"/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
{{ define "page-content" }}
|
{{ define "page-content" }}
|
||||||
<div id="shares">
|
<div id="shares">
|
||||||
{{ range $share := . }}
|
{{ range $share := .ShareInfos }}
|
||||||
<div id="share-{{$share.UUID}}" class="share">
|
<div id="share-{{$share.UUID}}" class="share">
|
||||||
UUID: {{ $share.UUID }} <form style="display: inline-block;" action="delete-share" method="post">
|
UUID: {{ $share.UUID }} <form style="display: inline-block;" action="delete-share" method="post">
|
||||||
<input type="hidden" name="share" value="{{ $share.UUID }}"/>
|
<input type="hidden" name="share" value="{{ $share.UUID }}"/>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{{ range $user := .}}<tr>
|
{{ range $user := .Users }}<tr>
|
||||||
<td>{{ $user.Username }}</td>
|
<td>{{ $user.Username }}</td>
|
||||||
<td>{{ $user.Role }}</td>
|
<td>{{ $user.Role }}</td>
|
||||||
</tr>{{ end }}
|
</tr>{{ end }}
|
||||||
|
|
340
webadmin.go
340
webadmin.go
|
@ -26,19 +26,26 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi"
|
||||||
uuid "github.com/satori/go.uuid"
|
uuid "github.com/satori/go.uuid"
|
||||||
|
"github.com/tidwall/buntdb"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
type webAdminHandler struct {
|
type webAdminHandler struct {
|
||||||
router chi.Router
|
router chi.Router
|
||||||
tplError *template.Template
|
tplError *template.Template
|
||||||
tplConfirm *template.Template
|
tplConfirm *template.Template
|
||||||
|
tplLogin *template.Template
|
||||||
tplIndex *template.Template
|
tplIndex *template.Template
|
||||||
tplUsers *template.Template
|
tplUsers *template.Template
|
||||||
tplShares *template.Template
|
tplShares *template.Template
|
||||||
|
@ -46,7 +53,26 @@ type webAdminHandler struct {
|
||||||
tplCreateShare *template.Template
|
tplCreateShare *template.Template
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type sessionContext struct {
|
||||||
|
h *webAdminHandler
|
||||||
|
w http.ResponseWriter
|
||||||
|
r *http.Request
|
||||||
|
baseModel map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sessionCookieName = "sharedavsession"
|
||||||
|
const sessionLifetime = 1 * time.Hour
|
||||||
|
|
||||||
|
const confirmRequested = 0
|
||||||
|
const confirmAccepted = 1
|
||||||
|
const confirmDenied = 2
|
||||||
|
|
||||||
func newWebAdminHandler(app *app) *webAdminHandler {
|
func newWebAdminHandler(app *app) *webAdminHandler {
|
||||||
|
sessionStore, err := buntdb.Open(":memory:")
|
||||||
|
if err != nil {
|
||||||
|
panic("cannot initialize session store: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
loadTemplate := func(filename string) *template.Template {
|
loadTemplate := func(filename string) *template.Template {
|
||||||
t, err := template.ParseFiles(
|
t, err := template.ParseFiles(
|
||||||
path.Join("templates", "base.html"),
|
path.Join("templates", "base.html"),
|
||||||
|
@ -57,10 +83,18 @@ func newWebAdminHandler(app *app) *webAdminHandler {
|
||||||
}
|
}
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
loadSingleTemplate := func(filename string) *template.Template {
|
||||||
|
t, err := template.ParseFiles(path.Join("templates", filename+".html"))
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("cannot load template %q: %v", filename, err))
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
h := &webAdminHandler{
|
h := &webAdminHandler{
|
||||||
tplError: loadTemplate("error"),
|
tplError: loadTemplate("error"),
|
||||||
tplConfirm: loadTemplate("confirm"),
|
tplConfirm: loadTemplate("confirm"),
|
||||||
|
tplLogin: loadSingleTemplate("login"),
|
||||||
tplIndex: loadTemplate("index"),
|
tplIndex: loadTemplate("index"),
|
||||||
tplUsers: loadTemplate("users"),
|
tplUsers: loadTemplate("users"),
|
||||||
tplShares: loadTemplate("shares"),
|
tplShares: loadTemplate("shares"),
|
||||||
|
@ -68,66 +102,98 @@ func newWebAdminHandler(app *app) *webAdminHandler {
|
||||||
tplCreateShare: loadTemplate("create-share"),
|
tplCreateShare: loadTemplate("create-share"),
|
||||||
}
|
}
|
||||||
|
|
||||||
renderPage := func(w http.ResponseWriter, tmpl *template.Template, model interface{}) {
|
|
||||||
if err := tmpl.Execute(w, model); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderError := func(w http.ResponseWriter, msg, returnURL string) {
|
|
||||||
model := struct {
|
|
||||||
ErrorMessage string
|
|
||||||
ReturnURL string
|
|
||||||
}{
|
|
||||||
ErrorMessage: msg,
|
|
||||||
ReturnURL: returnURL,
|
|
||||||
}
|
|
||||||
|
|
||||||
renderPage(w, h.tplError, model)
|
|
||||||
}
|
|
||||||
|
|
||||||
const confirmRequested = 0
|
|
||||||
const confirmAccepted = 1
|
|
||||||
const confirmDenied = 2
|
|
||||||
confirm := func(w http.ResponseWriter, r *http.Request, msg template.HTML, returnURL string) int {
|
|
||||||
if r.FormValue("_yes") != "" {
|
|
||||||
return confirmAccepted
|
|
||||||
} else if r.FormValue("_no") != "" {
|
|
||||||
return confirmDenied
|
|
||||||
} else {
|
|
||||||
model := struct {
|
|
||||||
URL string
|
|
||||||
Fields map[string]string
|
|
||||||
Message template.HTML
|
|
||||||
}{
|
|
||||||
URL: returnURL,
|
|
||||||
Fields: make(map[string]string),
|
|
||||||
Message: msg,
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range r.Form {
|
|
||||||
model.Fields[k] = v[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
renderPage(w, h.tplConfirm, model)
|
|
||||||
return confirmRequested
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
r.Use(disableCaching)
|
||||||
renderPage(w, h.tplIndex, map[string]string{"foo": "bar"})
|
|
||||||
|
r.Route("/login", func(r chi.Router) {
|
||||||
|
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
sessionContext := h.buildSessionContext(w, r)
|
||||||
|
sessionContext.RenderPage(h.tplLogin, nil)
|
||||||
|
})
|
||||||
|
r.Post("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
sessionContext := h.buildSessionContext(w, r)
|
||||||
|
|
||||||
|
username := r.FormValue("username")
|
||||||
|
if strings.ContainsAny(username, "*:") {
|
||||||
|
sessionContext.RenderError("Username or password wrong.", "login")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := app.userStore.GetUser(username)
|
||||||
|
if err != nil {
|
||||||
|
sessionContext.RenderError("Username or password wrong.", "login")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(r.FormValue("password"))); err != nil {
|
||||||
|
sessionContext.RenderError("Username or password wrong.", "login")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionId := uuid.NewV4()
|
||||||
|
if err := sessionStore.Update(func(tx *buntdb.Tx) error {
|
||||||
|
_, _, err := tx.Set(sessionId.String(), user.Username, &buntdb.SetOptions{
|
||||||
|
Expires: true,
|
||||||
|
TTL: sessionLifetime,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
log.Printf("error setting session: %v\n", err)
|
||||||
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cookie := &http.Cookie{
|
||||||
|
Name: sessionCookieName,
|
||||||
|
Value: sessionId.String(),
|
||||||
|
Expires: time.Now().Add(sessionLifetime),
|
||||||
|
HttpOnly: true,
|
||||||
|
}
|
||||||
|
http.SetCookie(w, cookie)
|
||||||
|
|
||||||
|
sessionContext.Redirect("./")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
r.Get("/users", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
|
ar := r.With(authenticated(sessionStore, app.userStore))
|
||||||
|
|
||||||
|
ar.Get("/logout", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
sessionContext := h.buildSessionContext(w, r)
|
||||||
|
sessionCookie, err := r.Cookie(sessionCookieName)
|
||||||
|
if err != nil {
|
||||||
|
http.Redirect(w, r, "login", http.StatusFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := sessionStore.Update(func(tx *buntdb.Tx) error {
|
||||||
|
_, err := tx.Delete(sessionCookie.Value)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
log.Printf("error unsetting session: %v\n", err)
|
||||||
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sessionContext.Redirect("./")
|
||||||
|
})
|
||||||
|
ar.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
sessionContext := h.buildSessionContext(w, r)
|
||||||
|
sessionContext.RenderPage(h.tplIndex, nil)
|
||||||
|
})
|
||||||
|
ar.Get("/users", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
sessionContext := h.buildSessionContext(w, r)
|
||||||
|
|
||||||
users, err := app.userStore.GetUsers()
|
users, err := app.userStore.GetUsers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
renderPage(w, h.tplUsers, users)
|
sessionContext.RenderPage(h.tplUsers, map[string]interface{}{
|
||||||
|
"Users": users,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
r.Get("/shares", func(w http.ResponseWriter, r *http.Request) {
|
ar.Get("/shares", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
sessionContext := h.buildSessionContext(w, r)
|
||||||
|
|
||||||
shares, err := app.shareStore.GetShares()
|
shares, err := app.shareStore.GetShares()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
@ -150,10 +216,14 @@ func newWebAdminHandler(app *app) *webAdminHandler {
|
||||||
shareInfos[i].Users = users
|
shareInfos[i].Users = users
|
||||||
}
|
}
|
||||||
|
|
||||||
renderPage(w, h.tplShares, shareInfos)
|
sessionContext.RenderPage(h.tplShares, map[string]interface{}{
|
||||||
|
"ShareInfos": shareInfos,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
r.Route("/share-add-user", func(r chi.Router) {
|
ar.Route("/share-add-user", func(r chi.Router) {
|
||||||
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
sessionContext := h.buildSessionContext(w, r)
|
||||||
|
|
||||||
shareId := r.URL.Query().Get("share")
|
shareId := r.URL.Query().Get("share")
|
||||||
if shareId == "" {
|
if shareId == "" {
|
||||||
http.Error(w, "invalid share id", http.StatusBadRequest)
|
http.Error(w, "invalid share id", http.StatusBadRequest)
|
||||||
|
@ -166,67 +236,72 @@ func newWebAdminHandler(app *app) *webAdminHandler {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
model := map[string]interface{}{
|
sessionContext.RenderPage(h.tplShareAddUser, map[string]interface{}{
|
||||||
"ShareId": shareId,
|
"ShareId": shareId,
|
||||||
"Users": users,
|
"Users": users,
|
||||||
}
|
})
|
||||||
|
|
||||||
renderPage(w, h.tplShareAddUser, model)
|
|
||||||
})
|
})
|
||||||
r.Post("/", func(w http.ResponseWriter, r *http.Request) {
|
r.Post("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
sessionContext := h.buildSessionContext(w, r)
|
||||||
|
|
||||||
returnURL := "share-add-user?share=" + r.FormValue("share")
|
returnURL := "share-add-user?share=" + r.FormValue("share")
|
||||||
|
|
||||||
shareId, err := uuid.FromString(r.FormValue("share"))
|
shareId, err := uuid.FromString(r.FormValue("share"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
renderError(w, "Internal error: "+err.Error(), "")
|
sessionContext.RenderError(template.HTML("Internal error: "+err.Error()), "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
share := Share{UUID: shareId}
|
share := Share{UUID: shareId}
|
||||||
|
|
||||||
user, err := app.userStore.GetUser(r.FormValue("user"))
|
user, err := app.userStore.GetUser(r.FormValue("user"))
|
||||||
if err == ErrUserNotFound {
|
if err == ErrUserNotFound {
|
||||||
renderError(w, "User not found.", returnURL)
|
sessionContext.RenderError("User not found.", returnURL)
|
||||||
return
|
return
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
renderError(w, "Internal error: "+err.Error(), "")
|
sessionContext.RenderError(template.HTML("Internal error: "+err.Error()), "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = app.shareStore.AddUserToShare(share, user.Username, ShareRole(r.FormValue("role")))
|
err = app.shareStore.AddUserToShare(share, user.Username, ShareRole(r.FormValue("role")))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
renderError(w, "Cannot add user to share: "+err.Error(), returnURL)
|
sessionContext.RenderError(template.HTML("Cannot add user to share: "+err.Error()), returnURL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
http.Redirect(w, r, "shares", http.StatusFound)
|
sessionContext.Redirect("shares")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
r.Post("/share-delete-user", func(w http.ResponseWriter, r *http.Request) {
|
ar.Post("/share-delete-user", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
sessionContext := h.buildSessionContext(w, r)
|
||||||
|
|
||||||
returnURL := "shares"
|
returnURL := "shares"
|
||||||
|
|
||||||
shareId, err := uuid.FromString(r.FormValue("share"))
|
shareId, err := uuid.FromString(r.FormValue("share"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
renderError(w, "Internal error: "+err.Error(), "")
|
sessionContext.RenderError(template.HTML("Internal error: "+err.Error()), "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
share := Share{UUID: shareId}
|
share := Share{UUID: shareId}
|
||||||
|
|
||||||
err = app.shareStore.RemoveUserFromShare(share, r.FormValue("user"))
|
err = app.shareStore.RemoveUserFromShare(share, r.FormValue("user"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
renderError(w, "Cannot remove user from share: "+err.Error(), returnURL)
|
sessionContext.RenderError(template.HTML("Cannot remove user from share: "+err.Error()), returnURL)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
http.Redirect(w, r, "shares", http.StatusFound)
|
http.Redirect(w, r, "shares", http.StatusFound)
|
||||||
})
|
})
|
||||||
r.Route("/create-share", func(r chi.Router) {
|
ar.Route("/create-share", func(r chi.Router) {
|
||||||
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
renderPage(w, h.tplCreateShare, nil)
|
sessionContext := h.buildSessionContext(w, r)
|
||||||
|
sessionContext.RenderPage(h.tplCreateShare, nil)
|
||||||
})
|
})
|
||||||
r.Post("/", func(w http.ResponseWriter, r *http.Request) {
|
r.Post("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
sessionContext := h.buildSessionContext(w, r)
|
||||||
|
|
||||||
share, err := app.shareStore.CreateShare()
|
share, err := app.shareStore.CreateShare()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
renderError(w, "Cannot create share: "+err.Error(), "")
|
sessionContext.RenderError(template.HTML("Cannot create share: "+err.Error()), "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,39 +309,152 @@ func newWebAdminHandler(app *app) *webAdminHandler {
|
||||||
share.Description = r.FormValue("description")
|
share.Description = r.FormValue("description")
|
||||||
|
|
||||||
if err := app.shareStore.UpdateShareAttributes(share); err != nil {
|
if err := app.shareStore.UpdateShareAttributes(share); err != nil {
|
||||||
renderError(w, "Cannot update share: "+err.Error(), "")
|
sessionContext.RenderError(template.HTML("Cannot update share: "+err.Error()), "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
http.Redirect(w, r, "shares#share-"+share.UUID.String(), http.StatusFound)
|
sessionContext.Redirect("shares#share-" + share.UUID.String())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
r.Post("/delete-share", func(w http.ResponseWriter, r *http.Request) {
|
ar.Post("/delete-share", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
sessionContext := h.buildSessionContext(w, r)
|
||||||
|
|
||||||
share, err := app.shareStore.GetShare(r.FormValue("share"))
|
share, err := app.shareStore.GetShare(r.FormValue("share"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
renderError(w, "Internal error: "+err.Error(), "")
|
sessionContext.RenderError(template.HTML("Internal error: "+err.Error()), "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
message := fmt.Sprintf(`You are about to delete the share %s (%s).<br/>
|
message := fmt.Sprintf(`You are about to delete the share %s (%s).<br/>
|
||||||
This will delete all data permanently.<br/><br/>
|
This will delete all data permanently.<br/><br/>
|
||||||
Are you sure you want to continue?`, share.UUID, share.Name)
|
Are you sure you want to continue?`, share.UUID, share.Name)
|
||||||
if confirmStatus := confirm(w, r, template.HTML(message), "delete-share"); confirmStatus == confirmRequested {
|
if confirmStatus := sessionContext.RequestConfirmation(template.HTML(message), "delete-share"); confirmStatus == confirmRequested {
|
||||||
// We have already rendered. Nothing to do.
|
// We have already rendered. Nothing to do.
|
||||||
return
|
return
|
||||||
} else if confirmStatus == confirmAccepted {
|
} else if confirmStatus == confirmAccepted {
|
||||||
if err := app.shareStore.RemoveShare(share.UUID); err != nil {
|
if err := app.shareStore.RemoveShare(share.UUID); err != nil {
|
||||||
renderError(w, "Share cannot be removed: "+err.Error(), "shares")
|
sessionContext.RenderError(template.HTML("Share cannot be removed: "+err.Error()), "shares")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
http.Redirect(w, r, "shares", http.StatusFound)
|
sessionContext.Redirect("shares")
|
||||||
})
|
})
|
||||||
h.router = r
|
h.router = r
|
||||||
|
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h webAdminHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (h *webAdminHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
h.router.ServeHTTP(w, r)
|
h.router.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *webAdminHandler) buildSessionContext(w http.ResponseWriter, r *http.Request) *sessionContext {
|
||||||
|
sessionContext := &sessionContext{
|
||||||
|
h: h,
|
||||||
|
w: w,
|
||||||
|
r: r,
|
||||||
|
baseModel: map[string]interface{}{},
|
||||||
|
}
|
||||||
|
sessionUser := userFromContext(r)
|
||||||
|
if sessionUser != nil {
|
||||||
|
sessionContext.baseModel["SessionUser"] = sessionUser
|
||||||
|
}
|
||||||
|
return sessionContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sessionContext) Redirect(target string) {
|
||||||
|
http.Redirect(s.w, s.r, target, http.StatusFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sessionContext) RenderPage(tmpl *template.Template, model map[string]interface{}) {
|
||||||
|
effectiveModel := map[string]interface{}{}
|
||||||
|
for k, v := range s.baseModel {
|
||||||
|
effectiveModel[k] = v
|
||||||
|
}
|
||||||
|
for k, v := range model {
|
||||||
|
effectiveModel[k] = v
|
||||||
|
}
|
||||||
|
if err := tmpl.Execute(s.w, effectiveModel); err != nil {
|
||||||
|
http.Error(s.w, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sessionContext) RenderError(msg template.HTML, returnURL string) {
|
||||||
|
model := map[string]interface{}{
|
||||||
|
"ErrorMessage": msg,
|
||||||
|
"ReturnURL": returnURL,
|
||||||
|
}
|
||||||
|
s.RenderPage(s.h.tplError, model)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sessionContext) RequestConfirmation(msg template.HTML, returnURL string) int {
|
||||||
|
if s.r.FormValue("_yes") != "" {
|
||||||
|
return confirmAccepted
|
||||||
|
} else if s.r.FormValue("_no") != "" {
|
||||||
|
return confirmDenied
|
||||||
|
} else {
|
||||||
|
fields := map[string]string{}
|
||||||
|
for k, v := range s.r.Form {
|
||||||
|
fields[k] = v[0]
|
||||||
|
}
|
||||||
|
model := map[string]interface{}{
|
||||||
|
"URL": returnURL,
|
||||||
|
"Message": msg,
|
||||||
|
"Fields": fields,
|
||||||
|
}
|
||||||
|
|
||||||
|
s.RenderPage(s.h.tplConfirm, model)
|
||||||
|
return confirmRequested
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func disableCaching(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||||
|
w.Header().Set("Pragma", "no-cache")
|
||||||
|
w.Header().Set("Expires", "0")
|
||||||
|
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func authenticated(sessionStore *buntdb.DB, userStore UserStore) func(http.Handler) http.Handler {
|
||||||
|
return func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
sessionCookie, err := r.Cookie(sessionCookieName)
|
||||||
|
if err != nil {
|
||||||
|
http.Redirect(w, r, "login", http.StatusFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var username string
|
||||||
|
if err := sessionStore.View(func(tx *buntdb.Tx) error {
|
||||||
|
val, err := tx.Get(sessionCookie.Value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
username = val
|
||||||
|
return nil
|
||||||
|
}); err != nil {
|
||||||
|
http.Redirect(w, r, "login", http.StatusFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := userStore.GetUser(username)
|
||||||
|
if err != nil {
|
||||||
|
http.Redirect(w, r, "login", http.StatusFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), "user", &user)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func userFromContext(r *http.Request) *User {
|
||||||
|
if user, ok := r.Context().Value("user").(*User); ok {
|
||||||
|
return user
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue