author | Alberto Bertogli
<albertito@blitiri.com.ar> 2024-05-10 10:42:07 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2024-05-10 11:19:49 UTC |
parent | e6a94103772e49590c714fc99b4b0b4f562eee7e |
chasquid.go | +12 | -11 |
cmd/chasquid-util/chasquid-util.go | +2 | -2 |
cmd/chasquid-util/test_users.cmy | +10 | -1 |
internal/aliases/aliases.go | +4 | -4 |
internal/aliases/aliases_test.go | +7 | -3 |
internal/smtpsrv/server.go | +3 | -3 |
internal/userdb/userdb.go | +7 | -0 |
diff --git a/chasquid.go b/chasquid.go index ac152c8..aba7886 100644 --- a/chasquid.go +++ b/chasquid.go @@ -282,30 +282,31 @@ func loadCert(name, dir string, s *smtpsrv.Server) { // Helper to load a single domain configuration into the server. func loadDomain(name, dir string, s *smtpsrv.Server) { - log.Infof(" %s", name) s.AddDomain(name) - err := s.AddUserDB(name, dir+"/users") + nu, err := s.AddUserDB(name, dir+"/users") if err != nil { // If there is an error loading users, fail hard to make sure this is // noticed and fixed as soon as it happens. - log.Fatalf(" users file error: %v", err) + log.Fatalf(" %s: users file error: %v", name, err) } - err = s.AddAliasesFile(name, dir+"/aliases") + na, err := s.AddAliasesFile(name, dir+"/aliases") if err != nil { // If there's an error loading aliases, fail hard to make sure this is // noticed and fixed as soon as it happens. - log.Fatalf(" aliases file error: %v", err) + log.Fatalf(" %s: aliases file error: %v", name, err) } - err = loadDKIM(name, dir, s) + nd, err := loadDKIM(name, dir, s) if err != nil { // DKIM errors are fatal because if the user set DKIM up, then we // don't want it to be failing silently, as that could cause // deliverability issues. - log.Fatalf(" DKIM loading error: %v", err) + log.Fatalf(" %s: DKIM loading error: %v", name, err) } + + log.Infof(" %s (%d users, %d aliases, %d DKIM keys)", name, nu, na, nd) } func loadDovecot(s *smtpsrv.Server, userdb, client string) { @@ -318,11 +319,11 @@ func loadDovecot(s *smtpsrv.Server, userdb, client string) { } } -func loadDKIM(domain, dir string, s *smtpsrv.Server) error { +func loadDKIM(domain, dir string, s *smtpsrv.Server) (int, error) { glob := path.Clean(dir + "/dkim:*.pem") pems, err := filepath.Glob(glob) if err != nil { - return err + return 0, err } for _, pem := range pems { @@ -332,10 +333,10 @@ func loadDKIM(domain, dir string, s *smtpsrv.Server) error { err = s.AddDKIMSigner(domain, selector, pem) if err != nil { - return err + return 0, err } } - return nil + return len(pems), nil } // Read a directory, which must have at least some entries. diff --git a/cmd/chasquid-util/chasquid-util.go b/cmd/chasquid-util/chasquid-util.go index f319fdd..7333951 100644 --- a/cmd/chasquid-util/chasquid-util.go +++ b/cmd/chasquid-util/chasquid-util.go @@ -156,12 +156,12 @@ func checkUserDB() { Fatalf("Error: file %q does not exist", path) } - _, err := userdb.Load(path) + udb, err := userdb.Load(path) if err != nil { Fatalf("Error loading database: %v", err) } - fmt.Println("Database loaded") + fmt.Printf("Database loaded (%d users)\n", udb.Len()) } // chasquid-util user-add <user@domain> [--password=<password>] [--receive_only] diff --git a/cmd/chasquid-util/test_users.cmy b/cmd/chasquid-util/test_users.cmy index 0786d0a..27946f7 100644 --- a/cmd/chasquid-util/test_users.cmy +++ b/cmd/chasquid-util/test_users.cmy @@ -1,16 +1,25 @@ # Tests for user management commands. +# Start with a clean slate by removing the database, which could have been +# manipulated by previous tests. +c = rm -f .config/domains/domain/users +c wait 0 + c = ./chasquid-util -C=.config user-add user@domain --password=passwd c <- Added user c wait 0 c = ./chasquid-util -C=.config check-userdb domain -c <- Database loaded +c <- Database loaded (1 users) c wait 0 c = ./chasquid-util -C=.config user-add receive@domain --receive_only c <- Added user c wait 0 +c = ./chasquid-util -C=.config check-userdb domain +c <- Database loaded (2 users) +c wait 0 + c = ./chasquid-util -C=.config user-add xxx@domain \ --password=passwd --receive_only c <- Cannot specify both --receive_only and --password diff --git a/internal/aliases/aliases.go b/internal/aliases/aliases.go index e56054f..9f61286 100644 --- a/internal/aliases/aliases.go +++ b/internal/aliases/aliases.go @@ -347,7 +347,7 @@ func (v *Resolver) AddDomain(domain string) { // AddAliasesFile to the resolver. The file will be parsed, and an error // returned if it does not parse correctly. Note that the file not existing // does NOT result in an error. -func (v *Resolver) AddAliasesFile(domain, path string) error { +func (v *Resolver) AddAliasesFile(domain, path string) (int, error) { // We unconditionally add the domain and file on our list. // Even if the file does not exist now, it may later. This makes it be // consider when doing Reload. @@ -360,10 +360,10 @@ func (v *Resolver) AddAliasesFile(domain, path string) error { aliases, err := v.parseFile(domain, path) if os.IsNotExist(err) { - return nil + return 0, nil } if err != nil { - return err + return 0, err } // Add the aliases to the resolver, overriding any previous values. @@ -373,7 +373,7 @@ func (v *Resolver) AddAliasesFile(domain, path string) error { } v.mu.Unlock() - return nil + return len(aliases), nil } // AddAliasForTesting adds an alias to the resolver, for testing purposes. diff --git a/internal/aliases/aliases_test.go b/internal/aliases/aliases_test.go index 3f96ebf..e8ab0ea 100644 --- a/internal/aliases/aliases_test.go +++ b/internal/aliases/aliases_test.go @@ -437,7 +437,7 @@ func TestAddFile(t *testing.T) { defer os.Remove(fname) resolver := NewResolver(allUsersExist) - err := resolver.AddAliasesFile("dom", fname) + _, err := resolver.AddAliasesFile("dom", fname) if err != nil { t.Fatalf("error adding file: %v", err) } @@ -491,11 +491,15 @@ func TestRichFile(t *testing.T) { resolver := NewResolver(allUsersExist) resolver.DropChars = "." resolver.SuffixSep = "+" - err := resolver.AddAliasesFile("dom", fname) + n, err := resolver.AddAliasesFile("dom", fname) if err != nil { t.Fatalf("failed to add file: %v", err) } + if n != 11 { + t.Fatalf("expected 11 aliases, got %d", n) + } + cases := Cases{ {"a@dom", []Recipient{{"b@dom", EMAIL}}, nil}, {"c@dom", []Recipient{{"d@e", EMAIL}, {"f@dom", EMAIL}}, nil}, @@ -539,7 +543,7 @@ func TestManyFiles(t *testing.T) { resolver := NewResolver(allUsersExist) for domain, fname := range files { - err := resolver.AddAliasesFile(domain, fname) + _, err := resolver.AddAliasesFile(domain, fname) if err != nil { t.Fatalf("failed to add file: %v", err) } diff --git a/internal/smtpsrv/server.go b/internal/smtpsrv/server.go index b5e05ed..3232621 100644 --- a/internal/smtpsrv/server.go +++ b/internal/smtpsrv/server.go @@ -133,16 +133,16 @@ func (s *Server) AddDomain(d string) { } // AddUserDB adds a userdb file as backend for the domain. -func (s *Server) AddUserDB(domain, f string) error { +func (s *Server) AddUserDB(domain, f string) (int, error) { // Load the userdb, and register it unconditionally (so reload works even // if there are errors right now). udb, err := userdb.Load(f) s.authr.Register(domain, auth.WrapNoErrorBackend(udb)) - return err + return udb.Len(), err } // AddAliasesFile adds an aliases file for the given domain. -func (s *Server) AddAliasesFile(domain, f string) error { +func (s *Server) AddAliasesFile(domain, f string) (int, error) { return s.aliasesR.AddAliasesFile(domain, f) } diff --git a/internal/userdb/userdb.go b/internal/userdb/userdb.go index 0c13f75..3df95e1 100644 --- a/internal/userdb/userdb.go +++ b/internal/userdb/userdb.go @@ -207,6 +207,13 @@ func (db *DB) Exists(name string) bool { return present } +// Len returns the number of users in the database. +func (db *DB) Len() int { + db.mu.Lock() + defer db.mu.Unlock() + return len(db.db.Users) +} + /////////////////////////////////////////////////////////// // Encryption schemes //