mirror of
https://blitiri.com.ar/repos/chasquid
synced 2025-12-19 14:57:04 +00:00
Normalize local usernames using PRECIS
This patch implements local username normalization using PRECIS (https://tools.ietf.org/html/rfc7564, https://tools.ietf.org/html/rfc7613) It makes chasquid accept local email and authentication regardless of the case. It covers both userdb and aliases. Note that non-local usernames remain untouched.
This commit is contained in:
@@ -37,12 +37,11 @@ import (
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/crypto/scrypt"
|
||||
|
||||
"blitiri.com.ar/go/chasquid/internal/normalize"
|
||||
"blitiri.com.ar/go/chasquid/internal/protoio"
|
||||
)
|
||||
|
||||
@@ -55,7 +54,7 @@ type DB struct {
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidUsername = errors.New("username contains invalid characters")
|
||||
ErrInvalidUsername = errors.New("invalid username")
|
||||
)
|
||||
|
||||
func New(fname string) *DB {
|
||||
@@ -107,15 +106,6 @@ func (db *DB) Write() error {
|
||||
return protoio.WriteTextMessage(db.fname, db.db, 0660)
|
||||
}
|
||||
|
||||
// Does this user exist in the database?
|
||||
func (db *DB) Exists(user string) bool {
|
||||
db.mu.RLock()
|
||||
_, ok := db.db.Users[user]
|
||||
db.mu.RUnlock()
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
// Is this password valid for the user?
|
||||
func (db *DB) Authenticate(name, plainPassword string) bool {
|
||||
db.mu.RLock()
|
||||
@@ -142,23 +132,10 @@ func (p *Password) PasswordMatches(plain string) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the given user name is valid.
|
||||
// User names have to be UTF-8, and must not have some particular characters,
|
||||
// including whitespace.
|
||||
func ValidUsername(name string) bool {
|
||||
return utf8.ValidString(name) &&
|
||||
!strings.ContainsAny(name, illegalUsernameChars)
|
||||
}
|
||||
|
||||
// Illegal characters. Only whitespace for now, to prevent/minimize the
|
||||
// chances of parsing issues.
|
||||
// TODO: do we want to stop other characters, specifically about email? Or
|
||||
// keep this generic and handle the mail-specific filtering in chasquid?
|
||||
const illegalUsernameChars = "\t\n\v\f\r \xa0\x85"
|
||||
|
||||
// Add a user to the database. If the user is already present, override it.
|
||||
// Note we enforce that the name has been normalized previously.
|
||||
func (db *DB) AddUser(name, plainPassword string) error {
|
||||
if !ValidUsername(name) {
|
||||
if norm, err := normalize.User(name); err != nil || name != norm {
|
||||
return ErrInvalidUsername
|
||||
}
|
||||
|
||||
@@ -195,7 +172,6 @@ func (db *DB) AddUser(name, plainPassword string) error {
|
||||
// otherwise.
|
||||
func (db *DB) RemoveUser(name string) bool {
|
||||
db.mu.Lock()
|
||||
|
||||
_, present := db.db.Users[name]
|
||||
delete(db.db.Users, name)
|
||||
db.mu.Unlock()
|
||||
|
||||
@@ -129,7 +129,7 @@ func TestWrite(t *testing.T) {
|
||||
|
||||
db = mustLoad(t, fname)
|
||||
for _, name := range []string{"user1", "ñoño"} {
|
||||
if !db.Exists(name) {
|
||||
if !db.HasUser(name) {
|
||||
t.Errorf("user %q not in database", name)
|
||||
}
|
||||
if db.db.Users[name].GetScheme() == nil {
|
||||
@@ -179,8 +179,17 @@ func TestInvalidUsername(t *testing.T) {
|
||||
defer removeIfSuccessful(t, fname)
|
||||
db := mustLoad(t, fname)
|
||||
|
||||
// Names that are invalid.
|
||||
names := []string{
|
||||
" ", " ", "a b", "ñ ñ", "a\xa0b", "a\x85b", "a\nb", "a\tb", "a\xffb"}
|
||||
// Contain various types of spaces.
|
||||
" ", " ", "a b", "ñ ñ", "a\xa0b", "a\x85b", "a\nb", "a\tb", "a\xffb",
|
||||
|
||||
// Contain characters not allowed by PRECIS.
|
||||
"\u00b9", "\u2163",
|
||||
|
||||
// Names that are not normalized, but would otherwise be valid.
|
||||
"A", "Ñ",
|
||||
}
|
||||
for _, name := range names {
|
||||
err := db.AddUser(name, "passwd")
|
||||
if err == nil {
|
||||
@@ -289,7 +298,7 @@ func TestHasUser(t *testing.T) {
|
||||
defer removeIfSuccessful(t, fname)
|
||||
db := mustLoad(t, fname)
|
||||
|
||||
if ok := db.HasUser("unknown"); ok {
|
||||
if db.HasUser("unknown") {
|
||||
t.Errorf("unknown user exists")
|
||||
}
|
||||
|
||||
@@ -297,15 +306,15 @@ func TestHasUser(t *testing.T) {
|
||||
t.Fatalf("error adding user: %v", err)
|
||||
}
|
||||
|
||||
if ok := db.HasUser("unknown"); ok {
|
||||
if db.HasUser("unknown") {
|
||||
t.Errorf("unknown user exists")
|
||||
}
|
||||
|
||||
if ok := db.HasUser("user"); !ok {
|
||||
if !db.HasUser("user") {
|
||||
t.Errorf("known user does not exist")
|
||||
}
|
||||
|
||||
if ok := db.HasUser("user"); !ok {
|
||||
if !db.HasUser("user") {
|
||||
t.Errorf("known user does not exist")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user