diff --git a/auth/auth.go b/auth/auth.go index 8b4230e..be689d3 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -91,6 +91,25 @@ func (a *Authorizer) Login(rw http.ResponseWriter, req *http.Request, username, return nil } +func (a *Authorizer) RegenSecret(user User, w http.ResponseWriter, r *http.Request) error { + cookie, err := a.cookiejar.Get(r, a.cookieName) + if err != nil { + return err + } + // genereate secret + b, err := bcrypt.GenerateFromPassword([]byte(user.GetHash()), a.cost) + if err != nil { + return err + } + cookie.Values["secret"] = string(b) + + err = cookie.Save(r, w) + if err != nil { + return err + } + return nil +} + // Logout remove cookie info func (a *Authorizer) Logout(rw http.ResponseWriter, req *http.Request) error { cookie, err := a.cookiejar.Get(req, a.cookieName) diff --git a/main.go b/main.go index 3fdbb88..840e3c3 100644 --- a/main.go +++ b/main.go @@ -1,8 +1,6 @@ package main import ( - "os" - "gitlab.quimbo.fr/odwrtw/canape-sql/auth" "gitlab.quimbo.fr/odwrtw/canape-sql/movies" "gitlab.quimbo.fr/odwrtw/canape-sql/users" @@ -28,8 +26,7 @@ func main() { pgdsn := "postgres://test:test@127.0.0.1:5432/test?sslmode=disable" db, err := sqlx.Connect("postgres", pgdsn) if err != nil { - log.Error(err) - os.Exit(1) + log.Panic(err) } uBackend := &UserBackend{db} authorizer := auth.New(uBackend, "peeper", "cookieName", "cookieKey", 10) @@ -37,16 +34,16 @@ func main() { env := web.NewEnv(db, authorizer, log, "./templates") authMiddleware := auth.NewMiddleware(env.Auth) - env.Handle("users.login", "/users/login", users.LoginHandler) - env.Handle("users.logout", "/users/logout", users.LogoutHandler) - env.HandleRole("users.details", "/users/details", users.DetailsHandler, users.UserRole) + env.Handle("users.login", "/users/login", users.Login) + env.Handle("users.logout", "/users/logout", users.Logout) + env.HandleRole("users.details", "/users/details", users.Details, users.UserRole) + env.HandleRole("users.edit", "/users/edit", users.Edit, users.UserRole) - env.HandleRole("movies.polochon", "/", movies.PolochonMovies, users.UserRole) + env.HandleRole("movies.polochon", "/movies/polochon", movies.FromPolochon, users.UserRole) err = env.SetLoginRoute("users.login") if err != nil { - log.Error(err) - os.Exit(1) + log.Panic(err) } n := negroni.Classic() diff --git a/movies/handlers.go b/movies/handlers.go index 9465833..40ac01a 100644 --- a/movies/handlers.go +++ b/movies/handlers.go @@ -14,7 +14,7 @@ import ( "gitlab.quimbo.fr/odwrtw/papi" ) -func PolochonMovies(env *web.Env, w http.ResponseWriter, r *http.Request) error { +func FromPolochon(env *web.Env, w http.ResponseWriter, r *http.Request) error { v := auth.GetCurrentUser(r) user, ok := v.(*users.User) diff --git a/templates/users/details.tmpl b/templates/users/details.tmpl new file mode 100644 index 0000000..557b892 --- /dev/null +++ b/templates/users/details.tmpl @@ -0,0 +1,26 @@ +
+
+ +
+

{{ $.Data.user.Name }}'s informations

+
+ +
+
+ Polochon URL +
+
{{ $.Data.polochon.URL }}
+
+
+
+ Polochon token +
+
{{ $.Data.polochon.Token }}
+
+ +
+ Edit +
+
+
+
diff --git a/templates/users/edit.tmpl b/templates/users/edit.tmpl new file mode 100644 index 0000000..df59aac --- /dev/null +++ b/templates/users/edit.tmpl @@ -0,0 +1,43 @@ + +
+
+
+

Edit user

+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ + +
+ +
+ + +
+ +
+ + Cancel +
+ +
+ +
+
+
+ + diff --git a/users/handlers.go b/users/handlers.go index bc677b1..b3e5796 100644 --- a/users/handlers.go +++ b/users/handlers.go @@ -5,12 +5,15 @@ import ( "net/http" "github.com/gorilla/Schema" + "github.com/kr/pretty" "gitlab.quimbo.fr/odwrtw/canape-sql/auth" + "gitlab.quimbo.fr/odwrtw/canape-sql/config" "gitlab.quimbo.fr/odwrtw/canape-sql/web" ) -func LoginHandler(e *web.Env, w http.ResponseWriter, r *http.Request) error { +// Login login user +func Login(e *web.Env, w http.ResponseWriter, r *http.Request) error { if r.Method == "GET" { return e.Rends(w, r, "users/login") } @@ -53,21 +56,101 @@ func LoginHandler(e *web.Env, w http.ResponseWriter, r *http.Request) error { return nil } -func LogoutHandler(e *web.Env, w http.ResponseWriter, r *http.Request) error { +// Logout just logout +func Logout(e *web.Env, w http.ResponseWriter, r *http.Request) error { e.Auth.Logout(w, r) route := e.GetLoginRouteGetter()() http.Redirect(w, r, route, http.StatusTemporaryRedirect) return nil } -func DetailsHandler(e *web.Env, w http.ResponseWriter, r *http.Request) error { - u := auth.GetCurrentUser(r) - if u == nil { - return nil - } - _, ok := u.(*User) +// Details show user details +func Details(env *web.Env, w http.ResponseWriter, r *http.Request) error { + v := auth.GetCurrentUser(r) + user, ok := v.(*User) if !ok { - return fmt.Errorf("Invalid user type") + return fmt.Errorf("invalid user type") } - return nil + + var polochonConfig config.UserPolochon + err := user.GetConfig("polochon", &polochonConfig) + if err != nil { + return err + } + + web.SetData(r, "user", user) + web.SetData(r, "polochon", polochonConfig) + return env.Rends(w, r, "users/details") +} + +// Edit allow editing user info and configuration +func Edit(env *web.Env, w http.ResponseWriter, r *http.Request) error { + v := auth.GetCurrentUser(r) + user, ok := v.(*User) + if !ok { + return fmt.Errorf("invalid user type") + } + var polochonConfig config.UserPolochon + err := user.GetConfig("polochon", &polochonConfig) + if err != nil { + return err + } + + if r.Method == "GET" { + web.SetData(r, "user", user) + web.SetData(r, "polochon", polochonConfig) + return env.Rends(w, r, "users/edit") + } + + type editForm struct { + PolochonURL string + PolochonToken string + Password string + PasswordVerify string + } + + err = r.ParseForm() + if err != nil { + return err + } + + form := new(editForm) + decoder := schema.NewDecoder() + err = decoder.Decode(form, r.PostForm) + if err != nil { + return err + } + + polochonConfig.URL = form.PolochonURL + polochonConfig.Token = form.PolochonToken + + err = user.SetConfig("polochon", polochonConfig) + if err != nil { + return err + } + + if form.Password != "" || form.PasswordVerify != "" { + if form.Password != form.PasswordVerify { + // TODO: manage form error + } + user.Hash, err = env.Auth.GenHash(form.Password) + if err != nil { + return err + } + } + + err = user.Update(env.Database) + if err != nil { + pretty.Println(err) + return err + } + + err = env.Auth.RegenSecret(user, w, r) + if err != nil { + return err + } + + web.SetData(r, "user", user) + web.SetData(r, "polochon", polochonConfig) + return env.Rends(w, r, "users/edit") } diff --git a/users/users.go b/users/users.go index 6b590d7..3069c4f 100644 --- a/users/users.go +++ b/users/users.go @@ -11,9 +11,9 @@ import ( ) const ( - addUserQuery = `INSERT INTO users (name, hash, admin) VALUES ($1, $2, $3) RETURNING id;` + addUserQuery = `INSERT INTO users (name, hash, admin, rawconfig) VALUES ($1, $2, $3, $4) RETURNING id;` getUserQuery = `SELECT * FROM users WHERE name=$1;` - updateUserQuery = `UPDATE users SET name=:name, hash=:hash, admin=:admin RETURNING *;` + updateUserQuery = `UPDATE users SET name=:name, hash=:hash, admin=:admin, rawconfig=:rawconfig WHERE id=:id RETURNING *;` deleteUseQuery = `DELETE FROM users WHERE id=:id;` addTokenQuery = `INSERT INTO tokens (value, user_id) VALUES ($1, $2) RETURNING id;` @@ -97,7 +97,7 @@ func Get(q sqlx.Queryer, name string) (*User, error) { // Add user to database or raises an error func (u *User) Add(q sqlx.Queryer) error { var id string - err := q.QueryRowx(addUserQuery, u.Name, u.Hash, u.Admin).Scan(&id) + err := q.QueryRowx(addUserQuery, u.Name, u.Hash, u.Admin, u.RawConfig).Scan(&id) if err != nil { return err } diff --git a/web/env.go b/web/env.go index 1871d97..8724c63 100644 --- a/web/env.go +++ b/web/env.go @@ -34,20 +34,28 @@ type Env struct { // NewEnv returns a new *Env func NewEnv(db *sqlx.DB, auth *auth.Authorizer, log *logrus.Entry, templatesDir string) *Env { - return &Env{ + e := &Env{ Database: db, Log: log, Router: mux.NewRouter(), - Render: render.New(render.Options{ - Directory: templatesDir, - Layout: "layout", - Funcs: tmplFuncs, - DisableHTTPErrorRendering: true, - RequirePartials: true, - }), - Auth: auth, - Mode: ProductionMode, + Auth: auth, + Mode: ProductionMode, } + + tmplFuncs = append(tmplFuncs, map[string]interface{}{ + "URL": func(name string, pairs ...string) (string, error) { + return e.GetURL(name, pairs...) + }, + }) + + e.Render = render.New(render.Options{ + Directory: templatesDir, + Layout: "layout", + Funcs: tmplFuncs, + DisableHTTPErrorRendering: true, + RequirePartials: true, + }) + return e } func (e *Env) SetLoginRoute(name string) error {