From 1ee8acf06062a6af0e2e567732fcc6ded2b9447a Mon Sep 17 00:00:00 2001 From: Noah Petherbridge Date: Tue, 15 Aug 2023 17:33:33 -0700 Subject: [PATCH] Various quick fixes * Signup: if entering an existing email, don't admit that the email exists. Instead, send a specialized email to its address. * Search: no longer search for users by email address. * Login: always hash the incoming password on user not found, to take constant time compared to when the user did exist. * Fix a pagination bug when a private (shy account) views a non-friend's photo gallery. --- pkg/controller/account/login.go | 6 ++++ pkg/controller/account/search.go | 18 +++++------ pkg/controller/account/signup.go | 17 ++++++++++- pkg/controller/photo/user_gallery.go | 2 +- pkg/models/user.go | 24 +++++++-------- web/templates/account/search.html | 2 +- web/templates/email/already_signed_up.html | 35 ++++++++++++++++++++++ web/templates/partials/simple_pager.html | 2 ++ 8 files changed, 82 insertions(+), 24 deletions(-) create mode 100644 web/templates/email/already_signed_up.html diff --git a/pkg/controller/account/login.go b/pkg/controller/account/login.go index 5c71799..216a4de 100644 --- a/pkg/controller/account/login.go +++ b/pkg/controller/account/login.go @@ -10,6 +10,7 @@ import ( "code.nonshy.com/nonshy/website/pkg/ratelimit" "code.nonshy.com/nonshy/website/pkg/session" "code.nonshy.com/nonshy/website/pkg/templates" + "golang.org/x/crypto/bcrypt" ) // Login controller. @@ -29,6 +30,11 @@ func Login() http.HandlerFunc { // Look up their account. user, err := models.FindUser(username) if err != nil { + // The user wasn't found, but still hash the incoming password to take time: + // so a mischievous user can't infer whether the username was valid based + // on the server response time. + bcrypt.GenerateFromPassword([]byte(password), config.BcryptCost) + session.FlashError(w, r, "Incorrect username or password.") templates.Redirect(w, r.URL.Path) return diff --git a/pkg/controller/account/search.go b/pkg/controller/account/search.go index cc52a56..3529cef 100644 --- a/pkg/controller/account/search.go +++ b/pkg/controller/account/search.go @@ -26,7 +26,7 @@ func Search() http.HandlerFunc { // Search filters. var ( isCertified = r.FormValue("certified") - username = r.FormValue("username") // email or username + username = r.FormValue("username") // username search gender = r.FormValue("gender") orientation = r.FormValue("orientation") maritalStatus = r.FormValue("marital_status") @@ -73,14 +73,14 @@ func Search() http.HandlerFunc { pager.ParsePage(r) users, err := models.SearchUsers(currentUser, &models.UserSearch{ - EmailOrUsername: username, - Gender: gender, - Orientation: orientation, - MaritalStatus: maritalStatus, - Certified: isCertified != "false", - InnerCircle: isCertified == "circle", - AgeMin: ageMin, - AgeMax: ageMax, + Username: username, + Gender: gender, + Orientation: orientation, + MaritalStatus: maritalStatus, + Certified: isCertified != "false", + InnerCircle: isCertified == "circle", + AgeMin: ageMin, + AgeMax: ageMax, }, pager) if err != nil { session.FlashError(w, r, "Couldn't search users: %s", err) diff --git a/pkg/controller/account/signup.go b/pkg/controller/account/signup.go index 0607b9a..b3d9e4d 100644 --- a/pkg/controller/account/signup.go +++ b/pkg/controller/account/signup.go @@ -126,7 +126,22 @@ func Signup() http.HandlerFunc { // Already an account? if _, err := models.FindUser(email); err == nil { - session.FlashError(w, r, "There is already an account with that e-mail address.") + // We don't want to admit that the email already is registered, so send an email to the + // address in case the user legitimately forgot, but flash the regular success message. + err := mail.Send(mail.Message{ + To: email, + Subject: "You already have a nonshy account", + Template: "email/already_signed_up.html", + Data: map[string]interface{}{ + "Title": config.Title, + "URL": config.Current.BaseURL + "/forgot-password", + }, + }) + if err != nil { + session.FlashError(w, r, "Error sending an email: %s", err) + } + + session.Flash(w, r, "We have sent an e-mail to %s with a link to continue signing up your account. Please go and check your e-mail.", email) templates.Redirect(w, r.URL.Path) return } diff --git a/pkg/controller/photo/user_gallery.go b/pkg/controller/photo/user_gallery.go index 72c2e92..4f60902 100644 --- a/pkg/controller/photo/user_gallery.go +++ b/pkg/controller/photo/user_gallery.go @@ -61,7 +61,7 @@ func UserPhotos() http.HandlerFunc { "User": user, "Photos": []*models.Photo{}, "PhotoCount": models.CountPhotos(user.ID), - "Pager": models.Pagination{}, + "Pager": &models.Pagination{}, } if err := tmpl.Execute(w, r, vars); err != nil { diff --git a/pkg/models/user.go b/pkg/models/user.go index aad98c5..226ec5a 100644 --- a/pkg/models/user.go +++ b/pkg/models/user.go @@ -184,14 +184,14 @@ func (u *User) IsShyFrom(other *User) bool { // UserSearch config. type UserSearch struct { - EmailOrUsername string - Gender string - Orientation string - MaritalStatus string - Certified bool - InnerCircle bool - AgeMin int - AgeMax int + Username string + Gender string + Orientation string + MaritalStatus string + Certified bool + InnerCircle bool + AgeMin int + AgeMax int } // SearchUsers from the perspective of a given user. @@ -213,10 +213,10 @@ func SearchUsers(user *User, search *UserSearch, pager *Pagination) ([]*User, er placeholders = append(placeholders, blockedUserIDs) } - if search.EmailOrUsername != "" { - ilike := "%" + strings.TrimSpace(strings.ToLower(search.EmailOrUsername)) + "%" - wheres = append(wheres, "(email LIKE ? OR username LIKE ?)") - placeholders = append(placeholders, ilike, ilike) + if search.Username != "" { + ilike := "%" + strings.TrimSpace(strings.ToLower(search.Username)) + "%" + wheres = append(wheres, "username LIKE ?") + placeholders = append(placeholders, ilike) } if search.Gender != "" { diff --git a/web/templates/account/search.html b/web/templates/account/search.html index 99d0feb..fc918fe 100644 --- a/web/templates/account/search.html +++ b/web/templates/account/search.html @@ -57,7 +57,7 @@
- + + + + +

You already have a {{.Data.Title}} account

+ +

+ Somebody (hopefully you) has tried to sign up a new account by entering your e-mail address. + We already have an account for this e-mail address, but we didn't admit that to whoever + just signed up. +

+ +

+ If it was not you, then you can disregard this e-mail. +

+ +

+ If you have forgotten your password, you can request a password reset at the link below: +

+ +

+ {{.Data.URL}} +

+ +

+ You may sign in to the website using your e-mail address and account password. +

+ +

+ This is an automated e-mail; do not reply to this message. +

+ + +{{end}} diff --git a/web/templates/partials/simple_pager.html b/web/templates/partials/simple_pager.html index c23613c..2d426a9 100644 --- a/web/templates/partials/simple_pager.html +++ b/web/templates/partials/simple_pager.html @@ -10,6 +10,7 @@ added. Should be suitable for most pagers that don't need any specialized logic. See also: template_funcs.go for the SimplePager wrapper function. --> {{define "SimplePager"}} + {{if .Pager.Pages}} + {{end}} {{end}}