diff options
Diffstat (limited to 'cli_test.go')
| -rw-r--r-- | cli_test.go | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/cli_test.go b/cli_test.go index 2ae9f8e..9b24196 100644 --- a/cli_test.go +++ b/cli_test.go @@ -5,7 +5,9 @@ package gotp import ( "bytes" + "errors" "fmt" + "io/fs" "os" "path/filepath" "testing" @@ -228,3 +230,294 @@ func TestCli_ViewEncrypted(t *testing.T) { issA.raw = nil test.Assert(t, `Get: testA`, issA, gotIssA) } + +func TestCli_withPassphrase(t *testing.T) { + var ( + tdata *test.Data + err error + ) + + tdata, err = test.LoadData(`testdata/cli_with_passphrase_test.txt`) + if err != nil { + t.Fatal(err) + } + + // Prepare directory with private key. + + var ( + dirConfig = t.TempDir() + filePrivateKey = filepath.Join(dirConfig, privateKeyFile) + ) + + err = os.WriteFile(filePrivateKey, tdata.Input[`gotp.key`], 0600) + if err != nil { + t.Fatal(err) + } + + var cli *Cli + + cli, err = NewCli(dirConfig) + if err != nil { + t.Fatal(err) + } + + t.Run(`Add`, func(t *testing.T) { + testAddWithPassphrase(t, tdata, cli) + }) + + t.Run(`Generate`, func(t *testing.T) { + testGenerateWithPassphrase(t, tdata, cli) + }) + + t.Run(`Get`, func(t *testing.T) { + testGetWithPassphrase(t, tdata, cli) + }) + + t.Run(`List`, func(t *testing.T) { + var ( + expListLabel = []string{`test-sha1`, `test-sha256`, `test-sha512`} + gotListLabel = cli.List() + ) + test.Assert(t, `List`, expListLabel, gotListLabel) + }) + + t.Run(`Remove`, func(t *testing.T) { + testRemoveWithPassphrase(t, tdata, cli) + }) + + t.Run(`Rename`, func(t *testing.T) { + testRenameWithPassphrase(t, tdata, cli) + }) + + t.Run(`RemovePrivateKey`, func(t *testing.T) { + testRemovePrivateKeyWithPassphrase(t, tdata, cli) + }) + + t.Run(`SetPrivateKey`, func(t *testing.T) { + testSetPrivateKeyWithPassphrase(t, tdata, cli) + }) +} + +func testAddWithPassphrase(t *testing.T, tdata *test.Data, cli *Cli) { + var ( + pass = string(tdata.Input[`gotp.pass`]) + "\r\n" + lines = bytes.Split(tdata.Input[`list_raw_issuer`], []byte{'\n'}) + + line []byte + labelIssuer [][]byte + issuer *Issuer + err error + ) + + for _, line = range lines { + labelIssuer = bytes.Split(line, []byte{'='}) + + issuer, err = NewIssuer(string(labelIssuer[0]), string(labelIssuer[1]), nil) + if err != nil { + t.Fatal(err) + } + + mockTermrw.BufRead.WriteString(pass) + + err = cli.Add(issuer) + if err != nil { + t.Fatal(err) + } + } + + assertGotpConf(t, cli, string(tdata.Output[`gotp.conf:encrypted`])) + + mockTermrw.BufRead.Reset() +} + +func testGenerateWithPassphrase(t *testing.T, tdata *test.Data, cli *Cli) { + type testCase struct { + label string + pass string + expListOTP []string + n int + } + + var validPass = string(tdata.Input[`gotp.pass`]) + "\r\n" + + var listCase = []testCase{{ + label: `test-sha1`, + n: 3, + pass: validPass, + expListOTP: []string{`002561`, `439480`, `508390`}, + }, { + label: `test-sha256`, + n: 3, + pass: validPass, + expListOTP: []string{`182691`, `322218`, `699844`}, + }, { + label: `test-sha512`, + n: 3, + pass: validPass, + expListOTP: []string{`595992`, `757602`, `224726`}, + }} + + var ( + c testCase + gotListOTP []string + err error + ) + + for _, c = range listCase { + mockTermrw.BufRead.WriteString(c.pass) + + gotListOTP, err = cli.Generate(c.label, c.n) + if err != nil { + t.Fatal(err) + } + + test.Assert(t, c.label, c.expListOTP, gotListOTP) + } + mockTermrw.BufRead.Reset() +} + +func testGetWithPassphrase(t *testing.T, tdata *test.Data, cli *Cli) { + type testCase struct { + label string + expIssuer string + } + + var listCase = []testCase{{ + label: `test-sha1`, + expIssuer: string(tdata.Output[`get:test-sha1`]), + }, { + label: `test-sha256`, + expIssuer: string(tdata.Output[`get:test-sha256`]), + }, { + label: `test-sha512`, + expIssuer: string(tdata.Output[`get:test-sha512`]), + }} + + var ( + pass = string(tdata.Input[`gotp.pass`]) + "\r\n" + + c testCase + issuer *Issuer + err error + ) + + for _, c = range listCase { + mockTermrw.BufRead.WriteString(pass) + + issuer, err = cli.Get(c.label) + if err != nil { + t.Fatal(err) + } + + test.Assert(t, c.label, c.expIssuer, issuer.String()) + } + mockTermrw.BufRead.Reset() +} + +func testRemoveWithPassphrase(t *testing.T, tdata *test.Data, cli *Cli) { + var err = cli.Remove(`test-sha512`) + if err != nil { + t.Fatal(err) + } + + assertGotpConf(t, cli, string(tdata.Output[`gotp.conf:remove:encrypted`])) + + mockTermrw.BufRead.Reset() +} + +// The Rename method does not require private key. +func testRenameWithPassphrase(t *testing.T, tdata *test.Data, cli *Cli) { + var err = cli.Rename(`test-sha1`, `renamed-sha1`) + if err != nil { + t.Fatal(err) + } + + assertGotpConf(t, cli, string(tdata.Output[`gotp.conf:rename:encrypted`])) + + mockTermrw.BufRead.Reset() +} + +func testRemovePrivateKeyWithPassphrase(t *testing.T, tdata *test.Data, cli *Cli) { + var pass = string(tdata.Input[`gotp.pass`]) + "\r\n" + + mockTermrw.BufRead.WriteString(pass) + + var err = cli.RemovePrivateKey() + if err != nil { + t.Fatal(err) + } + + assertGotpConf(t, cli, string(tdata.Output[`gotp.conf`])) + + var fileGotpKey = filepath.Join(cli.cfg.dir, privateKeyFile) + + _, err = os.Stat(fileGotpKey) + if !errors.Is(err, fs.ErrNotExist) { + t.Fatalf(`expecting gotp.key to be removed, but still exists`) + } + + mockTermrw.BufRead.Reset() +} + +func testSetPrivateKeyWithPassphrase(t *testing.T, tdata *test.Data, cli *Cli) { + // Write the private key file. + var newPrivateKeyFile = filepath.Join(cli.cfg.dir, `new.key`) + + var err = os.WriteFile(newPrivateKeyFile, tdata.Input[`gotp.key`], 0600) + if err != nil { + t.Fatal(err) + } + + var pass = string(tdata.Input[`gotp.pass`]) + "\r\n" + mockTermrw.BufRead.WriteString(pass) + + err = cli.SetPrivateKey(newPrivateKeyFile) + if err != nil { + t.Fatal(err) + } + + _, err = os.Stat(cli.cfg.privateKeyFile) + if err != nil { + t.Fatal(err) + } + + // When SetPrivateKey success, the output is somehow not + // deterministic. + // So we need to compare them with two possible outcomes. + + var gotpConf = filepath.Join(cli.cfg.dir, configFile) + var rawconf []byte + + rawconf, err = os.ReadFile(gotpConf) + if err != nil { + t.Fatal(err) + } + + rawconf = bytes.TrimSpace(rawconf) + + var gotConf = string(rawconf) + var exp = string(tdata.Output[`gotp.conf:set-private-key:encrypted`]) + + if exp != gotConf { + exp = string(tdata.Output[`gotp.conf:set-private-key:encrypted:alt`]) + test.Assert(t, `assertGotpConf`, exp, string(rawconf)) + } + + mockTermrw.BufRead.Reset() +} + +func assertGotpConf(t *testing.T, cli *Cli, exp string) { + var ( + gotpConf = filepath.Join(cli.cfg.dir, configFile) + + rawconf []byte + err error + ) + + rawconf, err = os.ReadFile(gotpConf) + if err != nil { + t.Fatal(err) + } + + test.Assert(t, `assertGotpConf`, exp, string(rawconf)) +} |
