aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThibaut Horel <thibaut.horel@gmail.com>2018-12-31 09:05:29 -0500
committerThibaut Horel <thibaut.horel@gmail.com>2018-12-31 09:05:29 -0500
commit3b49837d167e8770f1054457e172c36109169e51 (patch)
tree9ae844c7851f3f35aea677ebf4a8f80924f76045
parent6ae97fe4d7c4fa1c80571d41c356453199041067 (diff)
downloadid-3b49837d167e8770f1054457e172c36109169e51.tar.gz
Add password change feature
-rw-r--r--main.go38
-rw-r--r--store.go7
-rw-r--r--templates/index.tmpl2
-rw-r--r--templates/login.tmpl5
-rw-r--r--templates/password.tmpl40
5 files changed, 87 insertions, 5 deletions
diff --git a/main.go b/main.go
index 86fd969..10815a5 100644
--- a/main.go
+++ b/main.go
@@ -59,7 +59,6 @@ func (app *App) rootHandler(w http.ResponseWriter, r *http.Request) {
func (app *App) loginHandler(w http.ResponseWriter, r *http.Request) {
if _, ok := app.validate(r); ok {
http.Redirect(w, r, "/", http.StatusSeeOther)
- return
} else if r.Method == http.MethodPost {
username := r.FormValue("username")
hash := md5hex([]byte(r.FormValue("password")))
@@ -95,6 +94,42 @@ func (app *App) loginHandler(w http.ResponseWriter, r *http.Request) {
}
}
+type Flash struct {
+ Type string
+ Value string
+}
+
+func (app *App) passwordHandler(w http.ResponseWriter, r *http.Request) {
+ if s, ok := app.validate(r); !ok {
+ http.Redirect(w, r, "/login", http.StatusSeeOther)
+ return
+ } else if r.Method == http.MethodGet {
+ app.Template.ExecuteTemplate(w, "password.tmpl", Flash{})
+ } else if r.Method == http.MethodPost {
+ password := r.FormValue("password")
+ confirm := r.FormValue("confirm")
+ if password != "" && password == confirm {
+ hash := md5hex([]byte(password))
+ app.ChangePassword(s.UserId, hash)
+ app.Template.ExecuteTemplate(w, "password.tmpl", Flash{
+ "success",
+ "Mot de passe enregistré",
+ })
+ } else {
+ var bad string
+ if password != confirm {
+ bad = "Les deux mots de passe ne coïncident pas"
+ } else if password == "" {
+ bad = "Le mot de passe est vide"
+ }
+ app.Template.ExecuteTemplate(w, "password.tmpl", Flash{
+ "danger",
+ bad,
+ })
+ }
+ }
+}
+
func (app *App) logoutHandler(w http.ResponseWriter, r *http.Request) {
if s, ok := app.validate(r); ok {
// should we save old sessions in another table?
@@ -142,6 +177,7 @@ func main() {
http.HandleFunc("/login", app.loginHandler)
http.HandleFunc("/logout", app.logoutHandler)
http.HandleFunc("/", app.rootHandler)
+ http.HandleFunc("/password", app.passwordHandler)
if err := http.ListenAndServe(*address, logMux(http.DefaultServeMux)); err != nil {
panic(err)
}
diff --git a/store.go b/store.go
index 5e76cfc..d830150 100644
--- a/store.go
+++ b/store.go
@@ -25,6 +25,7 @@ type Store interface {
NewSession(userId int64) *Session
GetUser(name string) (*User, bool)
DeleteSession(id string)
+ ChangePassword(userId int64, hash []byte)
}
type PgStore struct {
@@ -68,10 +69,14 @@ func (store *PgStore) NewSession(userId int64) *Session {
}
func (store *PgStore) DeleteSession(id string) {
- store.Query("DELETE FROM sessions WHERE id = $1", id)
+ store.Exec("DELETE FROM sessions WHERE id = $1", id)
delete(store.sessionCache, id)
}
+func (store *PgStore) ChangePassword(userId int64, hash []byte) {
+ store.Exec("UPDATE users SET password=$1 WHERE id=$2", hash, userId)
+}
+
func (store *PgStore) GetUser(name string) (*User, bool) {
u := &User{Name: name}
row := store.QueryRow(
diff --git a/templates/index.tmpl b/templates/index.tmpl
index 8f6a3b9..d72d562 100644
--- a/templates/index.tmpl
+++ b/templates/index.tmpl
@@ -18,7 +18,7 @@ button:hover {background-color: #1967be; border-color: #1862b5}
</head>
<body>
- <p> <a href="/logout">Logout</a></p>
+ <p> <a href="/password">Changer le mot de passe</a> <a href="/logout">Logout</a></p>
<p>{{.Id}} {{.Created}}</p>
</body>
diff --git a/templates/login.tmpl b/templates/login.tmpl
index a78fdc1..8854695 100644
--- a/templates/login.tmpl
+++ b/templates/login.tmpl
@@ -9,7 +9,8 @@ body{margin: 0 auto; width: 400px; padding-top: 10em; font-family: "Source Sans
form > hr{border: none; border-top: 1px solid #e6e6e6; margin: 1.3em 0;}
form > h4{font-weight: 300; font-size: 19px; margin-bottom: 0} label {font-weight: bold; text-align: right;}
form > div {display: grid; grid-template-columns: 2fr 5fr; grid-gap: 1em 1em; align-items: center;}
-form .alert { background-color: #ff0039; grid-column: 1 / 3; color: white; padding: 0.8em 1em}
+form .danger { background-color: #f8d7da; grid-column: 1 / 3; color: #721c24; padding: 0.8em 1em}
+form .success { background-color: #d4edda; grid-column: 1 / 3; color: #155724; padding: 0.8em 1em}
input, button {font-size: inherit; font-family: inherit; line-height: inherit; padding: 0.8em 1em; border-radius: 0}
input {border: 1px solid #cccccc; transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s}
input:focus {border-color: #66afe9; outline: 0; box-shadow: inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}
@@ -29,7 +30,7 @@ button:hover {background-color: #1967be; border-color: #1862b5}
<label for="password">Mot de passe :</label>
<input type="password" id="password" name="password" placeholder="Mot de passe"/>
- {{ if .Flash }} <div class="alert">{{.Flash}}</div> {{end}}
+ {{ if .Flash }} <div class="danger">{{.Flash}}</div> {{end}}
<button type="submit" name="login">Se Connecter</button>
<input type="hidden" name="next" value="{{.Next}}"/>
</div>
diff --git a/templates/password.tmpl b/templates/password.tmpl
new file mode 100644
index 0000000..8b5e8cf
--- /dev/null
+++ b/templates/password.tmpl
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <title></title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+<style>
+body{margin: 0 auto; width: 600px; padding-top: 10em; font-family: "Source Sans Pro"; font-size: 15px;}
+form > hr{border: none; border-top: 1px solid #e6e6e6; margin: 1.3em 0;}
+form > h4{font-weight: 300; font-size: 19px; margin-bottom: 0} label {font-weight: bold; text-align: right;}
+form > div {display: grid; grid-template-columns: 3fr 5fr; grid-gap: 1em 1em; align-items: center;}
+form .danger { background-color: #f8d7da; grid-column: 1 / 3; color: #721c24; padding: 0.8em 1em}
+form .success { background-color: #d4edda; grid-column: 1 / 3; color: #155724; padding: 0.8em 1em}
+input, button {font-size: inherit; font-family: inherit; line-height: inherit; padding: 0.8em 1em; border-radius: 0}
+input {border: 1px solid #cccccc; transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s}
+input:focus {border-color: #66afe9; outline: 0; box-shadow: inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}
+button {border: 1px solid #2780e3; background-color: #2780e3; color: white; cursor: pointer; grid-column: 2; justify-self: left}
+button:hover {background-color: #1967be; border-color: #1862b5}
+</style>
+ </head>
+ <body>
+
+<form action="/password" role="form" method="post">
+ <h4>Mot de passe</h4>
+ <hr>
+ <div>
+ <label for="password">Nouveau mot de passe :</label>
+ <input type="password" id="password" name="password"/>
+
+ <label for="confirm">Répéter le mot de passe :</label>
+ <input type="password" id="confirm" name="confirm"/>
+
+ {{ if .Value }} <div class="{{.Type}}">{{.Value}}</div> {{end}}
+ <button type="submit" name="save">Enregistrer</button>
+ </div>
+ <hr>
+</form>
+
+ </body>
+</html>