Skip to content

Commit

Permalink
Merge pull request #177 from SCCapstone/142
Browse files Browse the repository at this point in the history
  • Loading branch information
jackiehdinh authored Apr 24, 2023
2 parents 859961f + e39a8c2 commit 4c43a5d
Show file tree
Hide file tree
Showing 4 changed files with 254 additions and 13 deletions.
100 changes: 100 additions & 0 deletions application.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"log"
"math/rand"
"net/http"
"net/smtp"
"os"
"strconv"
"strings"
Expand Down Expand Up @@ -157,6 +158,14 @@ func InitializeRoutes() {
userRoutes.POST("/edit_device", middleware.EnsureLoggedIn(), editDevice)

userRoutes.POST("/postCoordinates", middleware.EnsureLoggedIn(), changeDeviceCoordinates)

userRoutes.GET("/forgot-password", middleware.EnsureNotLoggedIn(), showForgotPassword)

userRoutes.POST("/forgot-password", middleware.EnsureNotLoggedIn(), performForgotPassword)

userRoutes.GET("/reset-password", middleware.EnsureNotLoggedIn(), showResetPassword)

userRoutes.POST("/reset-password", middleware.EnsureNotLoggedIn(), performResetPassword)
}
// Handle GET requests at /map, ensure user is logged in using middleware
// Render the index page
Expand Down Expand Up @@ -764,3 +773,94 @@ func getCurrentDevice() (deviceName string) {
return currentDevice
}

/*
Renders forgot password page
*/
func showForgotPassword(c *gin.Context) {
Render(c, gin.H{
"title": "Forgot Password"}, "forgot-password.html")
}

/*
Renders reset password page
*/
func showResetPassword(c *gin.Context) {
Render(c, gin.H{
"title": "Reset Password"}, "reset-password.html")
}

/*
Checks if inputted email is in database
If yes, returned to login page
If no, renders error
*/
func performForgotPassword(c *gin.Context) {
username := c.PostForm("username")
email := c.PostForm("email")
if err := db.CheckUsername(username); err == nil {
c.HTML(http.StatusBadRequest, "forgot-password.html", gin.H{
"title": "Forgot Password",
"Email": email,
"ErrorTitle": "Invalid Username",
"ErrorMessage": "Username not connected to user."})
} else if err := db.CheckEmailValid(email); err != nil {
c.HTML(http.StatusBadRequest, "forgot-password.html", gin.H{
"title": "Forgot Password",
"Username": username,
"ErrorTitle": "Invalid Email Address",
"ErrorMessage": "Please type a valid email address."})
} else if err := db.CheckEmail(email); err != nil {
from := "[email protected]"
password := "gydhmmllmtsfjxal"
to := []string{email}
smtpHost := "smtp.gmail.com"
smtpPort := "587"
resetCode := db.GenerateResetCode(username)
message := []byte("Subject: Reset Code\n\nHere is your reset password code: " + resetCode)
auth := smtp.PlainAuth("", from, password, smtpHost)
err := smtp.SendMail(smtpHost+":"+smtpPort, auth, from, to, message)
if err != nil {
c.HTML(http.StatusBadRequest, "forgot-password.html", gin.H{
"title": "Forgot Password",
"Username": username,
"Email": email,
"ErrorTitle": "Failed to Send Email",
"ErrorMessage": err.Error()})
} else {
showResetPassword(c)
}
} else {
c.HTML(http.StatusBadRequest, "forgot-password.html", gin.H{
"title": "Forgot Password",
"ErrorTitle": "Invalid Email Address",
"ErrorMessage": "Email not connected to a user."})
}
}

/*
Checks if password is valid
If yes, updates password
If not, renders error
*/
func performResetPassword(c *gin.Context) {
reset_code := c.PostForm("reset-code")
username := c.PostForm("username")
password := c.PostForm("password")
confirm_password := c.PostForm("confirm_password")

if err := db.CheckResetCode(reset_code, username); err != nil {
c.HTML(http.StatusBadRequest, "reset-password.html", gin.H{
"ErrorTitle": "Reset Password Failed",
"ErrorMessage": err.Error()})
} else if password != confirm_password {
c.HTML(http.StatusBadRequest, "reset-password.html", gin.H{
"ErrorTitle": "Reset Password Failed",
"ErrorMessage": fmt.Sprintf("Passwords \"%s\" and \"%s\" do not match.", password, confirm_password)})
} else if err := db.ResetPassword(username, password); err != nil {
c.HTML(http.StatusBadRequest, "reset-password.html", gin.H{
"ErrorTitle": "Reset Password Failed",
"ErrorMessage": err.Error()})
} else {
showLoginPage(c)
}
}
89 changes: 76 additions & 13 deletions db/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package db
import (
"bufio"
"fmt"
"math/rand"
"os"
"regexp"
"strings"
"time"

"golang.org/x/crypto/bcrypt"
)
Expand All @@ -16,18 +18,20 @@ const (
)

type user struct {
username string
password []byte
email string
admin int
username string
password []byte
email string
reset_code string
admin int
}

func CreateUser(username, password, email string, admin int) (user, error) {
u := user{
username: "",
password: []byte(""),
email: "",
admin: 0,
username: "",
password: []byte(""),
email: "",
reset_code: "",
admin: 0,
}
if CheckUsername(username) != nil {
return u, fmt.Errorf("Username \"%s\" is already in use.", username)
Expand Down Expand Up @@ -71,7 +75,7 @@ func writeUser(u user) error {
defer fil.Close()
// Creating the string from the user details
// to append to the file
writeString := fmt.Sprintf("%s\t%s\t%s\t%d\n", u.username, u.password, u.email, u.admin)
writeString := fmt.Sprintf("%s\t%s\t%s\t%s\t%d\n", u.username, u.password, u.email, u.reset_code, u.admin)
_, err = fil.WriteString(writeString)
if err != nil {
return err
Expand Down Expand Up @@ -101,10 +105,11 @@ func ReadUser(uname string) (u user, err error) {
if line[0] == uname {
// User found, creating it to return
u = user{
username: line[0],
password: []byte(line[1]),
email: line[2],
admin: 0,
username: line[0],
password: []byte(line[1]),
email: line[2],
reset_code: line[3],
admin: 0,
}
return u, nil
}
Expand Down Expand Up @@ -295,3 +300,61 @@ func CheckEmail(e string) error {
func hash(password string) ([]byte, error) {
return bcrypt.GenerateFromPassword([]byte(password), hashCost)
}

func ResetPassword(username string, password string) error {
u, err := ReadUser(username)
if err != nil {
return err
}
// User found, checking password
if err := CheckPassword(password); err != nil {
return err
} else {
// Password is valid, changing password
hashed, err := hash(password)
if err != nil {
return err
}
u.password = hashed
if err := DeleteUser(username); err != nil {
return err
}
if err := writeUser(u); err != nil {
return err
}
}
return nil
}

func GenerateResetCode(username string) string {
u, err := ReadUser(username)
if err != nil {
return ""
}
charset := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
var seededRand *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano()))
b := make([]byte, 5)
for i := range b {
b[i] = charset[seededRand.Intn(len(charset))]
}
u.reset_code = string(b)
if err := DeleteUser(username); err != nil {
return ""
}
if err := writeUser(u); err != nil {
return ""
}
return string(b)
}

func CheckResetCode(code string, username string) error {
u, err := ReadUser(username)
if err != nil {
return err
}
if u.reset_code != code {
return fmt.Errorf("Reset code does not match.")
} else {
return nil
}
}
36 changes: 36 additions & 0 deletions templates/forgot-password.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!--forgot-password.html-->

<!--Embed the header.html template at this location-->
{{ template "header.html" .}}

<head>
<link rel="stylesheet" href="/static/css/style.css">
</head>

<article class="center-page">
<h1>Forgot Your Password?</h1>
<p>If you have forgotten your password, please enter your account's username and email address below and click the "Reset Password" button. You will receive an email with a reset password code and be redirected to the reset password page.</p>
<form action="/u/forgot-password" method="POST">
<label for="username">Username:</label><br>
<input type="text" id="username" name="username" value = "{{ .Username}}"><br>
<label for="email">Email:</label><br>
<input type="text" id="email" name="email" value = "{{ .Email}}"><br>
{{ if .ErrorTitle}}
<div class="error">
{{.ErrorTitle}}: {{.ErrorMessage}}
</div>
{{end}}
<div class="absolute">
<input class="submit-btn" type="submit" value="Reset Password">
</div>
</form>
<form action="/u/login" method="GET">
<p style = "margin-bottom: 1px">Return to Login Page <input class="signup moveup" type="submit" value="Login"></p>
</form>
<form action="/u/register" method="GET">
<p style = "margin-top: 1px">Don't have an account? <input class="signup moveup" type="submit" value="Sign-up"></p>
</form>
</article>

<!--Embed the footer.html template at this location-->
{{ template "footer.html" .}}
42 changes: 42 additions & 0 deletions templates/reset-password.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<!--reset-password.html-->

<!--Embed the header.html template at this location-->
{{ template "header.html" .}}

<head>
<link rel="stylesheet" href="/static/css/style.css">
</head>

<article class="center-page">
<h1>Reset Password</h1>
<p style = "margin-top: 5px">Please type in the reset password code, your username, and your new password below. <br><br> Remember that passwords must </p>
<ul style = "color: white">
<li> have at least 10 characters </li>
<li> have at least 1 digit</li>
<li> have at least 1 symbol</li>
<li> have at least 1 uppercase character</li>
<li> have at least 1 lowercase character</li>
</ul>
<form class="form" action="/u/reset-password" method="POST">
<label for="reset-code">Reset Code</label><br>
<input type="text" name="reset-code"id="reset-code"><br>
<label for="username">Username</label><br>
<input type="text" name="username"id="username"><br>
<label for="password">Password</label><br>
<input type="password" name="password"id="password"><br>
<label for="confirm_password">Confirm Password</label><br>
<input type="password" name="confirm_password"id="confirm_password"><br>
{{ if .ErrorTitle}}
<div class="error">
{{.ErrorTitle}}: {{.ErrorMessage}}
</div>
{{end}}
<div class="absolute">
<input class="submit-btn" type="submit" value="Submit">
</div>
<br><br>
</form>
</article>

<!--Embed the footer.html template at this location-->
{{ template "footer.html" .}}

0 comments on commit 4c43a5d

Please sign in to comment.