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.
This commit is contained in:
parent
868aef6fb0
commit
1ee8acf060
|
@ -10,6 +10,7 @@ import (
|
||||||
"code.nonshy.com/nonshy/website/pkg/ratelimit"
|
"code.nonshy.com/nonshy/website/pkg/ratelimit"
|
||||||
"code.nonshy.com/nonshy/website/pkg/session"
|
"code.nonshy.com/nonshy/website/pkg/session"
|
||||||
"code.nonshy.com/nonshy/website/pkg/templates"
|
"code.nonshy.com/nonshy/website/pkg/templates"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Login controller.
|
// Login controller.
|
||||||
|
@ -29,6 +30,11 @@ func Login() http.HandlerFunc {
|
||||||
// Look up their account.
|
// Look up their account.
|
||||||
user, err := models.FindUser(username)
|
user, err := models.FindUser(username)
|
||||||
if err != nil {
|
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.")
|
session.FlashError(w, r, "Incorrect username or password.")
|
||||||
templates.Redirect(w, r.URL.Path)
|
templates.Redirect(w, r.URL.Path)
|
||||||
return
|
return
|
||||||
|
|
|
@ -26,7 +26,7 @@ func Search() http.HandlerFunc {
|
||||||
// Search filters.
|
// Search filters.
|
||||||
var (
|
var (
|
||||||
isCertified = r.FormValue("certified")
|
isCertified = r.FormValue("certified")
|
||||||
username = r.FormValue("username") // email or username
|
username = r.FormValue("username") // username search
|
||||||
gender = r.FormValue("gender")
|
gender = r.FormValue("gender")
|
||||||
orientation = r.FormValue("orientation")
|
orientation = r.FormValue("orientation")
|
||||||
maritalStatus = r.FormValue("marital_status")
|
maritalStatus = r.FormValue("marital_status")
|
||||||
|
@ -73,14 +73,14 @@ func Search() http.HandlerFunc {
|
||||||
pager.ParsePage(r)
|
pager.ParsePage(r)
|
||||||
|
|
||||||
users, err := models.SearchUsers(currentUser, &models.UserSearch{
|
users, err := models.SearchUsers(currentUser, &models.UserSearch{
|
||||||
EmailOrUsername: username,
|
Username: username,
|
||||||
Gender: gender,
|
Gender: gender,
|
||||||
Orientation: orientation,
|
Orientation: orientation,
|
||||||
MaritalStatus: maritalStatus,
|
MaritalStatus: maritalStatus,
|
||||||
Certified: isCertified != "false",
|
Certified: isCertified != "false",
|
||||||
InnerCircle: isCertified == "circle",
|
InnerCircle: isCertified == "circle",
|
||||||
AgeMin: ageMin,
|
AgeMin: ageMin,
|
||||||
AgeMax: ageMax,
|
AgeMax: ageMax,
|
||||||
}, pager)
|
}, pager)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
session.FlashError(w, r, "Couldn't search users: %s", err)
|
session.FlashError(w, r, "Couldn't search users: %s", err)
|
||||||
|
|
|
@ -126,7 +126,22 @@ func Signup() http.HandlerFunc {
|
||||||
|
|
||||||
// Already an account?
|
// Already an account?
|
||||||
if _, err := models.FindUser(email); err == nil {
|
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)
|
templates.Redirect(w, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ func UserPhotos() http.HandlerFunc {
|
||||||
"User": user,
|
"User": user,
|
||||||
"Photos": []*models.Photo{},
|
"Photos": []*models.Photo{},
|
||||||
"PhotoCount": models.CountPhotos(user.ID),
|
"PhotoCount": models.CountPhotos(user.ID),
|
||||||
"Pager": models.Pagination{},
|
"Pager": &models.Pagination{},
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := tmpl.Execute(w, r, vars); err != nil {
|
if err := tmpl.Execute(w, r, vars); err != nil {
|
||||||
|
|
|
@ -184,14 +184,14 @@ func (u *User) IsShyFrom(other *User) bool {
|
||||||
|
|
||||||
// UserSearch config.
|
// UserSearch config.
|
||||||
type UserSearch struct {
|
type UserSearch struct {
|
||||||
EmailOrUsername string
|
Username string
|
||||||
Gender string
|
Gender string
|
||||||
Orientation string
|
Orientation string
|
||||||
MaritalStatus string
|
MaritalStatus string
|
||||||
Certified bool
|
Certified bool
|
||||||
InnerCircle bool
|
InnerCircle bool
|
||||||
AgeMin int
|
AgeMin int
|
||||||
AgeMax int
|
AgeMax int
|
||||||
}
|
}
|
||||||
|
|
||||||
// SearchUsers from the perspective of a given user.
|
// 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)
|
placeholders = append(placeholders, blockedUserIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
if search.EmailOrUsername != "" {
|
if search.Username != "" {
|
||||||
ilike := "%" + strings.TrimSpace(strings.ToLower(search.EmailOrUsername)) + "%"
|
ilike := "%" + strings.TrimSpace(strings.ToLower(search.Username)) + "%"
|
||||||
wheres = append(wheres, "(email LIKE ? OR username LIKE ?)")
|
wheres = append(wheres, "username LIKE ?")
|
||||||
placeholders = append(placeholders, ilike, ilike)
|
placeholders = append(placeholders, ilike)
|
||||||
}
|
}
|
||||||
|
|
||||||
if search.Gender != "" {
|
if search.Gender != "" {
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
|
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">Email or username:</label>
|
<label class="label">Partial username:</label>
|
||||||
<input type="text" class="input"
|
<input type="text" class="input"
|
||||||
name="username"
|
name="username"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
|
|
35
web/templates/email/already_signed_up.html
Normal file
35
web/templates/email/already_signed_up.html
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
{{define "content"}}
|
||||||
|
<html>
|
||||||
|
<body bakground="#ffffff" color="#000000" link="#0000FF" vlink="#990099" alink="#FF0000">
|
||||||
|
<basefont face="Arial,Helvetica,sans-serif" size="3" color="#000000"></basefont>
|
||||||
|
|
||||||
|
<h1>You already have a {{.Data.Title}} account</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
If it was not you, then you can disregard this e-mail.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
If you have forgotten your password, you can request a password reset at the link below:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href="{{.Data.URL}}" target="_blank">{{.Data.URL}}</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
You may sign in to the website using your e-mail address and account password.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This is an automated e-mail; do not reply to this message.
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{{end}}
|
|
@ -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.
|
See also: template_funcs.go for the SimplePager wrapper function.
|
||||||
-->
|
-->
|
||||||
{{define "SimplePager"}}
|
{{define "SimplePager"}}
|
||||||
|
{{if .Pager.Pages}}
|
||||||
<nav class="pagination" role="navigation" aria-label="pagination">
|
<nav class="pagination" role="navigation" aria-label="pagination">
|
||||||
<a class="pagination-previous{{if not .Pager.HasPrevious}} is-disabled{{end}}" title="Previous"
|
<a class="pagination-previous{{if not .Pager.HasPrevious}} is-disabled{{end}}" title="Previous"
|
||||||
href="{{.Request.URL.Path}}?{{QueryPlus "page" .Pager.Previous}}">Previous</a>
|
href="{{.Request.URL.Path}}?{{QueryPlus "page" .Pager.Previous}}">Previous</a>
|
||||||
|
@ -28,4 +29,5 @@ See also: template_funcs.go for the SimplePager wrapper function.
|
||||||
{{end}}
|
{{end}}
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user