Skip to content

Commit

Permalink
Support loading credentials from io.Reader (#3)
Browse files Browse the repository at this point in the history
Add the NewFromReader constructor and ReloadFromReader method for
*HtpasswdFile.

This makes it easier for users that receive credentials from sources
other than a file, because they don't have to create temporary files.

Signed-off-by: Peter Schultz <peter.schultz@classmarkets.com>
  • Loading branch information
pschultz authored and tg123 committed Mar 5, 2019
1 parent d822828 commit fbeb472
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 20 deletions.
25 changes: 24 additions & 1 deletion htpasswd.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ package htpasswd
import (
"bufio"
"fmt"
"io"
"os"
"strings"
"sync"
Expand Down Expand Up @@ -83,6 +84,21 @@ func New(filename string, parsers []PasswdParser, bad BadLineHandler) (*Htpasswd
return &bf, nil
}

// NewFromReader is like new but reads from r instead of a named file. Calling
// Reload on the returned HtpasswdFile will result in an error; use
// ReloadFromReader instead.
func NewFromReader(r io.Reader, parsers []PasswdParser, bad BadLineHandler) (*HtpasswdFile, error) {
bf := HtpasswdFile{
parsers: parsers,
}

if err := bf.ReloadFromReader(r, bad); err != nil {
return nil, err
}

return &bf, nil
}

// Match checks the username and password combination to see if it represents
// a valid account from the htpassword file.
func (bf *HtpasswdFile) Match(username, password string) bool {
Expand Down Expand Up @@ -111,11 +127,18 @@ func (bf *HtpasswdFile) Reload(bad BadLineHandler) error {
}
defer f.Close()

return bf.ReloadFromReader(f, bad)
}

// ReloadFromReader is like Reload but reads credentials from r instead of a named
// file. If HtpasswdFile was created by New, it is okay to call Reload and
// ReloadFromReader as desired.
func (bf *HtpasswdFile) ReloadFromReader(r io.Reader, bad BadLineHandler) error {
// ... and a new map ...
newPasswdMap := passwdTable{}

// ... for each line ...
scanner := bufio.NewScanner(f)
scanner := bufio.NewScanner(r)
for scanner.Scan() {
line := scanner.Text()

Expand Down
54 changes: 35 additions & 19 deletions htpasswd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package htpasswd
import (
"io/ioutil"
"os"
"strings"
"testing"
)

Expand Down Expand Up @@ -1420,6 +1421,26 @@ user198:$1$D89ubl/e$SdHoMvPduS1kS3KVqEw9W.
user199:$1$D89ubl/e$1FQtoOElFQQCBL53IT2LL0
user200:$1$D89ubl/e$xO3.z/20nsNEXnaWJdsfB/`

func testSystemReader(t *testing.T, name string, contents string) {
r := strings.NewReader(contents)

htp, err := NewFromReader(r, DefaultSystems, nil)
if err != nil {
t.Fatalf("Failed to read htpasswd reader")
}

for _, u := range testUsers {
if good := htp.Match(u.username, u.password); !good {
t.Errorf("%s user %s, password %s failed to authenticate: %t", name, u.username, u.password, good)
}

notPass := u.password + "not"
if bad := htp.Match(u.username, notPass); bad {
t.Errorf("%s user %s, password %s erroneously authenticated: %t", name, u.username, notPass, bad)
}
}
}

func testSystem(t *testing.T, name string, contents string) {
f, err := ioutil.TempFile("", "??")
if err != nil {
Expand Down Expand Up @@ -1450,27 +1471,22 @@ func testSystem(t *testing.T, name string, contents string) {
t.Errorf("%s user %s, password %s erroneously authenticated: %t", name, u.username, notPass, bad)
}
}

}

func Test_PlainFile(t *testing.T) {
testSystem(t, "plain", textPlain)
}
func Test_ShaFile(t *testing.T) {
testSystem(t, "sha", textSha)
}
func Test_Apr1File(t *testing.T) {
testSystem(t, "md5", textApr1)
}
func Test_PlainReader(t *testing.T) { testSystemReader(t, "plain", textPlain) }
func Test_PlainFile(t *testing.T) { testSystem(t, "plain", textPlain) }

func Test_Md5File(t *testing.T) {
testSystem(t, "md5", textMd5Crypt)
}
func Test_ShaReader(t *testing.T) { testSystemReader(t, "sha", textSha) }
func Test_ShaFile(t *testing.T) { testSystem(t, "sha", textSha) }

func Test_BcryptFile(t *testing.T) {
testSystem(t, "bcrypt", textBcrypt)
}
func Test_Apr1Reader(t *testing.T) { testSystemReader(t, "md5", textApr1) }
func Test_Apr1File(t *testing.T) { testSystem(t, "md5", textApr1) }

func Test_SshaFile(t *testing.T) {
testSystem(t, "ssha", textSsha)
}
func Test_Md5Reader(t *testing.T) { testSystemReader(t, "md5", textMd5Crypt) }
func Test_Md5File(t *testing.T) { testSystem(t, "md5", textMd5Crypt) }

func Test_BcryptReader(t *testing.T) { testSystemReader(t, "bcrypt", textBcrypt) }
func Test_BcryptFile(t *testing.T) { testSystem(t, "bcrypt", textBcrypt) }

func Test_SshaReader(t *testing.T) { testSystemReader(t, "ssha", textSsha) }
func Test_SshaFile(t *testing.T) { testSystem(t, "ssha", textSsha) }

0 comments on commit fbeb472

Please sign in to comment.