Ability to change username
This commit is contained in:
parent
ef8abec7bf
commit
fedfbed4eb
|
@ -18,6 +18,7 @@ import (
|
||||||
"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"
|
||||||
"code.nonshy.com/nonshy/website/pkg/utility"
|
"code.nonshy.com/nonshy/website/pkg/utility"
|
||||||
|
"code.nonshy.com/nonshy/website/pkg/worker"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -50,6 +51,10 @@ func Settings() http.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Is the user currently in the chat room? Gate username changes when so.
|
||||||
|
var isOnChat = worker.GetChatStatistics().IsOnline(user.Username)
|
||||||
|
vars["OnChat"] = isOnChat
|
||||||
|
|
||||||
// URL hashtag to redirect to
|
// URL hashtag to redirect to
|
||||||
var hashtag string
|
var hashtag string
|
||||||
|
|
||||||
|
@ -293,32 +298,79 @@ func Settings() http.HandlerFunc {
|
||||||
case "settings":
|
case "settings":
|
||||||
hashtag = "#account"
|
hashtag = "#account"
|
||||||
var (
|
var (
|
||||||
oldPassword = r.PostFormValue("old_password")
|
oldPassword = r.PostFormValue("old_password")
|
||||||
changeEmail = strings.TrimSpace(strings.ToLower(r.PostFormValue("change_email")))
|
changeEmail = strings.TrimSpace(strings.ToLower(r.PostFormValue("change_email")))
|
||||||
password1 = strings.TrimSpace(r.PostFormValue("new_password"))
|
changeUsername = strings.TrimSpace(strings.ToLower(r.PostFormValue("change_username")))
|
||||||
password2 = strings.TrimSpace(r.PostFormValue("new_password2"))
|
password1 = strings.TrimSpace(r.PostFormValue("new_password"))
|
||||||
|
password2 = strings.TrimSpace(r.PostFormValue("new_password2"))
|
||||||
)
|
)
|
||||||
|
|
||||||
// Their old password is needed to make any changes to their account.
|
// Their old password is needed to make any changes to their account.
|
||||||
if err := user.CheckPassword(oldPassword); err != nil {
|
if err := user.CheckPassword(oldPassword); err != nil {
|
||||||
session.FlashError(w, r, "Could not make changes to your account settings as the 'current password' you entered was incorrect.")
|
session.FlashError(w, r, "Could not make changes to your account settings as the 'current password' you entered was incorrect.")
|
||||||
templates.Redirect(w, r.URL.Path)
|
templates.Redirect(w, r.URL.Path+hashtag)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Changing their username?
|
||||||
|
if changeUsername != user.Username {
|
||||||
|
// Not if they are in the chat room!
|
||||||
|
if isOnChat {
|
||||||
|
session.FlashError(w, r, "Your username could not be changed right now because you are logged into the chat room. Please exit the chat room, wait a minute, and try your request again.")
|
||||||
|
templates.Redirect(w, r.URL.Path+hashtag)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the new name is OK.
|
||||||
|
if err := models.IsValidUsername(changeUsername); err != nil {
|
||||||
|
session.FlashError(w, r, "Could not change your username: %s", err.Error())
|
||||||
|
templates.Redirect(w, r.URL.Path+hashtag)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set their name.
|
||||||
|
origUsername := user.Username
|
||||||
|
user.Username = changeUsername
|
||||||
|
if err := user.Save(); err != nil {
|
||||||
|
session.FlashError(w, r, "Error saving your new username: %s", err)
|
||||||
|
} else {
|
||||||
|
session.Flash(w, r, "Your username has been updated to: %s", user.Username)
|
||||||
|
|
||||||
|
// Notify the admin about this to keep tabs if someone is acting strangely
|
||||||
|
// with too-frequent username changes.
|
||||||
|
fb := &models.Feedback{
|
||||||
|
Intent: "report",
|
||||||
|
Subject: "Change of username",
|
||||||
|
UserID: user.ID,
|
||||||
|
TableName: "users",
|
||||||
|
TableID: user.ID,
|
||||||
|
Message: fmt.Sprintf(
|
||||||
|
"A user has modified their username on their profile page!\n\n"+
|
||||||
|
"* Original: %s\n* Updated: %s",
|
||||||
|
origUsername, changeUsername,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the feedback.
|
||||||
|
if err := models.CreateFeedback(fb); err != nil {
|
||||||
|
log.Error("Couldn't save feedback from user updating their DOB: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Changing their email?
|
// Changing their email?
|
||||||
if changeEmail != user.Email {
|
if changeEmail != user.Email {
|
||||||
// Validate the email.
|
// Validate the email.
|
||||||
if _, err := nm.ParseAddress(changeEmail); err != nil {
|
if _, err := nm.ParseAddress(changeEmail); err != nil {
|
||||||
session.FlashError(w, r, "The email address you entered is not valid: %s", err)
|
session.FlashError(w, r, "The email address you entered is not valid: %s", err)
|
||||||
templates.Redirect(w, r.URL.Path)
|
templates.Redirect(w, r.URL.Path+hashtag)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Email must not already exist.
|
// Email must not already exist.
|
||||||
if _, err := models.FindUser(changeEmail); err == nil {
|
if _, err := models.FindUser(changeEmail); err == nil {
|
||||||
session.FlashError(w, r, "That email address is already in use.")
|
session.FlashError(w, r, "That email address is already in use.")
|
||||||
templates.Redirect(w, r.URL.Path)
|
templates.Redirect(w, r.URL.Path+hashtag)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,7 +382,7 @@ func Settings() http.HandlerFunc {
|
||||||
}
|
}
|
||||||
if err := redis.Set(fmt.Sprintf(config.ChangeEmailRedisKey, token.Token), token, config.SignupTokenExpires); err != nil {
|
if err := redis.Set(fmt.Sprintf(config.ChangeEmailRedisKey, token.Token), token, config.SignupTokenExpires); err != nil {
|
||||||
session.FlashError(w, r, "Failed to create change email token: %s", err)
|
session.FlashError(w, r, "Failed to create change email token: %s", err)
|
||||||
templates.Redirect(w, r.URL.Path)
|
templates.Redirect(w, r.URL.Path+hashtag)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,9 @@ func Signup() http.HandlerFunc {
|
||||||
password = strings.TrimSpace(r.PostFormValue("password"))
|
password = strings.TrimSpace(r.PostFormValue("password"))
|
||||||
password2 = strings.TrimSpace(r.PostFormValue("password2"))
|
password2 = strings.TrimSpace(r.PostFormValue("password2"))
|
||||||
dob = r.PostFormValue("dob")
|
dob = r.PostFormValue("dob")
|
||||||
|
|
||||||
|
// Validation errors but still show the form again.
|
||||||
|
hasError bool
|
||||||
)
|
)
|
||||||
|
|
||||||
// Don't let them sneakily change their verified email address on us.
|
// Don't let them sneakily change their verified email address on us.
|
||||||
|
@ -95,21 +98,12 @@ func Signup() http.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reserved username check.
|
|
||||||
for _, cmp := range config.ReservedUsernames {
|
|
||||||
if username == cmp {
|
|
||||||
session.FlashError(w, r, "That username is reserved, please choose a different username.")
|
|
||||||
templates.Redirect(w, r.URL.Path+"?token="+tokenStr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache username in case of passwd validation errors.
|
// Cache username in case of passwd validation errors.
|
||||||
vars["Email"] = email
|
vars["Email"] = email
|
||||||
vars["Username"] = username
|
vars["Username"] = username
|
||||||
|
|
||||||
// Is the app not configured to send email?
|
// Is the app not configured to send email?
|
||||||
if !config.Current.Mail.Enabled {
|
if !config.Current.Mail.Enabled && !config.SkipEmailVerification {
|
||||||
session.FlashError(w, r, "This app is not configured to send email so you can not sign up at this time. "+
|
session.FlashError(w, r, "This app is not configured to send email so you can not sign up at this time. "+
|
||||||
"Please contact the website administrator about this issue!")
|
"Please contact the website administrator about this issue!")
|
||||||
templates.Redirect(w, r.URL.Path)
|
templates.Redirect(w, r.URL.Path)
|
||||||
|
@ -209,7 +203,6 @@ func Signup() http.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Full sign-up step (w/ email verification token), validate more things.
|
// Full sign-up step (w/ email verification token), validate more things.
|
||||||
var hasError bool
|
|
||||||
if len(password) < 3 {
|
if len(password) < 3 {
|
||||||
session.FlashError(w, r, "Please enter a password longer than 3 characters.")
|
session.FlashError(w, r, "Please enter a password longer than 3 characters.")
|
||||||
hasError = true
|
hasError = true
|
||||||
|
@ -218,8 +211,9 @@ func Signup() http.HandlerFunc {
|
||||||
hasError = true
|
hasError = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if !config.UsernameRegexp.MatchString(username) {
|
// Validate the username is OK: well formatted, not reserved, not existing.
|
||||||
session.FlashError(w, r, "Your username must consist of only numbers, letters, - . and be 3-32 characters.")
|
if err := models.IsValidUsername(username); err != nil {
|
||||||
|
session.FlashError(w, r, err.Error())
|
||||||
hasError = true
|
hasError = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -153,6 +153,10 @@ func Landing() http.HandlerFunc {
|
||||||
log.Error("SendBlocklist: %s", err)
|
log.Error("SendBlocklist: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mark them as online immediately: so e.g. on the Change Username screen we leave no window
|
||||||
|
// of time where they can exist in chat but change their name on the site.
|
||||||
|
worker.GetChatStatistics().SetOnlineNow(currentUser.Username)
|
||||||
|
|
||||||
// Redirect them to the chat room.
|
// Redirect them to the chat room.
|
||||||
templates.Redirect(w, strings.TrimSuffix(chatURL, "/")+"/?jwt="+ss)
|
templates.Redirect(w, strings.TrimSuffix(chatURL, "/")+"/?jwt="+ss)
|
||||||
return
|
return
|
||||||
|
|
|
@ -186,6 +186,28 @@ func FindUser(username string) (*User, error) {
|
||||||
return u, result.Error
|
return u, result.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsValidUsername checks if a username is available and not reserved.
|
||||||
|
func IsValidUsername(username string) error {
|
||||||
|
// Check the formatting of the name.
|
||||||
|
if !config.UsernameRegexp.MatchString(username) {
|
||||||
|
return errors.New("Your username must consist of only numbers, letters, - . and be 3-32 characters.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reserved username check.
|
||||||
|
for _, cmp := range config.ReservedUsernames {
|
||||||
|
if username == cmp {
|
||||||
|
return errors.New("That username is reserved, please choose a different username.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does the username already exist?
|
||||||
|
if _, err := FindUser(username); err == nil {
|
||||||
|
return errors.New("That username already exists. Please try a different username.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// IsShyFrom tells whether the user is shy from the perspective of the other user.
|
// IsShyFrom tells whether the user is shy from the perspective of the other user.
|
||||||
//
|
//
|
||||||
// That is, depending on our profile visibility and friendship status.
|
// That is, depending on our profile visibility and friendship status.
|
||||||
|
|
|
@ -2,7 +2,7 @@ package worker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -22,16 +22,10 @@ type ChatStatistics struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetChatStatistics returns the latest (cached) chat statistics.
|
// GetChatStatistics returns the latest (cached) chat statistics.
|
||||||
func GetChatStatistics() ChatStatistics {
|
func GetChatStatistics() *ChatStatistics {
|
||||||
chatStatisticsMu.RLock()
|
chatStatisticsMu.RLock()
|
||||||
defer chatStatisticsMu.RUnlock()
|
defer chatStatisticsMu.RUnlock()
|
||||||
|
return cachedChatStatistics
|
||||||
if cachedChatStatistics != nil {
|
|
||||||
return *cachedChatStatistics
|
|
||||||
}
|
|
||||||
return ChatStatistics{
|
|
||||||
Usernames: []string{},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetChatStatistics updates the cached chat statistics, holding a write lock briefly.
|
// SetChatStatistics updates the cached chat statistics, holding a write lock briefly.
|
||||||
|
@ -42,7 +36,7 @@ func SetChatStatistics(stats *ChatStatistics) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsOnline returns whether the username is currently logged-in to chat.
|
// IsOnline returns whether the username is currently logged-in to chat.
|
||||||
func (cs ChatStatistics) IsOnline(username string) bool {
|
func (cs *ChatStatistics) IsOnline(username string) bool {
|
||||||
for _, user := range cs.Usernames {
|
for _, user := range cs.Usernames {
|
||||||
if user == username {
|
if user == username {
|
||||||
return true
|
return true
|
||||||
|
@ -51,10 +45,20 @@ func (cs ChatStatistics) IsOnline(username string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetOnlineNow patches the current ChatStatistics to mark a user as online immediately, e.g.
|
||||||
|
// because the main site has just sent them to the chat with a JWT token.
|
||||||
|
func (cs *ChatStatistics) SetOnlineNow(username string) {
|
||||||
|
if !cs.IsOnline(username) {
|
||||||
|
chatStatisticsMu.Lock()
|
||||||
|
defer chatStatisticsMu.Unlock()
|
||||||
|
cs.Usernames = append(cs.Usernames, username)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type UserOnChatMap map[string]bool
|
type UserOnChatMap map[string]bool
|
||||||
|
|
||||||
// MapUsersOnline returns a hashmap of usernames to online status.
|
// MapUsersOnline returns a hashmap of usernames to online status.
|
||||||
func (cs ChatStatistics) MapUsersOnline(usernames []string) UserOnChatMap {
|
func (cs *ChatStatistics) MapUsersOnline(usernames []string) UserOnChatMap {
|
||||||
var result = UserOnChatMap{}
|
var result = UserOnChatMap{}
|
||||||
for _, user := range cs.Usernames {
|
for _, user := range cs.Usernames {
|
||||||
result[user] = true
|
result[user] = true
|
||||||
|
@ -68,7 +72,7 @@ func (m UserOnChatMap) Get(username string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
cachedChatStatistics *ChatStatistics
|
cachedChatStatistics = &ChatStatistics{}
|
||||||
chatStatisticsMu sync.RWMutex
|
chatStatisticsMu sync.RWMutex
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -117,7 +121,7 @@ func DoCheckBareRTC() {
|
||||||
|
|
||||||
if res.StatusCode == http.StatusOK {
|
if res.StatusCode == http.StatusOK {
|
||||||
var cs ChatStatistics
|
var cs ChatStatistics
|
||||||
body, _ := ioutil.ReadAll(res.Body)
|
body, _ := io.ReadAll(res.Body)
|
||||||
res.Body.Close()
|
res.Body.Close()
|
||||||
if err = json.Unmarshal(body, &cs); err != nil {
|
if err = json.Unmarshal(body, &cs); err != nil {
|
||||||
log.Error("WatchBareRTC: json decode error: %s", err)
|
log.Error("WatchBareRTC: json decode error: %s", err)
|
||||||
|
|
|
@ -1120,7 +1120,7 @@
|
||||||
<header class="card-header has-background-link">
|
<header class="card-header has-background-link">
|
||||||
<p class="card-header-title has-text-light">
|
<p class="card-header-title has-text-light">
|
||||||
<i class="fa fa-lock pr-2"></i>
|
<i class="fa fa-lock pr-2"></i>
|
||||||
Update E-mail or Password
|
Update E-mail, Username or Password
|
||||||
</p>
|
</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
@ -1129,6 +1129,13 @@
|
||||||
<input type="hidden" name="intent" value="settings">
|
<input type="hidden" name="intent" value="settings">
|
||||||
{{InputCSRF}}
|
{{InputCSRF}}
|
||||||
|
|
||||||
|
<div class="block">
|
||||||
|
You may use the fields below to change the e-mail address you log in with,
|
||||||
|
change your username on the site, or set a new password. You will need to
|
||||||
|
confirm your current password first before making these changes to your
|
||||||
|
account.
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label" for="old_password">
|
<label class="label" for="old_password">
|
||||||
Current Password
|
Current Password
|
||||||
|
@ -1152,6 +1159,28 @@
|
||||||
value="{{.CurrentUser.Email}}">
|
value="{{.CurrentUser.Email}}">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label" for="change_username">Change Username</label>
|
||||||
|
<input type="text" class="input"
|
||||||
|
id="change_username"
|
||||||
|
name="change_username"
|
||||||
|
placeholder="{{.CurrentUser.Username}}"
|
||||||
|
value="{{.CurrentUser.Username}}"
|
||||||
|
{{if .OnChat}}readonly{{end}}>
|
||||||
|
<p class="help">
|
||||||
|
{{if .OnChat}}
|
||||||
|
<span class="has-text-danger">
|
||||||
|
<i class="fa fa-exclamation-triangle mr-1"></i>
|
||||||
|
You are currently logged into the chat room, so your username may not be
|
||||||
|
updated at this time. To change your username, please log out of the chat
|
||||||
|
room and wait a minute before reloading this page.
|
||||||
|
</span>
|
||||||
|
{{else}}
|
||||||
|
Usernames are 3 to 32 characters a-z 0-9 . -
|
||||||
|
{{end}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">Change Password</label>
|
<label class="label">Change Password</label>
|
||||||
<input type="password" class="input mb-2"
|
<input type="password" class="input mb-2"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user