Author | Jakob Wakeling <[email protected]> |
Date | 2023-09-10 05:23:55 |
Commit | 68784b660505ad75a691be1e80ac2cc82788a37e |
Parent | d0994fe1eb5d3b189dd5382da841a23a2d54550e |
Implement password change functionality
Diffstat
M | res/style.css | | | 2 | +- |
M | res/user/edit.html | | | 39 | +++++++++++++++++++++++++-------------- |
M | src/user.go | | | 20 | ++++++++++++++++++-- |
M | src/user/edit.go | | | 77 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------- |
4 files changed, 100 insertions, 38 deletions
diff --git a/res/style.css b/res/style.css index cb7f3a6..0bd3552 100644 --- a/res/style.css +++ b/res/style.css @@ -19,7 +19,7 @@ table td.line { tab-size: 4; white-space: pre; } table input { border: 2px solid #333333; border-radius: 3px; background-color: #111111; padding: 2px; } table input[type="text"] { color: #888888; width: 24em; } table input[type="password"] { color: #888888; width: 24em; } -table input[type="submit"] { color: #FF7E00; width: 6em; } +table input[type="submit"] { color: #FF7E00; padding: 2px 1.6em; } table select { border: 2px solid #333333; border-radius: 3px; background-color: #111111; color: #888888; padding: 2px; diff --git a/res/user/edit.html b/res/user/edit.html index af2bb1b..2f8ee56 100644 --- a/res/user/edit.html +++ b/res/user/edit.html @@ -6,27 +6,38 @@ <h1>{{.Title}}</h1><hr> <form action="/user/edit" method="post"> <table> + <tr><td><label for="username">Username</label></td></tr> + <tr><td><input type="text" name="username" value="{{.Form.Name}}" spellcheck="false"></td></tr> + <tr><td><label for="fullname">Full Name</label></td></tr> + <tr><td><input type="text" name="fullname" value="{{.Form.FullName}}" spellcheck="false"></td></tr> <tr> - <td style="text-align: right;"><label for="username">Username</label></td> - <td><input type="text" name="username" value="{{.Form.Name}}" spellcheck="false"></td> - </tr> - <tr> - <td style="text-align: right;"><label for="fullname">Full Name</label></td> - <td><input type="text" name="fullname" value="{{.Form.FullName}}" spellcheck="false"></td> - </tr> - <tr> - <td></td> <td> - <input type="submit" value="Update"> - <a href="/" style="color: inherit;">Cancel</a> + <input type="submit" name="submit" value="Update"> + <!-- <a href="/" style="color: inherit;">Cancel</a> --> + <span style="color: #AA0000">{{.MessageA}}</span> </td> </tr> + <!-- <tr><td style="color: #AA0000">{{.MessageA}}</td></tr> --> + </table> + </form><hr> + <form action="/user/edit" method="post"> + <table> + <tr><td><label for="password">Current Password</label></td></tr> + <tr><td><input type="password" name="password"></td></tr> + <tr><td><label for="new_password">New Password</label></td></tr> + <tr><td><input type="password" name="new_password"></td></tr> + <tr><td><label for="confirm_password">Confirm New Password</label></td></tr> + <tr><td><input type="password" name="confirm_password"></td></tr> <tr> - <td></td> - <td style="color: #AA0000">{{.Message}}</td> + <td> + <input type="submit" name="submit" value="Update Password"> + <!-- <a href="/" style="color: inherit;">Cancel</a> --> + <span style="color: #AA0000">{{.MessageB}}</span> + </td> </tr> + <!-- <tr><td style="color: #AA0000">{{.MessageB}}</td></tr> --> </table> - </form> + </form><hr> <table> <tr><td style="text-align: right;"><span>ID:</span></td><td><span>{{.Form.Id}}</span></td></tr> </table> diff --git a/src/user.go b/src/user.go index e7688df..1ddf752 100644 --- a/src/user.go +++ b/src/user.go @@ -66,8 +66,8 @@ func GetUser(id int64) (*User, error) { u := User{} if err := db.QueryRow( - "SELECT id, name, name_full, is_admin FROM users WHERE id = ?", id, - ).Scan(&u.Id, &u.Name, &u.FullName, &u.IsAdmin); err != nil { + "SELECT id, name, name_full, pass, pass_algo, salt, is_admin FROM users WHERE id = ?", id, + ).Scan(&u.Id, &u.Name, &u.FullName, &u.Pass, &u.PassAlgo, &u.Salt, &u.IsAdmin); err != nil { if !errors.Is(err, sql.ErrNoRows) { return nil, fmt.Errorf("[SELECT:user] %w", err) } else { @@ -115,3 +115,19 @@ func UpdateUser(uid int64, user User) error { return nil } + +func UpdatePassword(uid int64, password string) error { + salt, err := Salt() + if err != nil { + return err + } + + if _, err := db.Exec( + "UPDATE users SET pass = ?, pass_algo = ?, salt = ? WHERE id = ?", + Hash(password, salt), "argon2", salt, uid, + ); err != nil { + return err + } + + return nil +} diff --git a/src/user/edit.go b/src/user/edit.go index 2c22694..909957c 100644 --- a/src/user/edit.go +++ b/src/user/edit.go @@ -1,6 +1,7 @@ package user import ( + "bytes" "fmt" "log" "net/http" @@ -28,7 +29,7 @@ func HandleEdit(w http.ResponseWriter, r *http.Request) { } data := struct { - Title, Message string + Title, MessageA, MessageB string Form struct{ Id, Name, FullName string } }{ @@ -40,31 +41,65 @@ func HandleEdit(w http.ResponseWriter, r *http.Request) { data.Form.FullName = user.FullName if r.Method == http.MethodPost { - data.Form.Name = r.FormValue("username") - data.Form.FullName = r.FormValue("fullname") + if r.FormValue("submit") == "Update" { + data.Form.Name = r.FormValue("username") + data.Form.FullName = r.FormValue("fullname") - if data.Form.Name == "" { - data.Message = "Username cannot be empty" - } else if slices.Contains(reserved, data.Form.Name) && uid != 0 { - data.Message = "Username \"" + data.Form.Name + "\" is reserved" - } else if exists, err := goit.UserExists(data.Form.Name); err != nil { - log.Println("[/user/edit]", err.Error()) - goit.HttpError(w, http.StatusInternalServerError) - return - } else if exists && data.Form.Name != user.Name { - data.Message = "Username \"" + data.Form.Name + "\" is taken" - } else if err := goit.UpdateUser(user.Id, goit.User{ - Name: data.Form.Name, FullName: data.Form.FullName, - }); err != nil { - log.Println("[/user/edit]", err.Error()) - goit.HttpError(w, http.StatusInternalServerError) - return + if data.Form.Name == "" { + data.MessageA = "Username cannot be empty" + } else if slices.Contains(reserved, data.Form.Name) && uid != 0 { + data.MessageA = "Username \"" + data.Form.Name + "\" is reserved" + } else if exists, err := goit.UserExists(data.Form.Name); err != nil { + log.Println("[/user/edit]", err.Error()) + goit.HttpError(w, http.StatusInternalServerError) + return + } else if exists && data.Form.Name != user.Name { + data.MessageA = "Username \"" + data.Form.Name + "\" is taken" + } else if err := goit.UpdateUser(user.Id, goit.User{ + Name: data.Form.Name, FullName: data.Form.FullName, + }); err != nil { + log.Println("[/user/edit]", err.Error()) + goit.HttpError(w, http.StatusInternalServerError) + return + } else { + http.Redirect(w, r, "/user/edit?m=a", http.StatusFound) + return + } + } else if r.FormValue("submit") == "Update Password" { + password := r.FormValue("password") + newPassword := r.FormValue("new_password") + confirmPassword := r.FormValue("confirm_password") + + if password == "" { + data.MessageB = "Current Password cannot be empty" + } else if newPassword == "" { + data.MessageB = "New Password cannot be empty" + } else if confirmPassword == "" { + data.MessageB = "Confirm New Password cannot be empty" + } else if newPassword != confirmPassword { + data.MessageB = "New Password and Confirm Password do not match" + } else if !bytes.Equal(goit.Hash(password, user.Salt), user.Pass) { + data.MessageB = "Password incorrect" + } else if err := goit.UpdatePassword(user.Id, newPassword); err != nil { + log.Println("[/user/edit]", err.Error()) + goit.HttpError(w, http.StatusInternalServerError) + return + } else { + http.Redirect(w, r, "/user/edit?m=b", http.StatusFound) + return + } } else { - http.Redirect(w, r, "/user/edit", http.StatusFound) - return + data.MessageA = "Invalid submit value" } } + switch r.FormValue("m") { + case "a": + data.MessageA = "User updated successfully" + case "b": + data.MessageB = "Password updated successfully" + } + if err := goit.Tmpl.ExecuteTemplate(w, "user/edit", data); err != nil { log.Println("[/user/edit]", err.Error()) }