From 1f2a5c9e5cc95d9531b5c307322947c0535d8917 Mon Sep 17 00:00:00 2001 From: Taishi Kasuga Date: Sat, 10 Feb 2024 12:48:11 +0900 Subject: [PATCH] feat: support pipeline (#10) --- .github/workflows/test.yaml | 33 ++++++++++++++++++-- Makefile | 12 +++++++- README.md | 10 +++++++ cmd/tool/main.go | 60 ++++++++++++++++++++++++------------- cmd/tool/main_test.go | 28 +++++++++++++++++ pkg/pgpasswd/crypto.go | 4 +++ pkg/pgpasswd/crypto_test.go | 24 +++++++++++++++ 7 files changed, 148 insertions(+), 23 deletions(-) create mode 100644 cmd/tool/main_test.go create mode 100644 pkg/pgpasswd/crypto_test.go diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index ae1ab13..fb4c591 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -12,8 +12,34 @@ on: branches: - master jobs: - test: - name: Test + unit: + name: Unit + timeout-minutes: 5 + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + check-latest: true + cache: true + + - name: Download modules + run: go mod download + + - name: Lint + run: make lint + + - name: Test + run: make test + feature: + name: Feature timeout-minutes: 5 runs-on: ubuntu-latest defaults: @@ -61,6 +87,9 @@ jobs: check-latest: true cache: true + - name: Download modules + run: go mod download + - name: Build run: make diff --git a/Makefile b/Makefile index a6c46d8..0ea155f 100644 --- a/Makefile +++ b/Makefile @@ -6,4 +6,14 @@ CGO_ENABLED ?= $(shell go env CGO_ENABLED) cmd/tool/encrypt: cmd/tool/main.go GOOS=${GOOS} GOARCH=${GOARCH} CGO_ENABLED=${CGO_ENABLED} go build -ldflags="-s -w" -trimpath -o $@ $^ -.PHONY: cmd/tool/encrypt +test: + @go clean -testcache + @go test -race ./... + +lint: + @go vet ./... + +clean: + @rm -rf cmd/tool/encrypt + +.PHONY: cmd/tool/encrypt test lint clean diff --git a/README.md b/README.md index 34db1a2..068f080 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,16 @@ $ scram-sha-256 mysecret SCRAM-SHA-256$4096:1Iuyc2XTVSv/GFgCWSv9Xw==$nU96dFyIuV+uWwiOly7HU5yinIJh55GsItyFAYrU2sc=:fEC668A2ufIsGS+9WC8xqD0hHvHQBbLiDxZ8hWlwkCw= ``` +``` +$ echo -n mysecret | scram-sha-256 +SCRAM-SHA-256$4096:67e60Pre+3h6dhUm+K2tWA==$MRZtokLiZoWqNLf05HKH7STvtAtWEOy1CZU+vg9hj/M=:jzbp7PPDFT8aBPuFk91KBO2HswNJrvMMuMkUgR1LClI= +``` + +``` +$ echo mysecret | scram-sha-256 +SCRAM-SHA-256$4096:wvtRpXoTijsOR2py/yjIjQ==$iQV2GGKBAnN3v339hDOSZWxbl7YH8I3ERh+RCHjOqGQ=:Ea9Pyj4/IR53wmdCISCIOsSINUirJzz6EzD0NJqa05M= +``` + ```go import "github.com/supercaracal/scram-sha-256/pkg/pgpasswd" diff --git a/cmd/tool/main.go b/cmd/tool/main.go index 943114a..4a88b54 100644 --- a/cmd/tool/main.go +++ b/cmd/tool/main.go @@ -7,7 +7,9 @@ package main // @see https://github.com/postgres/postgres/blob/e6bdfd9700ebfc7df811c97c2fc46d7e94e329a2/src/common/scram-common.c#L27-L85 import ( + "bufio" "fmt" + "io" "os" "syscall" @@ -15,28 +17,45 @@ import ( "golang.org/x/crypto/ssh/terminal" ) -func readRawPassword(fd int) ([]byte, error) { - input, err := terminal.ReadPassword(fd) +func readViaTerminal(fd int) ([]byte, error) { + fmt.Print("Raw password: ") + passwd, err := terminal.ReadPassword(fd) + fmt.Println() if err != nil { return nil, err } - return input, nil + return passwd, nil +} + +func readViaPipe() ([]byte, error) { + r := bufio.NewReader(os.Stdin) + passwd, err := r.ReadBytes('\n') + if err == io.EOF { + return passwd, nil + } else if err != nil { + return nil, err + } + return passwd[0 : len(passwd)-1], nil +} + +func getRawPassword(args []string) ([]byte, error) { + if len(args) > 1 { + return []byte(args[1]), nil + } + + fd := int(syscall.Stdin) + if terminal.IsTerminal(fd) { + return readViaTerminal(fd) + } + + return readViaPipe() } func main() { - var rawPassword []byte - - if len(os.Args) > 1 { - rawPassword = []byte(os.Args[1]) - } else { - fmt.Print("Raw password: ") - passwd, err := readRawPassword(int(syscall.Stdin)) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - rawPassword = passwd - fmt.Println() + rawPassword, err := getRawPassword(os.Args) + if err != nil { + fmt.Println(err) + os.Exit(1) } if len(rawPassword) == 0 { @@ -44,11 +63,12 @@ func main() { os.Exit(1) } - if password, err := pgpasswd.Encrypt(rawPassword); err != nil { + encrypted, err := pgpasswd.Encrypt(rawPassword) + if err != nil { fmt.Println(err) os.Exit(1) - } else { - fmt.Printf("%s\n", password) - os.Exit(0) } + + fmt.Printf("%s\n", encrypted) + os.Exit(0) } diff --git a/cmd/tool/main_test.go b/cmd/tool/main_test.go new file mode 100644 index 0000000..0ddae68 --- /dev/null +++ b/cmd/tool/main_test.go @@ -0,0 +1,28 @@ +package main + +import ( + "testing" +) + +func TestGetRawPassword(t *testing.T) { + cases := []struct { + args []string + want string + err error + }{ + {[]string{}, "", nil}, + {[]string{""}, "", nil}, + {[]string{"", ""}, "", nil}, + {[]string{"", "dummy"}, "dummy", nil}, + } + + for n, c := range cases { + if got, err := getRawPassword(c.args); c.err == nil && err != nil { + t.Errorf("%d: %s", n, err) + } else if c.err != nil && err == nil { + t.Errorf("%d: no error: %s", n, c.err) + } else if string(got) != c.want { + t.Errorf("%d: want: %s, got: %s", n, c.want, got) + } + } +} diff --git a/pkg/pgpasswd/crypto.go b/pkg/pgpasswd/crypto.go index 166d7cd..dafba16 100644 --- a/pkg/pgpasswd/crypto.go +++ b/pkg/pgpasswd/crypto.go @@ -75,6 +75,10 @@ func encryptPassword(rawPassword, salt []byte, iter, keyLen int) string { // Encrypt encrypts a raw password with scram-sha-256 func Encrypt(rawPassword []byte) (string, error) { + if rawPassword == nil || len(rawPassword) == 0 { + return "", nil + } + salt, err := genSalt(saltSize) if err != nil { return "", err diff --git a/pkg/pgpasswd/crypto_test.go b/pkg/pgpasswd/crypto_test.go new file mode 100644 index 0000000..5b1f812 --- /dev/null +++ b/pkg/pgpasswd/crypto_test.go @@ -0,0 +1,24 @@ +package pgpasswd + +import ( + "testing" +) + +func TestEncrypt(t *testing.T) { + cases := []struct { + raw []byte + err error + }{ + {[]byte("foo"), nil}, + {[]byte(""), nil}, + {nil, nil}, + } + + for n, c := range cases { + if _, err := Encrypt(c.raw); err != nil && c.err == nil { + t.Errorf("%d: %s", n, err) + } else if err == nil && c.err != nil { + t.Errorf("%d: no error", n) + } + } +}