commit 21a8e201400a9d6a3194934b250452e39a04f633 Author: Nagy Károly Gábriel Date: Tue Dec 22 16:24:49 2020 +0200 Initial commit Signed-off-by: Nagy Károly Gábriel diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..fb7bb93 --- /dev/null +++ b/.editorconfig @@ -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 + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..00bed3b --- /dev/null +++ b/.gitignore @@ -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/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..26b18b2 --- /dev/null +++ b/Dockerfile @@ -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"] + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3630990 --- /dev/null +++ b/LICENSE @@ -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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..feaba92 --- /dev/null +++ b/Makefile @@ -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)"' diff --git a/README.md b/README.md new file mode 100644 index 0000000..0def412 --- /dev/null +++ b/README.md @@ -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 diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..bd08229 --- /dev/null +++ b/go.mod @@ -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 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..f1057b7 --- /dev/null +++ b/go.sum @@ -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= diff --git a/main.go b/main.go new file mode 100644 index 0000000..6c4e62b --- /dev/null +++ b/main.go @@ -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))) +} diff --git a/password.go b/password.go new file mode 100644 index 0000000..37fb259 --- /dev/null +++ b/password.go @@ -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 +}