htpasswd-file.go 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. package auth
  2. import (
  3. "crypto/sha1"
  4. "encoding/base64"
  5. "encoding/csv"
  6. "net/http"
  7. "os"
  8. "regexp"
  9. "golang.org/x/crypto/bcrypt"
  10. )
  11. var (
  12. shaRe = regexp.MustCompile(`^{SHA}`)
  13. bcrRe = regexp.MustCompile(`^\$2b\$|^\$2a\$|^\$2y\$`)
  14. )
  15. // HtpasswdFile is a map for usernames to passwords.
  16. type HtpasswdFile struct {
  17. path string
  18. users map[string]string
  19. }
  20. // NewHtpasswdFromFile reads the users and passwords from a htpasswd file and returns them.
  21. func NewHtpasswdFromFile(path string) (*HtpasswdFile, error) {
  22. r, err := os.Open(path)
  23. if err != nil {
  24. return nil, err
  25. }
  26. defer r.Close()
  27. cr := csv.NewReader(r)
  28. cr.Comma = ':'
  29. cr.Comment = '#'
  30. cr.TrimLeadingSpace = true
  31. records, err := cr.ReadAll()
  32. if err != nil {
  33. return nil, err
  34. }
  35. users := make(map[string]string)
  36. for _, record := range records {
  37. users[record[0]] = record[1]
  38. }
  39. return &HtpasswdFile{
  40. path: path,
  41. users: users,
  42. }, nil
  43. }
  44. // Validate HTTP request credentials
  45. func (h *HtpasswdFile) Validate(r *http.Request) (ok bool, username string) {
  46. username, password, ok := r.BasicAuth()
  47. ok = ok && h.validateCredentials(username, password)
  48. return
  49. }
  50. func (h *HtpasswdFile) validateCredentials(user, password string) bool {
  51. pwd, exists := h.users[user]
  52. if !exists {
  53. return false
  54. }
  55. switch {
  56. case shaRe.MatchString(pwd):
  57. d := sha1.New()
  58. _, _ = d.Write([]byte(password))
  59. if pwd[5:] == base64.StdEncoding.EncodeToString(d.Sum(nil)) {
  60. return true
  61. }
  62. case bcrRe.MatchString(pwd):
  63. err := bcrypt.CompareHashAndPassword([]byte(pwd), []byte(password))
  64. if err == nil {
  65. return true
  66. }
  67. }
  68. return false
  69. }