mirror of
https://github.com/Luzifer/password.git
synced 2024-11-10 02:10:00 +00:00
Moved error handling for short passwords to library
This commit is contained in:
parent
660118b203
commit
fe235ab869
3 changed files with 50 additions and 8 deletions
|
@ -1,6 +1,8 @@
|
||||||
|
// Package securepassword implements a password generator and check.
|
||||||
package securepassword // import "github.com/Luzifer/password/lib"
|
package securepassword // import "github.com/Luzifer/password/lib"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
@ -8,12 +10,21 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// SecurePassword provides methods for generating secure passwords and
|
||||||
|
// checking the security requirements of passwords
|
||||||
type SecurePassword struct {
|
type SecurePassword struct {
|
||||||
characterTables map[string]string
|
characterTables map[string]string
|
||||||
insecurePattern []string
|
insecurePattern []string
|
||||||
badCharacters []string
|
badCharacters []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrLengthTooLow represents an error thrown if the password will
|
||||||
|
// never be able match the security considerations in this package
|
||||||
|
ErrLengthTooLow = errors.New("Passwords with a length lower than 4 will never meet the security requirements")
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewSecurePassword initializes a new SecurePassword generator
|
||||||
func NewSecurePassword() *SecurePassword {
|
func NewSecurePassword() *SecurePassword {
|
||||||
return &SecurePassword{
|
return &SecurePassword{
|
||||||
characterTables: map[string]string{
|
characterTables: map[string]string{
|
||||||
|
@ -36,7 +47,16 @@ func NewSecurePassword() *SecurePassword {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SecurePassword) GeneratePassword(length int, special bool) string {
|
// GeneratePassword generates a new password with a given length and
|
||||||
|
// optional special characters in it. The password is automatically
|
||||||
|
// checked against CheckPasswordSecurity in order to only deliver secure
|
||||||
|
// passwords.
|
||||||
|
func (s *SecurePassword) GeneratePassword(length int, special bool) (string, error) {
|
||||||
|
// Sanity check
|
||||||
|
if length < 4 {
|
||||||
|
return "", ErrLengthTooLow
|
||||||
|
}
|
||||||
|
|
||||||
characterTable := strings.Join([]string{
|
characterTable := strings.Join([]string{
|
||||||
s.characterTables["simple"],
|
s.characterTables["simple"],
|
||||||
strings.ToUpper(s.characterTables["simple"]),
|
strings.ToUpper(s.characterTables["simple"]),
|
||||||
|
@ -60,9 +80,14 @@ func (s *SecurePassword) GeneratePassword(length int, special bool) string {
|
||||||
password = ""
|
password = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return password
|
return password, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckPasswordSecurity executes three checks to ensure the passwords
|
||||||
|
// meet the security considerations in this package:
|
||||||
|
// - The password may not contain pattern found on the keyboard or in alphabet
|
||||||
|
// - The password must have 3 or 4 different character groups in it
|
||||||
|
// - The password may not have repeating characters
|
||||||
func (s *SecurePassword) CheckPasswordSecurity(password string, needsSpecialCharacters bool) bool {
|
func (s *SecurePassword) CheckPasswordSecurity(password string, needsSpecialCharacters bool) bool {
|
||||||
return !s.hasInsecurePattern(password) &&
|
return !s.hasInsecurePattern(password) &&
|
||||||
s.matchesBasicSecurity(password, needsSpecialCharacters) &&
|
s.matchesBasicSecurity(password, needsSpecialCharacters) &&
|
||||||
|
|
|
@ -80,6 +80,15 @@ func TestSecurePasswordWithSpecialCharacter(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestImpossiblePasswords(t *testing.T) {
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
_, err := NewSecurePassword().GeneratePassword(i, false)
|
||||||
|
if err != ErrLengthTooLow {
|
||||||
|
t.Errorf("Password with a length of %d did not throw as ErrLengthTooLow error", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkGeneratePasswords8Char(b *testing.B) {
|
func BenchmarkGeneratePasswords8Char(b *testing.B) {
|
||||||
pwd := NewSecurePassword()
|
pwd := NewSecurePassword()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
|
|
20
main.go
20
main.go
|
@ -65,12 +65,18 @@ func startAPIServer(c *cli.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func printPassword(c *cli.Context) {
|
func printPassword(c *cli.Context) {
|
||||||
if c.Int("length") < 5 {
|
password, err := pwd.GeneratePassword(c.Int("length"), c.Bool("special"))
|
||||||
fmt.Println("Passwords with fewer than 5 characters are too insecure.")
|
if err != nil {
|
||||||
|
switch {
|
||||||
|
case err == securepassword.ErrLengthTooLow:
|
||||||
|
fmt.Println("The password has to be more than 4 characters long to meet the security considerations")
|
||||||
|
default:
|
||||||
|
fmt.Println("An unknown error occured")
|
||||||
|
}
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(pwd.GeneratePassword(c.Int("length"), c.Bool("special")))
|
fmt.Println(password)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleAPIGetPasswordv1(res http.ResponseWriter, r *http.Request) {
|
func handleAPIGetPasswordv1(res http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -80,12 +86,14 @@ func handleAPIGetPasswordv1(res http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
special := r.URL.Query().Get("special") == "true"
|
special := r.URL.Query().Get("special") == "true"
|
||||||
|
|
||||||
if length > 128 || length < 5 {
|
if length > 128 || length < 4 {
|
||||||
http.Error(res, "Please do not use length with more than 128 or fewer than 5 characters!", http.StatusNotAcceptable)
|
http.Error(res, "Please do not use length with more than 128 or fewer than 4 characters!", http.StatusNotAcceptable)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
password, err := pwd.GeneratePassword(length, special)
|
||||||
|
|
||||||
res.Header().Add("Content-Type", "text/plain")
|
res.Header().Add("Content-Type", "text/plain")
|
||||||
res.Header().Add("Cache-Control", "no-cache")
|
res.Header().Add("Cache-Control", "no-cache")
|
||||||
res.Write([]byte(pwd.GeneratePassword(length, special)))
|
res.Write([]byte(password))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue