96 lines
2 KiB
Go
96 lines
2 KiB
Go
package password
|
|
|
|
import (
|
|
"bufio"
|
|
"embed"
|
|
"fmt"
|
|
"io/fs"
|
|
"strings"
|
|
)
|
|
|
|
// CommonPasswordError
|
|
type CommonPasswordError struct {
|
|
CommonPassword
|
|
}
|
|
|
|
func (cpe CommonPasswordError) Error() string {
|
|
return fmt.Sprintf("%q from %q", cpe.Password, cpe.Source)
|
|
}
|
|
|
|
type CommonPassword struct {
|
|
Password string
|
|
Source string
|
|
}
|
|
|
|
//go:embed common
|
|
var commonEmbed embed.FS
|
|
|
|
// CommonPasswords returns a channel that contains all passwords.
|
|
// The caller must drain the channel.
|
|
func CommonPasswords() <-chan CommonPassword {
|
|
pChan := make(chan CommonPassword, 10)
|
|
go func() {
|
|
defer close(pChan)
|
|
|
|
fs.WalkDir(commonEmbed, ".", func(path string, d fs.DirEntry, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// get the full path
|
|
if d.IsDir() || !strings.HasSuffix(path, ".txt") {
|
|
return nil
|
|
}
|
|
|
|
// open it
|
|
file, err := commonEmbed.Open(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
|
|
// scan it line by line
|
|
scanner := bufio.NewScanner(file)
|
|
for scanner.Scan() {
|
|
line := strings.TrimSpace(scanner.Text())
|
|
if line == "" || strings.HasPrefix(line, "//") {
|
|
continue
|
|
}
|
|
pChan <- CommonPassword{
|
|
Password: line,
|
|
Source: path,
|
|
}
|
|
}
|
|
|
|
return scanner.Err()
|
|
})
|
|
}()
|
|
return pChan
|
|
}
|
|
|
|
// CheckCommonPassword checks if a password is a common password.
|
|
//
|
|
// check is called with each candidate password to perform the check.
|
|
// check should return a boolean indicating if the password in question corresponds to the candidate.
|
|
//
|
|
// CheckCommonPassword returns one of three error values.
|
|
//
|
|
// - a CommonPasswordError (when a password matches a common password)
|
|
// - an error returned by check (assuming some check went wrong)
|
|
// - or nil (when a password is not a common password
|
|
func CheckCommonPassword(check func(candidate string) (bool, error)) error {
|
|
for commmon := range CommonPasswords() {
|
|
ok, err := check(commmon.Password)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// password validation passed
|
|
if ok {
|
|
return CommonPasswordError{
|
|
CommonPassword: commmon,
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|