mirror of
https://github.com/decke/smtprelay.git
synced 2025-12-25 07:43:06 -07:00
Expand allowedUsers email field to support comma-separated and domains (#9)
* Expand allowedUsers email field to support comma-separated and domains Closes #8 * Refactor AuthFetch() to return AuthUser struct Also, this breaks out a parseLine() function which can be easily tested. * Ignore empty addrs after splitting commas This ignores a trailing comma * Add tests for auth parseLine() * Update documentation in smtprelay.ini * Fix bug where addrAllowed() was incorrectly case-sensitive * Update allowedUsers allowed domain format to require leading @ This disambiguates a local user ('john.smith') from a domain ('example.com')
This commit is contained in:
committed by
GitHub
parent
5c2e28ac36
commit
0e8986ca79
62
auth.go
62
auth.go
@@ -13,6 +13,12 @@ var (
|
||||
filename string
|
||||
)
|
||||
|
||||
type AuthUser struct {
|
||||
username string
|
||||
passwordHash string
|
||||
allowedAddresses []string
|
||||
}
|
||||
|
||||
func AuthLoadFile(file string) error {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
@@ -28,50 +34,66 @@ func AuthReady() bool {
|
||||
return (filename != "")
|
||||
}
|
||||
|
||||
// Returns bcrypt-hash, email
|
||||
// email can be empty in which case it is not checked
|
||||
func AuthFetch(username string) (string, string, error) {
|
||||
// Split a string and ignore empty results
|
||||
// https://stackoverflow.com/a/46798310/119527
|
||||
func splitstr(s string, sep rune) []string {
|
||||
return strings.FieldsFunc(s, func(c rune) bool { return c == sep })
|
||||
}
|
||||
|
||||
func parseLine(line string) *AuthUser {
|
||||
parts := strings.Fields(line)
|
||||
|
||||
if len(parts) < 2 || len(parts) > 3 {
|
||||
return nil
|
||||
}
|
||||
|
||||
user := AuthUser{
|
||||
username: parts[0],
|
||||
passwordHash: parts[1],
|
||||
allowedAddresses: nil,
|
||||
}
|
||||
|
||||
if len(parts) >= 3 {
|
||||
user.allowedAddresses = splitstr(parts[2], ',')
|
||||
}
|
||||
|
||||
return &user
|
||||
}
|
||||
|
||||
func AuthFetch(username string) (*AuthUser, error) {
|
||||
if !AuthReady() {
|
||||
return "", "", errors.New("Authentication file not specified. Call LoadFile() first")
|
||||
return nil, errors.New("Authentication file not specified. Call LoadFile() first")
|
||||
}
|
||||
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
parts := strings.Fields(scanner.Text())
|
||||
|
||||
if len(parts) < 2 || len(parts) > 3 {
|
||||
user := parseLine(scanner.Text())
|
||||
if user == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.ToLower(username) != strings.ToLower(parts[0]) {
|
||||
if strings.ToLower(username) != strings.ToLower(user.username) {
|
||||
continue
|
||||
}
|
||||
|
||||
hash := parts[1]
|
||||
email := ""
|
||||
|
||||
if len(parts) >= 3 {
|
||||
email = parts[2]
|
||||
}
|
||||
|
||||
return hash, email, nil
|
||||
return user, nil
|
||||
}
|
||||
|
||||
return "", "", errors.New("User not found")
|
||||
return nil, errors.New("User not found")
|
||||
}
|
||||
|
||||
func AuthCheckPassword(username string, secret string) error {
|
||||
hash, _, err := AuthFetch(username)
|
||||
user, err := AuthFetch(username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if bcrypt.CompareHashAndPassword([]byte(hash), []byte(secret)) == nil {
|
||||
if bcrypt.CompareHashAndPassword([]byte(user.passwordHash), []byte(secret)) == nil {
|
||||
return nil
|
||||
}
|
||||
return errors.New("Password invalid")
|
||||
|
||||
Reference in New Issue
Block a user