Allow edit user info

This commit is contained in:
Nicolas Duhamel 2016-04-30 16:14:28 +02:00
parent b4bb23056b
commit 0e2733086e
8 changed files with 210 additions and 34 deletions

View File

@ -91,6 +91,25 @@ func (a *Authorizer) Login(rw http.ResponseWriter, req *http.Request, username,
return nil 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 // Logout remove cookie info
func (a *Authorizer) Logout(rw http.ResponseWriter, req *http.Request) error { func (a *Authorizer) Logout(rw http.ResponseWriter, req *http.Request) error {
cookie, err := a.cookiejar.Get(req, a.cookieName) cookie, err := a.cookiejar.Get(req, a.cookieName)

17
main.go
View File

@ -1,8 +1,6 @@
package main package main
import ( import (
"os"
"gitlab.quimbo.fr/odwrtw/canape-sql/auth" "gitlab.quimbo.fr/odwrtw/canape-sql/auth"
"gitlab.quimbo.fr/odwrtw/canape-sql/movies" "gitlab.quimbo.fr/odwrtw/canape-sql/movies"
"gitlab.quimbo.fr/odwrtw/canape-sql/users" "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" pgdsn := "postgres://test:test@127.0.0.1:5432/test?sslmode=disable"
db, err := sqlx.Connect("postgres", pgdsn) db, err := sqlx.Connect("postgres", pgdsn)
if err != nil { if err != nil {
log.Error(err) log.Panic(err)
os.Exit(1)
} }
uBackend := &UserBackend{db} uBackend := &UserBackend{db}
authorizer := auth.New(uBackend, "peeper", "cookieName", "cookieKey", 10) authorizer := auth.New(uBackend, "peeper", "cookieName", "cookieKey", 10)
@ -37,16 +34,16 @@ func main() {
env := web.NewEnv(db, authorizer, log, "./templates") env := web.NewEnv(db, authorizer, log, "./templates")
authMiddleware := auth.NewMiddleware(env.Auth) authMiddleware := auth.NewMiddleware(env.Auth)
env.Handle("users.login", "/users/login", users.LoginHandler) env.Handle("users.login", "/users/login", users.Login)
env.Handle("users.logout", "/users/logout", users.LogoutHandler) env.Handle("users.logout", "/users/logout", users.Logout)
env.HandleRole("users.details", "/users/details", users.DetailsHandler, users.UserRole) 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") err = env.SetLoginRoute("users.login")
if err != nil { if err != nil {
log.Error(err) log.Panic(err)
os.Exit(1)
} }
n := negroni.Classic() n := negroni.Classic()

View File

@ -14,7 +14,7 @@ import (
"gitlab.quimbo.fr/odwrtw/papi" "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) v := auth.GetCurrentUser(r)
user, ok := v.(*users.User) user, ok := v.(*users.User)

View File

@ -0,0 +1,26 @@
<div class="container">
<div id="user-details" class="content-fluid">
<div class="col-md-6 col-md-offset-3 col-xs-12">
<h2>{{ $.Data.user.Name }}'s informations</h2>
<hr>
<div class="panel panel-info">
<div class="panel-heading">
Polochon URL
</div>
<div class="panel-body">{{ $.Data.polochon.URL }}</div>
</div>
<div class="panel panel-info">
<div class="panel-heading">
Polochon token
</div>
<div class="panel-body">{{ $.Data.polochon.Token }}</div>
</div>
<div>
<a class="btn btn-default pull-left" href="{{ URL "users.edit" }}">Edit</a>
</div>
</div>
</div>
</div>

43
templates/users/edit.tmpl Normal file
View File

@ -0,0 +1,43 @@
<div class="container">
<div class="content-fluid">
<div class="col-md-6 col-md-offset-3 col-xs-12">
<h2>Edit user</h2>
<hr>
<form accept-charset="UTF-8" action="{{ URL "users.edit" }}" method="POST" class="form-horizontal" id="user">
<div class="form-group">
<label class="control-label" for="PolochonURL">Polochon URL</label>
<input autofocus="autofocus" class="form-control" name="PolochonURL" type="text" value="{{ $.Data.polochon.URL }}">
</div>
<div class="form-group">
<label class="control-label" for="PolochonToken">Polochon token</label>
<input autofocus="autofocus" class="form-control" name="PolochonToken" type="text" value="{{ $.Data.polochon.Token }}" >
</div>
<hr>
<div class="form-group">
<label class="control-label" for="Password">Password</label>
<input autocomplete="off" class="form-control" name="Password" type="password" value="">
</div>
<div class="form-group">
<label class="control-label" for="PasswordVerify">Confirm Password</label>
<input autocomplete="off" class="form-control" name="PasswordVerify" type="password" value="">
</div>
<div>
<input class="btn btn-primary pull-right" type="submit" value="Update">
<a class="btn btn-default pull-left" href="{{ URL "users.details" }}">Cancel</a>
</div>
</form>
</div>
</div>
</div>

View File

@ -5,12 +5,15 @@ import (
"net/http" "net/http"
"github.com/gorilla/Schema" "github.com/gorilla/Schema"
"github.com/kr/pretty"
"gitlab.quimbo.fr/odwrtw/canape-sql/auth" "gitlab.quimbo.fr/odwrtw/canape-sql/auth"
"gitlab.quimbo.fr/odwrtw/canape-sql/config"
"gitlab.quimbo.fr/odwrtw/canape-sql/web" "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" { if r.Method == "GET" {
return e.Rends(w, r, "users/login") 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 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) e.Auth.Logout(w, r)
route := e.GetLoginRouteGetter()() route := e.GetLoginRouteGetter()()
http.Redirect(w, r, route, http.StatusTemporaryRedirect) http.Redirect(w, r, route, http.StatusTemporaryRedirect)
return nil return nil
} }
func DetailsHandler(e *web.Env, w http.ResponseWriter, r *http.Request) error { // Details show user details
u := auth.GetCurrentUser(r) func Details(env *web.Env, w http.ResponseWriter, r *http.Request) error {
if u == nil { v := auth.GetCurrentUser(r)
return nil user, ok := v.(*User)
}
_, ok := u.(*User)
if !ok { 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")
} }

View File

@ -11,9 +11,9 @@ import (
) )
const ( 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;` 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;` deleteUseQuery = `DELETE FROM users WHERE id=:id;`
addTokenQuery = `INSERT INTO tokens (value, user_id) VALUES ($1, $2) RETURNING 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 // Add user to database or raises an error
func (u *User) Add(q sqlx.Queryer) error { func (u *User) Add(q sqlx.Queryer) error {
var id string 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 { if err != nil {
return err return err
} }

View File

@ -34,20 +34,28 @@ type Env struct {
// NewEnv returns a new *Env // NewEnv returns a new *Env
func NewEnv(db *sqlx.DB, auth *auth.Authorizer, log *logrus.Entry, templatesDir string) *Env { func NewEnv(db *sqlx.DB, auth *auth.Authorizer, log *logrus.Entry, templatesDir string) *Env {
return &Env{ e := &Env{
Database: db, Database: db,
Log: log, Log: log,
Router: mux.NewRouter(), Router: mux.NewRouter(),
Render: render.New(render.Options{ Auth: auth,
Directory: templatesDir, Mode: ProductionMode,
Layout: "layout",
Funcs: tmplFuncs,
DisableHTTPErrorRendering: true,
RequirePartials: true,
}),
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 { func (e *Env) SetLoginRoute(name string) error {