From b3599fbe0eaf9cf13208698504d9ea29954c6d4d Mon Sep 17 00:00:00 2001
From: Immanuel Onyeka <immanuel@onyeka.ca>
Date: Wed, 16 Aug 2023 16:07:22 -0400
Subject: [PATCH] Add password reset endpoint

---
 components/settings.vue | 23 +++++++----------
 skouter.go              | 57 +++++++++++++++++++++++++++++++++++++++--
 2 files changed, 64 insertions(+), 16 deletions(-)

diff --git a/components/settings.vue b/components/settings.vue
index f91de0f..e38cc43 100644
--- a/components/settings.vue
+++ b/components/settings.vue
@@ -42,16 +42,16 @@
 </section>
 
 <section class="form inputs special">
-<h3>Login</h3>
+<h3>Reset Password</h3>
 <label for="">Old Password</label>
-<input type="text" v-model="oldPass">
+<input type="password" v-model="oldPass">
 <label for="">New Password</label>
-<input type="text" v-model="newPass">
+<input type="password" v-model="newPass">
 <label for="">Confirm Password</label>
-<input type="text" v-model="confirmPass">
+<input type="password" v-model="confirmPass">
 <button
 :disabled="!(oldPass && newPass && confirmPass)"
-@click="check">Save</button>
+@click="changePassword">Save</button>
 <label class="error">{{passError}}</label>
 </section>
 
@@ -190,18 +190,12 @@ function saveProfile() {
 }
 
 function changePassword(f) {
-    const validTypes = ['image/jpeg', 'image/png']
-    
-    if (!validTypes.includes(f.type)) {
-        letterheadError.value = 'Image must be JPEG of PNG format'
-        return
-    }
         fetch(`/api/user/password`,
 	    {method: 'POST',
 	        body: JSON.stringify({
-	            oldPass: oldPass.value,
-	            newPass: newPass.value,
-	            confirmPass: confirmPass.value,
+	            old: oldPass.value,
+	            new: newPass.value,
+	            confirm: confirmPass.value,
 	        }),
     		headers: {
         	"Accept": "application/json",
@@ -213,6 +207,7 @@ function changePassword(f) {
                     oldPass.value = ""
                     newPass.value = ""
                     confirmPass.value = ""
+                    passError.value = ""
                 })        
             } else {
                 resp.text().then(e => passError.value = e)
diff --git a/skouter.go b/skouter.go
index 82456ae..8711b76 100644
--- a/skouter.go
+++ b/skouter.go
@@ -133,8 +133,8 @@ type Result struct {
 }
 
 type Estimate struct {
-	Id		int 	`json:"id"`
-	User		int 	`json:"user"`
+	Id	int 	`json:"id"`
+	User	int 	`json:"user"`
 	Borrower	Borrower 	`json:"borrower"`
 	Transaction	string 	`json:"transaction"`
 	Price		int 	`json:"price"`
@@ -146,6 +146,13 @@ type Estimate struct {
 	Results 	[]Result 	`json:"results"`
 }
 
+type Password struct {
+	Old	string	`json:"old"`
+	New	string	`json:"new"`
+}
+
+type Endpoint func (http.ResponseWriter, *sql.DB, *http.Request)
+
 var (
     regexen = make(map[string]*regexp.Regexp)
     relock  sync.Mutex
@@ -968,6 +975,47 @@ func createUser(w http.ResponseWriter, db *sql.DB, r *http.Request) {
 	json.NewEncoder(w).Encode(user)
 }
 
+func checkPassword(db *sql.DB, id int, pass string) bool {
+	var p string
+	query := `SELECT
+	password
+	FROM user WHERE user.id = ? AND password = sha2(?, 256)
+	`
+	row := db.QueryRow(query, id, pass)
+	err := row.Scan(&p)
+	if err != nil { return false }
+	
+	return true
+}
+
+func setPassword(db *sql.DB, id int, pass string) error {
+	query := `UPDATE user
+	SET password = sha2(?, 256)
+	WHERE user.id = ?
+	`
+	_, err := db.Exec(query, pass, id)
+	if err != nil { return errors.New("Could not insert password.") }
+	
+	return nil
+}
+
+func changePassword(w http.ResponseWriter, db *sql.DB, r *http.Request) {
+		var pass Password
+		claim, err := getClaims(r)
+		err = json.NewDecoder(r.Body).Decode(&pass)
+		if err != nil { http.Error(w, "Bad fields.", 422); return }
+		fmt.Println(pass)
+		
+		if checkPassword(db, claim.Id, pass.Old) {
+			err = setPassword(db, claim.Id, pass.New)
+		} else {
+			http.Error(w, "Incorrect old password.", 401)
+			return
+		}
+		
+		if err != nil { http.Error(w, err.Error(), 500); return }
+}
+
 func fetchAvatar(db *sql.DB, user int) ( []byte, error ) {
 	var img []byte
 	var query string
@@ -1711,6 +1759,7 @@ func clipLetterhead(w http.ResponseWriter, db *sql.DB, r *http.Request) {
 	if err != nil { http.Error(w, "Error encoding.", 500); return }
 }
 
+
 func api(w http.ResponseWriter, r *http.Request) {
 	var args []string
 
@@ -1777,6 +1826,10 @@ func api(w http.ResponseWriter, r *http.Request) {
 	r.Method == http.MethodPost &&
 	guard(r, 1):
 		setLetterhead(w, db, r)
+	case match(p, "/api/user/password", &args) &&
+	r.Method == http.MethodPost &&
+	guard(r, 1):
+		changePassword(w, db, r)
 	case match(p, "/api/fees", &args) &&
 	r.Method == http.MethodGet &&
 	guard(r, 1):