Files
glchat/internal/auth/policy.go
T

56 lines
1.5 KiB
Go
Raw Normal View History

package auth
import (
"bufio"
"embed"
"fmt"
"strings"
"glchat/internal/crypto"
)
//go:embed data/leaked-passwords.txt
var leakedPasswordsFS embed.FS
// passwordPolicy проверяет пароль по локальному словарю утечек: внешние API
// запрещены (AGENT.md 7.1).
type passwordPolicy struct {
leaked map[string]struct{}
}
func newPasswordPolicy() (*passwordPolicy, error) {
file, err := leakedPasswordsFS.Open("data/leaked-passwords.txt")
if err != nil {
return nil, fmt.Errorf("open leaked password list: %w", err)
}
defer file.Close()
leaked := make(map[string]struct{}, 10000)
scanner := bufio.NewScanner(file)
for scanner.Scan() {
word := strings.TrimSpace(scanner.Text())
if word == "" {
continue
}
leaked[strings.ToLower(word)] = struct{}{}
}
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("read leaked password list: %w", err)
}
return &passwordPolicy{leaked: leaked}, nil
}
// validate проверяет длину и отсутствие пароля в словаре утечек.
func (p *passwordPolicy) validate(password string) error {
if err := crypto.ValidatePassword(password); err != nil {
return err
}
if _, found := p.leaked[strings.ToLower(password)]; found {
return fmt.Errorf("password occurs in the leaked password list")
}
return nil
}
// LeakedCount возвращает размер словаря (для диагностики и метрик).
func (p *passwordPolicy) LeakedCount() int { return len(p.leaked) }