github.com/infraboard/keyauth@v0.8.1/common/password/generator.go (about)

     1  package password
     2  
     3  import (
     4  	"crypto/rand"
     5  	"math/big"
     6  	"strings"
     7  )
     8  
     9  // Generator is what generates the password
    10  type Generator struct {
    11  	*Config
    12  }
    13  
    14  // New returns a new generator
    15  func New(config *Config) *Generator {
    16  	if config == nil {
    17  		config = &DefaultConfig
    18  	}
    19  
    20  	if !config.IncludeSymbols &&
    21  		!config.IncludeUppercaseLetters &&
    22  		!config.IncludeLowercaseLetters &&
    23  		!config.IncludeNumbers &&
    24  		config.CharacterSet == "" {
    25  		config = &DefaultConfig
    26  	}
    27  
    28  	if config.Length == 0 {
    29  		config.Length = LengthStrong
    30  	}
    31  
    32  	if config.CharacterSet == "" {
    33  		config.CharacterSet = buildCharacterSet(config)
    34  	}
    35  
    36  	return &Generator{Config: config}
    37  }
    38  
    39  func buildCharacterSet(config *Config) string {
    40  	var characterSet string
    41  	if config.IncludeLowercaseLetters {
    42  		characterSet += DefaultLetterSet
    43  		if config.ExcludeSimilarCharacters {
    44  			characterSet = removeCharacters(characterSet, DefaultLetterAmbiguousSet)
    45  		}
    46  	}
    47  
    48  	if config.IncludeUppercaseLetters {
    49  		characterSet += strings.ToUpper(DefaultLetterSet)
    50  		if config.ExcludeSimilarCharacters {
    51  			characterSet = removeCharacters(characterSet, strings.ToUpper(DefaultLetterAmbiguousSet))
    52  		}
    53  	}
    54  
    55  	if config.IncludeNumbers {
    56  		characterSet += DefaultNumberSet
    57  		if config.ExcludeSimilarCharacters {
    58  			characterSet = removeCharacters(characterSet, DefaultNumberAmbiguousSet)
    59  		}
    60  	}
    61  
    62  	if config.IncludeSymbols {
    63  		characterSet += DefaultSymbolSet
    64  		if config.ExcludeAmbiguousCharacters {
    65  			characterSet = removeCharacters(characterSet, DefaultSymbolAmbiguousSet)
    66  		}
    67  	}
    68  
    69  	return characterSet
    70  }
    71  
    72  func removeCharacters(str, characters string) string {
    73  	return strings.Map(func(r rune) rune {
    74  		if !strings.ContainsRune(characters, r) {
    75  			return r
    76  		}
    77  		return -1
    78  	}, str)
    79  }
    80  
    81  // NewWithDefault returns a new generator with the default
    82  // config
    83  func NewWithDefault() *Generator {
    84  	return New(&DefaultConfig)
    85  }
    86  
    87  // Generate generates one password with length set in the
    88  // config
    89  func (g Generator) Generate() (*string, error) {
    90  	var generated string
    91  	characterSet := strings.Split(g.Config.CharacterSet, "")
    92  	max := big.NewInt(int64(len(characterSet)))
    93  
    94  	for i := 0; i < g.Config.Length; i++ {
    95  		val, err := rand.Int(rand.Reader, max)
    96  		if err != nil {
    97  			return nil, err
    98  		}
    99  		generated += characterSet[val.Int64()]
   100  	}
   101  	return &generated, nil
   102  }
   103  
   104  // GenerateMany generates multiple passwords with length set
   105  // in the config
   106  func (g Generator) GenerateMany(amount int) ([]string, error) {
   107  	var generated []string
   108  	for i := 0; i < amount; i++ {
   109  		str, err := g.Generate()
   110  		if err != nil {
   111  			return nil, err
   112  		}
   113  
   114  		generated = append(generated, *str)
   115  	}
   116  	return generated, nil
   117  }
   118  
   119  // GenerateWithLength generate one password with set length
   120  func (g Generator) GenerateWithLength(length int) (*string, error) {
   121  	var generated string
   122  	characterSet := strings.Split(g.Config.CharacterSet, "")
   123  	max := big.NewInt(int64(len(characterSet)))
   124  	for i := 0; i < length; i++ {
   125  		val, err := rand.Int(rand.Reader, max)
   126  		if err != nil {
   127  			return nil, err
   128  		}
   129  		generated += characterSet[val.Int64()]
   130  	}
   131  	return &generated, nil
   132  }
   133  
   134  // GenerateManyWithLength generates multiple passwords with set length
   135  func (g Generator) GenerateManyWithLength(amount, length int) ([]string, error) {
   136  	var generated []string
   137  	for i := 0; i < amount; i++ {
   138  		str, err := g.GenerateWithLength(length)
   139  		if err != nil {
   140  			return nil, err
   141  		}
   142  		generated = append(generated, *str)
   143  	}
   144  	return generated, nil
   145  }