github.com/pusher/oauth2_proxy@v3.2.0+incompatible/htpasswd.go (about)

     1  package main
     2  
     3  import (
     4  	"crypto/sha1"
     5  	"encoding/base64"
     6  	"encoding/csv"
     7  	"io"
     8  	"log"
     9  	"os"
    10  
    11  	"golang.org/x/crypto/bcrypt"
    12  )
    13  
    14  // Lookup passwords in a htpasswd file
    15  // Passwords must be generated with -B for bcrypt or -s for SHA1.
    16  
    17  // HtpasswdFile represents the structure of an htpasswd file
    18  type HtpasswdFile struct {
    19  	Users map[string]string
    20  }
    21  
    22  // NewHtpasswdFromFile constructs an HtpasswdFile from the file at the path given
    23  func NewHtpasswdFromFile(path string) (*HtpasswdFile, error) {
    24  	r, err := os.Open(path)
    25  	if err != nil {
    26  		return nil, err
    27  	}
    28  	defer r.Close()
    29  	return NewHtpasswd(r)
    30  }
    31  
    32  // NewHtpasswd  consctructs an HtpasswdFile from an io.Reader (opened file)
    33  func NewHtpasswd(file io.Reader) (*HtpasswdFile, error) {
    34  	csvReader := csv.NewReader(file)
    35  	csvReader.Comma = ':'
    36  	csvReader.Comment = '#'
    37  	csvReader.TrimLeadingSpace = true
    38  
    39  	records, err := csvReader.ReadAll()
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  	h := &HtpasswdFile{Users: make(map[string]string)}
    44  	for _, record := range records {
    45  		h.Users[record[0]] = record[1]
    46  	}
    47  	return h, nil
    48  }
    49  
    50  // Validate checks a users password against the HtpasswdFile entries
    51  func (h *HtpasswdFile) Validate(user string, password string) bool {
    52  	realPassword, exists := h.Users[user]
    53  	if !exists {
    54  		return false
    55  	}
    56  
    57  	shaPrefix := realPassword[:5]
    58  	if shaPrefix == "{SHA}" {
    59  		shaValue := realPassword[5:]
    60  		d := sha1.New()
    61  		d.Write([]byte(password))
    62  		return shaValue == base64.StdEncoding.EncodeToString(d.Sum(nil))
    63  	}
    64  
    65  	bcryptPrefix := realPassword[:4]
    66  	if bcryptPrefix == "$2a$" || bcryptPrefix == "$2b$" || bcryptPrefix == "$2x$" || bcryptPrefix == "$2y$" {
    67  		return bcrypt.CompareHashAndPassword([]byte(realPassword), []byte(password)) == nil
    68  	}
    69  
    70  	log.Printf("Invalid htpasswd entry for %s. Must be a SHA or bcrypt entry.", user)
    71  	return false
    72  }