introduce the password reminder infrastructure

This commit is contained in:
2025-10-06 16:42:23 -06:00
parent 33c2fcc471
commit fe360e23d3
4 changed files with 41 additions and 12 deletions
+30 -8
View File
@@ -15,7 +15,6 @@ import (
"errors"
"fmt"
"hash/crc32"
"math/rand"
"strconv"
"strings"
"sync"
@@ -26,6 +25,33 @@ import (
log "github.com/sirupsen/logrus"
)
// PasswordChangeRequest represents a temporary password change request.
type PasswordChangeRequest struct {
Uid int32
Username string
Email string
Authentication int32
Expires time.Time
}
// passwordRequests contains a map of password change requests currently managed.
var passwordRequests map[int32]*PasswordChangeRequest = make(map[int32]*PasswordChangeRequest)
/* AmNewPasswordChangeRequest creates a new password change request and enrolls it.
* Parameters:
* uid - The UID of the user.
* username - The user name of the user.
* email - The E-mail address of the user.
* Returns:
* Pointer to the new PasswordChangeRequest.
*/
func AmNewPasswordChangeRequest(uid int32, username string, email string) *PasswordChangeRequest {
rc := PasswordChangeRequest{Uid: uid, Username: username, Email: email,
Authentication: util.GenerateRandomConfirmationNumber(), Expires: time.Now().Add(time.Hour)}
passwordRequests[uid] = &rc
return &rc
}
// User represents a user in the Amsterdam database.
type User struct {
Mutex sync.RWMutex
@@ -140,7 +166,7 @@ func (u *User) ConfirmEMailAddress(confnum int32, remoteIP string) error {
func (u *User) NewEmailConfirmationNumber() error {
u.Mutex.Lock()
defer u.Mutex.Unlock()
newnum := newEmailConfirmationNumber()
newnum := util.GenerateRandomConfirmationNumber()
_, err := amdb.Exec("UPDATE user SET email_confnum = ? WHERE uid = ?", newnum, u.Uid)
if err != nil {
u.EmailConfNum = newnum
@@ -384,11 +410,6 @@ func AmAuthenticateUserByToken(authString string, remoteIP string) (*User, error
return user, nil
}
// newEmailConfirmationNumber returns a new E-mail confirmation number.
func newEmailConfirmationNumber() int32 {
return rand.Int31n(9000000) + 1000000
}
/* AmCreateNewUser creates a new user record in the database.
* Parameters:
* username - New user name.
@@ -421,7 +442,8 @@ func AmCreateNewUser(username string, password string, reminder string, dob *tim
// Insert the user record.
_, err2 := amdb.Exec(`INSERT INTO users (username, passhash, verify_email, lockout, email_confnum,
base_lvl, created, lastaccess, passreminder, description, dob) VALUES (?, ?, 0, 0, ?, ?, NOW(), NOW(), ?, '', ?)`,
username, hashPassword(password), newEmailConfirmationNumber(), AmDefaultRole("Global.NewUser").Level(), reminder, *dob)
username, hashPassword(password), util.GenerateRandomConfirmationNumber(), AmDefaultRole("Global.NewUser").Level(),
reminder, *dob)
if err2 != nil {
return nil, err2
}
+1 -1
View File
@@ -7,7 +7,7 @@ If this reminder is not sufficient for you to remember what your password is,
then the system can change your password for you. To do so, please visit
the following URL:
http://example.com/passrecovery/{{ change_uid }}.{{ change_auth }}
http://example.com/passrecovery/{{ change_uid }}/{{ change_auth }}
Your password will be changed and a new password will be E-mailed to you
at this address.
+2 -1
View File
@@ -80,13 +80,14 @@ func Login(ctxt ui.AmContext) (string, any, error) {
ci, uerr = user.ContactInfo()
if uerr == nil {
if ci != nil && ci.Email != nil && *ci.Email != "" {
pchange := database.AmNewPasswordChangeRequest(user.Uid, user.Username, *ci.Email)
msg := email.AmNewEmailMessage(ctxt.CurrentUserId(), ctxt.RemoteIP())
msg.AddTo(*ci.Email, "")
msg.SetTemplate("pass_remind.jet")
msg.AddVariable("username", user.Username)
msg.AddVariable("reminder", user.PassReminder)
msg.AddVariable("change_uid", user.Uid)
msg.AddVariable("change_auth", "TODO") // TODO: add change auth link
msg.AddVariable("change_auth", pchange.Authentication)
msg.Send()
} else {
uerr = errors.New("cannot find email address")
+8 -2
View File
@@ -11,8 +11,9 @@
package util
import (
"crypto/rand"
crand "crypto/rand"
"io"
mrand "math/rand"
)
// authAlphabet is the set of characters from which we generate auth strings.
@@ -24,7 +25,7 @@ const authStringLen = 32
// GenerateRandomAuthString generates a random authentication string.
func GenerateRandomAuthString() string {
b := make([]byte, authStringLen)
if _, err := io.ReadFull(rand.Reader, b); err != nil {
if _, err := io.ReadFull(crand.Reader, b); err != nil {
// can't happen (at least on a modern OS)
panic("failed to read random: " + err.Error())
}
@@ -33,3 +34,8 @@ func GenerateRandomAuthString() string {
}
return string(b)
}
// GenerateRandomConfirmationNumber generates a random 7-digit confirmation number.
func GenerateRandomConfirmationNumber() int32 {
return mrand.Int31n(9000000) + 1000000
}