diff --git a/handler/user/ResetPassword/ResetPassword.go b/handler/user/ResetPassword/ResetPassword.go new file mode 100644 index 0000000..26776c6 --- /dev/null +++ b/handler/user/ResetPassword/ResetPassword.go @@ -0,0 +1,45 @@ +package userHandlerResetPassword + +import ( + "github.com/fossyy/filekeeper/cache" + "github.com/fossyy/filekeeper/db" + "github.com/fossyy/filekeeper/session" + "github.com/fossyy/filekeeper/types" + "github.com/fossyy/filekeeper/utils" + "net/http" +) + +func POST(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + userSession := r.Context().Value("user").(types.User) + currentPassword := r.Form.Get("currentPassword") + password := r.Form.Get("password") + user, err := cache.GetUser(userSession.Email) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + hashPassword, err := utils.HashPassword(password) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + if !utils.CheckPasswordHash(currentPassword, user.Password) { + http.Redirect(w, r, "/user?error=password_not_match", http.StatusSeeOther) + return + } + + err = db.DB.UpdateUserPassword(user.Email, hashPassword) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + session.RemoveAllSessions(userSession.Email) + cache.DeleteUser(userSession.Email) + + http.Redirect(w, r, "/signin", http.StatusSeeOther) + return +} diff --git a/handler/user/user.go b/handler/user/user.go index 1abe5f7..0a212cc 100644 --- a/handler/user/user.go +++ b/handler/user/user.go @@ -1,6 +1,7 @@ package userHandler import ( + "github.com/a-h/templ" "github.com/fossyy/filekeeper/types" "net/http" @@ -15,9 +16,30 @@ func init() { log = logger.Logger() } +var errorMessages = map[string]string{ + "password_not_match": "The passwords provided do not match. Please try again.", +} + func GET(w http.ResponseWriter, r *http.Request) { + var component templ.Component userSession := r.Context().Value("user").(types.User) - component := userView.Main("Filekeeper - User Page", userSession, session.GetSessions(userSession.Email)) + + if err := r.URL.Query().Get("error"); err != "" { + message, ok := errorMessages[err] + if !ok { + message = "Unknown error occurred. Please contact support at bagas@fossy.my.id for assistance." + } + + component = userView.Main("Filekeeper - User Page", userSession, session.GetSessions(userSession.Email), types.Message{ + Code: 0, + Message: message, + }) + } else { + component = userView.Main("Filekeeper - User Page", userSession, session.GetSessions(userSession.Email), types.Message{ + Code: 1, + Message: "", + }) + } err := component.Render(r.Context(), w) if err != nil { w.WriteHeader(http.StatusInternalServerError) diff --git a/public/validatePassword.js b/public/validatePassword.js index 4402f57..566bec2 100644 --- a/public/validatePassword.js +++ b/public/validatePassword.js @@ -1,6 +1,5 @@ var isMatch = false var isValid = false -var isSecure = false function validatePasswords() { var password = document.getElementById('password').value; var confirmPassword = document.getElementById('confirmPassword').value; @@ -8,18 +7,10 @@ function validatePasswords() { var matchBadPath = document.getElementById('matchBadPath'); var lengthGoodPath = document.getElementById('lengthGoodPath'); var lengthBadPath = document.getElementById('lengthBadPath'); - var requirementsGoodPath = document.getElementById('requirementsGoodPath'); - var requirementsBadPath = document.getElementById('requirementsBadPath'); var matchSvgContainer = document.getElementById('matchSvgContainer'); var lengthSvgContainer = document.getElementById('lengthSvgContainer'); - var requirementsSvgContainer = document.getElementById('requirementsSvgContainer'); var matchStatusText = document.getElementById('matchStatusText'); var lengthStatusText = document.getElementById('lengthStatusText'); - var requirementsStatusText = document.getElementById('requirementsStatusText'); - var symbolRegex = /[!@#$%^&*]/; - var uppercaseRegex = /[A-Z]/; - var numberRegex = /\d/g; - if (password === confirmPassword && password.length > 0 && confirmPassword.length > 0 && password.length === confirmPassword.length) { matchSvgContainer.classList.remove('bg-red-200'); @@ -29,7 +20,6 @@ function validatePasswords() { matchGoodPath.style.display = 'inline'; matchBadPath.style.display = 'none'; matchStatusText.textContent = "Passwords match"; - console.log("anjay") isMatch = true } else { matchSvgContainer.classList.remove('bg-green-200'); @@ -62,31 +52,7 @@ function validatePasswords() { isValid = false } - var symbolCheck = symbolRegex.test(password); - var uppercaseCheck = uppercaseRegex.test(password); - var numberCount = (password.match(numberRegex) || []).length; - - if (symbolCheck && uppercaseCheck && numberCount >= 3) { - requirementsSvgContainer.classList.remove('bg-red-200'); - requirementsSvgContainer.classList.add('bg-green-200'); - requirementsStatusText.classList.remove('text-red-700'); - requirementsStatusText.classList.add('text-green-700'); - requirementsGoodPath.style.display = 'inline'; - requirementsBadPath.style.display = 'none'; - requirementsStatusText.textContent = "Password meets additional requirements"; - isSecure = true - } else { - requirementsSvgContainer.classList.remove('bg-green-200'); - requirementsSvgContainer.classList.add('bg-red-200'); - requirementsStatusText.classList.remove('text-green-700'); - requirementsStatusText.classList.add('text-red-700'); - requirementsGoodPath.style.display = 'none'; - requirementsBadPath.style.display = 'inline'; - requirementsStatusText.textContent = "The password must contain at least one symbol (!@#$%^&*), one uppercase letter, and three numbers"; - isSecure = false - } - - if (isSecure && isValid && isSecure && password === confirmPassword) { + if ( isValid && isMatch) { document.getElementById("submit").disabled = false; } else { document.getElementById("submit").disabled = true; diff --git a/routes/routes.go b/routes/routes.go index 85eecd4..890c668 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -17,6 +17,7 @@ import ( uploadHandler "github.com/fossyy/filekeeper/handler/upload" "github.com/fossyy/filekeeper/handler/upload/initialisation" userHandler "github.com/fossyy/filekeeper/handler/user" + userHandlerResetPassword "github.com/fossyy/filekeeper/handler/user/ResetPassword" userSessionTerminateHandler "github.com/fossyy/filekeeper/handler/user/session/terminate" userHandlerTotpSetup "github.com/fossyy/filekeeper/handler/user/totp" "github.com/fossyy/filekeeper/middleware" @@ -93,6 +94,10 @@ func SetupRoutes() *http.ServeMux { middleware.Auth(userHandler.GET, w, r) }) + handler.HandleFunc("POST /user/reset-password", func(w http.ResponseWriter, r *http.Request) { + middleware.Auth(userHandlerResetPassword.POST, w, r) + }) + handler.HandleFunc("DELETE /user/session/terminate/{id}", func(w http.ResponseWriter, r *http.Request) { middleware.Auth(userSessionTerminateHandler.DELETE, w, r) }) diff --git a/utils/utils.go b/utils/utils.go index 68ba77e..4de0821 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -72,19 +72,10 @@ func ValidatePassword(password string) bool { } var ( - hasSymbol bool hasNumber int hasUppercase bool ) - symbols := []string{"!", "@", "#", "$", "%", "^", "&", "*"} - - for _, symbol := range symbols { - if strings.Contains(password, symbol) { - hasSymbol = true - } - } - for _, char := range password { switch { case unicode.IsNumber(char): @@ -94,7 +85,7 @@ func ValidatePassword(password string) bool { } } - return hasSymbol && hasNumber >= 3 && hasUppercase + return hasNumber >= 3 && hasUppercase } func ConvertFileSize(byte int64) string { diff --git a/view/auth/auth.templ b/view/auth/auth.templ index 413d7fc..c74f3dd 100644 --- a/view/auth/auth.templ +++ b/view/auth/auth.templ @@ -58,15 +58,6 @@ templ form(err types.Message, title string) { Password length must be at least 8 characters -
  • -
    - - - - -
    - The password must contain at least one symbol (!@#$%^&*), one uppercase letter, and three numbers -
  • -
    - - -
    -
    - - -
    - - +
    @@ -258,6 +288,17 @@ templ content(title string, user types.User, ListSession []*session.SessionInfo)
    + +