git » chasquid » commit 3d06fb3

Turn chasquid-userdb into chasquid-util

author Alberto Bertogli
2016-09-23 23:44:13 UTC
committer Alberto Bertogli
2016-10-09 23:51:04 UTC
parent 859d4733f8429e86dbf04af06f95848602c258f6

Turn chasquid-userdb into chasquid-util

This patch removes chasquid-userdb and adds a more generic and extensive
chasquid-util, that supports various operations on user databases as well as
aliases lookups.

The code is not very pretty but for now I took a more practical approach, the
tool is ancillary and can be tidied up later.

chasquid.go +1 -0
cmd/chasquid-userdb/chasquid-userdb.go +0 -92
cmd/chasquid-util/chasquid-util.go +198 -0
internal/aliases/aliases.go +3 -3
internal/config/config.go +1 -2
test/util/lib.sh +2 -4

diff --git a/chasquid.go b/chasquid.go
index 462619e..ee60ef5 100644
--- a/chasquid.go
+++ b/chasquid.go
@@ -60,6 +60,7 @@ func main() {
 	if err != nil {
 		glog.Fatalf("Error reading config")
 	}
+	config.LogConfig(conf)
 
 	// Change to the config dir.
 	// This allow us to use relative paths for configuration directories.
diff --git a/cmd/chasquid-userdb/chasquid-userdb.go b/cmd/chasquid-userdb/chasquid-userdb.go
deleted file mode 100644
index 8f6f7e6..0000000
--- a/cmd/chasquid-userdb/chasquid-userdb.go
+++ /dev/null
@@ -1,92 +0,0 @@
-package main
-
-import (
-	"bytes"
-	"flag"
-	"fmt"
-	"os"
-	"syscall"
-
-	"golang.org/x/crypto/ssh/terminal"
-
-	"blitiri.com.ar/go/chasquid/internal/userdb"
-)
-
-var (
-	dbFname  = flag.String("database", "", "database file")
-	adduser  = flag.String("add_user", "", "user to add")
-	password = flag.String("password", "",
-		"password for the user to add (will prompt if missing)")
-	disableChecks = flag.Bool("dangerously_disable_checks", false,
-		"disable security checks - DANGEROUS, use for testing only")
-)
-
-func main() {
-	flag.Parse()
-
-	if *dbFname == "" {
-		fmt.Printf("database name missing, forgot --database?\n")
-		os.Exit(1)
-	}
-
-	db, err := userdb.Load(*dbFname)
-	if err != nil {
-		if *adduser != "" && os.IsNotExist(err) {
-			fmt.Printf("creating database\n")
-		} else {
-			fmt.Printf("error loading database: %v\n", err)
-			os.Exit(1)
-		}
-	}
-
-	if *adduser == "" {
-		fmt.Printf("database loaded\n")
-		return
-	}
-
-	if *password == "" {
-		fmt.Printf("Password: ")
-		p1, err := terminal.ReadPassword(syscall.Stdin)
-		fmt.Printf("\n")
-		if err != nil {
-			fmt.Printf("error reading password: %v\n", err)
-			os.Exit(1)
-		}
-
-		fmt.Printf("Confirm password: ")
-		p2, err := terminal.ReadPassword(syscall.Stdin)
-		fmt.Printf("\n")
-		if err != nil {
-			fmt.Printf("error reading password: %v\n", err)
-			os.Exit(1)
-		}
-
-		if !bytes.Equal(p1, p2) {
-			fmt.Printf("passwords don't match\n")
-			os.Exit(1)
-		}
-
-		*password = string(p1)
-	}
-
-	if !*disableChecks {
-		if len(*password) < 8 {
-			fmt.Printf("password is too short\n")
-			os.Exit(1)
-		}
-	}
-
-	err = db.AddUser(*adduser, *password)
-	if err != nil {
-		fmt.Printf("error adding user: %v\n", err)
-		os.Exit(1)
-	}
-
-	err = db.Write()
-	if err != nil {
-		fmt.Printf("error writing database: %v\n", err)
-		os.Exit(1)
-	}
-
-	fmt.Printf("added user\n")
-}
diff --git a/cmd/chasquid-util/chasquid-util.go b/cmd/chasquid-util/chasquid-util.go
new file mode 100644
index 0000000..3d737e6
--- /dev/null
+++ b/cmd/chasquid-util/chasquid-util.go
@@ -0,0 +1,198 @@
+// chasquid-util is a command-line utility for chasquid-related operations.
+package main
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"syscall"
+
+	"bytes"
+
+	"blitiri.com.ar/go/chasquid/internal/aliases"
+	"blitiri.com.ar/go/chasquid/internal/config"
+	"blitiri.com.ar/go/chasquid/internal/userdb"
+
+	"github.com/docopt/docopt-go"
+	"golang.org/x/crypto/ssh/terminal"
+)
+
+// Usage, which doubles as parameter definitions thanks to docopt.
+const usage = `
+Usage:
+  chasquid-util adduser <db> <username> [--password=<password>]
+  chasquid-util removeuser <db> <username>
+  chasquid-util authenticate <db> <username> [--password=<password>]
+  chasquid-util check-userdb <db>
+  chasquid-util aliases-resolve <configdir> <address>
+`
+
+// Command-line arguments.
+var args map[string]interface{}
+
+func main() {
+	args, _ = docopt.Parse(usage, nil, true, "", false)
+
+	commands := map[string]func(){
+		"adduser":         AddUser,
+		"removeuser":      RemoveUser,
+		"authenticate":    Authenticate,
+		"check-userdb":    CheckUserDB,
+		"aliases-resolve": AliasesResolve,
+	}
+
+	for cmd, f := range commands {
+		if args[cmd].(bool) {
+			f()
+		}
+	}
+}
+
+func Fatalf(s string, arg ...interface{}) {
+	fmt.Printf(s+"\n", arg...)
+	os.Exit(1)
+}
+
+// chasquid-util check-userdb <db>
+func CheckUserDB() {
+	_, err := userdb.Load(args["<db>"].(string))
+	if err != nil {
+		Fatalf("Error loading database: %v", err)
+	}
+
+	fmt.Println("Database loaded")
+}
+
+// chasquid-util adduser <db> <username> [--password=<password>]
+func AddUser() {
+	db, err := userdb.Load(args["<db>"].(string))
+	if err != nil {
+		if os.IsNotExist(err) {
+			fmt.Println("Creating database")
+		} else {
+			Fatalf("Error loading database: %v", err)
+		}
+	}
+
+	password := getPassword()
+
+	err = db.AddUser(args["<username>"].(string), password)
+	if err != nil {
+		Fatalf("Error adding user: %v", err)
+	}
+
+	err = db.Write()
+	if err != nil {
+		Fatalf("Error writing database: %v", err)
+	}
+
+	fmt.Println("Added user")
+}
+
+// chasquid-util authenticate <db> <username> [--password=<password>]
+func Authenticate() {
+	db, err := userdb.Load(args["<db>"].(string))
+	if err != nil {
+		Fatalf("Error loading database: %v", err)
+	}
+
+	password := getPassword()
+	ok := db.Authenticate(args["<username>"].(string), password)
+	if ok {
+		fmt.Println("Authentication succeeded")
+	} else {
+		Fatalf("Authentication failed")
+	}
+}
+
+func getPassword() string {
+	password, ok := args["--password"].(string)
+	if ok {
+		return password
+	}
+
+	fmt.Printf("Password: ")
+	p1, err := terminal.ReadPassword(syscall.Stdin)
+	fmt.Printf("\n")
+	if err != nil {
+		Fatalf("Error reading password: %v\n", err)
+	}
+
+	fmt.Printf("Confirm password: ")
+	p2, err := terminal.ReadPassword(syscall.Stdin)
+	fmt.Printf("\n")
+	if err != nil {
+		Fatalf("Error reading password: %v", err)
+	}
+
+	if !bytes.Equal(p1, p2) {
+		Fatalf("Passwords don't match")
+	}
+
+	return string(p1)
+}
+
+// chasquid-util removeuser <db> <username>
+func RemoveUser() {
+	db, err := userdb.Load(args["<db>"].(string))
+	if err != nil {
+		Fatalf("Error loading database: %v", err)
+	}
+
+	present := db.RemoveUser(args["<username>"].(string))
+	if !present {
+		Fatalf("Unknown user")
+	}
+
+	err = db.Write()
+	if err != nil {
+		Fatalf("Error writing database: %v", err)
+	}
+
+	fmt.Println("Removed user")
+}
+
+// chasquid-util aliases-resolve <configdir> <address>
+func AliasesResolve() {
+	configDir := args["<configdir>"].(string)
+	conf, err := config.Load(configDir + "/chasquid.conf")
+	if err != nil {
+		Fatalf("Error reading config")
+	}
+	os.Chdir(configDir)
+
+	r := aliases.NewResolver()
+	r.SuffixSep = conf.SuffixSeparators
+	r.DropChars = conf.DropCharacters
+
+	domainDirs, err := ioutil.ReadDir("domains/")
+	if err != nil {
+		Fatalf("Error reading domains/ directory: %v", err)
+	}
+	if len(domainDirs) == 0 {
+		Fatalf("No domains found in config")
+	}
+
+	for _, info := range domainDirs {
+		name := info.Name()
+		aliasfile := "domains/" + name + "/aliases"
+		r.AddDomain(name)
+		err := r.AddAliasesFile(name, aliasfile)
+		if err == nil {
+			fmt.Printf("%s: loaded %q\n", name, aliasfile)
+		} else if err != nil && os.IsNotExist(err) {
+			fmt.Printf("%s: no aliases file\n", name)
+		} else {
+			fmt.Printf("%s: error loading %q: %v\n", name, aliasfile, err)
+		}
+	}
+
+	rcpts, err := r.Resolve(args["<address>"].(string))
+	if err != nil {
+		Fatalf("Error resolving: %v", err)
+	}
+	for _, rcpt := range rcpts {
+		fmt.Printf("%v  %s\n", rcpt.Type, rcpt.Addr)
+	}
+
+}
diff --git a/internal/aliases/aliases.go b/internal/aliases/aliases.go
index 1b37fa1..d251f48 100644
--- a/internal/aliases/aliases.go
+++ b/internal/aliases/aliases.go
@@ -69,11 +69,11 @@ type Recipient struct {
 	Type RType
 }
 
-type RType int
+type RType string
 
 const (
-	EMAIL RType = iota
-	PIPE
+	EMAIL RType = "(email)"
+	PIPE  RType = "(pipe)"
 )
 
 var (
diff --git a/internal/config/config.go b/internal/config/config.go
index bbf4e0a..e815d03 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -62,11 +62,10 @@ func Load(path string) (*Config, error) {
 		c.DataDir = "/var/lib/chasquid"
 	}
 
-	logConfig(c)
 	return c, nil
 }
 
-func logConfig(c *Config) {
+func LogConfig(c *Config) {
 	glog.Infof("Configuration:")
 	glog.Infof("  Hostname: %q", c.Hostname)
 	glog.Infof("  Max data size (MB): %d", c.MaxDataSizeMb)
diff --git a/test/util/lib.sh b/test/util/lib.sh
index 3ea873e..f876039 100644
--- a/test/util/lib.sh
+++ b/test/util/lib.sh
@@ -35,10 +35,8 @@ function chasquid() {
 }
 
 function add_user() {
-	go run ${TBASE}/../../cmd/chasquid-userdb/chasquid-userdb.go \
-		--database "config/domains/${1}/users" \
-		--add_user "${2}" \
-		--password "${3}" \
+	go run ${TBASE}/../../cmd/chasquid-util/chasquid-util.go \
+		adduser "config/domains/${1}/users" "${2}" --password "${3}" \
 		>> .add_user_logs
 }