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/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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 != "" {
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
|
||||
<div class="column">
|
||||
<div class="field">
|
||||
<label class="label">Email or username:</label>
|
||||
<label class="label">Partial username:</label>
|
||||
<input type="text" class="input"
|
||||
name="username"
|
||||
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.
|
||||
-->
|
||||
{{define "SimplePager"}}
|
||||
{{if .Pager.Pages}}
|
||||
<nav class="pagination" role="navigation" aria-label="pagination">
|
||||
<a class="pagination-previous{{if not .Pager.HasPrevious}} is-disabled{{end}}" title="Previous"
|
||||
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}}
|
||||
</ul>
|
||||
</nav>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
|
Loading…
Reference in New Issue
Block a user