diff --git a/cmd/scanner/main.go b/cmd/scanner/main.go index 7ec4b2d..8e0798c 100644 --- a/cmd/scanner/main.go +++ b/cmd/scanner/main.go @@ -197,10 +197,11 @@ func main() { orm.FirstOrCreate(&db.User{}, db.User{ Name: "admin", Password: "admin", + IsAdmin: true, }) orm.FirstOrCreate(&db.User{}, db.User{ - Name: "senan", - Password: "password", + Name: "stephen", + Password: "stephen", }) startTime := time.Now() tx = orm.Begin() diff --git a/cmd/server/main.go b/cmd/server/main.go index cf2ee58..8686f2c 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -65,25 +65,32 @@ func setSubsonicRoutes(mux *http.ServeMux) { func setAdminRoutes(mux *http.ServeMux) { cont := handler.Controller{ DB: dbCon, - SStore: gormstore.New(dbCon, []byte("saynothinboys")), + SStore: gormstore.New(dbCon, []byte("saynothinboys")), // TODO: not this } - withBaseWare := newChain( + withPublicWare := newChain( cont.WithLogging, + cont.WithSession, ) - withAuthWare := newChain( - withBaseWare, - cont.WithValidSession, + withUserWare := newChain( + withPublicWare, + cont.WithUserSession, + ) + withAdminWare := newChain( + withUserWare, + cont.WithAdminSession, ) server := http.FileServer(http.Dir("static")) mux.Handle("/admin/static/", http.StripPrefix("/admin/static/", server)) - 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)) + mux.HandleFunc("/admin/login", withPublicWare(cont.ServeLogin)) + mux.HandleFunc("/admin/login_do", withPublicWare(cont.ServeLoginDo)) + mux.HandleFunc("/admin/logout", withUserWare(cont.ServeLogout)) + mux.HandleFunc("/admin/home", withUserWare(cont.ServeHome)) + mux.HandleFunc("/admin/change_own_password", withUserWare(cont.ServeChangeOwnPassword)) + mux.HandleFunc("/admin/change_own_password_do", withUserWare(cont.ServeChangeOwnPasswordDo)) + mux.HandleFunc("/admin/change_password", withAdminWare(cont.ServeChangePassword)) + mux.HandleFunc("/admin/change_password_do", withAdminWare(cont.ServeChangePasswordDo)) + mux.HandleFunc("/admin/create_user", withAdminWare(cont.ServeCreateUser)) + mux.HandleFunc("/admin/create_user_do", withAdminWare(cont.ServeCreateUserDo)) } func main() { diff --git a/db/model.go b/db/model.go index b812ca8..1e67aa2 100644 --- a/db/model.go +++ b/db/model.go @@ -53,4 +53,5 @@ type User struct { Base Name string `gorm:"not null;unique_index"` Password string + IsAdmin bool } diff --git a/handler/admin.go b/handler/admin.go index 7b40a0d..a553134 100644 --- a/handler/admin.go +++ b/handler/admin.go @@ -4,17 +4,18 @@ import ( "fmt" "net/http" + "github.com/gorilla/sessions" "github.com/jinzhu/gorm" "github.com/sentriz/gonic/db" + "github.com/sentriz/gonic/handler/utilities" ) func (c *Controller) ServeLogin(w http.ResponseWriter, r *http.Request) { - session, _ := c.SStore.Get(r, "gonic") - renderTemplate(w, r, session, "login", &templateData{}) + renderTemplate(w, r, "login", nil) } func (c *Controller) ServeLoginDo(w http.ResponseWriter, r *http.Request) { - session, _ := c.SStore.Get(r, "gonic") + session := r.Context().Value("session").(*sessions.Session) username := r.FormValue("username") password := r.FormValue("password") if username == "" || password == "" { @@ -37,24 +38,43 @@ func (c *Controller) ServeLoginDo(w http.ResponseWriter, r *http.Request) { } func (c *Controller) ServeLogout(w http.ResponseWriter, r *http.Request) { - session, _ := c.SStore.Get(r, "gonic") + session := r.Context().Value("session").(*sessions.Session) 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) + renderTemplate(w, r, "home", &data) +} + +func (c *Controller) ServeChangeOwnPassword(w http.ResponseWriter, r *http.Request) { + renderTemplate(w, r, "change_own_password", nil) +} + +func (c *Controller) ServeChangeOwnPasswordDo(w http.ResponseWriter, r *http.Request) { + session := r.Context().Value("session").(*sessions.Session) + passwordOne := r.FormValue("password_one") + passwordTwo := r.FormValue("password_two") + err := utilities.ValidatePasswords(passwordOne, passwordTwo) + if err != nil { + session.AddFlash(err.Error()) + session.Save(r, w) + http.Redirect(w, r, r.Header.Get("Referer"), 302) + return + } + user, _ := session.Values["user"].(*db.User) + user.Password = passwordOne + c.DB.Save(user) + http.Redirect(w, r, "/admin/home", 303) } 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) @@ -68,60 +88,56 @@ func (c *Controller) ServeChangePassword(w http.ResponseWriter, r *http.Request) } var data templateData data.SelectedUser = &user - renderTemplate(w, r, session, "change_password", &data) + renderTemplate(w, r, "change_password", &data) } func (c *Controller) ServeChangePasswordDo(w http.ResponseWriter, r *http.Request) { - session, _ := c.SStore.Get(r, "gonic") + session := r.Context().Value("session").(*sessions.Session) 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") + passwordOne := r.FormValue("password_one") + passwordTwo := r.FormValue("password_two") + err := utilities.ValidatePasswords(passwordOne, passwordTwo) + if err != nil { + session.AddFlash(err.Error()) 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 + user.Password = passwordOne 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{}) + renderTemplate(w, r, "create_user", nil) } func (c *Controller) ServeCreateUserDo(w http.ResponseWriter, r *http.Request) { - session, _ := c.SStore.Get(r, "gonic") + session := r.Context().Value("session").(*sessions.Session) 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") + err := utilities.ValidateUsername(username) + if err != nil { + session.AddFlash(err.Error()) 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") + passwordOne := r.FormValue("password_one") + passwordTwo := r.FormValue("password_two") + err = utilities.ValidatePasswords(passwordOne, passwordTwo) + if err != nil { + session.AddFlash(err.Error()) session.Save(r, w) http.Redirect(w, r, r.Header.Get("Referer"), 302) return } user := db.User{ Name: username, - Password: password_one, + Password: passwordOne, } - err := c.DB.Create(&user).Error + err = c.DB.Create(&user).Error if err != nil { session.AddFlash(fmt.Sprintf( "could not create user `%s`: %v", username, err, diff --git a/handler/handler.go b/handler/handler.go index cbc0bd9..c1df8e2 100644 --- a/handler/handler.go +++ b/handler/handler.go @@ -32,22 +32,26 @@ func init() { filepath.Join("templates", "user.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", "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"), )) + templates["change_own_password"] = template.Must(template.ParseFiles( + filepath.Join("templates", "layout.tmpl"), + filepath.Join("templates", "user.tmpl"), + filepath.Join("templates", "pages", "change_own_password.tmpl"), + )) + templates["create_user"] = template.Must(template.ParseFiles( + filepath.Join("templates", "layout.tmpl"), + filepath.Join("templates", "user.tmpl"), + filepath.Join("templates", "pages", "create_user.tmpl"), + )) } type Controller struct { - DB *gorm.DB - SStore *gormstore.Store - Templates map[string]*template.Template + DB *gorm.DB + SStore *gormstore.Store } type templateData struct { @@ -131,15 +135,16 @@ func respondError(w http.ResponseWriter, r *http.Request, } func renderTemplate(w http.ResponseWriter, r *http.Request, - s *sessions.Session, name string, data *templateData) { - // take the flashes from the session and add to template - data.Flashes = s.Flashes() - s.Save(r, w) - // 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) + name string, data *templateData) { + session := r.Context().Value("session").(*sessions.Session) + if data == nil { + data = &templateData{} + } + data.Flashes = session.Flashes() + session.Save(r, w) + user, ok := session.Values["user"].(*db.User) + if ok { + data.User = user } err := templates[name].ExecuteTemplate(w, "layout", data) if err != nil { diff --git a/handler/middleware.go b/handler/middleware.go index 734d0a7..4bf73af 100644 --- a/handler/middleware.go +++ b/handler/middleware.go @@ -1,12 +1,14 @@ package handler import ( + "context" "crypto/md5" "encoding/hex" "fmt" "log" "net/http" + "github.com/gorilla/sessions" "github.com/jinzhu/gorm" "github.com/sentriz/gonic/db" ) @@ -99,16 +101,41 @@ func (c *Controller) WithCORS(next http.HandlerFunc) http.HandlerFunc { } } -func (c *Controller) WithValidSession(next http.HandlerFunc) http.HandlerFunc { +func (c *Controller) WithSession(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 { + withSession := context.WithValue(r.Context(), "session", session) + next.ServeHTTP(w, r.WithContext(withSession)) + } +} + +func (c *Controller) WithUserSession(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + // session exists at this point + session := r.Context().Value("session").(*sessions.Session) + _, ok := session.Values["user"] + if !ok { session.AddFlash("you are not authenticated") session.Save(r, w) http.Redirect(w, r, "/admin/login", 303) return } + withSession := context.WithValue(r.Context(), "session", session) + next.ServeHTTP(w, r.WithContext(withSession)) + } +} + +func (c *Controller) WithAdminSession(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + // session and user exist at this point + session := r.Context().Value("session").(*sessions.Session) + user := session.Values["user"].(*db.User) + if !user.IsAdmin { + session.AddFlash("you are not an admin") + session.Save(r, w) + http.Redirect(w, r, "/admin/login", 303) + return + } next.ServeHTTP(w, r) } } diff --git a/handler/utilities/utilities.go b/handler/utilities/utilities.go new file mode 100644 index 0000000..d9371f8 --- /dev/null +++ b/handler/utilities/utilities.go @@ -0,0 +1,20 @@ +package utilities + +import "fmt" + +func ValidateUsername(username string) error { + if username == "" { + return fmt.Errorf("please enter the username") + } + return nil +} + +func ValidatePasswords(pOne, pTwo string) error { + if pOne == "" || pTwo == "" { + return fmt.Errorf("please enter the password twice") + } + if !(pOne == pTwo) { + return fmt.Errorf("the two passwords entered were not the same") + } + return nil +} diff --git a/static/stylesheets/main.css b/static/stylesheets/main.css index f1359e3..3df79d1 100644 --- a/static/stylesheets/main.css +++ b/static/stylesheets/main.css @@ -73,7 +73,6 @@ form input[type=password], form input[type=text] { } #footer { - background-color: #fdad1b0d; border-top: 1px solid; text-align: right; } diff --git a/templates/pages/change_own_password.tmpl b/templates/pages/change_own_password.tmpl new file mode 100644 index 0000000..e4b7f64 --- /dev/null +++ b/templates/pages/change_own_password.tmpl @@ -0,0 +1,14 @@ +{{ define "title" }}home{{ end }} + +{{ define "user" }} +