Added password generator library
This commit is contained in:
parent
bb5a453bec
commit
6893ae5489
65
password/password.go
Normal file
65
password/password.go
Normal file
|
@ -0,0 +1,65 @@
|
|||
package password
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
var upperRunes = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
var lowerRunes = []rune("abcdefghijklmnopqrstuvwxyz")
|
||||
var numberRunes = []rune("0123456789")
|
||||
var symbolRunes = []rune("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~")
|
||||
|
||||
// Generate generates a random string with the specified size containing runes from the designated sets.
|
||||
func Generate(upper, lower, number, symbol bool, size int) (string, error) {
|
||||
if size <= 0 {
|
||||
return "", fmt.Errorf("password: Password size must be greater than 0")
|
||||
}
|
||||
|
||||
runeSet := []rune{}
|
||||
|
||||
if upper {
|
||||
runeSet = append(runeSet, upperRunes...)
|
||||
}
|
||||
if lower {
|
||||
runeSet = append(runeSet, lowerRunes...)
|
||||
}
|
||||
if number {
|
||||
runeSet = append(runeSet, numberRunes...)
|
||||
}
|
||||
if symbol {
|
||||
runeSet = append(runeSet, symbolRunes...)
|
||||
}
|
||||
|
||||
if len(runeSet) == 0 {
|
||||
return "", fmt.Errorf("password: No runes selected for password")
|
||||
}
|
||||
|
||||
password := make([]rune, size)
|
||||
for i := 0; i < size; i++ {
|
||||
n, err := rand.Int(rand.Reader, big.NewInt(int64(len(runeSet))))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("password: Error while generating random number: %v", err)
|
||||
}
|
||||
password[i] = runeSet[n.Int64()]
|
||||
}
|
||||
|
||||
return string(password), nil
|
||||
}
|
||||
|
||||
// Random creates an array of random bytes with specified size.
|
||||
// If used to generate password salt, then size of 16 bytes (128 bits) is recommended.
|
||||
func Random(size int) ([]byte, error) {
|
||||
if size < 0 {
|
||||
return nil, fmt.Errorf("password: Size cannot be less than zero")
|
||||
}
|
||||
random := make([]byte, size)
|
||||
|
||||
_, err := rand.Read(random)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("password: Error while reading random bytes: %v", err)
|
||||
}
|
||||
|
||||
return random, nil
|
||||
}
|
170
password/password_test.go
Normal file
170
password/password_test.go
Normal file
|
@ -0,0 +1,170 @@
|
|||
package password
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRuneCount(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
arg []rune
|
||||
want int
|
||||
}{
|
||||
{"upperRunes", upperRunes, 26},
|
||||
{"lowerRunes", lowerRunes, 26},
|
||||
{"numberRunes", numberRunes, 10},
|
||||
{"symbolRunes", symbolRunes, 32},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := len(tt.arg)
|
||||
if got != tt.want {
|
||||
t.Fatalf("len(%s) = %d, want %d", tt.name, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateReturnsPasswordWithCorrectSize(t *testing.T) {
|
||||
tests := []struct {
|
||||
upper bool
|
||||
lower bool
|
||||
number bool
|
||||
symbol bool
|
||||
size int
|
||||
}{
|
||||
{true, true, true, true, 3},
|
||||
{true, true, true, true, 45},
|
||||
{true, false, false, false, 20},
|
||||
{false, true, true, false, 100},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
password, err := Generate(tt.upper, tt.lower, tt.number, tt.symbol, tt.size)
|
||||
if err != nil {
|
||||
t.Fatalf("Want Generate() err = nil, got %v", err)
|
||||
}
|
||||
|
||||
len := len(password)
|
||||
if len != tt.size {
|
||||
t.Fatalf("len(password) = %d want %d", len, tt.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateReturnsErrorWithBadInput(t *testing.T) {
|
||||
tests := []struct {
|
||||
upper bool
|
||||
lower bool
|
||||
number bool
|
||||
symbol bool
|
||||
size int
|
||||
}{
|
||||
{false, false, false, false, 3},
|
||||
{true, true, true, true, 0},
|
||||
{true, false, false, false, -20},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
_, err := Generate(tt.upper, tt.lower, tt.number, tt.symbol, tt.size)
|
||||
if err == nil {
|
||||
t.Fatalf("Generate() err = nil, want not nil")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateReturnsPasswordWithCorrectRunes(t *testing.T) {
|
||||
tests := []struct {
|
||||
upper bool
|
||||
lower bool
|
||||
number bool
|
||||
symbol bool
|
||||
size int
|
||||
}{
|
||||
{true, false, false, false, 100},
|
||||
{false, true, false, false, 100},
|
||||
{false, false, true, false, 100},
|
||||
{false, false, false, true, 100},
|
||||
{true, true, false, false, 100},
|
||||
{true, false, true, false, 100},
|
||||
{true, false, false, true, 100},
|
||||
{false, true, true, false, 100},
|
||||
{false, true, false, true, 100},
|
||||
{false, false, true, true, 100},
|
||||
{true, true, true, false, 100},
|
||||
{true, true, false, true, 100},
|
||||
{true, false, true, true, 100},
|
||||
{false, true, true, true, 100},
|
||||
{true, true, true, true, 100},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
password, err := Generate(tt.upper, tt.lower, tt.number, tt.symbol, tt.size)
|
||||
if err != nil {
|
||||
t.Fatalf("Want Generate() err = nil, got %v", err)
|
||||
}
|
||||
|
||||
for _, r := range password {
|
||||
if !tt.upper && strings.ContainsRune(string(upperRunes), r) {
|
||||
t.Fatalf("Got password with upper rune, want no upper runes")
|
||||
}
|
||||
|
||||
if !tt.lower && strings.ContainsRune(string(lowerRunes), r) {
|
||||
t.Fatalf("Got password with lower rune, want no lower runes")
|
||||
}
|
||||
|
||||
if !tt.number && strings.ContainsRune(string(numberRunes), r) {
|
||||
t.Fatalf("Got password with number rune, want no number runes")
|
||||
}
|
||||
|
||||
if !tt.symbol && strings.ContainsRune(string(symbolRunes), r) {
|
||||
t.Fatalf("Got password with symbol rune, want no symbol runes")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomReturnsCorrectNumberOfBytes(t *testing.T) {
|
||||
tests := []struct {
|
||||
size int
|
||||
}{
|
||||
{0},
|
||||
{3},
|
||||
{45},
|
||||
{20},
|
||||
{100},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
randomBytes, err := Random(tt.size)
|
||||
if err != nil {
|
||||
t.Fatalf("Want Random() err = nil, got %v", err)
|
||||
}
|
||||
|
||||
len := len(randomBytes)
|
||||
if len != tt.size {
|
||||
t.Fatalf("len(randomBytes) = %d, want %d", len, tt.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomReturnsErrorWithNegativeSize(t *testing.T) {
|
||||
tests := []struct {
|
||||
size int
|
||||
}{
|
||||
{-1},
|
||||
{-3},
|
||||
{-45},
|
||||
{-20},
|
||||
{-100},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
_, err := Random(tt.size)
|
||||
if err == nil {
|
||||
t.Fatalf("Random() err = nil, want not nil")
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue