Reorganize the store, add a FindAccounts method

This commit is contained in:
2018-03-08 00:46:22 +00:00
parent 442ca833ea
commit be7ca459a5
4 changed files with 53 additions and 40 deletions

View File

@@ -16,7 +16,7 @@ var (
// type Session implements the User interface for emersion/go-imap // type Session implements the User interface for emersion/go-imap
type Session struct { type Session struct {
ID uint64 ID uint64
Account *store.Account Account store.Account
ServiceName string ServiceName string
} }

View File

@@ -11,7 +11,7 @@ import (
// logged-in account per-session // logged-in account per-session
type sender struct { type sender struct {
msa *msa msa *msa
account *store.Account account store.Account
} }
func (s *sender) ServeSMTP(from string, to []string, r io.Reader) error { func (s *sender) ServeSMTP(from string, to []string, r io.Reader) error {

View File

@@ -1,18 +1,16 @@
package store package store
import ( import (
"fmt"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
// HashPassword turns a plaintext password into a crypt()ed string, using bcrypt type AccountInterface interface {
func HashPassword(password string) (string, error) { CreateAccount(*Account) error
b, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) FindAccount(string) (Account, error)
FindAccounts(...string) ([]Account, error)
return string(b), err FindAccountWithPassword(string, string) (Account, error)
}
func CheckPassword(hashed, plain string) bool {
return bcrypt.CompareHashAndPassword([]byte(hashed), []byte(plain)) == nil
} }
// Account is stored in the database as domains/<domain>/accounts/<id>/config // Account is stored in the database as domains/<domain>/accounts/<id>/config
@@ -32,3 +30,46 @@ type Account struct {
// As generated by HashPassword // As generated by HashPassword
PasswordHash string PasswordHash string
} }
// HashPassword turns a plaintext password into a crypt()ed string, using bcrypt
func HashPassword(password string) (string, error) {
b, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
return string(b), err
}
func CheckPassword(hashed, plain string) bool {
return bcrypt.CompareHashAndPassword([]byte(hashed), []byte(plain)) == nil
}
func (c *concrete) CreateAccount(account *Account) error {
return c.storm.Save(account)
}
func (c *concrete) FindAccount(username string) (Account, error) {
var account Account
return account, c.storm.One("Username", username, &account)
}
func (c *concrete) FindAccounts(usernames ...string) ([]Account, error) {
var accounts []Account
return accounts, c.storm.Find("Username", usernames, &accounts)
}
func (c *concrete) FindAccountWithPassword(username, password string) (Account, error) {
account, err := c.FindAccount(username)
if err != nil {
// Always do a bcrypt check to avoid timing attacks
_ = CheckPassword("", "")
return Account{}, err
}
if !CheckPassword(account.PasswordHash, password) {
return Account{}, fmt.Errorf("bad password")
}
return account, nil
}

View File

@@ -18,9 +18,7 @@ type Interface interface {
SetDomain(string) error SetDomain(string) error
SetTLS([]byte, []byte) error SetTLS([]byte, []byte) error
CreateAccount(*Account) error AccountInterface
FindAccount(string) (*Account, error)
FindAccountWithPassword(string, string) (*Account, error)
io.Closer io.Closer
} }
@@ -142,32 +140,6 @@ func (c *concrete) SetTLS(certPEM, keyPEM []byte) error {
return nil return nil
} }
func (c *concrete) CreateAccount(account *Account) error {
return c.storm.Save(account)
}
func (c *concrete) FindAccount(username string) (*Account, error) {
var account Account
return &account, c.storm.One("Username", username, &account)
}
func (c *concrete) FindAccountWithPassword(username, password string) (*Account, error) {
account, err := c.FindAccount(username)
if err != nil {
// Always do a bcrypt check to avoid timing attacks
_ = CheckPassword("", "")
return nil, err
}
if !CheckPassword(account.PasswordHash, password) {
return nil, fmt.Errorf("bad password")
}
return account, nil
}
func (c *concrete) Close() error { func (c *concrete) Close() error {
return c.storm.Close() return c.storm.Close()
} }