Goit

Simple and lightweight Git web server
git clone http://git.omkov.net/Goit
Log | Tree | Refs | README | Download

AuthorJakob Wakeling <[email protected]>
Date2023-09-10 05:23:55
Commit68784b660505ad75a691be1e80ac2cc82788a37e
Parentd0994fe1eb5d3b189dd5382da841a23a2d54550e

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())
 	}