From 0d1c25a550b6d7f0de3fd446082c20476558e410 Mon Sep 17 00:00:00 2001 From: sentriz Date: Tue, 16 Apr 2019 17:46:15 +0100 Subject: [PATCH] add user manage routes --- cmd/server/main.go | 33 ++++--- handler/admin.go | 135 +++++++++++++++++++-------- handler/handler.go | 35 ++++--- handler/middleware.go | 20 +++- static/stylesheets/main.css | 28 ++++-- templates/admin/create_user.tmpl | 12 --- templates/admin/login.tmpl | 12 --- templates/layout.tmpl | 2 +- templates/pages/change_password.tmpl | 14 +++ templates/pages/create_user.tmpl | 15 +++ templates/{admin => pages}/home.tmpl | 12 +-- templates/pages/login.tmpl | 14 +++ templates/user.tmpl | 8 +- 13 files changed, 234 insertions(+), 106 deletions(-) delete mode 100644 templates/admin/create_user.tmpl delete mode 100644 templates/admin/login.tmpl create mode 100644 templates/pages/change_password.tmpl create mode 100644 templates/pages/create_user.tmpl rename templates/{admin => pages}/home.tmpl (78%) create mode 100644 templates/pages/login.tmpl diff --git a/cmd/server/main.go b/cmd/server/main.go index e74734a..cf2ee58 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -1,6 +1,7 @@ package main import ( + "encoding/gob" "log" "net/http" "time" @@ -35,9 +36,9 @@ func setSubsonicRoutes(mux *http.ServeMux) { DB: dbCon, } withWare := newChain( - cont.LogConnection, - cont.EnableCORS, - cont.CheckParameters, + cont.WithLogging, + cont.WithCORS, + cont.WithValidSubsonicArgs, ) mux.HandleFunc("/rest/ping", withWare(cont.Ping)) mux.HandleFunc("/rest/ping.view", withWare(cont.Ping)) @@ -66,20 +67,30 @@ func setAdminRoutes(mux *http.ServeMux) { DB: dbCon, SStore: gormstore.New(dbCon, []byte("saynothinboys")), } - withWare := newChain( - cont.LogConnection, - cont.EnableCORS, + withBaseWare := newChain( + cont.WithLogging, + ) + withAuthWare := newChain( + withBaseWare, + cont.WithValidSession, ) server := http.FileServer(http.Dir("static")) mux.Handle("/admin/static/", http.StripPrefix("/admin/static/", server)) - mux.HandleFunc("/admin/login", withWare(cont.ServeLogin)) - mux.HandleFunc("/admin/authenticate", withWare(cont.ServeAuthenticate)) - mux.HandleFunc("/admin/home", withWare(cont.ServeHome)) - mux.HandleFunc("/admin/create_user", withWare(cont.ServeCreateUser)) - mux.HandleFunc("/admin/logout", withWare(cont.ServeLogout)) + mux.HandleFunc("/admin/login", withBaseWare(cont.ServeLogin)) + mux.HandleFunc("/admin/login_do", withBaseWare(cont.ServeLoginDo)) + mux.HandleFunc("/admin/logout", withAuthWare(cont.ServeLogout)) + mux.HandleFunc("/admin/home", withAuthWare(cont.ServeHome)) + mux.HandleFunc("/admin/change_password", withAuthWare(cont.ServeChangePassword)) + mux.HandleFunc("/admin/change_password_do", withBaseWare(cont.ServeChangePasswordDo)) + mux.HandleFunc("/admin/create_user", withAuthWare(cont.ServeCreateUser)) + mux.HandleFunc("/admin/create_user_do", withBaseWare(cont.ServeCreateUserDo)) } func main() { + // init stuff. needed to store the current user in + // the gorilla session + gob.Register(&db.User{}) + // setup the subsonic and admin routes address := ":5000" mux := http.NewServeMux() setSubsonicRoutes(mux) diff --git a/handler/admin.go b/handler/admin.go index 8cb8774..7b40a0d 100644 --- a/handler/admin.go +++ b/handler/admin.go @@ -1,8 +1,10 @@ package handler import ( + "fmt" "net/http" + "github.com/jinzhu/gorm" "github.com/sentriz/gonic/db" ) @@ -11,14 +13,14 @@ func (c *Controller) ServeLogin(w http.ResponseWriter, r *http.Request) { renderTemplate(w, r, session, "login", &templateData{}) } -func (c *Controller) ServeAuthenticate(w http.ResponseWriter, r *http.Request) { +func (c *Controller) ServeLoginDo(w http.ResponseWriter, r *http.Request) { session, _ := c.SStore.Get(r, "gonic") username := r.FormValue("username") password := r.FormValue("password") if username == "" || password == "" { session.AddFlash("please provide both a username and password") session.Save(r, w) - http.Redirect(w, r, "/admin/login", 303) + http.Redirect(w, r, r.Header.Get("Referer"), 302) return } var user db.User @@ -26,52 +28,107 @@ func (c *Controller) ServeAuthenticate(w http.ResponseWriter, r *http.Request) { if !(username == user.Name && password == user.Password) { session.AddFlash("invalid username / password") session.Save(r, w) - http.Redirect(w, r, "/admin/login", 303) + http.Redirect(w, r, r.Header.Get("Referer"), 302) return } - session.Values["authenticated"] = true - session.Values["user"] = user.ID + session.Values["user"] = user session.Save(r, w) http.Redirect(w, r, "/admin/home", 303) } -func (c *Controller) ServeHome(w http.ResponseWriter, r *http.Request) { - session, _ := c.SStore.Get(r, "gonic") - authed, _ := session.Values["authenticated"].(bool) - if !authed { - session.AddFlash("you are not authenticated") - session.Save(r, w) - http.Redirect(w, r, "/admin/login", 303) - return - } - var data templateData - var user db.User - c.DB.First(&user, session.Values["user"]) - data.UserID = user.ID - data.Username = user.Name - c.DB.Table("album_artists").Count(&data.ArtistCount) - c.DB.Table("albums").Count(&data.AlbumCount) - c.DB.Table("tracks").Count(&data.TrackCount) - c.DB.Find(&data.Users) - renderTemplate(w, r, session, "home", &data) -} - -func (c *Controller) ServeCreateUser(w http.ResponseWriter, r *http.Request) { - session, _ := c.SStore.Get(r, "gonic") - authed, _ := session.Values["authenticated"].(bool) - if !authed { - session.AddFlash("you are not authenticated") - session.Save(r, w) - http.Redirect(w, r, "/admin/login", 303) - return - } - renderTemplate(w, r, session, "create_user", &templateData{}) -} - func (c *Controller) ServeLogout(w http.ResponseWriter, r *http.Request) { session, _ := c.SStore.Get(r, "gonic") - delete(session.Values, "authenticated") delete(session.Values, "user") session.Save(r, w) http.Redirect(w, r, "/admin/login", 303) } + +func (c *Controller) ServeHome(w http.ResponseWriter, r *http.Request) { + session, _ := c.SStore.Get(r, "gonic") + var data templateData + c.DB.Table("album_artists").Count(&data.ArtistCount) + c.DB.Table("albums").Count(&data.AlbumCount) + c.DB.Table("tracks").Count(&data.TrackCount) + c.DB.Find(&data.AllUsers) + renderTemplate(w, r, session, "home", &data) +} + +func (c *Controller) ServeChangePassword(w http.ResponseWriter, r *http.Request) { + session, _ := c.SStore.Get(r, "gonic") + username := r.URL.Query().Get("user") + if username == "" { + http.Error(w, "please provide a username", 400) + return + } + var user db.User + err := c.DB.Where("name = ?", username).First(&user).Error + if gorm.IsRecordNotFoundError(err) { + http.Error(w, "couldn't find a user with that name", 400) + return + } + var data templateData + data.SelectedUser = &user + renderTemplate(w, r, session, "change_password", &data) +} + +func (c *Controller) ServeChangePasswordDo(w http.ResponseWriter, r *http.Request) { + session, _ := c.SStore.Get(r, "gonic") + username := r.URL.Query().Get("user") + var user db.User + c.DB.Where("name = ?", username).First(&user) + password_one := r.FormValue("password_one") + password_two := r.FormValue("password_two") + if password_one == "" || password_two == "" { + session.AddFlash("please provide both passwords") + session.Save(r, w) + http.Redirect(w, r, r.Header.Get("Referer"), 302) + return + } + if !(password_one == password_two) { + session.AddFlash("the two passwords entered were not the same") + session.Save(r, w) + http.Redirect(w, r, r.Header.Get("Referer"), 302) + return + } + user.Password = password_one + c.DB.Save(&user) + http.Redirect(w, r, "/admin/home", 303) +} + +func (c *Controller) ServeCreateUser(w http.ResponseWriter, r *http.Request) { + session, _ := c.SStore.Get(r, "gonic") + renderTemplate(w, r, session, "create_user", &templateData{}) +} + +func (c *Controller) ServeCreateUserDo(w http.ResponseWriter, r *http.Request) { + session, _ := c.SStore.Get(r, "gonic") + username := r.FormValue("username") + password_one := r.FormValue("password_one") + password_two := r.FormValue("password_two") + if username == "" || password_one == "" || password_two == "" { + session.AddFlash("please fill out all fields") + session.Save(r, w) + http.Redirect(w, r, r.Header.Get("Referer"), 302) + return + } + if !(password_one == password_two) { + session.AddFlash("the two passwords entered were not the same") + session.Save(r, w) + http.Redirect(w, r, r.Header.Get("Referer"), 302) + return + } + user := db.User{ + Name: username, + Password: password_one, + } + err := c.DB.Create(&user).Error + if err != nil { + session.AddFlash(fmt.Sprintf( + "could not create user `%s`: %v", username, err, + )) + session.Save(r, w) + http.Redirect(w, r, r.Header.Get("Referer"), 302) + return + } + http.Redirect(w, r, "/admin/home", 303) +} diff --git a/handler/handler.go b/handler/handler.go index 23dc730..cbc0bd9 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -25,17 +25,22 @@ var ( func init() { templates["login"] = template.Must(template.ParseFiles( filepath.Join("templates", "layout.tmpl"), - filepath.Join("templates", "admin", "login.tmpl"), + filepath.Join("templates", "pages", "login.tmpl"), )) templates["home"] = template.Must(template.ParseFiles( filepath.Join("templates", "layout.tmpl"), filepath.Join("templates", "user.tmpl"), - filepath.Join("templates", "admin", "home.tmpl"), + filepath.Join("templates", "pages", "home.tmpl"), )) templates["create_user"] = template.Must(template.ParseFiles( filepath.Join("templates", "layout.tmpl"), filepath.Join("templates", "user.tmpl"), - filepath.Join("templates", "admin", "create_user.tmpl"), + filepath.Join("templates", "pages", "create_user.tmpl"), + )) + templates["change_password"] = template.Must(template.ParseFiles( + filepath.Join("templates", "layout.tmpl"), + filepath.Join("templates", "user.tmpl"), + filepath.Join("templates", "pages", "change_password.tmpl"), )) } @@ -46,13 +51,13 @@ type Controller struct { } type templateData struct { - Flashes []interface{} - UserID uint - Username string - ArtistCount uint - AlbumCount uint - TrackCount uint - Users []*db.User + Flashes []interface{} + User *db.User + SelectedUser *db.User + AllUsers []*db.User + ArtistCount uint + AlbumCount uint + TrackCount uint } func getStrParam(r *http.Request, key string) string { @@ -127,9 +132,15 @@ func respondError(w http.ResponseWriter, r *http.Request, func renderTemplate(w http.ResponseWriter, r *http.Request, s *sessions.Session, name string, data *templateData) { - flashes := s.Flashes() + // take the flashes from the session and add to template + data.Flashes = s.Flashes() s.Save(r, w) - data.Flashes = flashes + // take the user gob from the session (if we're logged in and + // it's there) cast to a user and add to the template + userIntf := s.Values["user"] + if userIntf != nil { + data.User = s.Values["user"].(*db.User) + } err := templates[name].ExecuteTemplate(w, "layout", data) if err != nil { http.Error(w, fmt.Sprintf("500 when executing: %v", err), 500) diff --git a/handler/middleware.go b/handler/middleware.go index 57b801a..734d0a7 100644 --- a/handler/middleware.go +++ b/handler/middleware.go @@ -30,14 +30,14 @@ func checkCredentialsBasic(password, givenPassword string) bool { return password == givenPassword } -func (c *Controller) LogConnection(next http.HandlerFunc) http.HandlerFunc { +func (c *Controller) WithLogging(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { log.Printf("connection from `%s` for `%s`", r.RemoteAddr, r.URL) next.ServeHTTP(w, r) } } -func (c *Controller) CheckParameters(next http.HandlerFunc) http.HandlerFunc { +func (c *Controller) WithValidSubsonicArgs(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { for _, req := range requiredParameters { param := r.URL.Query().Get(req) @@ -83,7 +83,7 @@ func (c *Controller) CheckParameters(next http.HandlerFunc) http.HandlerFunc { } } -func (c *Controller) EnableCORS(next http.HandlerFunc) http.HandlerFunc { +func (c *Controller) WithCORS(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Methods", @@ -98,3 +98,17 @@ func (c *Controller) EnableCORS(next http.HandlerFunc) http.HandlerFunc { next.ServeHTTP(w, r) } } + +func (c *Controller) WithValidSession(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + session, _ := c.SStore.Get(r, "gonic") + user, _ := session.Values["user"] + if user == nil { + session.AddFlash("you are not authenticated") + session.Save(r, w) + http.Redirect(w, r, "/admin/login", 303) + return + } + next.ServeHTTP(w, r) + } +} diff --git a/static/stylesheets/main.css b/static/stylesheets/main.css index a946a34..f1359e3 100644 --- a/static/stylesheets/main.css +++ b/static/stylesheets/main.css @@ -1,3 +1,16 @@ +/* reset from awsm */ +form input[type], form select, form textarea { + margin-bottom: 0; +} + +/* reset from awsm */ +form { + max-width: 400px; + margin-left: auto; + margin-right: 0; +} + +/* reset from awsm */ div { margin: 0; padding: 0; @@ -12,12 +25,13 @@ body { justify-content: space-between; } -form { - max-width: unset; + +form input[type=password], form input[type=text] { + margin-bottom: 0.25rem; } #content > * { - margin: 1rem 0; + margin: 2rem 0; } .right { @@ -40,13 +54,15 @@ form { background-color: #0000000a; } +.box-title { + margin-bottom: 0.5rem; +} + .padded { padding: 1rem; } #header { - /* background-color: #fdc71b08; */ - background-image: linear-gradient(white, #fdad1b0d); border-bottom: 1px solid; padding-top: 3rem; } @@ -63,7 +79,7 @@ form { } #flashes { - background-color: #fd1b1b29; + background-color: #fd1b1b1c; } #login-form { diff --git a/templates/admin/create_user.tmpl b/templates/admin/create_user.tmpl deleted file mode 100644 index 8afd6ff..0000000 --- a/templates/admin/create_user.tmpl +++ /dev/null @@ -1,12 +0,0 @@ -{{ define "title" }}home{{ end }} - -{{ define "user" }} -
-
- create new user -
-
- howdy
-
-
-{{ end }} diff --git a/templates/admin/login.tmpl b/templates/admin/login.tmpl deleted file mode 100644 index d5ad2b1..0000000 --- a/templates/admin/login.tmpl +++ /dev/null @@ -1,12 +0,0 @@ -{{ define "title" }}gonic{{ end }} - -{{ define "content" }} -
- login -
-
- - - -
-{{ end }} diff --git a/templates/layout.tmpl b/templates/layout.tmpl index c85f443..6a38995 100644 --- a/templates/layout.tmpl +++ b/templates/layout.tmpl @@ -15,7 +15,7 @@
{{ if .Flashes }} -
+

warning: {{ index .Flashes 0 }}

{{ end }} diff --git a/templates/pages/change_password.tmpl b/templates/pages/change_password.tmpl new file mode 100644 index 0000000..6169c5e --- /dev/null +++ b/templates/pages/change_password.tmpl @@ -0,0 +1,14 @@ +{{ define "title" }}home{{ end }} + +{{ define "user" }} +
+
+ changing {{ .SelectedUser.Name }}'s password +
+
+ + + +
+
+{{ end }} diff --git a/templates/pages/create_user.tmpl b/templates/pages/create_user.tmpl new file mode 100644 index 0000000..17fd06c --- /dev/null +++ b/templates/pages/create_user.tmpl @@ -0,0 +1,15 @@ +{{ define "title" }}home{{ end }} + +{{ define "user" }} +
+
+ create new user +
+
+ + + + +
+
+{{ end }} diff --git a/templates/admin/home.tmpl b/templates/pages/home.tmpl similarity index 78% rename from templates/admin/home.tmpl rename to templates/pages/home.tmpl index 84b83bb..c337ddd 100644 --- a/templates/admin/home.tmpl +++ b/templates/pages/home.tmpl @@ -2,7 +2,7 @@ {{ define "user" }}
-
+
stats
@@ -12,20 +12,20 @@
-
- last.fm +
+ last fm
unlinked
-
+
users
- {{ range $user := .Users }} - {{ $user.Name }} created {{ $user.CreatedAt.Format "Jan 02, 2006" }} change password
+ {{ range $user := .AllUsers }} + {{ $user.Name }} created {{ $user.CreatedAt.Format "Jan 02, 2006" }} change password
{{ end }}
create new diff --git a/templates/pages/login.tmpl b/templates/pages/login.tmpl new file mode 100644 index 0000000..b53f544 --- /dev/null +++ b/templates/pages/login.tmpl @@ -0,0 +1,14 @@ +{{ define "title" }}gonic{{ end }} + +{{ define "content" }} +
+
+ please login +
+
+ + + +
+
+{{ end }} diff --git a/templates/user.tmpl b/templates/user.tmpl index ea3dd38..a14bd0f 100644 --- a/templates/user.tmpl +++ b/templates/user.tmpl @@ -1,8 +1,8 @@ {{ define "title" }}home{{ end }} {{ define "content" }} -
- welcome {{ .Username }} | logout -
- {{ template "user" . }} +
+ welcome {{ .User.Name }} | logout +
+{{ template "user" . }} {{ end }}