Nagy Károly Gábriel
4 years ago
commit
21a8e20140
10 changed files with 542 additions and 0 deletions
@ -0,0 +1,19 @@ |
|||||||
|
# http://editorconfig.org |
||||||
|
|
||||||
|
root = true |
||||||
|
|
||||||
|
[*] |
||||||
|
charset = utf-8 |
||||||
|
end_of_line = lf |
||||||
|
insert_final_newline = true |
||||||
|
trim_trailing_whitespace = true |
||||||
|
indent_size = 4 |
||||||
|
indent_style = space |
||||||
|
|
||||||
|
[*.go] |
||||||
|
indent_style = tab |
||||||
|
indent_size = 4 |
||||||
|
|
||||||
|
[Makefile] |
||||||
|
indent_style = tab |
||||||
|
|
@ -0,0 +1,26 @@ |
|||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects) |
||||||
|
*.o |
||||||
|
*.a |
||||||
|
*.so |
||||||
|
|
||||||
|
# Folders |
||||||
|
_obj |
||||||
|
_test |
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes |
||||||
|
*.[568vq] |
||||||
|
[568vq].out |
||||||
|
|
||||||
|
*.cgo1.go |
||||||
|
*.cgo2.c |
||||||
|
_cgo_defun.c |
||||||
|
_cgo_gotypes.go |
||||||
|
_cgo_export.* |
||||||
|
|
||||||
|
_testmain.go |
||||||
|
|
||||||
|
*.exe |
||||||
|
*.test |
||||||
|
*.prof |
||||||
|
bin/ |
||||||
|
release/ |
@ -0,0 +1,12 @@ |
|||||||
|
FROM golang:alpine as build |
||||||
|
|
||||||
|
WORKDIR /go/src/ranpass |
||||||
|
COPY . /go/src/ranpass |
||||||
|
RUN CGO_ENABLED=0 go build -o /go/bin/ranpass |
||||||
|
|
||||||
|
FROM scratch |
||||||
|
COPY --from=build /go/bin/ranpass / |
||||||
|
|
||||||
|
EXPOSE 8080 |
||||||
|
ENTRYPOINT ["./ranpass"] |
||||||
|
|
@ -0,0 +1,21 @@ |
|||||||
|
MIT License |
||||||
|
|
||||||
|
Copyright (c) 2019 Nagy Károly Gábriel |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
of this software and associated documentation files (the "Software"), to deal |
||||||
|
in the Software without restriction, including without limitation the rights |
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
copies of the Software, and to permit persons to whom the Software is |
||||||
|
furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all |
||||||
|
copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
SOFTWARE. |
@ -0,0 +1,57 @@ |
|||||||
|
# Makefile for projects using go mod.
|
||||||
|
|
||||||
|
|
||||||
|
#V := 1 # When V is set, print commands and build progress.
|
||||||
|
|
||||||
|
ARCHES:=amd64 arm64
|
||||||
|
OSES:=linux
|
||||||
|
BINARY:=ranpass
|
||||||
|
|
||||||
|
all: release |
||||||
|
|
||||||
|
build: |
||||||
|
$Q for arch in $(ARCHES) ; do \
|
||||||
|
for os in $(OSES); do \
|
||||||
|
CGO_ENABLED=0 GOOS=$$os GOARCH=$$arch go build $(VERSION_FLAGS) -a -installsuffix cgo -o ./bin/$(BINARY)-$$os-$$arch $(IMPORT_PATH); \
|
||||||
|
done ; \
|
||||||
|
done
|
||||||
|
|
||||||
|
release: clean build |
||||||
|
$Q mkdir -p ./release
|
||||||
|
$Q for arch in $(ARCHES) ; do \
|
||||||
|
for os in $(OSES) ; do \
|
||||||
|
rm -rf ./tmp/$(BINARY) ; \
|
||||||
|
mkdir -p ./tmp/$(BINARY) ; \
|
||||||
|
cp -a ./bin/$(BINARY)-$$os-$$arch ./tmp/$(BINARY)/$(BINARY) ; \
|
||||||
|
tar -cz -C ./tmp -f ./release/$(BINARY)-$$os-$$arch-$(VERSION).tar.gz ./$(BINARY) ; \
|
||||||
|
rm -rf ./tmp ; \
|
||||||
|
done ; \
|
||||||
|
done
|
||||||
|
|
||||||
|
##### =====> Utility targets <===== #####
|
||||||
|
|
||||||
|
.PHONY: clean gen setup |
||||||
|
|
||||||
|
clean: |
||||||
|
$Q rm -rf bin
|
||||||
|
$Q rm -rf /release/*-$(VERSION)*
|
||||||
|
|
||||||
|
gen: |
||||||
|
@echo "Running go generate"
|
||||||
|
$Q go generate
|
||||||
|
@echo "Done!"
|
||||||
|
|
||||||
|
setup: |
||||||
|
@echo "Running go mod init"
|
||||||
|
$Q go mod init
|
||||||
|
@echo "Done!"
|
||||||
|
|
||||||
|
.PHONY: all build release |
||||||
|
|
||||||
|
##### =====> Internals <===== #####
|
||||||
|
|
||||||
|
Q := $(if $V,,@)
|
||||||
|
IMPORT_PATH := $(shell awk -F" " '$$1=="module"{print $$2;exit;}' go.mod)
|
||||||
|
VERSION := $(shell git describe --tags --always --dirty="-dev")
|
||||||
|
DATE := $(shell date -u '+%Y-%m-%d-%H%M UTC')
|
||||||
|
VERSION_FLAGS := -ldflags='-s -w -X "main.Version=$(VERSION)" -X "main.BuildTime=$(DATE)"'
|
@ -0,0 +1,5 @@ |
|||||||
|
# gonew |
||||||
|
This is the go mod version of gonew. |
||||||
|
If you want the previous version that is |
||||||
|
using some hackery around GOPATH please |
||||||
|
use the GOPATH branch https://github.com/karasz/gonew/tree/GOPATH |
@ -0,0 +1,11 @@ |
|||||||
|
module git.jpi.io/JPI/ranpass |
||||||
|
|
||||||
|
go 1.14 |
||||||
|
|
||||||
|
require ( |
||||||
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect |
||||||
|
github.com/labstack/echo v3.3.10+incompatible |
||||||
|
github.com/labstack/gommon v0.3.0 // indirect |
||||||
|
github.com/valyala/fasttemplate v1.2.1 // indirect |
||||||
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect |
||||||
|
) |
@ -0,0 +1,50 @@ |
|||||||
|
github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= |
||||||
|
github.com/GeertJohan/go.rice v1.0.2 h1:PtRw+Tg3oa3HYwiDBZyvOJ8LdIyf6lAovJJtr7YOAYk= |
||||||
|
github.com/GeertJohan/go.rice v1.0.2/go.mod h1:af5vUNlDNkCjOZeSGFgIJxDje9qdjsO6hshx0gTmZt4= |
||||||
|
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= |
||||||
|
github.com/daaku/go.zipexe v1.0.0 h1:VSOgZtH418pH9L16hC/JrgSNJbbAL26pj7lmD1+CGdY= |
||||||
|
github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E= |
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
||||||
|
github.com/dgrijalva/jwt-go v1.0.2 h1:KPldsxuKGsS2FPWsNeg9ZO18aCrGKujPoWXn2yo+KQM= |
||||||
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= |
||||||
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= |
||||||
|
github.com/foolin/echo-template v0.0.0-20190415034849-543a88245eec h1:BurQ3oNjJkB5+zibQGHw97kZOBN/DLlAKYlvfsacBD0= |
||||||
|
github.com/foolin/echo-template v0.0.0-20190415034849-543a88245eec/go.mod h1:4ePCtze3Ivy48ps0gJWfSByLgfqvT0rF+RpKQFe4Ja0= |
||||||
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= |
||||||
|
github.com/labstack/echo v1.4.4 h1:1bEiBNeGSUKxcPDGfZ/7IgdhJJZx8wV/pICJh4W2NJI= |
||||||
|
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg= |
||||||
|
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= |
||||||
|
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= |
||||||
|
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= |
||||||
|
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= |
||||||
|
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= |
||||||
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= |
||||||
|
github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg= |
||||||
|
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= |
||||||
|
github.com/nkovacs/streamquote v1.0.0/go.mod h1:BN+NaZ2CmdKqUuTUXUEm9j95B2TRbpOWpxbJYzzgUsc= |
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= |
||||||
|
github.com/sethvargo/go-password v0.2.0 h1:BTDl4CC/gjf/axHMaDQtw507ogrXLci6XRiLc7i/UHI= |
||||||
|
github.com/sethvargo/go-password v0.2.0/go.mod h1:Ym4Mr9JXLBycr02MFuVQ/0JHidNetSgbzutTr3zsYXE= |
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= |
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= |
||||||
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= |
||||||
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= |
||||||
|
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= |
||||||
|
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= |
||||||
|
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= |
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= |
||||||
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= |
||||||
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= |
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= |
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= |
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
||||||
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |
||||||
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= |
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
||||||
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= |
||||||
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= |
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= |
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
@ -0,0 +1,93 @@ |
|||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"flag" |
||||||
|
"net/http" |
||||||
|
"strconv" |
||||||
|
|
||||||
|
"github.com/labstack/echo" |
||||||
|
"github.com/labstack/echo/middleware" |
||||||
|
) |
||||||
|
|
||||||
|
type passwordData struct { |
||||||
|
Length int |
||||||
|
Digits int |
||||||
|
Symbols int |
||||||
|
NoUpper bool |
||||||
|
DenyRepeat bool |
||||||
|
Password string |
||||||
|
} |
||||||
|
|
||||||
|
var d, defaults passwordData |
||||||
|
|
||||||
|
func generatePassword(c echo.Context) error { |
||||||
|
|
||||||
|
var err error |
||||||
|
|
||||||
|
d.Length, err = strconv.Atoi(c.FormValue("length")) |
||||||
|
if err != nil { |
||||||
|
d.Length = defaults.Length |
||||||
|
} |
||||||
|
|
||||||
|
d.Digits, err = strconv.Atoi(c.FormValue("digits")) |
||||||
|
if err != nil { |
||||||
|
d.Digits = defaults.Digits |
||||||
|
} |
||||||
|
|
||||||
|
d.Symbols, err = strconv.Atoi(c.FormValue("symbols")) |
||||||
|
if err != nil { |
||||||
|
d.Symbols = defaults.Symbols |
||||||
|
} |
||||||
|
|
||||||
|
if c.FormValue("noupper") == "on" { |
||||||
|
d.NoUpper = true |
||||||
|
} |
||||||
|
|
||||||
|
if c.FormValue("denyrepeat") == "on" { |
||||||
|
d.DenyRepeat = true |
||||||
|
} |
||||||
|
|
||||||
|
// Generate a password
|
||||||
|
d.Password, err = generate(d.Length, d.Digits, d.Symbols, d.NoUpper, !d.DenyRepeat) |
||||||
|
if err != nil { |
||||||
|
d.Password = "Error: " + err.Error() |
||||||
|
} |
||||||
|
if d.Length == 0 { |
||||||
|
d.Password = "Error: password can not have zero length" |
||||||
|
} |
||||||
|
|
||||||
|
return c.String(http.StatusOK, d.Password) |
||||||
|
} |
||||||
|
|
||||||
|
func checkHealth(c echo.Context) error { |
||||||
|
s := `{"status":"OK"}` |
||||||
|
return c.String(http.StatusOK, s) |
||||||
|
} |
||||||
|
|
||||||
|
func main() { |
||||||
|
|
||||||
|
// Set defaults
|
||||||
|
portPtr := flag.Int("listen", 8080, "Specify on which port to listen") |
||||||
|
lengthPtr := flag.Int("length", 16, "Specify the password length") |
||||||
|
digitsPtr := flag.Int("digits", 2, "Specify the the number of digits in the password") |
||||||
|
symbolsPtr := flag.Int("symbols", 2, "Specify the the number of symbols in the password") |
||||||
|
flag.Parse() |
||||||
|
|
||||||
|
defaults.Length = *lengthPtr |
||||||
|
defaults.Digits = *digitsPtr |
||||||
|
defaults.Symbols = *symbolsPtr |
||||||
|
|
||||||
|
e := echo.New() |
||||||
|
e.HideBanner = true |
||||||
|
|
||||||
|
// Middleware
|
||||||
|
e.Use(middleware.Logger()) |
||||||
|
e.Use(middleware.Recover()) |
||||||
|
|
||||||
|
e.GET("/", generatePassword) |
||||||
|
e.POST("/", generatePassword) |
||||||
|
|
||||||
|
e.GET("/health", checkHealth) |
||||||
|
|
||||||
|
e.Logger.Fatal(e.Start(":" + strconv.Itoa(*portPtr))) |
||||||
|
} |
@ -0,0 +1,248 @@ |
|||||||
|
// Package password provides a library for generating high-entropy random
|
||||||
|
// password strings via the crypto/rand package.
|
||||||
|
//
|
||||||
|
// res, err := Generate(64, 10, 10, false, false)
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatal(err)
|
||||||
|
// }
|
||||||
|
// log.Printf(res)
|
||||||
|
//
|
||||||
|
// Most functions are safe for concurrent use.
|
||||||
|
package main |
||||||
|
|
||||||
|
import ( |
||||||
|
"crypto/rand" |
||||||
|
"errors" |
||||||
|
"io" |
||||||
|
"math/big" |
||||||
|
"strings" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
// LowerLetters is the list of lowercase letters.
|
||||||
|
LowerLetters = "abcdefghijklmnopqrstuvwxyz" |
||||||
|
|
||||||
|
// UpperLetters is the list of uppercase letters.
|
||||||
|
UpperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
||||||
|
|
||||||
|
// Digits is the list of permitted digits.
|
||||||
|
Digits = "0123456789" |
||||||
|
|
||||||
|
// Symbols is the list of symbols.
|
||||||
|
Symbols = "~!@#$%^&*()_+`-={}|[]\\:\"<>?,./" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
// ErrExceedsTotalLength is the error returned with the number of digits and
|
||||||
|
// symbols is greater than the total length.
|
||||||
|
ErrExceedsTotalLength = errors.New("number of digits and symbols must be less than total length") |
||||||
|
|
||||||
|
// ErrLettersExceedsAvailable is the error returned with the number of letters
|
||||||
|
// exceeds the number of available letters and repeats are not allowed.
|
||||||
|
ErrLettersExceedsAvailable = errors.New("number of letters exceeds available letters and repeats are not allowed") |
||||||
|
|
||||||
|
// ErrDigitsExceedsAvailable is the error returned with the number of digits
|
||||||
|
// exceeds the number of available digits and repeats are not allowed.
|
||||||
|
ErrDigitsExceedsAvailable = errors.New("number of digits exceeds available digits and repeats are not allowed") |
||||||
|
|
||||||
|
// ErrSymbolsExceedsAvailable is the error returned with the number of symbols
|
||||||
|
// exceeds the number of available symbols and repeats are not allowed.
|
||||||
|
ErrSymbolsExceedsAvailable = errors.New("number of symbols exceeds available symbols and repeats are not allowed") |
||||||
|
) |
||||||
|
|
||||||
|
type generator struct { |
||||||
|
lowerLetters string |
||||||
|
upperLetters string |
||||||
|
digits string |
||||||
|
symbols string |
||||||
|
reader io.Reader |
||||||
|
} |
||||||
|
|
||||||
|
// GeneratorInput is used as input to the NewGenerator function.
|
||||||
|
type generatorInput struct { |
||||||
|
LowerLetters string |
||||||
|
UpperLetters string |
||||||
|
Digits string |
||||||
|
Symbols string |
||||||
|
Reader io.Reader // rand.Reader by default
|
||||||
|
} |
||||||
|
|
||||||
|
// NewGenerator creates a new Generator from the specified configuration. If no
|
||||||
|
// input is given, all the default values are used. This function is safe for
|
||||||
|
// concurrent use.
|
||||||
|
func newGenerator(i *generatorInput) (*generator, error) { |
||||||
|
if i == nil { |
||||||
|
i = new(generatorInput) |
||||||
|
} |
||||||
|
|
||||||
|
g := &generator{ |
||||||
|
lowerLetters: i.LowerLetters, |
||||||
|
upperLetters: i.UpperLetters, |
||||||
|
digits: i.Digits, |
||||||
|
symbols: i.Symbols, |
||||||
|
reader: i.Reader, |
||||||
|
} |
||||||
|
|
||||||
|
if g.lowerLetters == "" { |
||||||
|
g.lowerLetters = LowerLetters |
||||||
|
} |
||||||
|
|
||||||
|
if g.upperLetters == "" { |
||||||
|
g.upperLetters = UpperLetters |
||||||
|
} |
||||||
|
|
||||||
|
if g.digits == "" { |
||||||
|
g.digits = Digits |
||||||
|
} |
||||||
|
|
||||||
|
if g.symbols == "" { |
||||||
|
g.symbols = Symbols |
||||||
|
} |
||||||
|
|
||||||
|
if g.reader == nil { |
||||||
|
g.reader = rand.Reader |
||||||
|
} |
||||||
|
|
||||||
|
return g, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Generate generates a password with the given requirements. length is the
|
||||||
|
// total number of characters in the password. numDigits is the number of digits
|
||||||
|
// to include in the result. numSymbols is the number of symbols to include in
|
||||||
|
// the result. noUpper excludes uppercase letters from the results. allowRepeat
|
||||||
|
// allows characters to repeat.
|
||||||
|
//
|
||||||
|
// The algorithm is fast, but it's not designed to be performant; it favors
|
||||||
|
// entropy over speed. This function is safe for concurrent use.
|
||||||
|
func (g *generator) generate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) (string, error) { |
||||||
|
letters := g.lowerLetters |
||||||
|
if !noUpper { |
||||||
|
letters += g.upperLetters |
||||||
|
} |
||||||
|
|
||||||
|
chars := length - numDigits - numSymbols |
||||||
|
if chars < 0 { |
||||||
|
return "", ErrExceedsTotalLength |
||||||
|
} |
||||||
|
|
||||||
|
if !allowRepeat && chars > len(letters) { |
||||||
|
return "", ErrLettersExceedsAvailable |
||||||
|
} |
||||||
|
|
||||||
|
if !allowRepeat && numDigits > len(g.digits) { |
||||||
|
return "", ErrDigitsExceedsAvailable |
||||||
|
} |
||||||
|
|
||||||
|
if !allowRepeat && numSymbols > len(g.symbols) { |
||||||
|
return "", ErrSymbolsExceedsAvailable |
||||||
|
} |
||||||
|
|
||||||
|
var result string |
||||||
|
|
||||||
|
// Characters
|
||||||
|
for i := 0; i < chars; i++ { |
||||||
|
ch, err := randomElement(g.reader, letters) |
||||||
|
if err != nil { |
||||||
|
return "", err |
||||||
|
} |
||||||
|
|
||||||
|
if !allowRepeat && strings.Contains(result, ch) { |
||||||
|
i-- |
||||||
|
continue |
||||||
|
} |
||||||
|
|
||||||
|
result, err = randomInsert(g.reader, result, ch) |
||||||
|
if err != nil { |
||||||
|
return "", err |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Digits
|
||||||
|
for i := 0; i < numDigits; i++ { |
||||||
|
d, err := randomElement(g.reader, g.digits) |
||||||
|
if err != nil { |
||||||
|
return "", err |
||||||
|
} |
||||||
|
|
||||||
|
if !allowRepeat && strings.Contains(result, d) { |
||||||
|
i-- |
||||||
|
continue |
||||||
|
} |
||||||
|
|
||||||
|
result, err = randomInsert(g.reader, result, d) |
||||||
|
if err != nil { |
||||||
|
return "", err |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Symbols
|
||||||
|
for i := 0; i < numSymbols; i++ { |
||||||
|
sym, err := randomElement(g.reader, g.symbols) |
||||||
|
if err != nil { |
||||||
|
return "", err |
||||||
|
} |
||||||
|
|
||||||
|
if !allowRepeat && strings.Contains(result, sym) { |
||||||
|
i-- |
||||||
|
continue |
||||||
|
} |
||||||
|
|
||||||
|
result, err = randomInsert(g.reader, result, sym) |
||||||
|
if err != nil { |
||||||
|
return "", err |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return result, nil |
||||||
|
} |
||||||
|
|
||||||
|
// MustGenerate is the same as Generate, but panics on error.
|
||||||
|
func (g *generator) mustGenerate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) string { |
||||||
|
res, err := g.generate(length, numDigits, numSymbols, noUpper, allowRepeat) |
||||||
|
if err != nil { |
||||||
|
panic(err) |
||||||
|
} |
||||||
|
return res |
||||||
|
} |
||||||
|
|
||||||
|
// Generate is the package shortcut for Generator.Generate.
|
||||||
|
func generate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) (string, error) { |
||||||
|
gen, err := newGenerator(nil) |
||||||
|
if err != nil { |
||||||
|
return "", err |
||||||
|
} |
||||||
|
|
||||||
|
return gen.generate(length, numDigits, numSymbols, noUpper, allowRepeat) |
||||||
|
} |
||||||
|
|
||||||
|
// MustGenerate is the package shortcut for Generator.MustGenerate.
|
||||||
|
func mustGenerate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) string { |
||||||
|
res, err := generate(length, numDigits, numSymbols, noUpper, allowRepeat) |
||||||
|
if err != nil { |
||||||
|
panic(err) |
||||||
|
} |
||||||
|
return res |
||||||
|
} |
||||||
|
|
||||||
|
// randomInsert randomly inserts the given value into the given string.
|
||||||
|
func randomInsert(reader io.Reader, s, val string) (string, error) { |
||||||
|
if s == "" { |
||||||
|
return val, nil |
||||||
|
} |
||||||
|
|
||||||
|
n, err := rand.Int(reader, big.NewInt(int64(len(s)+1))) |
||||||
|
if err != nil { |
||||||
|
return "", err |
||||||
|
} |
||||||
|
i := n.Int64() |
||||||
|
return s[0:i] + val + s[i:], nil |
||||||
|
} |
||||||
|
|
||||||
|
// randomElement extracts a random element from the given string.
|
||||||
|
func randomElement(reader io.Reader, s string) (string, error) { |
||||||
|
n, err := rand.Int(reader, big.NewInt(int64(len(s)))) |
||||||
|
if err != nil { |
||||||
|
return "", err |
||||||
|
} |
||||||
|
return string(s[n.Int64()]), nil |
||||||
|
} |
Loading…
Reference in new issue