94 lines
1.8 KiB
Go
94 lines
1.8 KiB
Go
package auth
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
type AuthStore interface {
|
|
Register(user, pass string) error
|
|
Authenticate(user, pass string) error
|
|
Remove(user string) error
|
|
}
|
|
|
|
type Htpasswd struct {
|
|
accounts map[string]string
|
|
filePath string
|
|
}
|
|
|
|
func NewHtpasswd(path string) (AuthStore, error) {
|
|
s := Htpasswd{
|
|
filePath: path,
|
|
}
|
|
err := s.read()
|
|
return s, err
|
|
}
|
|
|
|
func (s *Htpasswd) read() (err error) {
|
|
file, err := os.Open(s.filePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
|
|
fileScanner := bufio.NewScanner(file)
|
|
fileScanner.Split(bufio.ScanLines)
|
|
|
|
s.accounts = make(map[string]string)
|
|
for fileScanner.Scan() {
|
|
arr := strings.SplitN(fileScanner.Text(), ":", 2)
|
|
if len(arr) < 2 {
|
|
return fmt.Errorf("invalid data %s", arr)
|
|
}
|
|
s.accounts[arr[0]] = arr[1]
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *Htpasswd) write() (err error) {
|
|
file, err := os.OpenFile(s.filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to open htpasswd file: %w", err)
|
|
}
|
|
defer file.Close()
|
|
|
|
for u, p := range s.accounts {
|
|
_, err = fmt.Fprintf(file, "%s:%s\n", u, p)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s Htpasswd) Register(user, pass string) (err error) {
|
|
s.accounts[user], err = hash(pass)
|
|
if err != nil {
|
|
return
|
|
}
|
|
return s.write()
|
|
}
|
|
|
|
func (s Htpasswd) Authenticate(user, pass string) (err error) {
|
|
hashed, ok := s.accounts[user]
|
|
if !ok {
|
|
return errors.New("user not found")
|
|
}
|
|
return bcrypt.CompareHashAndPassword([]byte(hashed), []byte(pass))
|
|
}
|
|
|
|
func (s Htpasswd) Remove(user string) (err error) {
|
|
delete(s.accounts, user)
|
|
return s.write()
|
|
}
|
|
|
|
func hash(pass string) (string, error) {
|
|
output, err := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost)
|
|
return string(output), err
|
|
}
|