Merge pull request #27 from tarent/magikstm-master

Magikstm master
This commit is contained in:
Sebastian Mancke
2017-05-30 23:02:36 +02:00
committed by GitHub
2 changed files with 68 additions and 6 deletions
+33
View File
@@ -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)
+35 -6
View File
@@ -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)