loginsrv

Unnamed repository; edit this file 'description' to name the repository.
git clone git@jamesshield.xyz:repos/loginsrv.git
Log | Files | Refs | README | LICENSE

commit 19fdb618da94b45efb120492cda4eab723ade5de
parent cf1fa0d70bcfaa7c0c22c0c1234440baf8e4ce8d
Author: Sebastian Mancke <s.mancke@tarent.de>
Date:   Tue, 30 May 2017 23:02:36 +0200

Merge pull request #27 from tarent/magikstm-master

Magikstm master
Diffstat:
Mhtpasswd/auth.go | 33+++++++++++++++++++++++++++++++++
Mhtpasswd/auth_test.go | 41+++++++++++++++++++++++++++++++++++------
2 files changed, 68 insertions(+), 6 deletions(-)

diff --git a/htpasswd/auth.go b/htpasswd/auth.go @@ -12,12 +12,17 @@ import ( "io" "os" "strings" + "sync" + "time" ) // Auth is the htpassword authenticater type Auth struct { filename string userHash map[string]string + // Used in func reloadIfChanged to reload htpasswd file if it changed + modTime time.Time + mu sync.RWMutex } // NewAuth creates an htpassword authenticater @@ -33,11 +38,20 @@ func (a *Auth) parse(filename string) error { if err != nil { return err } + + fileInfo, err := os.Stat(filename) + if err != nil { + return err + } + a.modTime = fileInfo.ModTime() + cr := csv.NewReader(r) cr.Comma = ':' cr.Comment = '#' cr.TrimLeadingSpace = true + a.mu.Lock() + defer a.mu.Unlock() a.userHash = map[string]string{} for { record, err := cr.Read() @@ -57,6 +71,9 @@ func (a *Auth) parse(filename string) error { // Authenticate the user func (a *Auth) Authenticate(username, password string) (bool, error) { + reloadIfChanged(a) + a.mu.RLock() + defer a.mu.RUnlock() if hash, exist := a.userHash[username]; exist { h := []byte(hash) p := []byte(password) @@ -75,6 +92,22 @@ func (a *Auth) Authenticate(username, password string) (bool, error) { return false, nil } +// Reload htpasswd file if it changed during current run +func reloadIfChanged(a *Auth) { + fileInfo, err := os.Stat(a.filename) + if err != nil { + //On error, retain current file + return + } + + currentmodTime := fileInfo.ModTime() + + if currentmodTime != a.modTime { + a.modTime = currentmodTime + a.parse(a.filename) + } +} + func compareSha(hashedPassword, password []byte) bool { d := sha1.New() d.Write(password) diff --git a/htpasswd/auth_test.go b/htpasswd/auth_test.go @@ -4,6 +4,7 @@ import ( . "github.com/stretchr/testify/assert" "io/ioutil" "testing" + "time" ) // password for all of them is 'secret' @@ -18,7 +19,7 @@ bob-foo:{fooo}sdcsdcsdc/BfQ= ` -func TestClient_Hashes(t *testing.T) { +func TestAuth_Hashes(t *testing.T) { auth, err := NewAuth(writeTmpfile(testfile)) NoError(t, err) @@ -36,7 +37,35 @@ func TestClient_Hashes(t *testing.T) { } } -func TestClient_UnknownUser(t *testing.T) { +func TestAuth_ReloadFile(t *testing.T) { + filename := writeTmpfile(`bob:$apr1$IDZSCL/o$N68zaFDDRivjour94OVeB.`) + auth, err := NewAuth(filename) + NoError(t, err) + + authenticated, err := auth.Authenticate("bob", "secret") + NoError(t, err) + True(t, authenticated) + + authenticated, err = auth.Authenticate("alice", "secret") + NoError(t, err) + False(t, authenticated) + + // The refresh is time based, so we have to wait a second, here + time.Sleep(time.Second) + + err = ioutil.WriteFile(filename, []byte(`alice:$apr1$IDZSCL/o$N68zaFDDRivjour94OVeB.`), 06644) + NoError(t, err) + + authenticated, err = auth.Authenticate("bob", "secret") + NoError(t, err) + False(t, authenticated) + + authenticated, err = auth.Authenticate("alice", "secret") + NoError(t, err) + True(t, authenticated) +} + +func TestAuth_UnknownUser(t *testing.T) { auth, err := NewAuth(writeTmpfile(testfile)) NoError(t, err) @@ -45,12 +74,12 @@ func TestClient_UnknownUser(t *testing.T) { False(t, authenticated) } -func TestClient_ErrorOnMissingFile(t *testing.T) { +func TestAuth_ErrorOnMissingFile(t *testing.T) { _, err := NewAuth("/tmp/foo/bar/nothing") Error(t, err) } -func TestClient_ErrorOnInvalidFileContents(t *testing.T) { +func TestAuth_ErrorOnInvalidFileContents(t *testing.T) { _, err := NewAuth(writeTmpfile("foo bar bazz")) Error(t, err) @@ -58,7 +87,7 @@ func TestClient_ErrorOnInvalidFileContents(t *testing.T) { Error(t, err) } -func TestClient_BadMD5Format(t *testing.T) { +func TestAuth_BadMD5Format(t *testing.T) { // missing $ separator in md5 hash a, err := NewAuth(writeTmpfile("foo:$apr1$IDZSCL/oN68zaFDDRivjour94OVeB.")) NoError(t, err) @@ -68,7 +97,7 @@ func TestClient_BadMD5Format(t *testing.T) { False(t, authenticated) } -func TestClient_Hashes_UnknownAlgoError(t *testing.T) { +func TestAuth_Hashes_UnknownAlgoError(t *testing.T) { auth, err := NewAuth(writeTmpfile(testfile)) NoError(t, err)