add auth layer

This commit is contained in:
Arkadiy Kukarkin
2025-12-23 12:23:32 +01:00
parent c4889d7d16
commit 725a673fb0
4 changed files with 148 additions and 0 deletions

9
auth/auth.go Normal file
View File

@@ -0,0 +1,9 @@
package auth
import "errors"
var ErrAuthFailed = errors.New("authentication failed")
type Authenticator interface {
Authenticate(user, pass string) error
}

41
auth/htpasswd.go Normal file
View File

@@ -0,0 +1,41 @@
package auth
import (
"bufio"
"os"
"strings"
"golang.org/x/crypto/bcrypt"
)
type HtpasswdAuth struct {
path string
}
func NewHtpasswdAuth(path string) *HtpasswdAuth {
return &HtpasswdAuth{path: path}
}
func (h *HtpasswdAuth) Authenticate(user, pass string) error {
f, err := os.Open(h.path)
if err != nil {
return err
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
parts := strings.SplitN(line, ":", 2)
if len(parts) != 2 {
continue
}
if parts[0] == user {
if bcrypt.CompareHashAndPassword([]byte(parts[1]), []byte(pass)) == nil {
return nil
}
return ErrAuthFailed
}
}
return ErrAuthFailed
}

80
auth/ldap.go Normal file
View File

@@ -0,0 +1,80 @@
package auth
import (
"fmt"
"strings"
"github.com/go-ldap/ldap/v3"
)
type LDAPAuth struct {
uri string
baseDN string
bindDN string
bindPass string
userFilter string
startTLS bool
}
func NewLDAPAuth(uri, baseDN, bindDN, bindPass, userFilter string, startTLS bool) *LDAPAuth {
return &LDAPAuth{
uri: uri,
baseDN: baseDN,
bindDN: bindDN,
bindPass: bindPass,
userFilter: userFilter,
startTLS: startTLS,
}
}
func (l *LDAPAuth) Authenticate(user, pass string) error {
if pass == "" {
return ErrAuthFailed
}
conn, err := ldap.DialURL(l.uri)
if err != nil {
return fmt.Errorf("ldap connect: %w", err)
}
defer conn.Close()
if l.startTLS {
if err := conn.StartTLS(nil); err != nil {
return fmt.Errorf("ldap starttls: %w", err)
}
}
if err := conn.Bind(l.bindDN, l.bindPass); err != nil {
return fmt.Errorf("ldap admin bind: %w", err)
}
filter := strings.Replace(l.userFilter, "%s", ldap.EscapeFilter(user), 1)
req := ldap.NewSearchRequest(
l.baseDN,
ldap.ScopeWholeSubtree,
ldap.NeverDerefAliases,
0, 0, false,
filter,
[]string{"dn"},
nil,
)
res, err := conn.Search(req)
if err != nil {
return fmt.Errorf("ldap search: %w", err)
}
if len(res.Entries) == 0 {
return ErrAuthFailed
}
if len(res.Entries) > 1 {
return fmt.Errorf("ldap: ambiguous user filter matched %d users", len(res.Entries))
}
userDN := res.Entries[0].DN
if err := conn.Bind(userDN, pass); err != nil {
return ErrAuthFailed
}
return nil
}

18
auth/password.go Normal file
View File

@@ -0,0 +1,18 @@
package auth
import "golang.org/x/crypto/bcrypt"
type PasswordAuth struct {
hash []byte
}
func NewPasswordAuth(hash string) *PasswordAuth {
return &PasswordAuth{hash: []byte(hash)}
}
func (p *PasswordAuth) Authenticate(_, pass string) error {
if err := bcrypt.CompareHashAndPassword(p.hash, []byte(pass)); err != nil {
return ErrAuthFailed
}
return nil
}