feat: add login name migration
This commit is contained in:
@@ -49,9 +49,9 @@ func (a *App) handleAdminOverview(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) handleListUsers(w http.ResponseWriter, r *http.Request) {
|
func (a *App) handleListUsers(w http.ResponseWriter, r *http.Request) {
|
||||||
rows, err := a.db.QueryContext(r.Context(), `SELECT u.id,u.email,u.display_name,u.role,u.disabled,u.two_factor_enabled,u.mailbox_limit_override,u.created_at,COUNT(mb.id),COALESCE(GROUP_CONCAT(mb.address), '')
|
rows, err := a.db.QueryContext(r.Context(), `SELECT u.id,u.login_name,u.email,u.display_name,u.role,u.disabled,u.two_factor_enabled,u.mailbox_limit_override,u.created_at,COUNT(mb.id),COALESCE(GROUP_CONCAT(mb.address), '')
|
||||||
FROM users u LEFT JOIN mailboxes mb ON mb.user_id=u.id
|
FROM users u LEFT JOIN mailboxes mb ON mb.user_id=u.id
|
||||||
GROUP BY u.id,u.email,u.display_name,u.role,u.disabled,u.two_factor_enabled,u.mailbox_limit_override,u.created_at
|
GROUP BY u.id,u.login_name,u.email,u.display_name,u.role,u.disabled,u.two_factor_enabled,u.mailbox_limit_override,u.created_at
|
||||||
ORDER BY u.created_at DESC`)
|
ORDER BY u.created_at DESC`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respondError(w, http.StatusInternalServerError, "failed to list users")
|
respondError(w, http.StatusInternalServerError, "failed to list users")
|
||||||
@@ -64,7 +64,7 @@ func (a *App) handleListUsers(w http.ResponseWriter, r *http.Request) {
|
|||||||
var disabled, twoFactorEnabled int
|
var disabled, twoFactorEnabled int
|
||||||
var mailboxLimitOverride sql.NullInt64
|
var mailboxLimitOverride sql.NullInt64
|
||||||
var created, mailboxCSV string
|
var created, mailboxCSV string
|
||||||
if err := rows.Scan(&item.ID, &item.Email, &item.DisplayName, &item.Role, &disabled, &twoFactorEnabled, &mailboxLimitOverride, &created, &item.MailboxCount, &mailboxCSV); err != nil {
|
if err := rows.Scan(&item.ID, &item.LoginName, &item.Email, &item.DisplayName, &item.Role, &disabled, &twoFactorEnabled, &mailboxLimitOverride, &created, &item.MailboxCount, &mailboxCSV); err != nil {
|
||||||
respondError(w, http.StatusInternalServerError, "failed to scan users")
|
respondError(w, http.StatusInternalServerError, "failed to scan users")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -154,8 +154,8 @@ func (a *App) handleCreateUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
if _, err = tx.ExecContext(r.Context(), `INSERT INTO users(id,email,display_name,role,password_hash,disabled,mailbox_limit_override,created_at,updated_at)
|
if _, err = tx.ExecContext(r.Context(), `INSERT INTO users(id,login_name,email,display_name,role,password_hash,disabled,mailbox_limit_override,created_at,updated_at)
|
||||||
VALUES(?,?,?,?,?,?,?,?,?)`, id, loginName, displayName, role, string(passwordHash), boolInt(req.Disabled), nullableInt(mailboxLimitOverride), now, now); err != nil {
|
VALUES(?,?,?,?,?,?,?,?,?,?)`, id, loginName, loginName, displayName, role, string(passwordHash), boolInt(req.Disabled), nullableInt(mailboxLimitOverride), now, now); err != nil {
|
||||||
badRequest(w, err)
|
badRequest(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -598,7 +598,7 @@ func (a *App) handleCreateMailbox(w http.ResponseWriter, r *http.Request) {
|
|||||||
badRequest(w, err)
|
badRequest(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = tx.QueryRowContext(r.Context(), `SELECT id FROM users WHERE email=? AND disabled=0`, ownerLoginName).Scan(&userID)
|
err = tx.QueryRowContext(r.Context(), `SELECT id FROM users WHERE (login_name=? OR email=?) AND disabled=0`, ownerLoginName, ownerLoginName).Scan(&userID)
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
passwordHash, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
|
passwordHash, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -610,8 +610,8 @@ func (a *App) handleCreateMailbox(w http.ResponseWriter, r *http.Request) {
|
|||||||
if !strings.EqualFold(ownerLoginName, address) {
|
if !strings.EqualFold(ownerLoginName, address) {
|
||||||
ownerDisplayName = ownerLoginName
|
ownerDisplayName = ownerLoginName
|
||||||
}
|
}
|
||||||
_, err = tx.ExecContext(r.Context(), `INSERT INTO users(id,email,display_name,role,password_hash,disabled,created_at,updated_at)
|
_, err = tx.ExecContext(r.Context(), `INSERT INTO users(id,login_name,email,display_name,role,password_hash,disabled,created_at,updated_at)
|
||||||
VALUES(?,?,?,?,?,?,?,?)`, userID, ownerLoginName, ownerDisplayName, role, string(passwordHash), 0, now, now)
|
VALUES(?,?,?,?,?,?,?,?,?)`, userID, ownerLoginName, ownerLoginName, ownerDisplayName, role, string(passwordHash), 0, now, now)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
badRequest(w, err)
|
badRequest(w, err)
|
||||||
return
|
return
|
||||||
@@ -1077,15 +1077,15 @@ func (a *App) domainByID(ctx context.Context, id string) (*Domain, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) adminUserByID(ctx context.Context, id string) (*AdminUser, error) {
|
func (a *App) adminUserByID(ctx context.Context, id string) (*AdminUser, error) {
|
||||||
row := a.db.QueryRowContext(ctx, `SELECT u.id,u.email,u.display_name,u.role,u.disabled,u.two_factor_enabled,u.mailbox_limit_override,u.created_at,COUNT(mb.id),COALESCE(GROUP_CONCAT(mb.address), '')
|
row := a.db.QueryRowContext(ctx, `SELECT u.id,u.login_name,u.email,u.display_name,u.role,u.disabled,u.two_factor_enabled,u.mailbox_limit_override,u.created_at,COUNT(mb.id),COALESCE(GROUP_CONCAT(mb.address), '')
|
||||||
FROM users u LEFT JOIN mailboxes mb ON mb.user_id=u.id
|
FROM users u LEFT JOIN mailboxes mb ON mb.user_id=u.id
|
||||||
WHERE u.id=?
|
WHERE u.id=?
|
||||||
GROUP BY u.id,u.email,u.display_name,u.role,u.disabled,u.two_factor_enabled,u.mailbox_limit_override,u.created_at`, id)
|
GROUP BY u.id,u.login_name,u.email,u.display_name,u.role,u.disabled,u.two_factor_enabled,u.mailbox_limit_override,u.created_at`, id)
|
||||||
var item AdminUser
|
var item AdminUser
|
||||||
var disabled, twoFactorEnabled int
|
var disabled, twoFactorEnabled int
|
||||||
var mailboxLimitOverride sql.NullInt64
|
var mailboxLimitOverride sql.NullInt64
|
||||||
var created, mailboxCSV string
|
var created, mailboxCSV string
|
||||||
if err := row.Scan(&item.ID, &item.Email, &item.DisplayName, &item.Role, &disabled, &twoFactorEnabled, &mailboxLimitOverride, &created, &item.MailboxCount, &mailboxCSV); err != nil {
|
if err := row.Scan(&item.ID, &item.LoginName, &item.Email, &item.DisplayName, &item.Role, &disabled, &twoFactorEnabled, &mailboxLimitOverride, &created, &item.MailboxCount, &mailboxCSV); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
item.Disabled = intBool(disabled)
|
item.Disabled = intBool(disabled)
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ func (a *App) migrate(ctx context.Context) error {
|
|||||||
stmts := []string{
|
stmts := []string{
|
||||||
`CREATE TABLE IF NOT EXISTS users (
|
`CREATE TABLE IF NOT EXISTS users (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
|
login_name TEXT NOT NULL DEFAULT '',
|
||||||
email TEXT NOT NULL UNIQUE,
|
email TEXT NOT NULL UNIQUE,
|
||||||
display_name TEXT NOT NULL,
|
display_name TEXT NOT NULL,
|
||||||
role TEXT NOT NULL CHECK(role IN ('admin','user')),
|
role TEXT NOT NULL CHECK(role IN ('admin','user')),
|
||||||
@@ -601,6 +602,9 @@ func (a *App) migrate(ctx context.Context) error {
|
|||||||
if err := a.rebuildHTMLOnlyMessageSnippets(ctx); err != nil {
|
if err := a.rebuildHTMLOnlyMessageSnippets(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := a.migrateUserLoginNames(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := a.migrateUsersForTwoFactor(ctx); err != nil {
|
if err := a.migrateUsersForTwoFactor(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1054,6 +1058,99 @@ func (a *App) migrateUsersForTwoFactor(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) migrateUserLoginNames(ctx context.Context) error {
|
||||||
|
rows, err := a.db.QueryContext(ctx, `PRAGMA table_info(users)`)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
columns := map[string]bool{}
|
||||||
|
for rows.Next() {
|
||||||
|
var cid int
|
||||||
|
var name, typ string
|
||||||
|
var notnull int
|
||||||
|
var dflt any
|
||||||
|
var pk int
|
||||||
|
if err := rows.Scan(&cid, &name, &typ, ¬null, &dflt, &pk); err != nil {
|
||||||
|
rows.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
columns[name] = true
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
rows.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := rows.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !columns["login_name"] {
|
||||||
|
if _, err := a.db.ExecContext(ctx, `ALTER TABLE users ADD COLUMN login_name TEXT NOT NULL DEFAULT ''`); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type loginUser struct {
|
||||||
|
id string
|
||||||
|
email string
|
||||||
|
loginName string
|
||||||
|
}
|
||||||
|
userRows, err := a.db.QueryContext(ctx, `SELECT id,email,login_name FROM users ORDER BY created_at,id`)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
items := []loginUser{}
|
||||||
|
localCounts := map[string]int{}
|
||||||
|
used := map[string]bool{}
|
||||||
|
for userRows.Next() {
|
||||||
|
var item loginUser
|
||||||
|
if err := userRows.Scan(&item.id, &item.email, &item.loginName); err != nil {
|
||||||
|
userRows.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
item.email = normalizeEmail(item.email)
|
||||||
|
item.loginName = normalizeLoginName(item.loginName)
|
||||||
|
if item.loginName != "" {
|
||||||
|
used[item.loginName] = true
|
||||||
|
}
|
||||||
|
if strings.Contains(item.email, "@") {
|
||||||
|
localCounts[strings.SplitN(item.email, "@", 2)[0]]++
|
||||||
|
}
|
||||||
|
items = append(items, item)
|
||||||
|
}
|
||||||
|
if err := userRows.Err(); err != nil {
|
||||||
|
userRows.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := userRows.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
now := a.now().UTC().Format(time.RFC3339Nano)
|
||||||
|
for _, item := range items {
|
||||||
|
if item.loginName != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
candidate := item.email
|
||||||
|
if strings.Contains(item.email, "@") {
|
||||||
|
local := strings.SplitN(item.email, "@", 2)[0]
|
||||||
|
if localCounts[local] == 1 && !used[local] {
|
||||||
|
candidate = local
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if candidate == "" {
|
||||||
|
candidate = normalizeLoginName(item.id)
|
||||||
|
}
|
||||||
|
base := candidate
|
||||||
|
for suffix := 2; used[candidate]; suffix++ {
|
||||||
|
candidate = fmt.Sprintf("%s-%d", base, suffix)
|
||||||
|
}
|
||||||
|
if _, err := a.db.ExecContext(ctx, `UPDATE users SET login_name=?, updated_at=? WHERE id=?`, candidate, now, item.id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
used[candidate] = true
|
||||||
|
}
|
||||||
|
_, err = a.db.ExecContext(ctx, `CREATE UNIQUE INDEX IF NOT EXISTS idx_users_login_name ON users(login_name) WHERE login_name <> ''`)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) migrateUserMailboxLimitOverride(ctx context.Context) error {
|
func (a *App) migrateUserMailboxLimitOverride(ctx context.Context) error {
|
||||||
rows, err := a.db.QueryContext(ctx, `PRAGMA table_info(users)`)
|
rows, err := a.db.QueryContext(ctx, `PRAGMA table_info(users)`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1261,8 +1358,9 @@ func (a *App) seed(ctx context.Context) error {
|
|||||||
if adminEmail == "" || !strings.Contains(adminEmail, "@") {
|
if adminEmail == "" || !strings.Contains(adminEmail, "@") {
|
||||||
return errors.New("invalid admin email")
|
return errors.New("invalid admin email")
|
||||||
}
|
}
|
||||||
if _, err := a.db.ExecContext(ctx, `INSERT INTO users(id,email,display_name,role,password_hash,disabled,created_at,updated_at)
|
adminLoginName := normalizeLoginName(strings.SplitN(adminEmail, "@", 2)[0])
|
||||||
VALUES(?,?,?,?,?,?,?,?)`, userID, adminEmail, "NewSzxcn Admin", "admin", string(passwordHash), 0, now, now); err != nil {
|
if _, err := a.db.ExecContext(ctx, `INSERT INTO users(id,login_name,email,display_name,role,password_hash,disabled,created_at,updated_at)
|
||||||
|
VALUES(?,?,?,?,?,?,?,?,?)`, userID, adminLoginName, adminEmail, "NewSzxcn Admin", "admin", string(passwordHash), 0, now, now); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
a.log.Warn("created default administrator; change LANQIN_ADMIN_PASSWORD in production", "email", adminEmail)
|
a.log.Warn("created default administrator; change LANQIN_ADMIN_PASSWORD in production", "email", adminEmail)
|
||||||
|
|||||||
@@ -132,8 +132,8 @@ func (a *App) handleRegister(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
now := a.now().UTC().Format(time.RFC3339Nano)
|
now := a.now().UTC().Format(time.RFC3339Nano)
|
||||||
userID := newID("usr")
|
userID := newID("usr")
|
||||||
if _, err := a.db.ExecContext(r.Context(), `INSERT INTO users(id,email,display_name,role,password_hash,disabled,created_at,updated_at)
|
if _, err := a.db.ExecContext(r.Context(), `INSERT INTO users(id,login_name,email,display_name,role,password_hash,disabled,created_at,updated_at)
|
||||||
VALUES(?,?,?,?,?,?,?,?)`, userID, email, displayName, "user", string(passwordHash), 0, now, now); err != nil {
|
VALUES(?,?,?,?,?,?,?,?,?)`, userID, email, email, displayName, "user", string(passwordHash), 0, now, now); err != nil {
|
||||||
if strings.Contains(strings.ToLower(err.Error()), "unique") {
|
if strings.Contains(strings.ToLower(err.Error()), "unique") {
|
||||||
respondError(w, http.StatusConflict, "该邮箱已被注册")
|
respondError(w, http.StatusConflict, "该邮箱已被注册")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -826,7 +826,7 @@ func (a *App) resolveMailboxOwnerTx(ctx context.Context, tx *sql.Tx, userID, own
|
|||||||
return "", errors.New("invalid owner email")
|
return "", errors.New("invalid owner email")
|
||||||
}
|
}
|
||||||
var existing string
|
var existing string
|
||||||
err := tx.QueryRowContext(ctx, `SELECT id FROM users WHERE email=? AND disabled=0`, email).Scan(&existing)
|
err := tx.QueryRowContext(ctx, `SELECT id FROM users WHERE (login_name=? OR email=?) AND disabled=0`, email, email).Scan(&existing)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return existing, nil
|
return existing, nil
|
||||||
}
|
}
|
||||||
@@ -838,8 +838,8 @@ func (a *App) resolveMailboxOwnerTx(ctx context.Context, tx *sql.Tx, userID, own
|
|||||||
if displayName == "" {
|
if displayName == "" {
|
||||||
displayName = email
|
displayName = email
|
||||||
}
|
}
|
||||||
_, err = tx.ExecContext(ctx, `INSERT INTO users(id,email,display_name,role,password_hash,disabled,created_at,updated_at)
|
_, err = tx.ExecContext(ctx, `INSERT INTO users(id,login_name,email,display_name,role,password_hash,disabled,created_at,updated_at)
|
||||||
VALUES(?,?,?,?,?,?,?,?)`, userID, email, displayName, "user", passwordHash, 0, now, now)
|
VALUES(?,?,?,?,?,?,?,?,?)`, userID, email, email, displayName, "user", passwordHash, 0, now, now)
|
||||||
return userID, err
|
return userID, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -270,14 +270,14 @@ func (a *App) authenticateRequest(r *http.Request) (*User, error) {
|
|||||||
if err != nil || cookie.Value == "" {
|
if err != nil || cookie.Value == "" {
|
||||||
return nil, errors.New("no session")
|
return nil, errors.New("no session")
|
||||||
}
|
}
|
||||||
row := a.db.QueryRowContext(r.Context(), `SELECT u.id,u.email,u.display_name,u.role,u.disabled,u.two_factor_enabled,u.mailbox_limit_override,u.created_at
|
row := a.db.QueryRowContext(r.Context(), `SELECT u.id,u.login_name,u.email,u.display_name,u.role,u.disabled,u.two_factor_enabled,u.mailbox_limit_override,u.created_at
|
||||||
FROM sessions s JOIN users u ON u.id=s.user_id
|
FROM sessions s JOIN users u ON u.id=s.user_id
|
||||||
WHERE s.token_hash=? AND s.expires_at > ?`, hashToken(cookie.Value), a.now().UTC().Format(time.RFC3339Nano))
|
WHERE s.token_hash=? AND s.expires_at > ?`, hashToken(cookie.Value), a.now().UTC().Format(time.RFC3339Nano))
|
||||||
var u User
|
var u User
|
||||||
var disabled, twoFactorEnabled int
|
var disabled, twoFactorEnabled int
|
||||||
var mailboxLimitOverride sql.NullInt64
|
var mailboxLimitOverride sql.NullInt64
|
||||||
var created string
|
var created string
|
||||||
if err := row.Scan(&u.ID, &u.Email, &u.DisplayName, &u.Role, &disabled, &twoFactorEnabled, &mailboxLimitOverride, &created); err != nil {
|
if err := row.Scan(&u.ID, &u.LoginName, &u.Email, &u.DisplayName, &u.Role, &disabled, &twoFactorEnabled, &mailboxLimitOverride, &created); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
u.Disabled = intBool(disabled)
|
u.Disabled = intBool(disabled)
|
||||||
@@ -299,7 +299,7 @@ func (a *App) authenticateAPIToken(r *http.Request) (*User, map[string]bool, err
|
|||||||
return nil, nil, errors.New("no api token")
|
return nil, nil, errors.New("no api token")
|
||||||
}
|
}
|
||||||
now := a.now().UTC().Format(time.RFC3339Nano)
|
now := a.now().UTC().Format(time.RFC3339Nano)
|
||||||
row := a.db.QueryRowContext(r.Context(), `SELECT at.id,at.scopes_json,u.id,u.email,u.display_name,u.role,u.disabled,u.two_factor_enabled,u.mailbox_limit_override,u.created_at
|
row := a.db.QueryRowContext(r.Context(), `SELECT at.id,at.scopes_json,u.id,u.login_name,u.email,u.display_name,u.role,u.disabled,u.two_factor_enabled,u.mailbox_limit_override,u.created_at
|
||||||
FROM api_tokens at JOIN users u ON u.id=at.user_id
|
FROM api_tokens at JOIN users u ON u.id=at.user_id
|
||||||
WHERE at.token_hash=? AND at.disabled=0 AND at.expires_at > ?`, hashToken(token), now)
|
WHERE at.token_hash=? AND at.disabled=0 AND at.expires_at > ?`, hashToken(token), now)
|
||||||
var tokenID, scopesJSON string
|
var tokenID, scopesJSON string
|
||||||
@@ -307,7 +307,7 @@ func (a *App) authenticateAPIToken(r *http.Request) (*User, map[string]bool, err
|
|||||||
var disabled, twoFactorEnabled int
|
var disabled, twoFactorEnabled int
|
||||||
var mailboxLimitOverride sql.NullInt64
|
var mailboxLimitOverride sql.NullInt64
|
||||||
var created string
|
var created string
|
||||||
if err := row.Scan(&tokenID, &scopesJSON, &u.ID, &u.Email, &u.DisplayName, &u.Role, &disabled, &twoFactorEnabled, &mailboxLimitOverride, &created); err != nil {
|
if err := row.Scan(&tokenID, &scopesJSON, &u.ID, &u.LoginName, &u.Email, &u.DisplayName, &u.Role, &disabled, &twoFactorEnabled, &mailboxLimitOverride, &created); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
u.Disabled = intBool(disabled)
|
u.Disabled = intBool(disabled)
|
||||||
@@ -337,13 +337,16 @@ func bearerToken(r *http.Request) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) userByEmail(ctx context.Context, email string) (*User, string, error) {
|
func (a *App) userByEmail(ctx context.Context, email string) (*User, string, error) {
|
||||||
row := a.db.QueryRowContext(ctx, `SELECT id,email,display_name,role,password_hash,disabled,two_factor_enabled,mailbox_limit_override,created_at FROM users WHERE email=?`, email)
|
loginName := normalizeLoginName(email)
|
||||||
|
row := a.db.QueryRowContext(ctx, `SELECT id,login_name,email,display_name,role,password_hash,disabled,two_factor_enabled,mailbox_limit_override,created_at
|
||||||
|
FROM users WHERE login_name=? OR email=?
|
||||||
|
ORDER BY CASE WHEN login_name=? THEN 0 ELSE 1 END LIMIT 1`, loginName, loginName, loginName)
|
||||||
var u User
|
var u User
|
||||||
var passwordHash string
|
var passwordHash string
|
||||||
var disabled, twoFactorEnabled int
|
var disabled, twoFactorEnabled int
|
||||||
var mailboxLimitOverride sql.NullInt64
|
var mailboxLimitOverride sql.NullInt64
|
||||||
var created string
|
var created string
|
||||||
if err := row.Scan(&u.ID, &u.Email, &u.DisplayName, &u.Role, &passwordHash, &disabled, &twoFactorEnabled, &mailboxLimitOverride, &created); err != nil {
|
if err := row.Scan(&u.ID, &u.LoginName, &u.Email, &u.DisplayName, &u.Role, &passwordHash, &disabled, &twoFactorEnabled, &mailboxLimitOverride, &created); err != nil {
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
return nil, "", errNotFound
|
return nil, "", errNotFound
|
||||||
}
|
}
|
||||||
@@ -360,12 +363,12 @@ func (a *App) userByEmail(ctx context.Context, email string) (*User, string, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) userByID(ctx context.Context, id string) (*User, error) {
|
func (a *App) userByID(ctx context.Context, id string) (*User, error) {
|
||||||
row := a.db.QueryRowContext(ctx, `SELECT id,email,display_name,role,disabled,two_factor_enabled,mailbox_limit_override,created_at FROM users WHERE id=?`, id)
|
row := a.db.QueryRowContext(ctx, `SELECT id,login_name,email,display_name,role,disabled,two_factor_enabled,mailbox_limit_override,created_at FROM users WHERE id=?`, id)
|
||||||
var u User
|
var u User
|
||||||
var disabled, twoFactorEnabled int
|
var disabled, twoFactorEnabled int
|
||||||
var mailboxLimitOverride sql.NullInt64
|
var mailboxLimitOverride sql.NullInt64
|
||||||
var created string
|
var created string
|
||||||
if err := row.Scan(&u.ID, &u.Email, &u.DisplayName, &u.Role, &disabled, &twoFactorEnabled, &mailboxLimitOverride, &created); err != nil {
|
if err := row.Scan(&u.ID, &u.LoginName, &u.Email, &u.DisplayName, &u.Role, &disabled, &twoFactorEnabled, &mailboxLimitOverride, &created); err != nil {
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
return nil, errNotFound
|
return nil, errNotFound
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,12 +122,12 @@ func (a *App) deleteLoginChallenge(ctx context.Context, id string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) loadUserAuthByID(ctx context.Context, id string) (*User, string, error) {
|
func (a *App) loadUserAuthByID(ctx context.Context, id string) (*User, string, error) {
|
||||||
row := a.db.QueryRowContext(ctx, `SELECT id,email,display_name,role,disabled,two_factor_enabled,two_factor_secret,mailbox_limit_override,created_at FROM users WHERE id=?`, id)
|
row := a.db.QueryRowContext(ctx, `SELECT id,login_name,email,display_name,role,disabled,two_factor_enabled,two_factor_secret,mailbox_limit_override,created_at FROM users WHERE id=?`, id)
|
||||||
var u User
|
var u User
|
||||||
var disabled, twoFactorEnabled int
|
var disabled, twoFactorEnabled int
|
||||||
var mailboxLimitOverride sql.NullInt64
|
var mailboxLimitOverride sql.NullInt64
|
||||||
var secret, created string
|
var secret, created string
|
||||||
if err := row.Scan(&u.ID, &u.Email, &u.DisplayName, &u.Role, &disabled, &twoFactorEnabled, &secret, &mailboxLimitOverride, &created); err != nil {
|
if err := row.Scan(&u.ID, &u.LoginName, &u.Email, &u.DisplayName, &u.Role, &disabled, &twoFactorEnabled, &secret, &mailboxLimitOverride, &created); err != nil {
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
return nil, "", errNotFound
|
return nil, "", errNotFound
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user