From 0c90ee2be72ea6220e8c7f2bd13d280f0bde5b72 Mon Sep 17 00:00:00 2001 From: Aaron Bieber Date: Thu, 20 May 2021 06:47:38 -0600 Subject: [PATCH] Add the ability to manage .htpasswd from widdler. --- README.md | 10 +++--- go.sum | 2 +- main.go | 106 ++++++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 85 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 244bb4c..fbb5bc2 100644 --- a/README.md +++ b/README.md @@ -26,11 +26,11 @@ go get -u suah.dev/widdler ``` mkdir wiki cd wiki -# OpenBSD: -htpasswd .htpasswd youruser -# or on Linux/macOS -# htpasswd -c -B youruser -widdler +# Generate a .htpasswd file: +widdler -gen +Username: qbit +Passwd: ****** +./widdler ``` Now open your browser to [http://localhost:8080](http://localhost:8080). diff --git a/go.sum b/go.sum index 329f638..7a06127 100644 --- a/go.sum +++ b/go.sum @@ -7,9 +7,9 @@ golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= suah.dev/protect v1.0.0 h1:X8pzDvDIZIiugmkmr6DES6JFO1XUdJWi34Ffmk6CMZY= diff --git a/main.go b/main.go index b97dd65..7426146 100644 --- a/main.go +++ b/main.go @@ -19,6 +19,7 @@ import ( "time" "golang.org/x/crypto/bcrypt" + "golang.org/x/crypto/ssh/terminal" "golang.org/x/net/webdav" "suah.dev/protect" ) @@ -60,6 +61,7 @@ var ( auth bool davDir string fullListen string + genHtpass bool handlers map[string]userHandlers listen string passPath string @@ -82,6 +84,7 @@ func init() { flag.StringVar(&tlsKey, "tlskey", "", "TLS key.") flag.StringVar(&passPath, "htpass", fmt.Sprintf("%s/.htpasswd", dir), "Path to .htpasswd file..") flag.BoolVar(&auth, "auth", true, "Enable HTTP Basic Authentication.") + flag.BoolVar(&genHtpass, "gen", false, "Generate a .htpasswd file or add a new entry to an existing file.") flag.Parse() // These are OpenBSD specific protections used to prevent unnecessary file access. @@ -96,33 +99,6 @@ func init() { log.Fatalln(err) } - _, fErr := os.Stat(passPath) - if os.IsNotExist(fErr) { - if auth { - fmt.Println("No .htpasswd file found!") - os.Exit(1) - } - } else { - p, err := os.Open(passPath) - if err != nil { - log.Fatal(err) - } - defer p.Close() - - ht := csv.NewReader(p) - ht.Comma = ':' - ht.Comment = '#' - ht.TrimLeadingSpace = true - - entries, err := ht.ReadAll() - if err != nil { - log.Fatal(err) - } - - for _, parts := range entries { - users[parts[0]] = parts[1] - } - } } func authenticate(user string, pass string) bool { @@ -164,7 +140,83 @@ func createEmpty(path string) error { return nil } +func prompt(prompt string, secure bool) (string, error) { + var input string + fmt.Print(prompt) + + if secure { + b, err := terminal.ReadPassword(int(os.Stdin.Fd())) + if err != nil { + return "", err + } + input = string(b) + } else { + fmt.Scanln(&input) + } + return input, nil +} + func main() { + if genHtpass { + user, err := prompt("Username: ", false) + if err != nil { + log.Fatalln(err) + } + + pass, err := prompt("Password: ", true) + if err != nil { + log.Fatalln(err) + } + + hash, err := bcrypt.GenerateFromPassword([]byte(pass), 11) + if err != nil { + log.Fatalln(err) + } + + f, err := os.OpenFile(passPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600) + if err != nil { + log.Fatalln(err) + } + + defer f.Close() + + if _, err := f.WriteString(fmt.Sprintf("%s:%s\n", user, hash)); err != nil { + log.Fatalln(err) + } + + fmt.Printf("Added %q to %q\n", user, passPath) + + os.Exit(0) + } + + _, fErr := os.Stat(passPath) + if os.IsNotExist(fErr) { + if auth { + fmt.Println("No .htpasswd file found!") + os.Exit(1) + } + } else { + p, err := os.Open(passPath) + if err != nil { + log.Fatal(err) + } + defer p.Close() + + ht := csv.NewReader(p) + ht.Comma = ':' + ht.Comment = '#' + ht.TrimLeadingSpace = true + + entries, err := ht.ReadAll() + if err != nil { + log.Fatal(err) + } + + for _, parts := range entries { + users[parts[0]] = parts[1] + } + } + if auth { for u := range users { uPath := path.Join(davDir, u)