distribution/registry/auth/basic/htpasswd.go

55 lines
1.5 KiB
Go
Raw Normal View History

package basic
import (
"crypto/sha1"
"encoding/base64"
"encoding/csv"
"errors"
"os"
)
// ErrSHARequired - returned in error field of challenge when the htpasswd was not made using SHA1 algorithm.
// (SHA1 is considered obsolete but the alternative for htpasswd is MD5, or system crypt...)
var ErrSHARequired = errors.New("htpasswd file must use SHA (htpasswd -s)")
// HTPasswd - holds a path to a system .htpasswd file and the machinery to parse it.
type HTPasswd struct {
path string
reader *csv.Reader
}
// NewHTPasswd - Create a new HTPasswd with the given path to .htpasswd file.
func NewHTPasswd(htpath string) *HTPasswd {
return &HTPasswd{path: htpath}
}
// AuthenticateUser - Check a given user:password credential against the receiving HTPasswd's file.
func (htpasswd *HTPasswd) AuthenticateUser(user string, pwd string) (bool, error) {
// Hash the credential.
sha := sha1.New()
sha.Write([]byte(pwd))
hash := base64.StdEncoding.EncodeToString(sha.Sum(nil))
// Open the file.
in, err := os.Open(htpasswd.path)
if err != nil {
return false, err
}
// Parse the contents of the standard .htpasswd until we hit the end or find a match.
reader := csv.NewReader(in)
reader.Comma = ':'
reader.Comment = '#'
reader.TrimLeadingSpace = true
for entry, readerr := reader.Read(); entry != nil || readerr != nil; entry, readerr = reader.Read() {
if entry[0] == user {
if len(entry[1]) < 6 || entry[1][0:5] != "{SHA}" {
return false, ErrSHARequired
}
return entry[1][5:] == hash, nil
}
}
return false, nil
}