introduce the password reminder infrastructure
This commit is contained in:
+30
-8
@@ -15,7 +15,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash/crc32"
|
"hash/crc32"
|
||||||
"math/rand"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -26,6 +25,33 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
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.
|
// User represents a user in the Amsterdam database.
|
||||||
type User struct {
|
type User struct {
|
||||||
Mutex sync.RWMutex
|
Mutex sync.RWMutex
|
||||||
@@ -140,7 +166,7 @@ func (u *User) ConfirmEMailAddress(confnum int32, remoteIP string) error {
|
|||||||
func (u *User) NewEmailConfirmationNumber() error {
|
func (u *User) NewEmailConfirmationNumber() error {
|
||||||
u.Mutex.Lock()
|
u.Mutex.Lock()
|
||||||
defer u.Mutex.Unlock()
|
defer u.Mutex.Unlock()
|
||||||
newnum := newEmailConfirmationNumber()
|
newnum := util.GenerateRandomConfirmationNumber()
|
||||||
_, err := amdb.Exec("UPDATE user SET email_confnum = ? WHERE uid = ?", newnum, u.Uid)
|
_, err := amdb.Exec("UPDATE user SET email_confnum = ? WHERE uid = ?", newnum, u.Uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
u.EmailConfNum = newnum
|
u.EmailConfNum = newnum
|
||||||
@@ -384,11 +410,6 @@ func AmAuthenticateUserByToken(authString string, remoteIP string) (*User, error
|
|||||||
return user, nil
|
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.
|
/* AmCreateNewUser creates a new user record in the database.
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* username - New user name.
|
* username - New user name.
|
||||||
@@ -421,7 +442,8 @@ func AmCreateNewUser(username string, password string, reminder string, dob *tim
|
|||||||
// Insert the user record.
|
// Insert the user record.
|
||||||
_, err2 := amdb.Exec(`INSERT INTO users (username, passhash, verify_email, lockout, email_confnum,
|
_, 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(), ?, '', ?)`,
|
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 {
|
if err2 != nil {
|
||||||
return nil, err2
|
return nil, err2
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
then the system can change your password for you. To do so, please visit
|
||||||
the following URL:
|
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
|
Your password will be changed and a new password will be E-mailed to you
|
||||||
at this address.
|
at this address.
|
||||||
|
|||||||
@@ -80,13 +80,14 @@ func Login(ctxt ui.AmContext) (string, any, error) {
|
|||||||
ci, uerr = user.ContactInfo()
|
ci, uerr = user.ContactInfo()
|
||||||
if uerr == nil {
|
if uerr == nil {
|
||||||
if ci != nil && ci.Email != nil && *ci.Email != "" {
|
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 := email.AmNewEmailMessage(ctxt.CurrentUserId(), ctxt.RemoteIP())
|
||||||
msg.AddTo(*ci.Email, "")
|
msg.AddTo(*ci.Email, "")
|
||||||
msg.SetTemplate("pass_remind.jet")
|
msg.SetTemplate("pass_remind.jet")
|
||||||
msg.AddVariable("username", user.Username)
|
msg.AddVariable("username", user.Username)
|
||||||
msg.AddVariable("reminder", user.PassReminder)
|
msg.AddVariable("reminder", user.PassReminder)
|
||||||
msg.AddVariable("change_uid", user.Uid)
|
msg.AddVariable("change_uid", user.Uid)
|
||||||
msg.AddVariable("change_auth", "TODO") // TODO: add change auth link
|
msg.AddVariable("change_auth", pchange.Authentication)
|
||||||
msg.Send()
|
msg.Send()
|
||||||
} else {
|
} else {
|
||||||
uerr = errors.New("cannot find email address")
|
uerr = errors.New("cannot find email address")
|
||||||
|
|||||||
+8
-2
@@ -11,8 +11,9 @@
|
|||||||
package util
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
crand "crypto/rand"
|
||||||
"io"
|
"io"
|
||||||
|
mrand "math/rand"
|
||||||
)
|
)
|
||||||
|
|
||||||
// authAlphabet is the set of characters from which we generate auth strings.
|
// 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.
|
// GenerateRandomAuthString generates a random authentication string.
|
||||||
func GenerateRandomAuthString() string {
|
func GenerateRandomAuthString() string {
|
||||||
b := make([]byte, authStringLen)
|
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)
|
// can't happen (at least on a modern OS)
|
||||||
panic("failed to read random: " + err.Error())
|
panic("failed to read random: " + err.Error())
|
||||||
}
|
}
|
||||||
@@ -33,3 +34,8 @@ func GenerateRandomAuthString() string {
|
|||||||
}
|
}
|
||||||
return string(b)
|
return string(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateRandomConfirmationNumber generates a random 7-digit confirmation number.
|
||||||
|
func GenerateRandomConfirmationNumber() int32 {
|
||||||
|
return mrand.Int31n(9000000) + 1000000
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user