Skip to content

Commit

Permalink
Update variable get to retrieve multiple variables
Browse files Browse the repository at this point in the history
  • Loading branch information
rpothier committed Feb 28, 2023
1 parent 906b8e2 commit b0a6ed1
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 24 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Nothing should go in this section, please add to the latest unreleased version
(and update the corresponding date), or add a new version.

## [8.0.4] - 2023-02-23
## [8.0.4] - 2023-02-28

### Fixed
- Allow hostfactory cidrs to specify a subnet
[cyberark/conjur-cli-go#113](https://github.com/cyberark/conjur-cli-go/pull/113)
- Update variable get to retrieve multiple variables
[cyberark/conjur-cli-go#114](https://github.com/cyberark/conjur-cli-go/pull/114)

## [8.0.3] - 2023-02-21

Expand Down
12 changes: 11 additions & 1 deletion cmd/integration/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func TestIntegration(t *testing.T) {

t.Run("policy load", func(t *testing.T) {
stdOut, stdErr, err = conjurCLI.RunWithStdin(
bytes.NewReader([]byte("- !variable meow\n- !user alice\n- !host bob")),
bytes.NewReader([]byte("- !variable meow\n- !variable woof\n- !user alice\n- !host bob")),
"policy", "load", "-b", "root", "-f", "-",
)
assertPolicyLoadCmd(t, err, stdOut, stdErr)
Expand All @@ -81,11 +81,21 @@ func TestIntegration(t *testing.T) {
assertSetVariableCmd(t, err, stdOut, stdErr)
})

t.Run("set another variable after policy load", func(t *testing.T) {
stdOut, stdErr, err = conjurCLI.Run("variable", "set", "-i", "woof", "-v", "quack")
assertSetVariableCmd(t, err, stdOut, stdErr)
})

t.Run("get variable after policy load", func(t *testing.T) {
stdOut, stdErr, err = conjurCLI.Run("variable", "get", "-i", "meow")
assertGetVariableCmd(t, err, stdOut, stdErr)
})

t.Run("get two variables after policy load", func(t *testing.T) {
stdOut, stdErr, err = conjurCLI.Run("variable", "get", "-i", "meow,woof")
assertGetTwoVariablesCmd(t, err, stdOut, stdErr)
})

t.Run("exists returns false", func(t *testing.T) {
stdOut, stdErr, err = conjurCLI.Run("role", "exists", "dev:user:meow")
assertExistsCmd(t, err, stdOut, stdErr)
Expand Down
7 changes: 7 additions & 0 deletions cmd/integration/shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,13 @@ func assertGetVariableCmd(t *testing.T, err error, stdOut string, stdErr string)
assert.Equal(t, "", stdErr)
}

func assertGetTwoVariablesCmd(t *testing.T, err error, stdOut string, stdErr string) {
assert.NoError(t, err)
assert.Contains(t, stdOut, "moo")
assert.Contains(t, stdOut, "quack")
assert.Equal(t, "", stdErr)
}

func assertExistsCmd(t *testing.T, err error, stdOut string, stdErr string) {
assert.NoError(t, err)
assert.Equal(t, "false\n", stdOut)
Expand Down
1 change: 1 addition & 0 deletions dev/policy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- !layer test-layer

- !variable secret
- !variable top-secret

- !permit
# Give permissions to the human user to update the secret and fetch the secret.
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ go 1.19

require (
github.com/chzyer/readline v1.5.1
github.com/cyberark/conjur-api-go v0.10.3-0.20230217143503-a059d3d0c5da // Run "go get github.com/cyberark/conjur-api-go@main" to update
github.com/cyberark/conjur-api-go v0.10.3-0.20230217154521-e01f713716d2 // Run "go get github.com/cyberark/conjur-api-go@main" to update
github.com/manifoldco/promptui v0.9.0
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8
github.com/spf13/cobra v1.5.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyberark/conjur-api-go v0.10.3-0.20230217143503-a059d3d0c5da h1:nwoF3JbH7dL+RqxpHkwkKu+or+QUkdKrYzoGUAGnw2Q=
github.com/cyberark/conjur-api-go v0.10.3-0.20230217143503-a059d3d0c5da/go.mod h1:AbU7bDVW6ygUdgTDCKkh4wyfIVrOtdEeE/r01OE1EYo=
github.com/cyberark/conjur-api-go v0.10.3-0.20230217154521-e01f713716d2 h1:bvwd6+PiMS71vTjbk+I7CoZ5y0lk8StzoalRaTdCUP4=
github.com/cyberark/conjur-api-go v0.10.3-0.20230217154521-e01f713716d2/go.mod h1:AbU7bDVW6ygUdgTDCKkh4wyfIVrOtdEeE/r01OE1EYo=
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down
1 change: 1 addition & 0 deletions pkg/clients/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type ConjurClient interface {
LoadPolicy(mode conjurapi.PolicyMode, policyID string, policy io.Reader) (*conjurapi.PolicyResponse, error)
AddSecret(variableID string, secretValue string) error
RetrieveSecret(variableID string) ([]byte, error)
RetrieveBatchSecretsSafe(variableIDs []string) (map[string][]byte, error)
RetrieveSecretWithVersion(variableID string, version int) ([]byte, error)
CheckPermission(resourceID string, privilege string) (bool, error)
CheckPermissionForRole(resourceID string, roleID string, privilege string) (bool, error)
Expand Down
57 changes: 43 additions & 14 deletions pkg/cmd/variable.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package cmd

import (
"fmt"
"strconv"
"strings"

"github.com/cyberark/conjur-cli-go/pkg/clients"
"github.com/spf13/cobra"
Expand All @@ -26,7 +28,8 @@ func newVariableCmd(

// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
variableGetCmd.Flags().StringP("id", "i", "", "Provide variable identifier")
ids := make([]string, 0)
variableGetCmd.Flags().StringSliceP("id", "i", ids, "Provide variable identifiers")
variableGetCmd.MarkFlagRequired("id")

// Variable versions start from 1 and then increase so we don't know
Expand All @@ -44,6 +47,7 @@ func newVariableCmd(

type variableGetClient interface {
RetrieveSecret(string) ([]byte, error)
RetrieveBatchSecretsSafe(variableIDs []string) (map[string][]byte, error)
RetrieveSecretWithVersion(string, int) ([]byte, error)
}
type variableSetClient interface {
Expand All @@ -61,17 +65,44 @@ func variableSetClientFactory(cmd *cobra.Command) (variableSetClient, error) {
return clients.AuthenticatedConjurClientForCommand(cmd)
}

func printMultilineResults(cmd *cobra.Command, secrets map[string][]byte) error {
if len(secrets) > 1 {
cmd.Println("{")
for fullID, value := range secrets {
id := strings.Split(string(fullID), ":")
cmd.Printf(" \"%s\": \"%s\"\n", id[len(id)-1], value)
}
cmd.Println("}")
} else {
for _, v := range secrets {
cmd.Println(string(v))
}
}
return nil
}

func newVariableGetCmd(clientFactory variableGetClientFactoryFunc) *cobra.Command {
return &cobra.Command{
Use: "get",
Short: "Use the get subcommand to get the value of one or more Conjur variables",
Use: "get",
Short: "Use the get subcommand to get the value of one or more Conjur variables",
Long: `Use the get subcommand to get the value of one or more Conjur variables
Examples:
- conjur variable set -i secret -v my_secret_value
- conjur variable get -i secret
- conjur variable get -i secret,secret2
- conjur variable get -i secret -v 1
`,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
id, err := cmd.Flags().GetString("id")
id, err := cmd.Flags().GetStringSlice("id")
if err != nil {
return err
}

singleID := ""
if len(id) == 1 {
singleID = id[0]
}
client, err := clientFactory(cmd)
if err != nil {
return err
Expand All @@ -82,26 +113,24 @@ func newVariableGetCmd(clientFactory variableGetClientFactoryFunc) *cobra.Comman
return err
}

var data []byte
data := map[string][]byte{}

if versionStr == "" {
data, err = client.RetrieveSecret(id)
data, err = client.RetrieveBatchSecretsSafe(id)
} else {
if len(id) > 1 {
return fmt.Errorf("version can not be used with multiple variables")
}
version, err := strconv.Atoi(versionStr)
if err != nil {
return err
}

data, err = client.RetrieveSecretWithVersion(id, version)
data[singleID], err = client.RetrieveSecretWithVersion(singleID, version)
}

if err != nil {
return err
}

cmd.Println(string(data))

return nil
return printMultilineResults(cmd, data)
},
}
}
Expand Down
36 changes: 31 additions & 5 deletions pkg/cmd/variable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type mockVariableClient struct {
t *testing.T
get func(*testing.T, string) ([]byte, error)
getWithVersion func(*testing.T, string, int) ([]byte, error)
getBatch func(*testing.T, []string) (map[string][]byte, error)
set func(*testing.T, string, string) error
}

Expand All @@ -21,6 +22,9 @@ func (m mockVariableClient) RetrieveSecret(path string) ([]byte, error) {
func (m mockVariableClient) RetrieveSecretWithVersion(path string, version int) ([]byte, error) {
return m.getWithVersion(m.t, path, version)
}
func (m mockVariableClient) RetrieveBatchSecretsSafe(paths []string) (map[string][]byte, error) {
return m.getBatch(m.t, paths)
}
func (m mockVariableClient) AddSecret(path string, value string) error {
return m.set(m.t, path, value)
}
Expand All @@ -30,6 +34,7 @@ var variableCmdTestCases = []struct {
args []string
get func(t *testing.T, path string) ([]byte, error)
getWithVersion func(t *testing.T, path string, version int) ([]byte, error)
getBatch func(t *testing.T, paths []string) (map[string][]byte, error)
set func(t *testing.T, path string, value string) error
clientFactoryError error
assert func(t *testing.T, stdout string, stderr string, err error)
Expand Down Expand Up @@ -58,11 +63,11 @@ var variableCmdTestCases = []struct {
{
name: "get subcommand",
args: []string{"variable", "get", "-i", "meow"},
get: func(t *testing.T, path string) ([]byte, error) {
getBatch: func(t *testing.T, path []string) (map[string][]byte, error) {
// Assert on arguments
assert.Equal(t, "meow", path)
assert.Equal(t, ("meow"), path[0])

return []byte("moo"), nil
return map[string][]byte{"meow": []byte("moo")}, nil
},
assert: func(t *testing.T, stdout, stderr string, err error) {
assert.Contains(t, stdout, "moo")
Expand All @@ -71,7 +76,7 @@ var variableCmdTestCases = []struct {
{
name: "get subcommand error",
args: []string{"variable", "get", "-i", "meow"},
get: func(t *testing.T, path string) ([]byte, error) {
getBatch: func(t *testing.T, path []string) (map[string][]byte, error) {
return nil, fmt.Errorf("%s", "get error")
},
assert: func(t *testing.T, stdout, stderr string, err error) {
Expand Down Expand Up @@ -106,6 +111,27 @@ var variableCmdTestCases = []struct {
assert.Contains(t, stderr, "Error: client factory error\n")
},
},
{
name: "get two variables",
args: []string{"variable", "get", "-i", "meow,woof"},
getBatch: func(t *testing.T, path []string) (map[string][]byte, error) {
// Assert on arguments
assert.Equal(t, "meow", path[0])
assert.Equal(t, "woof", path[1])
return map[string][]byte{"meow": []byte("moo"), "woof": []byte("quack")}, nil
},
assert: func(t *testing.T, stdout, stderr string, err error) {
assert.Contains(t, stdout, "moo")
assert.Contains(t, stdout, "quack")
},
},
{
name: "get two variables with version",
args: []string{"variable", "get", "-i", "meow,woof", "-v", "1"},
assert: func(t *testing.T, stdout, stderr string, err error) {
assert.Contains(t, stderr, "version can not be used with multiple variables")
},
},
{
name: "set subcommand",
args: []string{"variable", "set", "-i", "meow", "-v", "moo"},
Expand Down Expand Up @@ -153,7 +179,7 @@ func TestVariableCmd(t *testing.T) {
for _, tc := range variableCmdTestCases {
t.Run(tc.name, func(t *testing.T) {
mockClient := mockVariableClient{
t: t, set: tc.set, get: tc.get, getWithVersion: tc.getWithVersion,
t: t, set: tc.set, get: tc.get, getWithVersion: tc.getWithVersion, getBatch: tc.getBatch,
}

cmd := newVariableCmd(
Expand Down

0 comments on commit b0a6ed1

Please sign in to comment.