From be92d38e60192518786f128806afc2a533f3b70d Mon Sep 17 00:00:00 2001 From: Arun Date: Tue, 30 May 2023 20:02:03 -0700 Subject: [PATCH 01/49] Working shell plugin with environment variable support for the password. Does not support host, port or database fields yet. --- plugins/redis/password.go | 38 +++++++++++++++++++++++++++++++ plugins/redis/password_test.go | 24 +++++++++++++++++++ plugins/redis/plugin.go | 22 ++++++++++++++++++ plugins/redis/redis-cli.go | 28 +++++++++++++++++++++++ sdk/schema/credname/names.go | 2 ++ sdk/schema/credname/names_test.go | 2 ++ 6 files changed, 116 insertions(+) create mode 100644 plugins/redis/password.go create mode 100644 plugins/redis/password_test.go create mode 100644 plugins/redis/plugin.go create mode 100644 plugins/redis/redis-cli.go diff --git a/plugins/redis/password.go b/plugins/redis/password.go new file mode 100644 index 000000000..29da3efe1 --- /dev/null +++ b/plugins/redis/password.go @@ -0,0 +1,38 @@ +package redis + +import ( + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/importer" + "github.com/1Password/shell-plugins/sdk/provision" + "github.com/1Password/shell-plugins/sdk/schema" + "github.com/1Password/shell-plugins/sdk/schema/credname" + "github.com/1Password/shell-plugins/sdk/schema/fieldname" +) + +func Password() schema.CredentialType { + return schema.CredentialType{ + Name: credname.Password, + DocsURL: sdk.URL("https://redis.io/docs/ui/cli/#host-port-password-and-database"), + Fields: []schema.CredentialField{ + { + Name: fieldname.Password, + MarkdownDescription: "Password used to authenticate to Redis.", + Secret: true, + Composition: &schema.ValueComposition{ + Length: 32, + Charset: schema.Charset{ + Lowercase: true, + Digits: true, + }, + }, + }, + }, + DefaultProvisioner: provision.EnvVars(defaultEnvVarMapping), + Importer: importer.TryAll( + importer.TryEnvVarPair(defaultEnvVarMapping), + )} +} + +var defaultEnvVarMapping = map[string]sdk.FieldName{ + "REDISCLI_AUTH": fieldname.Password, +} diff --git a/plugins/redis/password_test.go b/plugins/redis/password_test.go new file mode 100644 index 000000000..f8d93c267 --- /dev/null +++ b/plugins/redis/password_test.go @@ -0,0 +1,24 @@ +package redis + +import ( + "testing" + + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/plugintest" + "github.com/1Password/shell-plugins/sdk/schema/fieldname" +) + +func TestPasswordProvisioner(t *testing.T) { + plugintest.TestProvisioner(t, Password().DefaultProvisioner, map[string]plugintest.ProvisionCase{ + "default": { + ItemFields: map[sdk.FieldName]string{ + fieldname.Password: "pjtxpc2gaddifapjvalggspojexample", + }, + ExpectedOutput: sdk.ProvisionOutput{ + Environment: map[string]string{ + "REDISCLI_AUTH": "pjtxpc2gaddifapjvalggspojexample", + }, + }, + }, + }) +} diff --git a/plugins/redis/plugin.go b/plugins/redis/plugin.go new file mode 100644 index 000000000..cdc9f9ea3 --- /dev/null +++ b/plugins/redis/plugin.go @@ -0,0 +1,22 @@ +package redis + +import ( + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/schema" +) + +func New() schema.Plugin { + return schema.Plugin{ + Name: "redis", + Platform: schema.PlatformInfo{ + Name: "Redis", + Homepage: sdk.URL("https://redis.io/"), + }, + Credentials: []schema.CredentialType{ + Password(), + }, + Executables: []schema.Executable{ + RedisCLI(), + }, + } +} diff --git a/plugins/redis/redis-cli.go b/plugins/redis/redis-cli.go new file mode 100644 index 000000000..dbb060ed9 --- /dev/null +++ b/plugins/redis/redis-cli.go @@ -0,0 +1,28 @@ +package redis + +import ( + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/needsauth" + "github.com/1Password/shell-plugins/sdk/schema" + "github.com/1Password/shell-plugins/sdk/schema/credname" +) + +func RedisCLI() schema.Executable { + return schema.Executable{ + Name: "Redis CLI", + Runs: []string{"redis-cli"}, + DocsURL: sdk.URL("https://redis.io/docs/manual/cli"), + NeedsAuth: needsauth.IfAll( + needsauth.NotWithoutArgs(), + needsauth.NotWhenContainsArgs("-u"), + needsauth.NotWhenContainsArgs("-a"), + needsauth.NotWhenContainsArgs("-v"), + needsauth.NotWhenContainsArgs("--version"), + ), + Uses: []schema.CredentialUsage{ + { + Name: credname.Password, + }, + }, + } +} diff --git a/sdk/schema/credname/names.go b/sdk/schema/credname/names.go index c230ed51b..80f0f3484 100644 --- a/sdk/schema/credname/names.go +++ b/sdk/schema/credname/names.go @@ -18,6 +18,7 @@ const ( DatabaseCredentials = sdk.CredentialName("Database Credentials") DeployKey = sdk.CredentialName("Deploy Key") LoginDetails = sdk.CredentialName("Login Details") + Password = sdk.CredentialName("Password") PersonalAPIToken = sdk.CredentialName("Personal API Token") PersonalAccessToken = sdk.CredentialName("Personal Access Token") RegistryCredentials = sdk.CredentialName("Registry Credentials") @@ -41,6 +42,7 @@ func ListAll() []sdk.CredentialName { DatabaseCredentials, DeployKey, LoginDetails, + Password, PersonalAPIToken, PersonalAccessToken, RegistryCredentials, diff --git a/sdk/schema/credname/names_test.go b/sdk/schema/credname/names_test.go index 1568cff35..47f3af7d0 100644 --- a/sdk/schema/credname/names_test.go +++ b/sdk/schema/credname/names_test.go @@ -22,6 +22,7 @@ func TestGettingCredentialIDsFromNames(t *testing.T) { Credentials, DatabaseCredentials, LoginDetails, + Password, PersonalAPIToken, PersonalAccessToken, RegistryCredentials, @@ -50,6 +51,7 @@ func TestGettingCredentialIDsFromNames(t *testing.T) { "credentials", "database_credentials", "login_details", + "password", "personal_api_token", "personal_access_token", "registry_credentials", From 0e6b4255dba17257d9bbef6a0ad6560ecf18f3a2 Mon Sep 17 00:00:00 2001 From: Arun Date: Tue, 30 May 2023 20:03:18 -0700 Subject: [PATCH 02/49] Use NotForVersion check instead of defining each version flag one by one. --- plugins/redis/redis-cli.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/redis/redis-cli.go b/plugins/redis/redis-cli.go index dbb060ed9..7935bab25 100644 --- a/plugins/redis/redis-cli.go +++ b/plugins/redis/redis-cli.go @@ -16,8 +16,7 @@ func RedisCLI() schema.Executable { needsauth.NotWithoutArgs(), needsauth.NotWhenContainsArgs("-u"), needsauth.NotWhenContainsArgs("-a"), - needsauth.NotWhenContainsArgs("-v"), - needsauth.NotWhenContainsArgs("--version"), + needsauth.NotForVersion(), ), Uses: []schema.CredentialUsage{ { From a7bc3720aca4cf1b5ce2d509d9da045034d69110 Mon Sep 17 00:00:00 2001 From: Arun Date: Tue, 30 May 2023 20:24:08 -0700 Subject: [PATCH 03/49] Update CLI docs URL --- plugins/redis/redis-cli.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/redis/redis-cli.go b/plugins/redis/redis-cli.go index 7935bab25..259a2c803 100644 --- a/plugins/redis/redis-cli.go +++ b/plugins/redis/redis-cli.go @@ -11,7 +11,7 @@ func RedisCLI() schema.Executable { return schema.Executable{ Name: "Redis CLI", Runs: []string{"redis-cli"}, - DocsURL: sdk.URL("https://redis.io/docs/manual/cli"), + DocsURL: sdk.URL("https://redis.io/docs/ui/cli"), NeedsAuth: needsauth.IfAll( needsauth.NotWithoutArgs(), needsauth.NotWhenContainsArgs("-u"), From 9892e54c88fd7d0894609c78c4df1307804c8603 Mon Sep 17 00:00:00 2001 From: Arun Date: Tue, 30 May 2023 20:25:32 -0700 Subject: [PATCH 04/49] Add test for environment variable importer --- plugins/redis/password_test.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/plugins/redis/password_test.go b/plugins/redis/password_test.go index f8d93c267..d2dfffc42 100644 --- a/plugins/redis/password_test.go +++ b/plugins/redis/password_test.go @@ -22,3 +22,20 @@ func TestPasswordProvisioner(t *testing.T) { }, }) } + +func TestPasswordImporter(t *testing.T) { + plugintest.TestImporter(t, Password().Importer, map[string]plugintest.ImportCase{ + "environment": { + Environment: map[string]string{ // TODO: Check if this is correct + "REDISCLI_AUTH": "pjtxpc2gaddifapjvalggspojexample", + }, + ExpectedCandidates: []sdk.ImportCandidate{ + { + Fields: map[sdk.FieldName]string{ + fieldname.Password: "pjtxpc2gaddifapjvalggspojexample", + }, + }, + }, + }, + }) +} From bed61f7cd78b6208f59bf9de1692687c53ae22c4 Mon Sep 17 00:00:00 2001 From: Arun Date: Wed, 31 May 2023 23:40:46 -0700 Subject: [PATCH 05/49] Add support for host and port, in conjunction with password as environment variable --- .../redis/env_var_and_flags_provisioner.go | 39 +++++++++++++++++++ plugins/redis/password.go | 32 +++++++++++++-- sdk/provisioner.go | 9 +++++ 3 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 plugins/redis/env_var_and_flags_provisioner.go diff --git a/plugins/redis/env_var_and_flags_provisioner.go b/plugins/redis/env_var_and_flags_provisioner.go new file mode 100644 index 000000000..f24de7103 --- /dev/null +++ b/plugins/redis/env_var_and_flags_provisioner.go @@ -0,0 +1,39 @@ +package redis + +import ( + "context" + + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/schema/fieldname" +) + +type EnvVarFlagsProvisioner struct { + sdk.Provisioner + + Schema map[string]sdk.FieldName +} + +func EnvVarFlags(schema map[string]sdk.FieldName) sdk.Provisioner { + return EnvVarFlagsProvisioner{ + Schema: schema, + } +} + +func (p EnvVarFlagsProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, out *sdk.ProvisionOutput) { + if value, ok := in.ItemFields[fieldname.Password]; ok { + out.AddEnvVar("REDISCLI_AUTH", value) + } + for flagName, fieldName := range p.Schema { + if value, ok := in.ItemFields[fieldName]; ok { + out.AddArgsImmediatelyAfterExecutableName(flagName, value) + } + } +} + +func (p EnvVarFlagsProvisioner) Deprovision(ctx context.Context, in sdk.DeprovisionInput, out *sdk.DeprovisionOutput) { + // Nothing to do here: credentials get wiped automatically when the process exits. +} + +func (p EnvVarFlagsProvisioner) Description() string { + return "Provision environment variables with master credentials or temporary STS credentials AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN" +} diff --git a/plugins/redis/password.go b/plugins/redis/password.go index 29da3efe1..58bd0af91 100644 --- a/plugins/redis/password.go +++ b/plugins/redis/password.go @@ -3,7 +3,6 @@ package redis import ( "github.com/1Password/shell-plugins/sdk" "github.com/1Password/shell-plugins/sdk/importer" - "github.com/1Password/shell-plugins/sdk/provision" "github.com/1Password/shell-plugins/sdk/schema" "github.com/1Password/shell-plugins/sdk/schema/credname" "github.com/1Password/shell-plugins/sdk/schema/fieldname" @@ -16,7 +15,7 @@ func Password() schema.CredentialType { Fields: []schema.CredentialField{ { Name: fieldname.Password, - MarkdownDescription: "Password used to authenticate to Redis.", + MarkdownDescription: "Password used to authenticate to Redis server.", Secret: true, Composition: &schema.ValueComposition{ Length: 32, @@ -26,8 +25,30 @@ func Password() schema.CredentialType { }, }, }, + { + Name: fieldname.Host, + MarkdownDescription: "Host address for the Redis server.", + Secret: false, + Composition: &schema.ValueComposition{ + Charset: schema.Charset{ + Lowercase: true, + Symbols: true, + Digits: true, + }, + }, + }, + { + Name: fieldname.Port, + MarkdownDescription: "Port for the Redis server.", + Secret: false, + Composition: &schema.ValueComposition{ + Charset: schema.Charset{ + Digits: true, + }, + }, + }, }, - DefaultProvisioner: provision.EnvVars(defaultEnvVarMapping), + DefaultProvisioner: EnvVarFlags(flagsToProvision), Importer: importer.TryAll( importer.TryEnvVarPair(defaultEnvVarMapping), )} @@ -36,3 +57,8 @@ func Password() schema.CredentialType { var defaultEnvVarMapping = map[string]sdk.FieldName{ "REDISCLI_AUTH": fieldname.Password, } + +var flagsToProvision = map[string]sdk.FieldName{ + "-h": fieldname.Host, + "-p": fieldname.Port, +} diff --git a/sdk/provisioner.go b/sdk/provisioner.go index 99883ffae..5e99cdbec 100644 --- a/sdk/provisioner.go +++ b/sdk/provisioner.go @@ -107,6 +107,15 @@ func (out *ProvisionOutput) AddArgs(args ...string) { out.CommandLine = append(out.CommandLine, args...) } +// AddArgsImmediatelyAfterExecutableName can be used to add additional arguments to the command line of the provision output, but immediately after the executable name. The rest of the command line appear after the additional arguments. +func (out *ProvisionOutput) AddArgsImmediatelyAfterExecutableName(args ...string) { + newCommandLine := []string{} + newCommandLine = append(newCommandLine, out.CommandLine[0]) + newCommandLine = append(newCommandLine, args...) + newCommandLine = append(newCommandLine, out.CommandLine[1:]...) + out.CommandLine = newCommandLine +} + // AddSecretFile can be used to add a file containing secrets to the provision output. func (out *ProvisionOutput) AddSecretFile(path string, contents []byte) { out.AddFile(path, OutputFile{ From 7365c7824a896fab41fe9a6a05b6e1542c441d56 Mon Sep 17 00:00:00 2001 From: Arun Date: Wed, 31 May 2023 23:42:10 -0700 Subject: [PATCH 06/49] Better description for the Redis environment variable and flags provisioner --- plugins/redis/env_var_and_flags_provisioner.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/redis/env_var_and_flags_provisioner.go b/plugins/redis/env_var_and_flags_provisioner.go index f24de7103..805b423fb 100644 --- a/plugins/redis/env_var_and_flags_provisioner.go +++ b/plugins/redis/env_var_and_flags_provisioner.go @@ -35,5 +35,5 @@ func (p EnvVarFlagsProvisioner) Deprovision(ctx context.Context, in sdk.Deprovis } func (p EnvVarFlagsProvisioner) Description() string { - return "Provision environment variables with master credentials or temporary STS credentials AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN" + return "Provision credentials using a combination of environment variables and command-line flags." } From c065d865e8565fd8e2d46938b3ce146c0b51831b Mon Sep 17 00:00:00 2001 From: Arun Date: Fri, 2 Jun 2023 00:08:06 -0700 Subject: [PATCH 07/49] Add username to the credential as well, thus allowing scoped access to the database --- plugins/redis/plugin.go | 2 +- plugins/redis/redis-cli.go | 3 ++- .../{password.go => user_credentials.go} | 23 +++++++++++++++---- ...sword_test.go => user_credentials_test.go} | 4 ++-- sdk/schema/credname/names.go | 2 ++ sdk/schema/credname/names_test.go | 2 ++ 6 files changed, 28 insertions(+), 8 deletions(-) rename plugins/redis/{password.go => user_credentials.go} (74%) rename plugins/redis/{password_test.go => user_credentials_test.go} (82%) diff --git a/plugins/redis/plugin.go b/plugins/redis/plugin.go index cdc9f9ea3..45393508d 100644 --- a/plugins/redis/plugin.go +++ b/plugins/redis/plugin.go @@ -13,7 +13,7 @@ func New() schema.Plugin { Homepage: sdk.URL("https://redis.io/"), }, Credentials: []schema.CredentialType{ - Password(), + UserCredentials(), }, Executables: []schema.Executable{ RedisCLI(), diff --git a/plugins/redis/redis-cli.go b/plugins/redis/redis-cli.go index 259a2c803..eaf8e80ad 100644 --- a/plugins/redis/redis-cli.go +++ b/plugins/redis/redis-cli.go @@ -15,12 +15,13 @@ func RedisCLI() schema.Executable { NeedsAuth: needsauth.IfAll( needsauth.NotWithoutArgs(), needsauth.NotWhenContainsArgs("-u"), + needsauth.NotWhenContainsArgs("--user"), needsauth.NotWhenContainsArgs("-a"), needsauth.NotForVersion(), ), Uses: []schema.CredentialUsage{ { - Name: credname.Password, + Name: credname.UserCredentials, }, }, } diff --git a/plugins/redis/password.go b/plugins/redis/user_credentials.go similarity index 74% rename from plugins/redis/password.go rename to plugins/redis/user_credentials.go index 58bd0af91..eb8ce08f0 100644 --- a/plugins/redis/password.go +++ b/plugins/redis/user_credentials.go @@ -8,11 +8,23 @@ import ( "github.com/1Password/shell-plugins/sdk/schema/fieldname" ) -func Password() schema.CredentialType { +func UserCredentials() schema.CredentialType { return schema.CredentialType{ - Name: credname.Password, + Name: credname.UserCredentials, DocsURL: sdk.URL("https://redis.io/docs/ui/cli/#host-port-password-and-database"), Fields: []schema.CredentialField{ + { + Name: fieldname.Username, + MarkdownDescription: "Username used to authenticate to Redis server.", + Secret: false, + Composition: &schema.ValueComposition{ + Charset: schema.Charset{ + Uppercase: true, + Lowercase: true, + Digits: true, + }, + }, + }, { Name: fieldname.Password, MarkdownDescription: "Password used to authenticate to Redis server.", @@ -20,8 +32,10 @@ func Password() schema.CredentialType { Composition: &schema.ValueComposition{ Length: 32, Charset: schema.Charset{ + Uppercase: true, Lowercase: true, Digits: true, + Symbols: true, }, }, }, @@ -59,6 +73,7 @@ var defaultEnvVarMapping = map[string]sdk.FieldName{ } var flagsToProvision = map[string]sdk.FieldName{ - "-h": fieldname.Host, - "-p": fieldname.Port, + "--user": fieldname.Username, + "-h": fieldname.Host, + "-p": fieldname.Port, } diff --git a/plugins/redis/password_test.go b/plugins/redis/user_credentials_test.go similarity index 82% rename from plugins/redis/password_test.go rename to plugins/redis/user_credentials_test.go index d2dfffc42..6c5b75f59 100644 --- a/plugins/redis/password_test.go +++ b/plugins/redis/user_credentials_test.go @@ -9,7 +9,7 @@ import ( ) func TestPasswordProvisioner(t *testing.T) { - plugintest.TestProvisioner(t, Password().DefaultProvisioner, map[string]plugintest.ProvisionCase{ + plugintest.TestProvisioner(t, UserCredentials().DefaultProvisioner, map[string]plugintest.ProvisionCase{ "default": { ItemFields: map[sdk.FieldName]string{ fieldname.Password: "pjtxpc2gaddifapjvalggspojexample", @@ -24,7 +24,7 @@ func TestPasswordProvisioner(t *testing.T) { } func TestPasswordImporter(t *testing.T) { - plugintest.TestImporter(t, Password().Importer, map[string]plugintest.ImportCase{ + plugintest.TestImporter(t, UserCredentials().Importer, map[string]plugintest.ImportCase{ "environment": { Environment: map[string]string{ // TODO: Check if this is correct "REDISCLI_AUTH": "pjtxpc2gaddifapjvalggspojexample", diff --git a/sdk/schema/credname/names.go b/sdk/schema/credname/names.go index 80f0f3484..1a2821673 100644 --- a/sdk/schema/credname/names.go +++ b/sdk/schema/credname/names.go @@ -24,6 +24,7 @@ const ( RegistryCredentials = sdk.CredentialName("Registry Credentials") SecretKey = sdk.CredentialName("Secret Key") UserLogin = sdk.CredentialName("User Login") + UserCredentials = sdk.CredentialName("User Credentials") ) func ListAll() []sdk.CredentialName { @@ -48,5 +49,6 @@ func ListAll() []sdk.CredentialName { RegistryCredentials, SecretKey, UserLogin, + UserCredentials, } } diff --git a/sdk/schema/credname/names_test.go b/sdk/schema/credname/names_test.go index 47f3af7d0..4b33ba03a 100644 --- a/sdk/schema/credname/names_test.go +++ b/sdk/schema/credname/names_test.go @@ -27,6 +27,7 @@ func TestGettingCredentialIDsFromNames(t *testing.T) { PersonalAccessToken, RegistryCredentials, SecretKey, + UserCredentials, sdk.CredentialName(""), sdk.CredentialName("Database-specific Credentials"), sdk.CredentialName("Public/Private Key-Pair"), @@ -56,6 +57,7 @@ func TestGettingCredentialIDsFromNames(t *testing.T) { "personal_access_token", "registry_credentials", "secret_key", + "user_credentials", "", "database_specific_credentials", "public_private_key_pair", From 1e158f8b2601bf6d66ed84e13a25010585b4eca0 Mon Sep 17 00:00:00 2001 From: Arun Date: Fri, 2 Jun 2023 00:10:18 -0700 Subject: [PATCH 08/49] Use shell plugin even without args, if one wants to enter redis shell mode and run queries in there instead of passing as args --- plugins/redis/redis-cli.go | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/redis/redis-cli.go b/plugins/redis/redis-cli.go index eaf8e80ad..f4a6dd5eb 100644 --- a/plugins/redis/redis-cli.go +++ b/plugins/redis/redis-cli.go @@ -13,7 +13,6 @@ func RedisCLI() schema.Executable { Runs: []string{"redis-cli"}, DocsURL: sdk.URL("https://redis.io/docs/ui/cli"), NeedsAuth: needsauth.IfAll( - needsauth.NotWithoutArgs(), needsauth.NotWhenContainsArgs("-u"), needsauth.NotWhenContainsArgs("--user"), needsauth.NotWhenContainsArgs("-a"), From 634c9dc520b9cc7d27cc0f7f3eecbbcae5225f63 Mon Sep 17 00:00:00 2001 From: Arun Date: Mon, 5 Jun 2023 13:45:37 -0400 Subject: [PATCH 09/49] Introduce second credential that'd be used only for authenticating to Redis Enterprise Cloud using rediscloud Terraform provider --- plugins/redis/plugin.go | 1 + plugins/redis/redis-cli.go | 8 +++- plugins/redis/secret_key.go | 55 ++++++++++++++++++++++++++ plugins/redis/secret_key_test.go | 45 +++++++++++++++++++++ plugins/redis/user_credentials.go | 20 +++++----- plugins/redis/user_credentials_test.go | 2 +- sdk/schema/fieldname/names.go | 4 ++ 7 files changed, 123 insertions(+), 12 deletions(-) create mode 100644 plugins/redis/secret_key.go create mode 100644 plugins/redis/secret_key_test.go diff --git a/plugins/redis/plugin.go b/plugins/redis/plugin.go index 45393508d..a7afcb342 100644 --- a/plugins/redis/plugin.go +++ b/plugins/redis/plugin.go @@ -14,6 +14,7 @@ func New() schema.Plugin { }, Credentials: []schema.CredentialType{ UserCredentials(), + SecretKey(), }, Executables: []schema.Executable{ RedisCLI(), diff --git a/plugins/redis/redis-cli.go b/plugins/redis/redis-cli.go index f4a6dd5eb..391d7175b 100644 --- a/plugins/redis/redis-cli.go +++ b/plugins/redis/redis-cli.go @@ -4,7 +4,6 @@ import ( "github.com/1Password/shell-plugins/sdk" "github.com/1Password/shell-plugins/sdk/needsauth" "github.com/1Password/shell-plugins/sdk/schema" - "github.com/1Password/shell-plugins/sdk/schema/credname" ) func RedisCLI() schema.Executable { @@ -20,7 +19,12 @@ func RedisCLI() schema.Executable { ), Uses: []schema.CredentialUsage{ { - Name: credname.UserCredentials, + SelectFrom: &schema.CredentialSelection{ + IncludeAllCredentials: false, + AllowMultiple: false, + }, + Optional: false, + Provisioner: EnvVarFlags(flagsToProvision), }, }, } diff --git a/plugins/redis/secret_key.go b/plugins/redis/secret_key.go new file mode 100644 index 000000000..38a15a36f --- /dev/null +++ b/plugins/redis/secret_key.go @@ -0,0 +1,55 @@ +package redis + +import ( + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/importer" + "github.com/1Password/shell-plugins/sdk/provision" + "github.com/1Password/shell-plugins/sdk/schema" + "github.com/1Password/shell-plugins/sdk/schema/credname" + "github.com/1Password/shell-plugins/sdk/schema/fieldname" +) + +func SecretKey() schema.CredentialType { + return schema.CredentialType{ + Name: credname.SecretKey, + DocsURL: sdk.URL("https://docs.redis.com/latest/rc/api/get-started/manage-api-keys/"), + Fields: []schema.CredentialField{ + { + Name: fieldname.AccessKey, + MarkdownDescription: "API account key (also known as Access Key, or just API Key) to authenticate to Redis Enterprise Cloud.", + Secret: true, + Optional: false, + Composition: &schema.ValueComposition{ + Length: 51, + Charset: schema.Charset{ + Uppercase: true, + Lowercase: true, + Digits: true, + }, + }, + }, + { + Name: fieldname.SecretKey, + MarkdownDescription: "API user key (also known as Secret Key) to authenticate to Redis Enterprise Cloud.", + Secret: true, + Optional: false, + Composition: &schema.ValueComposition{ + Length: 50, + Charset: schema.Charset{ + Uppercase: true, + Lowercase: true, + Digits: true, + }, + }, + }, + }, + DefaultProvisioner: provision.EnvVars(envVarMappingForRedisEnterpriseCloud), + Importer: importer.TryAll( + importer.TryEnvVarPair(envVarMappingForRedisEnterpriseCloud), + )} +} + +var envVarMappingForRedisEnterpriseCloud = map[string]sdk.FieldName{ + "REDISCLOUD_ACCESS_KEY": fieldname.AccessKey, + "REDISCLOUD_SECRET_KEY": fieldname.SecretKey, +} diff --git a/plugins/redis/secret_key_test.go b/plugins/redis/secret_key_test.go new file mode 100644 index 000000000..65a91e6f6 --- /dev/null +++ b/plugins/redis/secret_key_test.go @@ -0,0 +1,45 @@ +package redis + +import ( + "testing" + + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/plugintest" + "github.com/1Password/shell-plugins/sdk/schema/fieldname" +) + +func TestSecretKeyProvisioner(t *testing.T) { + plugintest.TestProvisioner(t, SecretKey().DefaultProvisioner, map[string]plugintest.ProvisionCase{ + "default": { + ItemFields: map[sdk.FieldName]string{ + fieldname.AccessKey: "5v0mPzRKNcvlwRMi4CjWISt15UfCRxjcNVMPCZfDOJTZEXAMPLE", + fieldname.SecretKey: "I2mLL1tjTKcyb5p0vWUSAcuO7XTut2QPPSSMavKQbrCEXAMPLE", + }, + ExpectedOutput: sdk.ProvisionOutput{ + Environment: map[string]string{ + "REDISCLOUD_ACCESS_KEY": "5v0mPzRKNcvlwRMi4CjWISt15UfCRxjcNVMPCZfDOJTZEXAMPLE", + "REDISCLOUD_SECRET_KEY": "I2mLL1tjTKcyb5p0vWUSAcuO7XTut2QPPSSMavKQbrCEXAMPLE", + }, + }, + }, + }) +} + +func TestSecretKeyImporter(t *testing.T) { + plugintest.TestImporter(t, SecretKey().Importer, map[string]plugintest.ImportCase{ + "environment": { + Environment: map[string]string{ + "REDISCLOUD_ACCESS_KEY": "5v0mPzRKNcvlwRMi4CjWISt15UfCRxjcNVMPCZfDOJTZEXAMPLE", + "REDISCLOUD_SECRET_KEY": "I2mLL1tjTKcyb5p0vWUSAcuO7XTut2QPPSSMavKQbrCEXAMPLE", + }, + ExpectedCandidates: []sdk.ImportCandidate{ + { + Fields: map[sdk.FieldName]string{ + fieldname.AccessKey: "5v0mPzRKNcvlwRMi4CjWISt15UfCRxjcNVMPCZfDOJTZEXAMPLE", + fieldname.SecretKey: "I2mLL1tjTKcyb5p0vWUSAcuO7XTut2QPPSSMavKQbrCEXAMPLE", + }, + }, + }, + }, + }) +} diff --git a/plugins/redis/user_credentials.go b/plugins/redis/user_credentials.go index eb8ce08f0..551190228 100644 --- a/plugins/redis/user_credentials.go +++ b/plugins/redis/user_credentials.go @@ -3,6 +3,7 @@ package redis import ( "github.com/1Password/shell-plugins/sdk" "github.com/1Password/shell-plugins/sdk/importer" + "github.com/1Password/shell-plugins/sdk/provision" "github.com/1Password/shell-plugins/sdk/schema" "github.com/1Password/shell-plugins/sdk/schema/credname" "github.com/1Password/shell-plugins/sdk/schema/fieldname" @@ -14,10 +15,12 @@ func UserCredentials() schema.CredentialType { DocsURL: sdk.URL("https://redis.io/docs/ui/cli/#host-port-password-and-database"), Fields: []schema.CredentialField{ { - Name: fieldname.Username, - MarkdownDescription: "Username used to authenticate to Redis server.", - Secret: false, + Name: fieldname.Password, + MarkdownDescription: "Password used to authenticate to Redis server.", + Secret: true, + Optional: false, Composition: &schema.ValueComposition{ + Length: 32, Charset: schema.Charset{ Uppercase: true, Lowercase: true, @@ -26,16 +29,15 @@ func UserCredentials() schema.CredentialType { }, }, { - Name: fieldname.Password, - MarkdownDescription: "Password used to authenticate to Redis server.", - Secret: true, + Name: fieldname.Username, + MarkdownDescription: "Username used to authenticate to Redis server. Defaults to 'default'.", + Secret: false, + Optional: true, Composition: &schema.ValueComposition{ - Length: 32, Charset: schema.Charset{ Uppercase: true, Lowercase: true, Digits: true, - Symbols: true, }, }, }, @@ -62,7 +64,7 @@ func UserCredentials() schema.CredentialType { }, }, }, - DefaultProvisioner: EnvVarFlags(flagsToProvision), + DefaultProvisioner: provision.EnvVars(defaultEnvVarMapping), Importer: importer.TryAll( importer.TryEnvVarPair(defaultEnvVarMapping), )} diff --git a/plugins/redis/user_credentials_test.go b/plugins/redis/user_credentials_test.go index 6c5b75f59..655fa032b 100644 --- a/plugins/redis/user_credentials_test.go +++ b/plugins/redis/user_credentials_test.go @@ -26,7 +26,7 @@ func TestPasswordProvisioner(t *testing.T) { func TestPasswordImporter(t *testing.T) { plugintest.TestImporter(t, UserCredentials().Importer, map[string]plugintest.ImportCase{ "environment": { - Environment: map[string]string{ // TODO: Check if this is correct + Environment: map[string]string{ "REDISCLI_AUTH": "pjtxpc2gaddifapjvalggspojexample", }, ExpectedCandidates: []sdk.ImportCandidate{ diff --git a/sdk/schema/fieldname/names.go b/sdk/schema/fieldname/names.go index 1ffc34a6d..e1e84a51e 100644 --- a/sdk/schema/fieldname/names.go +++ b/sdk/schema/fieldname/names.go @@ -9,6 +9,7 @@ const ( APIKey = sdk.FieldName("API Key") APIKeyID = sdk.FieldName("API Key ID") APISecret = sdk.FieldName("API Secret") + AccessKey = sdk.FieldName("Access Key") AccessKeyID = sdk.FieldName("Access Key ID") AccessToken = sdk.FieldName("Access Token") Account = sdk.FieldName("Account") @@ -50,6 +51,7 @@ const ( Project = sdk.FieldName("Project") Region = sdk.FieldName("Region") Secret = sdk.FieldName("Secret") + SecretKey = sdk.FieldName("Secret Key") SecretAccessKey = sdk.FieldName("Secret Access Key") Subdomain = sdk.FieldName("Subdomain") Token = sdk.FieldName("Token") @@ -66,6 +68,7 @@ func ListAll() []sdk.FieldName { APIKey, APIKeyID, APISecret, + AccessKey, AccessKeyID, AccessToken, Account, @@ -105,6 +108,7 @@ func ListAll() []sdk.FieldName { Project, Region, Secret, + SecretKey, SecretAccessKey, Token, URL, From 90c567465eb020929287b076e74f6bb61d358fc0 Mon Sep 17 00:00:00 2001 From: Arun Date: Mon, 5 Jun 2023 13:46:53 -0400 Subject: [PATCH 10/49] Do not use 1Password authentication when --help flag is used --- plugins/redis/redis-cli.go | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/redis/redis-cli.go b/plugins/redis/redis-cli.go index 391d7175b..32632465b 100644 --- a/plugins/redis/redis-cli.go +++ b/plugins/redis/redis-cli.go @@ -15,6 +15,7 @@ func RedisCLI() schema.Executable { needsauth.NotWhenContainsArgs("-u"), needsauth.NotWhenContainsArgs("--user"), needsauth.NotWhenContainsArgs("-a"), + needsauth.NotWhenContainsArgs("--help"), needsauth.NotForVersion(), ), Uses: []schema.CredentialUsage{ From 55ac59073ee1214b6218575d5901191bb40d855f Mon Sep 17 00:00:00 2001 From: Arun Date: Mon, 5 Jun 2023 13:55:01 -0400 Subject: [PATCH 11/49] Define ID for the credential selection and description for the credential selection prompt --- plugins/redis/redis-cli.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/redis/redis-cli.go b/plugins/redis/redis-cli.go index 32632465b..220ae0790 100644 --- a/plugins/redis/redis-cli.go +++ b/plugins/redis/redis-cli.go @@ -20,7 +20,9 @@ func RedisCLI() schema.Executable { ), Uses: []schema.CredentialUsage{ { + Description: "Credentials to use to connect to a redis server, or to the Redis Enterprise Cloud platform.", SelectFrom: &schema.CredentialSelection{ + ID: "redis", IncludeAllCredentials: false, AllowMultiple: false, }, From 14cb2e470fecd9792ca0d0d76f3bfa32212e75ae Mon Sep 17 00:00:00 2001 From: Arun Date: Tue, 6 Jun 2023 11:41:57 -0400 Subject: [PATCH 12/49] Switch from credential selection to credential reference with just UserCredentials; SecretKey credential remains defined but not necessarily used with the RedisCLI executable --- plugins/redis/redis-cli.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/plugins/redis/redis-cli.go b/plugins/redis/redis-cli.go index 220ae0790..772667f60 100644 --- a/plugins/redis/redis-cli.go +++ b/plugins/redis/redis-cli.go @@ -4,6 +4,7 @@ import ( "github.com/1Password/shell-plugins/sdk" "github.com/1Password/shell-plugins/sdk/needsauth" "github.com/1Password/shell-plugins/sdk/schema" + "github.com/1Password/shell-plugins/sdk/schema/credname" ) func RedisCLI() schema.Executable { @@ -20,13 +21,7 @@ func RedisCLI() schema.Executable { ), Uses: []schema.CredentialUsage{ { - Description: "Credentials to use to connect to a redis server, or to the Redis Enterprise Cloud platform.", - SelectFrom: &schema.CredentialSelection{ - ID: "redis", - IncludeAllCredentials: false, - AllowMultiple: false, - }, - Optional: false, + Name: credname.UserCredentials, Provisioner: EnvVarFlags(flagsToProvision), }, }, From f242d4308b343528bd295e1fdda9ff21ec24b688 Mon Sep 17 00:00:00 2001 From: Arun Date: Tue, 6 Jun 2023 11:44:32 -0400 Subject: [PATCH 13/49] Mark host and port as optional fields --- plugins/redis/user_credentials.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/redis/user_credentials.go b/plugins/redis/user_credentials.go index 551190228..c5dbd16a0 100644 --- a/plugins/redis/user_credentials.go +++ b/plugins/redis/user_credentials.go @@ -45,6 +45,7 @@ func UserCredentials() schema.CredentialType { Name: fieldname.Host, MarkdownDescription: "Host address for the Redis server.", Secret: false, + Optional: true, Composition: &schema.ValueComposition{ Charset: schema.Charset{ Lowercase: true, @@ -57,6 +58,7 @@ func UserCredentials() schema.CredentialType { Name: fieldname.Port, MarkdownDescription: "Port for the Redis server.", Secret: false, + Optional: true, Composition: &schema.ValueComposition{ Charset: schema.Charset{ Digits: true, From 94a056f0bd619490ee3b719fd19cc45902052393 Mon Sep 17 00:00:00 2001 From: Arun Date: Tue, 6 Jun 2023 11:50:07 -0400 Subject: [PATCH 14/49] Rename argument provision functions to AddArgsFirst and AddArgsLast --- plugins/redis/env_var_and_flags_provisioner.go | 2 +- sdk/provision/file_provisioner.go | 2 +- sdk/provisioner.go | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/redis/env_var_and_flags_provisioner.go b/plugins/redis/env_var_and_flags_provisioner.go index 805b423fb..f29a760e8 100644 --- a/plugins/redis/env_var_and_flags_provisioner.go +++ b/plugins/redis/env_var_and_flags_provisioner.go @@ -25,7 +25,7 @@ func (p EnvVarFlagsProvisioner) Provision(ctx context.Context, in sdk.ProvisionI } for flagName, fieldName := range p.Schema { if value, ok := in.ItemFields[fieldName]; ok { - out.AddArgsImmediatelyAfterExecutableName(flagName, value) + out.AddArgsFirst(flagName, value) } } } diff --git a/sdk/provision/file_provisioner.go b/sdk/provision/file_provisioner.go index 4d5d6041d..ccbe2a207 100644 --- a/sdk/provision/file_provisioner.go +++ b/sdk/provision/file_provisioner.go @@ -159,7 +159,7 @@ func (p FileProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, o argsResolved[i] = result.String() } - out.AddArgs(argsResolved...) + out.AddArgsLast(argsResolved...) } } diff --git a/sdk/provisioner.go b/sdk/provisioner.go index 5e99cdbec..f6dac0d88 100644 --- a/sdk/provisioner.go +++ b/sdk/provisioner.go @@ -103,12 +103,12 @@ func (out *ProvisionOutput) AddEnvVar(name string, value string) { } // AddArgs can be used to add additional arguments to the command line of the provision output. -func (out *ProvisionOutput) AddArgs(args ...string) { +func (out *ProvisionOutput) AddArgsLast(args ...string) { out.CommandLine = append(out.CommandLine, args...) } // AddArgsImmediatelyAfterExecutableName can be used to add additional arguments to the command line of the provision output, but immediately after the executable name. The rest of the command line appear after the additional arguments. -func (out *ProvisionOutput) AddArgsImmediatelyAfterExecutableName(args ...string) { +func (out *ProvisionOutput) AddArgsFirst(args ...string) { newCommandLine := []string{} newCommandLine = append(newCommandLine, out.CommandLine[0]) newCommandLine = append(newCommandLine, args...) From 68d35a5624070f83959302c403b0e6511f41c1ff Mon Sep 17 00:00:00 2001 From: Arun Date: Wed, 7 Jun 2023 00:54:57 -0400 Subject: [PATCH 15/49] Introduce flags provision and chained provision at the SDK level and use for redis-cli binary --- .../redis/env_var_and_flags_provisioner.go | 39 ------------------- plugins/redis/redis-cli.go | 8 +++- sdk/provision/chained_provisioner.go | 33 ++++++++++++++++ sdk/provision/flags_provisioner.go | 35 +++++++++++++++++ 4 files changed, 74 insertions(+), 41 deletions(-) delete mode 100644 plugins/redis/env_var_and_flags_provisioner.go create mode 100644 sdk/provision/chained_provisioner.go create mode 100644 sdk/provision/flags_provisioner.go diff --git a/plugins/redis/env_var_and_flags_provisioner.go b/plugins/redis/env_var_and_flags_provisioner.go deleted file mode 100644 index f29a760e8..000000000 --- a/plugins/redis/env_var_and_flags_provisioner.go +++ /dev/null @@ -1,39 +0,0 @@ -package redis - -import ( - "context" - - "github.com/1Password/shell-plugins/sdk" - "github.com/1Password/shell-plugins/sdk/schema/fieldname" -) - -type EnvVarFlagsProvisioner struct { - sdk.Provisioner - - Schema map[string]sdk.FieldName -} - -func EnvVarFlags(schema map[string]sdk.FieldName) sdk.Provisioner { - return EnvVarFlagsProvisioner{ - Schema: schema, - } -} - -func (p EnvVarFlagsProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, out *sdk.ProvisionOutput) { - if value, ok := in.ItemFields[fieldname.Password]; ok { - out.AddEnvVar("REDISCLI_AUTH", value) - } - for flagName, fieldName := range p.Schema { - if value, ok := in.ItemFields[fieldName]; ok { - out.AddArgsFirst(flagName, value) - } - } -} - -func (p EnvVarFlagsProvisioner) Deprovision(ctx context.Context, in sdk.DeprovisionInput, out *sdk.DeprovisionOutput) { - // Nothing to do here: credentials get wiped automatically when the process exits. -} - -func (p EnvVarFlagsProvisioner) Description() string { - return "Provision credentials using a combination of environment variables and command-line flags." -} diff --git a/plugins/redis/redis-cli.go b/plugins/redis/redis-cli.go index 772667f60..a617d91c4 100644 --- a/plugins/redis/redis-cli.go +++ b/plugins/redis/redis-cli.go @@ -3,6 +3,7 @@ package redis import ( "github.com/1Password/shell-plugins/sdk" "github.com/1Password/shell-plugins/sdk/needsauth" + "github.com/1Password/shell-plugins/sdk/provision" "github.com/1Password/shell-plugins/sdk/schema" "github.com/1Password/shell-plugins/sdk/schema/credname" ) @@ -21,8 +22,11 @@ func RedisCLI() schema.Executable { ), Uses: []schema.CredentialUsage{ { - Name: credname.UserCredentials, - Provisioner: EnvVarFlags(flagsToProvision), + Name: credname.UserCredentials, + Provisioner: provision.ChainProvisioners( + provision.EnvVars(defaultEnvVarMapping), + provision.Flags(flagsToProvision), + ), }, }, } diff --git a/sdk/provision/chained_provisioner.go b/sdk/provision/chained_provisioner.go new file mode 100644 index 000000000..4a4b29ce2 --- /dev/null +++ b/sdk/provision/chained_provisioner.go @@ -0,0 +1,33 @@ +package provision + +import ( + "context" + + "github.com/1Password/shell-plugins/sdk" +) + +type ChainedProvisioner struct { + sdk.Provisioner + + Provisioners []sdk.Provisioner +} + +func ChainProvisioners(provisioners ...sdk.Provisioner) sdk.Provisioner { + return ChainedProvisioner{ + Provisioners: provisioners, + } +} + +func (p ChainedProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, out *sdk.ProvisionOutput) { + for _, provisioner := range p.Provisioners { + provisioner.Provision(ctx, in, out) + } +} + +func (p ChainedProvisioner) Deprovision(ctx context.Context, in sdk.DeprovisionInput, out *sdk.DeprovisionOutput) { + // Nothing to do here: environment variables and flags get wiped automatically when the process exits. +} + +func (p ChainedProvisioner) Description() string { + return "Handle multiple provisioners at once" +} diff --git a/sdk/provision/flags_provisioner.go b/sdk/provision/flags_provisioner.go new file mode 100644 index 000000000..e616ffed8 --- /dev/null +++ b/sdk/provision/flags_provisioner.go @@ -0,0 +1,35 @@ +package provision + +import ( + "context" + + "github.com/1Password/shell-plugins/sdk" +) + +type FlagsProvisioner struct { + sdk.Provisioner + + Schema map[string]sdk.FieldName +} + +func Flags(schema map[string]sdk.FieldName) sdk.Provisioner { + return FlagsProvisioner{ + Schema: schema, + } +} + +func (p FlagsProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, out *sdk.ProvisionOutput) { + for flagName, fieldName := range p.Schema { + if value, ok := in.ItemFields[fieldName]; ok { + out.AddArgsFirst(flagName, value) + } + } +} + +func (p FlagsProvisioner) Deprovision(ctx context.Context, in sdk.DeprovisionInput, out *sdk.DeprovisionOutput) { + // Nothing to do here: credentials get wiped automatically when the process exits. +} + +func (p FlagsProvisioner) Description() string { + return "Provision credentials using command-line flags." +} From d93ce0a4c01d56253c66536f9b3ef394d9819487 Mon Sep 17 00:00:00 2001 From: Arun Date: Wed, 7 Jun 2023 01:13:01 -0400 Subject: [PATCH 16/49] Rename flags to args --- plugins/redis/redis-cli.go | 2 +- sdk/provision/args_provisioner.go | 35 ++++++++++++++++++++++++++++ sdk/provision/chained_provisioner.go | 2 +- sdk/provision/flags_provisioner.go | 35 ---------------------------- 4 files changed, 37 insertions(+), 37 deletions(-) create mode 100644 sdk/provision/args_provisioner.go delete mode 100644 sdk/provision/flags_provisioner.go diff --git a/plugins/redis/redis-cli.go b/plugins/redis/redis-cli.go index a617d91c4..9f6c06db1 100644 --- a/plugins/redis/redis-cli.go +++ b/plugins/redis/redis-cli.go @@ -25,7 +25,7 @@ func RedisCLI() schema.Executable { Name: credname.UserCredentials, Provisioner: provision.ChainProvisioners( provision.EnvVars(defaultEnvVarMapping), - provision.Flags(flagsToProvision), + provision.Args(flagsToProvision), ), }, }, diff --git a/sdk/provision/args_provisioner.go b/sdk/provision/args_provisioner.go new file mode 100644 index 000000000..4f43cd1b9 --- /dev/null +++ b/sdk/provision/args_provisioner.go @@ -0,0 +1,35 @@ +package provision + +import ( + "context" + + "github.com/1Password/shell-plugins/sdk" +) + +type ArgsProvisioner struct { + sdk.Provisioner + + Schema map[string]sdk.FieldName +} + +func Args(schema map[string]sdk.FieldName) sdk.Provisioner { + return ArgsProvisioner{ + Schema: schema, + } +} + +func (p ArgsProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, out *sdk.ProvisionOutput) { + for argName, fieldName := range p.Schema { + if value, ok := in.ItemFields[fieldName]; ok { + out.AddArgsFirst(argName, value) + } + } +} + +func (p ArgsProvisioner) Deprovision(ctx context.Context, in sdk.DeprovisionInput, out *sdk.DeprovisionOutput) { + // Nothing to do here: credentials get wiped automatically when the process exits. +} + +func (p ArgsProvisioner) Description() string { + return "Provision credentials using command-line args." +} diff --git a/sdk/provision/chained_provisioner.go b/sdk/provision/chained_provisioner.go index 4a4b29ce2..1bed345aa 100644 --- a/sdk/provision/chained_provisioner.go +++ b/sdk/provision/chained_provisioner.go @@ -25,7 +25,7 @@ func (p ChainedProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput } func (p ChainedProvisioner) Deprovision(ctx context.Context, in sdk.DeprovisionInput, out *sdk.DeprovisionOutput) { - // Nothing to do here: environment variables and flags get wiped automatically when the process exits. + // Nothing to do here: environment variables and args get wiped automatically when the process exits. } func (p ChainedProvisioner) Description() string { diff --git a/sdk/provision/flags_provisioner.go b/sdk/provision/flags_provisioner.go deleted file mode 100644 index e616ffed8..000000000 --- a/sdk/provision/flags_provisioner.go +++ /dev/null @@ -1,35 +0,0 @@ -package provision - -import ( - "context" - - "github.com/1Password/shell-plugins/sdk" -) - -type FlagsProvisioner struct { - sdk.Provisioner - - Schema map[string]sdk.FieldName -} - -func Flags(schema map[string]sdk.FieldName) sdk.Provisioner { - return FlagsProvisioner{ - Schema: schema, - } -} - -func (p FlagsProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, out *sdk.ProvisionOutput) { - for flagName, fieldName := range p.Schema { - if value, ok := in.ItemFields[fieldName]; ok { - out.AddArgsFirst(flagName, value) - } - } -} - -func (p FlagsProvisioner) Deprovision(ctx context.Context, in sdk.DeprovisionInput, out *sdk.DeprovisionOutput) { - // Nothing to do here: credentials get wiped automatically when the process exits. -} - -func (p FlagsProvisioner) Description() string { - return "Provision credentials using command-line flags." -} From 1f9b41f3868d12894e4a75667dd70ff5b05bfb6a Mon Sep 17 00:00:00 2001 From: Arun Date: Wed, 7 Jun 2023 01:46:26 -0400 Subject: [PATCH 17/49] Describe the new arguments and chained provisioners, and better handling of arguments position --- plugins/redis/redis-cli.go | 2 +- sdk/provision/args_provisioner.go | 23 +++++++++++++++++++---- sdk/provision/chained_provisioner.go | 2 ++ 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/plugins/redis/redis-cli.go b/plugins/redis/redis-cli.go index 9f6c06db1..ff6173ab4 100644 --- a/plugins/redis/redis-cli.go +++ b/plugins/redis/redis-cli.go @@ -25,7 +25,7 @@ func RedisCLI() schema.Executable { Name: credname.UserCredentials, Provisioner: provision.ChainProvisioners( provision.EnvVars(defaultEnvVarMapping), - provision.Args(flagsToProvision), + provision.Args(flagsToProvision, "first"), ), }, }, diff --git a/sdk/provision/args_provisioner.go b/sdk/provision/args_provisioner.go index 4f43cd1b9..25bd6cd2d 100644 --- a/sdk/provision/args_provisioner.go +++ b/sdk/provision/args_provisioner.go @@ -6,22 +6,37 @@ import ( "github.com/1Password/shell-plugins/sdk" ) +// EnvVarProvisioner provisions secrets as command-line arguments. +// ArgsPosition field is to control where the arguments are placed in the command line. We check for only "first" and provision those arguments first, otherwise we provision them last. type ArgsProvisioner struct { sdk.Provisioner - Schema map[string]sdk.FieldName + Schema map[string]sdk.FieldName + ArgsPosition string } -func Args(schema map[string]sdk.FieldName) sdk.Provisioner { +// Args creates an ArgsProvisioner that provisions secrets as command line arguments, based +// on the specified schema of field name and argument name. +func Args(schema map[string]sdk.FieldName, argsPosition string) sdk.Provisioner { return ArgsProvisioner{ - Schema: schema, + Schema: schema, + ArgsPosition: argsPosition, } } func (p ArgsProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, out *sdk.ProvisionOutput) { + if p.ArgsPosition == "first" { + for argName, fieldName := range p.Schema { + if value, ok := in.ItemFields[fieldName]; ok { + out.AddArgsFirst(argName, value) + } + } + return + } + for argName, fieldName := range p.Schema { if value, ok := in.ItemFields[fieldName]; ok { - out.AddArgsFirst(argName, value) + out.AddArgsLast(argName, value) } } } diff --git a/sdk/provision/chained_provisioner.go b/sdk/provision/chained_provisioner.go index 1bed345aa..c4c8a1e7d 100644 --- a/sdk/provision/chained_provisioner.go +++ b/sdk/provision/chained_provisioner.go @@ -6,12 +6,14 @@ import ( "github.com/1Password/shell-plugins/sdk" ) +// ChainedProvisioner chains multiple provisioners together for use at the same time. type ChainedProvisioner struct { sdk.Provisioner Provisioners []sdk.Provisioner } +// ChainProvisioners creates a ChainedProvisioner that chains multiple provisioners together for use at the same time. func ChainProvisioners(provisioners ...sdk.Provisioner) sdk.Provisioner { return ChainedProvisioner{ Provisioners: provisioners, From 26fea2921389c77f65c9051e453bc06429bdc911 Mon Sep 17 00:00:00 2001 From: Arun Date: Wed, 7 Jun 2023 22:11:09 -0400 Subject: [PATCH 18/49] Better handling of the arguments index using a map that maps the arguments to an index that they should be placed at --- plugins/redis/redis-cli.go | 2 +- plugins/redis/user_credentials.go | 12 +++++++++++- sdk/provision/args_provisioner.go | 15 +++------------ sdk/provision/file_provisioner.go | 2 +- sdk/provisioner.go | 13 ++++--------- 5 files changed, 20 insertions(+), 24 deletions(-) diff --git a/plugins/redis/redis-cli.go b/plugins/redis/redis-cli.go index ff6173ab4..7c05f571f 100644 --- a/plugins/redis/redis-cli.go +++ b/plugins/redis/redis-cli.go @@ -25,7 +25,7 @@ func RedisCLI() schema.Executable { Name: credname.UserCredentials, Provisioner: provision.ChainProvisioners( provision.EnvVars(defaultEnvVarMapping), - provision.Args(flagsToProvision, "first"), + provision.Args(argsToProvision, indexToProvisionAt), ), }, }, diff --git a/plugins/redis/user_credentials.go b/plugins/redis/user_credentials.go index c5dbd16a0..d0492203d 100644 --- a/plugins/redis/user_credentials.go +++ b/plugins/redis/user_credentials.go @@ -9,6 +9,10 @@ import ( "github.com/1Password/shell-plugins/sdk/schema/fieldname" ) +const ( + index uint = 1 +) + func UserCredentials() schema.CredentialType { return schema.CredentialType{ Name: credname.UserCredentials, @@ -76,8 +80,14 @@ var defaultEnvVarMapping = map[string]sdk.FieldName{ "REDISCLI_AUTH": fieldname.Password, } -var flagsToProvision = map[string]sdk.FieldName{ +var argsToProvision = map[string]sdk.FieldName{ "--user": fieldname.Username, "-h": fieldname.Host, "-p": fieldname.Port, } + +var indexToProvisionAt = map[string]uint{ + "--user": index, + "-h": index, + "-p": index, +} diff --git a/sdk/provision/args_provisioner.go b/sdk/provision/args_provisioner.go index 25bd6cd2d..d7859e4c1 100644 --- a/sdk/provision/args_provisioner.go +++ b/sdk/provision/args_provisioner.go @@ -12,12 +12,12 @@ type ArgsProvisioner struct { sdk.Provisioner Schema map[string]sdk.FieldName - ArgsPosition string + ArgsPosition map[string]uint } // Args creates an ArgsProvisioner that provisions secrets as command line arguments, based // on the specified schema of field name and argument name. -func Args(schema map[string]sdk.FieldName, argsPosition string) sdk.Provisioner { +func Args(schema map[string]sdk.FieldName, argsPosition map[string]uint) sdk.Provisioner { return ArgsProvisioner{ Schema: schema, ArgsPosition: argsPosition, @@ -25,18 +25,9 @@ func Args(schema map[string]sdk.FieldName, argsPosition string) sdk.Provisioner } func (p ArgsProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, out *sdk.ProvisionOutput) { - if p.ArgsPosition == "first" { - for argName, fieldName := range p.Schema { - if value, ok := in.ItemFields[fieldName]; ok { - out.AddArgsFirst(argName, value) - } - } - return - } - for argName, fieldName := range p.Schema { if value, ok := in.ItemFields[fieldName]; ok { - out.AddArgsLast(argName, value) + out.AddArgs(uint(p.ArgsPosition[argName]), argName, value) } } } diff --git a/sdk/provision/file_provisioner.go b/sdk/provision/file_provisioner.go index ccbe2a207..ed6c141d5 100644 --- a/sdk/provision/file_provisioner.go +++ b/sdk/provision/file_provisioner.go @@ -159,7 +159,7 @@ func (p FileProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, o argsResolved[i] = result.String() } - out.AddArgsLast(argsResolved...) + out.AddArgs(uint(len(argsResolved)), argsResolved...) } } diff --git a/sdk/provisioner.go b/sdk/provisioner.go index f6dac0d88..8b81afe2a 100644 --- a/sdk/provisioner.go +++ b/sdk/provisioner.go @@ -102,17 +102,12 @@ func (out *ProvisionOutput) AddEnvVar(name string, value string) { out.Environment[name] = value } -// AddArgs can be used to add additional arguments to the command line of the provision output. -func (out *ProvisionOutput) AddArgsLast(args ...string) { - out.CommandLine = append(out.CommandLine, args...) -} - -// AddArgsImmediatelyAfterExecutableName can be used to add additional arguments to the command line of the provision output, but immediately after the executable name. The rest of the command line appear after the additional arguments. -func (out *ProvisionOutput) AddArgsFirst(args ...string) { +// AddArgs can be used to add additional arguments to the command line of the provision output, at a specific index. +func (out *ProvisionOutput) AddArgs(index uint, args ...string) { newCommandLine := []string{} - newCommandLine = append(newCommandLine, out.CommandLine[0]) + newCommandLine = append(newCommandLine, out.CommandLine[:index]...) newCommandLine = append(newCommandLine, args...) - newCommandLine = append(newCommandLine, out.CommandLine[1:]...) + newCommandLine = append(newCommandLine, out.CommandLine[index:]...) out.CommandLine = newCommandLine } From 81293a9dda44752d5dddbde2a18df9d9b69589e0 Mon Sep 17 00:00:00 2001 From: Arun Date: Wed, 7 Jun 2023 22:13:08 -0400 Subject: [PATCH 19/49] Describe what ArgsProvisioner does better --- sdk/provision/args_provisioner.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/provision/args_provisioner.go b/sdk/provision/args_provisioner.go index d7859e4c1..73b6598bc 100644 --- a/sdk/provision/args_provisioner.go +++ b/sdk/provision/args_provisioner.go @@ -6,8 +6,8 @@ import ( "github.com/1Password/shell-plugins/sdk" ) -// EnvVarProvisioner provisions secrets as command-line arguments. -// ArgsPosition field is to control where the arguments are placed in the command line. We check for only "first" and provision those arguments first, otherwise we provision them last. +// ArgsProvisioner provisions secrets as command-line arguments. +// ArgsPosition field is to control where the arguments are placed in the command line. Setting it to 1 provisions the arguments immediately after the executable name, and setting it to the length of the command line provisions the arguments at the end. type ArgsProvisioner struct { sdk.Provisioner @@ -16,7 +16,7 @@ type ArgsProvisioner struct { } // Args creates an ArgsProvisioner that provisions secrets as command line arguments, based -// on the specified schema of field name and argument name. +// on the specified schema of field name and argument name, and the position of the argument. func Args(schema map[string]sdk.FieldName, argsPosition map[string]uint) sdk.Provisioner { return ArgsProvisioner{ Schema: schema, From e538aae09c3d5bef485438765cd36a0868a35ebd Mon Sep 17 00:00:00 2001 From: Arun Date: Wed, 7 Jun 2023 22:13:25 -0400 Subject: [PATCH 20/49] Remove unnecessary provisioner field in the ChainedProvisioner --- sdk/provision/chained_provisioner.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/sdk/provision/chained_provisioner.go b/sdk/provision/chained_provisioner.go index c4c8a1e7d..7ee852e4e 100644 --- a/sdk/provision/chained_provisioner.go +++ b/sdk/provision/chained_provisioner.go @@ -8,8 +8,6 @@ import ( // ChainedProvisioner chains multiple provisioners together for use at the same time. type ChainedProvisioner struct { - sdk.Provisioner - Provisioners []sdk.Provisioner } From a980f0cb41f3de1be2d370f7034f5e680a104fad Mon Sep 17 00:00:00 2001 From: Arun Date: Wed, 7 Jun 2023 22:15:22 -0400 Subject: [PATCH 21/49] Remove validation check that checks if multiple credentials are defined\, because multi-credential support is beginning to be possible --- sdk/schema/plugin.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/sdk/schema/plugin.go b/sdk/schema/plugin.go index 5c29fc11f..0235a003e 100644 --- a/sdk/schema/plugin.go +++ b/sdk/schema/plugin.go @@ -73,12 +73,6 @@ func (p Plugin) Validate() (bool, ValidationReport) { Severity: ValidationSeverityError, }) - report.AddCheck(ValidationCheck{ - Description: "Has no more than one credential type defined. Plugins with multiple credential types are not supported yet", - Assertion: len(p.Credentials) <= 1, - Severity: ValidationSeverityError, - }) - report.AddCheck(ValidationCheck{ Description: "Credentials referenced in executables are included in the same plugin definition", Assertion: CredentialReferencesInCredentialList(p), From 080d6db06b6d662ad8e14bde24547f8b6d67a056 Mon Sep 17 00:00:00 2001 From: Arun Date: Wed, 7 Jun 2023 22:32:19 -0400 Subject: [PATCH 22/49] Switch from provisioning at a specific index to either immediately after the executable name, or at the end, because specific index provisioning breaks some cases --- plugins/redis/user_credentials.go | 10 +++++----- sdk/provision/args_provisioner.go | 10 +++++----- sdk/provision/file_provisioner.go | 2 +- sdk/provisioner.go | 19 ++++++++++++------- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/plugins/redis/user_credentials.go b/plugins/redis/user_credentials.go index d0492203d..3e62fceb8 100644 --- a/plugins/redis/user_credentials.go +++ b/plugins/redis/user_credentials.go @@ -10,7 +10,7 @@ import ( ) const ( - index uint = 1 + provisionImmediatelyAfterExecutable bool = true ) func UserCredentials() schema.CredentialType { @@ -86,8 +86,8 @@ var argsToProvision = map[string]sdk.FieldName{ "-p": fieldname.Port, } -var indexToProvisionAt = map[string]uint{ - "--user": index, - "-h": index, - "-p": index, +var indexToProvisionAt = map[string]bool{ + "--user": provisionImmediatelyAfterExecutable, + "-h": provisionImmediatelyAfterExecutable, + "-p": provisionImmediatelyAfterExecutable, } diff --git a/sdk/provision/args_provisioner.go b/sdk/provision/args_provisioner.go index 73b6598bc..b2407875e 100644 --- a/sdk/provision/args_provisioner.go +++ b/sdk/provision/args_provisioner.go @@ -7,17 +7,17 @@ import ( ) // ArgsProvisioner provisions secrets as command-line arguments. -// ArgsPosition field is to control where the arguments are placed in the command line. Setting it to 1 provisions the arguments immediately after the executable name, and setting it to the length of the command line provisions the arguments at the end. +// ArgsPosition field is to control where the arguments are placed in the command line. A value of "true" provisions the arguments immediately after the executable name, or at the end of the command. type ArgsProvisioner struct { sdk.Provisioner Schema map[string]sdk.FieldName - ArgsPosition map[string]uint + ArgsPosition map[string]bool } // Args creates an ArgsProvisioner that provisions secrets as command line arguments, based -// on the specified schema of field name and argument name, and the position of the argument. -func Args(schema map[string]sdk.FieldName, argsPosition map[string]uint) sdk.Provisioner { +// on the specified schema of field name and argument name, and the value of ArgsPosition. +func Args(schema map[string]sdk.FieldName, argsPosition map[string]bool) sdk.Provisioner { return ArgsProvisioner{ Schema: schema, ArgsPosition: argsPosition, @@ -27,7 +27,7 @@ func Args(schema map[string]sdk.FieldName, argsPosition map[string]uint) sdk.Pro func (p ArgsProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, out *sdk.ProvisionOutput) { for argName, fieldName := range p.Schema { if value, ok := in.ItemFields[fieldName]; ok { - out.AddArgs(uint(p.ArgsPosition[argName]), argName, value) + out.AddArgs(p.ArgsPosition[argName], argName, value) } } } diff --git a/sdk/provision/file_provisioner.go b/sdk/provision/file_provisioner.go index ed6c141d5..60a5baff7 100644 --- a/sdk/provision/file_provisioner.go +++ b/sdk/provision/file_provisioner.go @@ -159,7 +159,7 @@ func (p FileProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, o argsResolved[i] = result.String() } - out.AddArgs(uint(len(argsResolved)), argsResolved...) + out.AddArgs(false, argsResolved...) } } diff --git a/sdk/provisioner.go b/sdk/provisioner.go index 8b81afe2a..1a7a8a45e 100644 --- a/sdk/provisioner.go +++ b/sdk/provisioner.go @@ -102,13 +102,18 @@ func (out *ProvisionOutput) AddEnvVar(name string, value string) { out.Environment[name] = value } -// AddArgs can be used to add additional arguments to the command line of the provision output, at a specific index. -func (out *ProvisionOutput) AddArgs(index uint, args ...string) { - newCommandLine := []string{} - newCommandLine = append(newCommandLine, out.CommandLine[:index]...) - newCommandLine = append(newCommandLine, args...) - newCommandLine = append(newCommandLine, out.CommandLine[index:]...) - out.CommandLine = newCommandLine +// AddArgs can be used to add additional arguments to the command line of the provision output, immediately after the executable name or at the end, depending on the value of provisionImmediatelyAfterExecutable. +func (out *ProvisionOutput) AddArgs(provisionImmediatelyAfterExecutable bool, args ...string) { + if provisionImmediatelyAfterExecutable { + newCommandLine := []string{} + newCommandLine = append(newCommandLine, out.CommandLine[0]) + newCommandLine = append(newCommandLine, args...) + newCommandLine = append(newCommandLine, out.CommandLine[1:]...) + out.CommandLine = newCommandLine + return + } + + out.CommandLine = append(out.CommandLine, args...) } // AddSecretFile can be used to add a file containing secrets to the provision output. From 588f520862c7f0384914a5ef99a55c04b17148ab Mon Sep 17 00:00:00 2001 From: Arun Date: Thu, 8 Jun 2023 08:52:57 -0400 Subject: [PATCH 23/49] Address feedback to rename credential name to APIKey and credential function to RedisCloudAPIKey --- plugins/redis/plugin.go | 2 +- plugins/redis/secret_key.go | 4 ++-- plugins/redis/secret_key_test.go | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/redis/plugin.go b/plugins/redis/plugin.go index a7afcb342..623743e25 100644 --- a/plugins/redis/plugin.go +++ b/plugins/redis/plugin.go @@ -14,7 +14,7 @@ func New() schema.Plugin { }, Credentials: []schema.CredentialType{ UserCredentials(), - SecretKey(), + RedisCloudAPIKey(), }, Executables: []schema.Executable{ RedisCLI(), diff --git a/plugins/redis/secret_key.go b/plugins/redis/secret_key.go index 38a15a36f..634d6aecb 100644 --- a/plugins/redis/secret_key.go +++ b/plugins/redis/secret_key.go @@ -9,9 +9,9 @@ import ( "github.com/1Password/shell-plugins/sdk/schema/fieldname" ) -func SecretKey() schema.CredentialType { +func RedisCloudAPIKey() schema.CredentialType { return schema.CredentialType{ - Name: credname.SecretKey, + Name: credname.APIKey, DocsURL: sdk.URL("https://docs.redis.com/latest/rc/api/get-started/manage-api-keys/"), Fields: []schema.CredentialField{ { diff --git a/plugins/redis/secret_key_test.go b/plugins/redis/secret_key_test.go index 65a91e6f6..c951ea534 100644 --- a/plugins/redis/secret_key_test.go +++ b/plugins/redis/secret_key_test.go @@ -8,8 +8,8 @@ import ( "github.com/1Password/shell-plugins/sdk/schema/fieldname" ) -func TestSecretKeyProvisioner(t *testing.T) { - plugintest.TestProvisioner(t, SecretKey().DefaultProvisioner, map[string]plugintest.ProvisionCase{ +func TestRedisCloudAPIKeyProvisioner(t *testing.T) { + plugintest.TestProvisioner(t, RedisCloudAPIKey().DefaultProvisioner, map[string]plugintest.ProvisionCase{ "default": { ItemFields: map[sdk.FieldName]string{ fieldname.AccessKey: "5v0mPzRKNcvlwRMi4CjWISt15UfCRxjcNVMPCZfDOJTZEXAMPLE", @@ -25,8 +25,8 @@ func TestSecretKeyProvisioner(t *testing.T) { }) } -func TestSecretKeyImporter(t *testing.T) { - plugintest.TestImporter(t, SecretKey().Importer, map[string]plugintest.ImportCase{ +func TestRedisCloudAPIKeyImporter(t *testing.T) { + plugintest.TestImporter(t, RedisCloudAPIKey().Importer, map[string]plugintest.ImportCase{ "environment": { Environment: map[string]string{ "REDISCLOUD_ACCESS_KEY": "5v0mPzRKNcvlwRMi4CjWISt15UfCRxjcNVMPCZfDOJTZEXAMPLE", From 1c666eddc0c286ba57c311be91cee1d9e96479e9 Mon Sep 17 00:00:00 2001 From: Arun Date: Thu, 8 Jun 2023 08:53:46 -0400 Subject: [PATCH 24/49] Add ManagementURL for the Redis Cloud API Keys credential --- plugins/redis/secret_key.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/redis/secret_key.go b/plugins/redis/secret_key.go index 634d6aecb..b2f9388b8 100644 --- a/plugins/redis/secret_key.go +++ b/plugins/redis/secret_key.go @@ -11,8 +11,9 @@ import ( func RedisCloudAPIKey() schema.CredentialType { return schema.CredentialType{ - Name: credname.APIKey, - DocsURL: sdk.URL("https://docs.redis.com/latest/rc/api/get-started/manage-api-keys/"), + Name: credname.APIKey, + DocsURL: sdk.URL("https://docs.redis.com/latest/rc/api/get-started/manage-api-keys/"), + ManagementURL: sdk.URL("https://app.redislabs.com/#/access-management/api-keys"), Fields: []schema.CredentialField{ { Name: fieldname.AccessKey, From c94621d457759fa3ccf0e973cb7363b57e3c8af4 Mon Sep 17 00:00:00 2001 From: Arun Date: Thu, 8 Jun 2023 09:01:23 -0400 Subject: [PATCH 25/49] Remove length for account key and user key fields, because they don't seem consistent, at least in the case of user key --- plugins/redis/secret_key.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/redis/secret_key.go b/plugins/redis/secret_key.go index b2f9388b8..9a2ddeba9 100644 --- a/plugins/redis/secret_key.go +++ b/plugins/redis/secret_key.go @@ -21,7 +21,6 @@ func RedisCloudAPIKey() schema.CredentialType { Secret: true, Optional: false, Composition: &schema.ValueComposition{ - Length: 51, Charset: schema.Charset{ Uppercase: true, Lowercase: true, @@ -35,7 +34,6 @@ func RedisCloudAPIKey() schema.CredentialType { Secret: true, Optional: false, Composition: &schema.ValueComposition{ - Length: 50, Charset: schema.Charset{ Uppercase: true, Lowercase: true, From 491172175b204412d1148baa9a48edf9138342ec Mon Sep 17 00:00:00 2001 From: Arun Date: Thu, 8 Jun 2023 13:34:34 -0400 Subject: [PATCH 26/49] Remove the description that the default username will be default, because by default, a redis server does not require an username or password to connect to it. --- plugins/redis/user_credentials.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/redis/user_credentials.go b/plugins/redis/user_credentials.go index 3e62fceb8..bb98b612a 100644 --- a/plugins/redis/user_credentials.go +++ b/plugins/redis/user_credentials.go @@ -34,7 +34,7 @@ func UserCredentials() schema.CredentialType { }, { Name: fieldname.Username, - MarkdownDescription: "Username used to authenticate to Redis server. Defaults to 'default'.", + MarkdownDescription: "Username used to authenticate to Redis server.", Secret: false, Optional: true, Composition: &schema.ValueComposition{ From 3f26c9d729017c6ec112b7345f77eed18512b782 Mon Sep 17 00:00:00 2001 From: Arun Date: Thu, 8 Jun 2023 15:26:19 -0400 Subject: [PATCH 27/49] Revert "Switch from provisioning at a specific index to either immediately after the executable name, or at the end, because specific index provisioning breaks some cases" This reverts commit fe969f8134371df21450f78d0c27235ebd3f19b6. --- plugins/redis/user_credentials.go | 10 +++++----- sdk/provision/args_provisioner.go | 10 +++++----- sdk/provision/file_provisioner.go | 2 +- sdk/provisioner.go | 19 +++++++------------ 4 files changed, 18 insertions(+), 23 deletions(-) diff --git a/plugins/redis/user_credentials.go b/plugins/redis/user_credentials.go index bb98b612a..c7758e029 100644 --- a/plugins/redis/user_credentials.go +++ b/plugins/redis/user_credentials.go @@ -10,7 +10,7 @@ import ( ) const ( - provisionImmediatelyAfterExecutable bool = true + index uint = 1 ) func UserCredentials() schema.CredentialType { @@ -86,8 +86,8 @@ var argsToProvision = map[string]sdk.FieldName{ "-p": fieldname.Port, } -var indexToProvisionAt = map[string]bool{ - "--user": provisionImmediatelyAfterExecutable, - "-h": provisionImmediatelyAfterExecutable, - "-p": provisionImmediatelyAfterExecutable, +var indexToProvisionAt = map[string]uint{ + "--user": index, + "-h": index, + "-p": index, } diff --git a/sdk/provision/args_provisioner.go b/sdk/provision/args_provisioner.go index b2407875e..73b6598bc 100644 --- a/sdk/provision/args_provisioner.go +++ b/sdk/provision/args_provisioner.go @@ -7,17 +7,17 @@ import ( ) // ArgsProvisioner provisions secrets as command-line arguments. -// ArgsPosition field is to control where the arguments are placed in the command line. A value of "true" provisions the arguments immediately after the executable name, or at the end of the command. +// ArgsPosition field is to control where the arguments are placed in the command line. Setting it to 1 provisions the arguments immediately after the executable name, and setting it to the length of the command line provisions the arguments at the end. type ArgsProvisioner struct { sdk.Provisioner Schema map[string]sdk.FieldName - ArgsPosition map[string]bool + ArgsPosition map[string]uint } // Args creates an ArgsProvisioner that provisions secrets as command line arguments, based -// on the specified schema of field name and argument name, and the value of ArgsPosition. -func Args(schema map[string]sdk.FieldName, argsPosition map[string]bool) sdk.Provisioner { +// on the specified schema of field name and argument name, and the position of the argument. +func Args(schema map[string]sdk.FieldName, argsPosition map[string]uint) sdk.Provisioner { return ArgsProvisioner{ Schema: schema, ArgsPosition: argsPosition, @@ -27,7 +27,7 @@ func Args(schema map[string]sdk.FieldName, argsPosition map[string]bool) sdk.Pro func (p ArgsProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, out *sdk.ProvisionOutput) { for argName, fieldName := range p.Schema { if value, ok := in.ItemFields[fieldName]; ok { - out.AddArgs(p.ArgsPosition[argName], argName, value) + out.AddArgs(uint(p.ArgsPosition[argName]), argName, value) } } } diff --git a/sdk/provision/file_provisioner.go b/sdk/provision/file_provisioner.go index 60a5baff7..ed6c141d5 100644 --- a/sdk/provision/file_provisioner.go +++ b/sdk/provision/file_provisioner.go @@ -159,7 +159,7 @@ func (p FileProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, o argsResolved[i] = result.String() } - out.AddArgs(false, argsResolved...) + out.AddArgs(uint(len(argsResolved)), argsResolved...) } } diff --git a/sdk/provisioner.go b/sdk/provisioner.go index 1a7a8a45e..8b81afe2a 100644 --- a/sdk/provisioner.go +++ b/sdk/provisioner.go @@ -102,18 +102,13 @@ func (out *ProvisionOutput) AddEnvVar(name string, value string) { out.Environment[name] = value } -// AddArgs can be used to add additional arguments to the command line of the provision output, immediately after the executable name or at the end, depending on the value of provisionImmediatelyAfterExecutable. -func (out *ProvisionOutput) AddArgs(provisionImmediatelyAfterExecutable bool, args ...string) { - if provisionImmediatelyAfterExecutable { - newCommandLine := []string{} - newCommandLine = append(newCommandLine, out.CommandLine[0]) - newCommandLine = append(newCommandLine, args...) - newCommandLine = append(newCommandLine, out.CommandLine[1:]...) - out.CommandLine = newCommandLine - return - } - - out.CommandLine = append(out.CommandLine, args...) +// AddArgs can be used to add additional arguments to the command line of the provision output, at a specific index. +func (out *ProvisionOutput) AddArgs(index uint, args ...string) { + newCommandLine := []string{} + newCommandLine = append(newCommandLine, out.CommandLine[:index]...) + newCommandLine = append(newCommandLine, args...) + newCommandLine = append(newCommandLine, out.CommandLine[index:]...) + out.CommandLine = newCommandLine } // AddSecretFile can be used to add a file containing secrets to the provision output. From 5a15a10f654573fffd52f2cfc8f80db8009bbe10 Mon Sep 17 00:00:00 2001 From: Arun Date: Thu, 8 Jun 2023 17:43:20 -0400 Subject: [PATCH 28/49] Rename AddArgs helper function to AddArgsAtIndex, and in the ArgsProvisioner, build a safeguard to ensure that arguments are not provisioned out of bounds. --- sdk/provision/args_provisioner.go | 9 ++++++++- sdk/provision/file_provisioner.go | 2 +- sdk/provisioner.go | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/sdk/provision/args_provisioner.go b/sdk/provision/args_provisioner.go index 73b6598bc..6ebf301d0 100644 --- a/sdk/provision/args_provisioner.go +++ b/sdk/provision/args_provisioner.go @@ -27,7 +27,14 @@ func Args(schema map[string]sdk.FieldName, argsPosition map[string]uint) sdk.Pro func (p ArgsProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, out *sdk.ProvisionOutput) { for argName, fieldName := range p.Schema { if value, ok := in.ItemFields[fieldName]; ok { - out.AddArgs(uint(p.ArgsPosition[argName]), argName, value) + // This safeguard is to ensure that the argsPosition is not out of bounds. + // + // Example: For a "redis-cli incr key" command, arguments cannot be provisioned at index 4 or higher. + if uint(p.ArgsPosition[argName]) > uint(len(out.CommandLine)) { + out.AddArgsAtIndex(uint(len(out.CommandLine)), argName, value) + return + } + out.AddArgsAtIndex(uint(p.ArgsPosition[argName]), argName, value) } } } diff --git a/sdk/provision/file_provisioner.go b/sdk/provision/file_provisioner.go index ed6c141d5..12701963a 100644 --- a/sdk/provision/file_provisioner.go +++ b/sdk/provision/file_provisioner.go @@ -159,7 +159,7 @@ func (p FileProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, o argsResolved[i] = result.String() } - out.AddArgs(uint(len(argsResolved)), argsResolved...) + out.AddArgsAtIndex(uint(len(argsResolved)), argsResolved...) } } diff --git a/sdk/provisioner.go b/sdk/provisioner.go index 8b81afe2a..f41217436 100644 --- a/sdk/provisioner.go +++ b/sdk/provisioner.go @@ -103,7 +103,7 @@ func (out *ProvisionOutput) AddEnvVar(name string, value string) { } // AddArgs can be used to add additional arguments to the command line of the provision output, at a specific index. -func (out *ProvisionOutput) AddArgs(index uint, args ...string) { +func (out *ProvisionOutput) AddArgsAtIndex(index uint, args ...string) { newCommandLine := []string{} newCommandLine = append(newCommandLine, out.CommandLine[:index]...) newCommandLine = append(newCommandLine, args...) From ca6978bb4373e151fe6886d08a81c73fd1550838 Mon Sep 17 00:00:00 2001 From: Arun Date: Thu, 8 Jun 2023 17:55:14 -0400 Subject: [PATCH 29/49] Swap around the index and args params in ArgsAtIndex function for better readability --- plugins/redis/redis-cli.go | 2 +- sdk/provision/args_provisioner.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/redis/redis-cli.go b/plugins/redis/redis-cli.go index 7c05f571f..1b04287f6 100644 --- a/plugins/redis/redis-cli.go +++ b/plugins/redis/redis-cli.go @@ -25,7 +25,7 @@ func RedisCLI() schema.Executable { Name: credname.UserCredentials, Provisioner: provision.ChainProvisioners( provision.EnvVars(defaultEnvVarMapping), - provision.Args(argsToProvision, indexToProvisionAt), + provision.ArgsAtIndex(indexToProvisionAt, argsToProvision), ), }, }, diff --git a/sdk/provision/args_provisioner.go b/sdk/provision/args_provisioner.go index 6ebf301d0..56a7d6e90 100644 --- a/sdk/provision/args_provisioner.go +++ b/sdk/provision/args_provisioner.go @@ -11,16 +11,16 @@ import ( type ArgsProvisioner struct { sdk.Provisioner - Schema map[string]sdk.FieldName ArgsPosition map[string]uint + Schema map[string]sdk.FieldName } // Args creates an ArgsProvisioner that provisions secrets as command line arguments, based // on the specified schema of field name and argument name, and the position of the argument. -func Args(schema map[string]sdk.FieldName, argsPosition map[string]uint) sdk.Provisioner { +func ArgsAtIndex(argsPosition map[string]uint, schema map[string]sdk.FieldName) sdk.Provisioner { return ArgsProvisioner{ - Schema: schema, ArgsPosition: argsPosition, + Schema: schema, } } From 26e621c9e01ca28fc68b56cc4abd4706febae691 Mon Sep 17 00:00:00 2001 From: Arun Date: Fri, 9 Jun 2023 02:14:20 -0400 Subject: [PATCH 30/49] Reintroduce AddArgs helper function and fix FileProvisioner breakage --- sdk/provision/file_provisioner.go | 2 +- sdk/provisioner.go | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/sdk/provision/file_provisioner.go b/sdk/provision/file_provisioner.go index 12701963a..4d5d6041d 100644 --- a/sdk/provision/file_provisioner.go +++ b/sdk/provision/file_provisioner.go @@ -159,7 +159,7 @@ func (p FileProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, o argsResolved[i] = result.String() } - out.AddArgsAtIndex(uint(len(argsResolved)), argsResolved...) + out.AddArgs(argsResolved...) } } diff --git a/sdk/provisioner.go b/sdk/provisioner.go index f41217436..db48364b7 100644 --- a/sdk/provisioner.go +++ b/sdk/provisioner.go @@ -102,7 +102,12 @@ func (out *ProvisionOutput) AddEnvVar(name string, value string) { out.Environment[name] = value } -// AddArgs can be used to add additional arguments to the command line of the provision output, at a specific index. +// AddArgs can be used to add additional arguments to the command line of the provision output. +func (out *ProvisionOutput) AddArgs(args ...string) { + out.CommandLine = append(out.CommandLine, args...) +} + +// AddArgsAtIndex can be used to add additional arguments to the command line of the provision output, at a specific index. func (out *ProvisionOutput) AddArgsAtIndex(index uint, args ...string) { newCommandLine := []string{} newCommandLine = append(newCommandLine, out.CommandLine[:index]...) From 1286d92c544794785a473bb8217947370af48dc7 Mon Sep 17 00:00:00 2001 From: Arun Date: Fri, 9 Jun 2023 15:16:53 -0400 Subject: [PATCH 31/49] Remove Arguments Provisioner completely and introduce redis-specific arguments provision that injects arguments at the first index. This works along with the environment variable provisioner as a chained provision. This is a temporary arrangement until the Arguments Provision is reintroduced at the SDK-level. --- plugins/redis/provisioner.go | 35 ++++++++++++++++++++++ plugins/redis/redis-cli.go | 2 +- plugins/redis/user_credentials.go | 2 +- sdk/provision/args_provisioner.go | 48 ------------------------------- 4 files changed, 37 insertions(+), 50 deletions(-) create mode 100644 plugins/redis/provisioner.go delete mode 100644 sdk/provision/args_provisioner.go diff --git a/plugins/redis/provisioner.go b/plugins/redis/provisioner.go new file mode 100644 index 000000000..0bdcbceca --- /dev/null +++ b/plugins/redis/provisioner.go @@ -0,0 +1,35 @@ +package redis + +import ( + "context" + + "github.com/1Password/shell-plugins/sdk" +) + +type redisArgsProvisioner struct { + AddArgsAtIndex map[string]uint + Schema map[string]sdk.FieldName +} + +func redisFlags(addArgsAtIndex map[string]uint, schema map[string]sdk.FieldName) sdk.Provisioner { + return redisArgsProvisioner{ + AddArgsAtIndex: addArgsAtIndex, + Schema: schema, + } +} + +func (p redisArgsProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, out *sdk.ProvisionOutput) { + for argName, fieldName := range p.Schema { + if value, ok := in.ItemFields[fieldName]; ok { + out.AddArgsAtIndex(p.AddArgsAtIndex[argName], argName, value) + } + } +} + +func (p redisArgsProvisioner) Deprovision(ctx context.Context, in sdk.DeprovisionInput, out *sdk.DeprovisionOutput) { + // Nothing to do here: credentials get wiped automatically when the process exits. +} + +func (p redisArgsProvisioner) Description() string { + return "Provision redis secrets as command-line arguments." +} diff --git a/plugins/redis/redis-cli.go b/plugins/redis/redis-cli.go index 1b04287f6..eae4b94c9 100644 --- a/plugins/redis/redis-cli.go +++ b/plugins/redis/redis-cli.go @@ -25,7 +25,7 @@ func RedisCLI() schema.Executable { Name: credname.UserCredentials, Provisioner: provision.ChainProvisioners( provision.EnvVars(defaultEnvVarMapping), - provision.ArgsAtIndex(indexToProvisionAt, argsToProvision), + redisFlags(indexToProvisionAt, argsToProvision), ), }, }, diff --git a/plugins/redis/user_credentials.go b/plugins/redis/user_credentials.go index c7758e029..8de41365d 100644 --- a/plugins/redis/user_credentials.go +++ b/plugins/redis/user_credentials.go @@ -10,7 +10,7 @@ import ( ) const ( - index uint = 1 + index uint = 1 // We inject arguments immediately after the redis-cli command, because that's where they're expected by the redis-cli binary. Placing arguments at the end or in the middle of the command will cause redis-cli to fail. ) func UserCredentials() schema.CredentialType { diff --git a/sdk/provision/args_provisioner.go b/sdk/provision/args_provisioner.go deleted file mode 100644 index 56a7d6e90..000000000 --- a/sdk/provision/args_provisioner.go +++ /dev/null @@ -1,48 +0,0 @@ -package provision - -import ( - "context" - - "github.com/1Password/shell-plugins/sdk" -) - -// ArgsProvisioner provisions secrets as command-line arguments. -// ArgsPosition field is to control where the arguments are placed in the command line. Setting it to 1 provisions the arguments immediately after the executable name, and setting it to the length of the command line provisions the arguments at the end. -type ArgsProvisioner struct { - sdk.Provisioner - - ArgsPosition map[string]uint - Schema map[string]sdk.FieldName -} - -// Args creates an ArgsProvisioner that provisions secrets as command line arguments, based -// on the specified schema of field name and argument name, and the position of the argument. -func ArgsAtIndex(argsPosition map[string]uint, schema map[string]sdk.FieldName) sdk.Provisioner { - return ArgsProvisioner{ - ArgsPosition: argsPosition, - Schema: schema, - } -} - -func (p ArgsProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, out *sdk.ProvisionOutput) { - for argName, fieldName := range p.Schema { - if value, ok := in.ItemFields[fieldName]; ok { - // This safeguard is to ensure that the argsPosition is not out of bounds. - // - // Example: For a "redis-cli incr key" command, arguments cannot be provisioned at index 4 or higher. - if uint(p.ArgsPosition[argName]) > uint(len(out.CommandLine)) { - out.AddArgsAtIndex(uint(len(out.CommandLine)), argName, value) - return - } - out.AddArgsAtIndex(uint(p.ArgsPosition[argName]), argName, value) - } - } -} - -func (p ArgsProvisioner) Deprovision(ctx context.Context, in sdk.DeprovisionInput, out *sdk.DeprovisionOutput) { - // Nothing to do here: credentials get wiped automatically when the process exits. -} - -func (p ArgsProvisioner) Description() string { - return "Provision credentials using command-line args." -} From 681375eaea4b7592970e22bc88db857c13d6a287 Mon Sep 17 00:00:00 2001 From: Arun Date: Fri, 9 Jun 2023 15:24:13 -0400 Subject: [PATCH 32/49] Update redis Enterprise Cloud platform credential fields to the modern names: Account Key and User Key. Credential name remains API Key --- plugins/redis/{secret_key.go => api_key.go} | 10 +++++----- plugins/redis/{secret_key_test.go => api_key_test.go} | 8 ++++---- sdk/schema/fieldname/names.go | 2 ++ 3 files changed, 11 insertions(+), 9 deletions(-) rename plugins/redis/{secret_key.go => api_key.go} (86%) rename plugins/redis/{secret_key_test.go => api_key_test.go} (78%) diff --git a/plugins/redis/secret_key.go b/plugins/redis/api_key.go similarity index 86% rename from plugins/redis/secret_key.go rename to plugins/redis/api_key.go index 9a2ddeba9..ebd6238e9 100644 --- a/plugins/redis/secret_key.go +++ b/plugins/redis/api_key.go @@ -16,8 +16,8 @@ func RedisCloudAPIKey() schema.CredentialType { ManagementURL: sdk.URL("https://app.redislabs.com/#/access-management/api-keys"), Fields: []schema.CredentialField{ { - Name: fieldname.AccessKey, - MarkdownDescription: "API account key (also known as Access Key, or just API Key) to authenticate to Redis Enterprise Cloud.", + Name: fieldname.AccountKey, + MarkdownDescription: "API Account key (also known as Access Key, or just API Key) to authenticate to Redis Enterprise Cloud.", Secret: true, Optional: false, Composition: &schema.ValueComposition{ @@ -29,7 +29,7 @@ func RedisCloudAPIKey() schema.CredentialType { }, }, { - Name: fieldname.SecretKey, + Name: fieldname.UserKey, MarkdownDescription: "API user key (also known as Secret Key) to authenticate to Redis Enterprise Cloud.", Secret: true, Optional: false, @@ -49,6 +49,6 @@ func RedisCloudAPIKey() schema.CredentialType { } var envVarMappingForRedisEnterpriseCloud = map[string]sdk.FieldName{ - "REDISCLOUD_ACCESS_KEY": fieldname.AccessKey, - "REDISCLOUD_SECRET_KEY": fieldname.SecretKey, + "REDISCLOUD_ACCESS_KEY": fieldname.AccountKey, + "REDISCLOUD_SECRET_KEY": fieldname.UserKey, } diff --git a/plugins/redis/secret_key_test.go b/plugins/redis/api_key_test.go similarity index 78% rename from plugins/redis/secret_key_test.go rename to plugins/redis/api_key_test.go index c951ea534..2758619d3 100644 --- a/plugins/redis/secret_key_test.go +++ b/plugins/redis/api_key_test.go @@ -12,8 +12,8 @@ func TestRedisCloudAPIKeyProvisioner(t *testing.T) { plugintest.TestProvisioner(t, RedisCloudAPIKey().DefaultProvisioner, map[string]plugintest.ProvisionCase{ "default": { ItemFields: map[sdk.FieldName]string{ - fieldname.AccessKey: "5v0mPzRKNcvlwRMi4CjWISt15UfCRxjcNVMPCZfDOJTZEXAMPLE", - fieldname.SecretKey: "I2mLL1tjTKcyb5p0vWUSAcuO7XTut2QPPSSMavKQbrCEXAMPLE", + fieldname.AccountKey: "5v0mPzRKNcvlwRMi4CjWISt15UfCRxjcNVMPCZfDOJTZEXAMPLE", + fieldname.UserKey: "I2mLL1tjTKcyb5p0vWUSAcuO7XTut2QPPSSMavKQbrCEXAMPLE", }, ExpectedOutput: sdk.ProvisionOutput{ Environment: map[string]string{ @@ -35,8 +35,8 @@ func TestRedisCloudAPIKeyImporter(t *testing.T) { ExpectedCandidates: []sdk.ImportCandidate{ { Fields: map[sdk.FieldName]string{ - fieldname.AccessKey: "5v0mPzRKNcvlwRMi4CjWISt15UfCRxjcNVMPCZfDOJTZEXAMPLE", - fieldname.SecretKey: "I2mLL1tjTKcyb5p0vWUSAcuO7XTut2QPPSSMavKQbrCEXAMPLE", + fieldname.AccountKey: "5v0mPzRKNcvlwRMi4CjWISt15UfCRxjcNVMPCZfDOJTZEXAMPLE", + fieldname.UserKey: "I2mLL1tjTKcyb5p0vWUSAcuO7XTut2QPPSSMavKQbrCEXAMPLE", }, }, }, diff --git a/sdk/schema/fieldname/names.go b/sdk/schema/fieldname/names.go index e1e84a51e..2292418aa 100644 --- a/sdk/schema/fieldname/names.go +++ b/sdk/schema/fieldname/names.go @@ -4,6 +4,7 @@ import "github.com/1Password/shell-plugins/sdk" // Credential field names. const ( + AccountKey = sdk.FieldName("Account Key") APIHost = sdk.FieldName("API Host") APIUrl = sdk.FieldName("API URL") APIKey = sdk.FieldName("API Key") @@ -58,6 +59,7 @@ const ( URL = sdk.FieldName("URL") User = sdk.FieldName("User") UserAccessToken = sdk.FieldName("User Access Token") + UserKey = sdk.FieldName("User Key") Username = sdk.FieldName("Username") Website = sdk.FieldName("Website") ) From d96f43ac6901ac7cdb31cfb78e91d09e5443a5bf Mon Sep 17 00:00:00 2001 From: Arun Date: Mon, 12 Jun 2023 08:16:49 -0400 Subject: [PATCH 33/49] Remove unused AccessKey and SecretKey field names --- sdk/schema/fieldname/names.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sdk/schema/fieldname/names.go b/sdk/schema/fieldname/names.go index 2292418aa..3899bac90 100644 --- a/sdk/schema/fieldname/names.go +++ b/sdk/schema/fieldname/names.go @@ -10,7 +10,6 @@ const ( APIKey = sdk.FieldName("API Key") APIKeyID = sdk.FieldName("API Key ID") APISecret = sdk.FieldName("API Secret") - AccessKey = sdk.FieldName("Access Key") AccessKeyID = sdk.FieldName("Access Key ID") AccessToken = sdk.FieldName("Access Token") Account = sdk.FieldName("Account") @@ -52,7 +51,6 @@ const ( Project = sdk.FieldName("Project") Region = sdk.FieldName("Region") Secret = sdk.FieldName("Secret") - SecretKey = sdk.FieldName("Secret Key") SecretAccessKey = sdk.FieldName("Secret Access Key") Subdomain = sdk.FieldName("Subdomain") Token = sdk.FieldName("Token") @@ -70,7 +68,6 @@ func ListAll() []sdk.FieldName { APIKey, APIKeyID, APISecret, - AccessKey, AccessKeyID, AccessToken, Account, @@ -110,7 +107,6 @@ func ListAll() []sdk.FieldName { Project, Region, Secret, - SecretKey, SecretAccessKey, Token, URL, From 510fe41d281e8da340d353980ee69ae1ce0a8f8a Mon Sep 17 00:00:00 2001 From: Arun Date: Mon, 12 Jun 2023 08:20:24 -0400 Subject: [PATCH 34/49] Move executable code from redis-cli to redis_cli as snake case is more common in go --- plugins/redis/{redis-cli.go => redis_cli.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename plugins/redis/{redis-cli.go => redis_cli.go} (100%) diff --git a/plugins/redis/redis-cli.go b/plugins/redis/redis_cli.go similarity index 100% rename from plugins/redis/redis-cli.go rename to plugins/redis/redis_cli.go From c4305987d8ababff7ec24aad81263a6692d63b0f Mon Sep 17 00:00:00 2001 From: Arun Date: Mon, 12 Jun 2023 08:21:52 -0400 Subject: [PATCH 35/49] Remove optional field in certain field name structs because the default value is false --- plugins/redis/api_key.go | 2 -- plugins/redis/user_credentials.go | 1 - 2 files changed, 3 deletions(-) diff --git a/plugins/redis/api_key.go b/plugins/redis/api_key.go index ebd6238e9..07dd01668 100644 --- a/plugins/redis/api_key.go +++ b/plugins/redis/api_key.go @@ -19,7 +19,6 @@ func RedisCloudAPIKey() schema.CredentialType { Name: fieldname.AccountKey, MarkdownDescription: "API Account key (also known as Access Key, or just API Key) to authenticate to Redis Enterprise Cloud.", Secret: true, - Optional: false, Composition: &schema.ValueComposition{ Charset: schema.Charset{ Uppercase: true, @@ -32,7 +31,6 @@ func RedisCloudAPIKey() schema.CredentialType { Name: fieldname.UserKey, MarkdownDescription: "API user key (also known as Secret Key) to authenticate to Redis Enterprise Cloud.", Secret: true, - Optional: false, Composition: &schema.ValueComposition{ Charset: schema.Charset{ Uppercase: true, diff --git a/plugins/redis/user_credentials.go b/plugins/redis/user_credentials.go index 8de41365d..67978ac6d 100644 --- a/plugins/redis/user_credentials.go +++ b/plugins/redis/user_credentials.go @@ -22,7 +22,6 @@ func UserCredentials() schema.CredentialType { Name: fieldname.Password, MarkdownDescription: "Password used to authenticate to Redis server.", Secret: true, - Optional: false, Composition: &schema.ValueComposition{ Length: 32, Charset: schema.Charset{ From 3507031398e965bffe1e29ed23cadb48d8fd57b2 Mon Sep 17 00:00:00 2001 From: Arun Date: Mon, 12 Jun 2023 08:33:00 -0400 Subject: [PATCH 36/49] Since redis CLI requires injecting arguments only at index 1, indexToProvisionAt param is removed --- plugins/redis/provisioner.go | 10 ++++------ plugins/redis/redis_cli.go | 2 +- plugins/redis/user_credentials.go | 10 ---------- 3 files changed, 5 insertions(+), 17 deletions(-) diff --git a/plugins/redis/provisioner.go b/plugins/redis/provisioner.go index 0bdcbceca..706a90b42 100644 --- a/plugins/redis/provisioner.go +++ b/plugins/redis/provisioner.go @@ -7,21 +7,19 @@ import ( ) type redisArgsProvisioner struct { - AddArgsAtIndex map[string]uint - Schema map[string]sdk.FieldName + Schema map[string]sdk.FieldName } -func redisFlags(addArgsAtIndex map[string]uint, schema map[string]sdk.FieldName) sdk.Provisioner { +func redisFlags(schema map[string]sdk.FieldName) sdk.Provisioner { return redisArgsProvisioner{ - AddArgsAtIndex: addArgsAtIndex, - Schema: schema, + Schema: schema, } } func (p redisArgsProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, out *sdk.ProvisionOutput) { for argName, fieldName := range p.Schema { if value, ok := in.ItemFields[fieldName]; ok { - out.AddArgsAtIndex(p.AddArgsAtIndex[argName], argName, value) + out.AddArgsAtIndex(1, argName, value) } } } diff --git a/plugins/redis/redis_cli.go b/plugins/redis/redis_cli.go index eae4b94c9..a68b5b07a 100644 --- a/plugins/redis/redis_cli.go +++ b/plugins/redis/redis_cli.go @@ -25,7 +25,7 @@ func RedisCLI() schema.Executable { Name: credname.UserCredentials, Provisioner: provision.ChainProvisioners( provision.EnvVars(defaultEnvVarMapping), - redisFlags(indexToProvisionAt, argsToProvision), + redisFlags(argsToProvision), ), }, }, diff --git a/plugins/redis/user_credentials.go b/plugins/redis/user_credentials.go index 67978ac6d..77cdb4e5d 100644 --- a/plugins/redis/user_credentials.go +++ b/plugins/redis/user_credentials.go @@ -9,10 +9,6 @@ import ( "github.com/1Password/shell-plugins/sdk/schema/fieldname" ) -const ( - index uint = 1 // We inject arguments immediately after the redis-cli command, because that's where they're expected by the redis-cli binary. Placing arguments at the end or in the middle of the command will cause redis-cli to fail. -) - func UserCredentials() schema.CredentialType { return schema.CredentialType{ Name: credname.UserCredentials, @@ -84,9 +80,3 @@ var argsToProvision = map[string]sdk.FieldName{ "-h": fieldname.Host, "-p": fieldname.Port, } - -var indexToProvisionAt = map[string]uint{ - "--user": index, - "-h": index, - "-p": index, -} From 3875a1394e77722a7618dcc12358634fcebe59fa Mon Sep 17 00:00:00 2001 From: Arun Date: Mon, 12 Jun 2023 08:39:10 -0400 Subject: [PATCH 37/49] Remove chained provisioner and add REDISCLI_AUTH directly in custom redis provisioner --- plugins/redis/provisioner.go | 4 ++++ plugins/redis/redis_cli.go | 8 ++----- sdk/provision/chained_provisioner.go | 33 ---------------------------- 3 files changed, 6 insertions(+), 39 deletions(-) delete mode 100644 sdk/provision/chained_provisioner.go diff --git a/plugins/redis/provisioner.go b/plugins/redis/provisioner.go index 706a90b42..bf244bf2d 100644 --- a/plugins/redis/provisioner.go +++ b/plugins/redis/provisioner.go @@ -4,6 +4,7 @@ import ( "context" "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/schema/fieldname" ) type redisArgsProvisioner struct { @@ -17,6 +18,9 @@ func redisFlags(schema map[string]sdk.FieldName) sdk.Provisioner { } func (p redisArgsProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, out *sdk.ProvisionOutput) { + if value, ok := in.ItemFields[fieldname.Password]; ok { + out.AddEnvVar("REDISCLI_AUTH", value) + } for argName, fieldName := range p.Schema { if value, ok := in.ItemFields[fieldName]; ok { out.AddArgsAtIndex(1, argName, value) diff --git a/plugins/redis/redis_cli.go b/plugins/redis/redis_cli.go index a68b5b07a..7dfbebd23 100644 --- a/plugins/redis/redis_cli.go +++ b/plugins/redis/redis_cli.go @@ -3,7 +3,6 @@ package redis import ( "github.com/1Password/shell-plugins/sdk" "github.com/1Password/shell-plugins/sdk/needsauth" - "github.com/1Password/shell-plugins/sdk/provision" "github.com/1Password/shell-plugins/sdk/schema" "github.com/1Password/shell-plugins/sdk/schema/credname" ) @@ -22,11 +21,8 @@ func RedisCLI() schema.Executable { ), Uses: []schema.CredentialUsage{ { - Name: credname.UserCredentials, - Provisioner: provision.ChainProvisioners( - provision.EnvVars(defaultEnvVarMapping), - redisFlags(argsToProvision), - ), + Name: credname.UserCredentials, + Provisioner: redisFlags(argsToProvision), }, }, } diff --git a/sdk/provision/chained_provisioner.go b/sdk/provision/chained_provisioner.go deleted file mode 100644 index 7ee852e4e..000000000 --- a/sdk/provision/chained_provisioner.go +++ /dev/null @@ -1,33 +0,0 @@ -package provision - -import ( - "context" - - "github.com/1Password/shell-plugins/sdk" -) - -// ChainedProvisioner chains multiple provisioners together for use at the same time. -type ChainedProvisioner struct { - Provisioners []sdk.Provisioner -} - -// ChainProvisioners creates a ChainedProvisioner that chains multiple provisioners together for use at the same time. -func ChainProvisioners(provisioners ...sdk.Provisioner) sdk.Provisioner { - return ChainedProvisioner{ - Provisioners: provisioners, - } -} - -func (p ChainedProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, out *sdk.ProvisionOutput) { - for _, provisioner := range p.Provisioners { - provisioner.Provision(ctx, in, out) - } -} - -func (p ChainedProvisioner) Deprovision(ctx context.Context, in sdk.DeprovisionInput, out *sdk.DeprovisionOutput) { - // Nothing to do here: environment variables and args get wiped automatically when the process exits. -} - -func (p ChainedProvisioner) Description() string { - return "Handle multiple provisioners at once" -} From 472076e880f3bfafe682c248652c0d7a66dc224b Mon Sep 17 00:00:00 2001 From: Arun Date: Mon, 12 Jun 2023 08:56:34 -0400 Subject: [PATCH 38/49] Branch out rediscloud as a separate shell plugin --- plugins/redis/plugin.go | 1 - plugins/{redis => rediscloud}/api_key.go | 8 ++++---- plugins/{redis => rediscloud}/api_key_test.go | 2 +- plugins/rediscloud/plugin.go | 19 +++++++++++++++++++ 4 files changed, 24 insertions(+), 6 deletions(-) rename plugins/{redis => rediscloud}/api_key.go (87%) rename plugins/{redis => rediscloud}/api_key_test.go (98%) create mode 100644 plugins/rediscloud/plugin.go diff --git a/plugins/redis/plugin.go b/plugins/redis/plugin.go index 623743e25..45393508d 100644 --- a/plugins/redis/plugin.go +++ b/plugins/redis/plugin.go @@ -14,7 +14,6 @@ func New() schema.Plugin { }, Credentials: []schema.CredentialType{ UserCredentials(), - RedisCloudAPIKey(), }, Executables: []schema.Executable{ RedisCLI(), diff --git a/plugins/redis/api_key.go b/plugins/rediscloud/api_key.go similarity index 87% rename from plugins/redis/api_key.go rename to plugins/rediscloud/api_key.go index 07dd01668..ed0f7d15b 100644 --- a/plugins/redis/api_key.go +++ b/plugins/rediscloud/api_key.go @@ -1,4 +1,4 @@ -package redis +package rediscloud import ( "github.com/1Password/shell-plugins/sdk" @@ -40,13 +40,13 @@ func RedisCloudAPIKey() schema.CredentialType { }, }, }, - DefaultProvisioner: provision.EnvVars(envVarMappingForRedisEnterpriseCloud), + DefaultProvisioner: provision.EnvVars(envVarMappingForRedisCloud), Importer: importer.TryAll( - importer.TryEnvVarPair(envVarMappingForRedisEnterpriseCloud), + importer.TryEnvVarPair(envVarMappingForRedisCloud), )} } -var envVarMappingForRedisEnterpriseCloud = map[string]sdk.FieldName{ +var envVarMappingForRedisCloud = map[string]sdk.FieldName{ "REDISCLOUD_ACCESS_KEY": fieldname.AccountKey, "REDISCLOUD_SECRET_KEY": fieldname.UserKey, } diff --git a/plugins/redis/api_key_test.go b/plugins/rediscloud/api_key_test.go similarity index 98% rename from plugins/redis/api_key_test.go rename to plugins/rediscloud/api_key_test.go index 2758619d3..9f4488273 100644 --- a/plugins/redis/api_key_test.go +++ b/plugins/rediscloud/api_key_test.go @@ -1,4 +1,4 @@ -package redis +package rediscloud import ( "testing" diff --git a/plugins/rediscloud/plugin.go b/plugins/rediscloud/plugin.go new file mode 100644 index 000000000..0a6d06e53 --- /dev/null +++ b/plugins/rediscloud/plugin.go @@ -0,0 +1,19 @@ +package rediscloud + +import ( + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/schema" +) + +func New() schema.Plugin { + return schema.Plugin{ + Name: "rediscloud", + Platform: schema.PlatformInfo{ + Name: "Redis Cloud", + Homepage: sdk.URL("https://redis.com/"), + }, + Credentials: []schema.CredentialType{ + RedisCloudAPIKey(), + }, + } +} From 80ad3bd479aa042256736fee3e6344d0501a244f Mon Sep 17 00:00:00 2001 From: Arun Sathiya Date: Mon, 12 Jun 2023 06:16:45 -0700 Subject: [PATCH 39/49] Update rediscloud platform homepage address Co-authored-by: Floris van der Grinten --- plugins/rediscloud/plugin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/rediscloud/plugin.go b/plugins/rediscloud/plugin.go index 0a6d06e53..ce6b86f4d 100644 --- a/plugins/rediscloud/plugin.go +++ b/plugins/rediscloud/plugin.go @@ -10,7 +10,7 @@ func New() schema.Plugin { Name: "rediscloud", Platform: schema.PlatformInfo{ Name: "Redis Cloud", - Homepage: sdk.URL("https://redis.com/"), + Homepage: sdk.URL("https://redis.com/redis-enterprise-cloud/overview/"), }, Credentials: []schema.CredentialType{ RedisCloudAPIKey(), From 72c737c77e619540d0d5330b74d704997fabc2b9 Mon Sep 17 00:00:00 2001 From: Arun Date: Mon, 12 Jun 2023 09:19:56 -0400 Subject: [PATCH 40/49] Since rediscloud is a separate shell plugin now, rename credential function from RedisCloudAPIKey to APIKey --- plugins/rediscloud/api_key.go | 2 +- plugins/rediscloud/api_key_test.go | 4 ++-- plugins/rediscloud/plugin.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/rediscloud/api_key.go b/plugins/rediscloud/api_key.go index ed0f7d15b..015efdd29 100644 --- a/plugins/rediscloud/api_key.go +++ b/plugins/rediscloud/api_key.go @@ -9,7 +9,7 @@ import ( "github.com/1Password/shell-plugins/sdk/schema/fieldname" ) -func RedisCloudAPIKey() schema.CredentialType { +func APIKey() schema.CredentialType { return schema.CredentialType{ Name: credname.APIKey, DocsURL: sdk.URL("https://docs.redis.com/latest/rc/api/get-started/manage-api-keys/"), diff --git a/plugins/rediscloud/api_key_test.go b/plugins/rediscloud/api_key_test.go index 9f4488273..4a6e57e76 100644 --- a/plugins/rediscloud/api_key_test.go +++ b/plugins/rediscloud/api_key_test.go @@ -9,7 +9,7 @@ import ( ) func TestRedisCloudAPIKeyProvisioner(t *testing.T) { - plugintest.TestProvisioner(t, RedisCloudAPIKey().DefaultProvisioner, map[string]plugintest.ProvisionCase{ + plugintest.TestProvisioner(t, APIKey().DefaultProvisioner, map[string]plugintest.ProvisionCase{ "default": { ItemFields: map[sdk.FieldName]string{ fieldname.AccountKey: "5v0mPzRKNcvlwRMi4CjWISt15UfCRxjcNVMPCZfDOJTZEXAMPLE", @@ -26,7 +26,7 @@ func TestRedisCloudAPIKeyProvisioner(t *testing.T) { } func TestRedisCloudAPIKeyImporter(t *testing.T) { - plugintest.TestImporter(t, RedisCloudAPIKey().Importer, map[string]plugintest.ImportCase{ + plugintest.TestImporter(t, APIKey().Importer, map[string]plugintest.ImportCase{ "environment": { Environment: map[string]string{ "REDISCLOUD_ACCESS_KEY": "5v0mPzRKNcvlwRMi4CjWISt15UfCRxjcNVMPCZfDOJTZEXAMPLE", diff --git a/plugins/rediscloud/plugin.go b/plugins/rediscloud/plugin.go index ce6b86f4d..8db45d9ea 100644 --- a/plugins/rediscloud/plugin.go +++ b/plugins/rediscloud/plugin.go @@ -13,7 +13,7 @@ func New() schema.Plugin { Homepage: sdk.URL("https://redis.com/redis-enterprise-cloud/overview/"), }, Credentials: []schema.CredentialType{ - RedisCloudAPIKey(), + APIKey(), }, } } From 3a138cd0fe4cb297c50eea359535db3580d2e1d7 Mon Sep 17 00:00:00 2001 From: Arun Date: Mon, 12 Jun 2023 13:19:51 -0400 Subject: [PATCH 41/49] Revert "Remove validation check that checks if multiple credentials are defined\, because multi-credential support is beginning to be possible" This reverts commit 0100c222bd8e3b00af20bf064ff054375187d2d2. --- sdk/schema/plugin.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sdk/schema/plugin.go b/sdk/schema/plugin.go index 0235a003e..5c29fc11f 100644 --- a/sdk/schema/plugin.go +++ b/sdk/schema/plugin.go @@ -73,6 +73,12 @@ func (p Plugin) Validate() (bool, ValidationReport) { Severity: ValidationSeverityError, }) + report.AddCheck(ValidationCheck{ + Description: "Has no more than one credential type defined. Plugins with multiple credential types are not supported yet", + Assertion: len(p.Credentials) <= 1, + Severity: ValidationSeverityError, + }) + report.AddCheck(ValidationCheck{ Description: "Credentials referenced in executables are included in the same plugin definition", Assertion: CredentialReferencesInCredentialList(p), From 8a1f3ca8b36591a204074c5f486f7f0e5816b136 Mon Sep 17 00:00:00 2001 From: Arun Date: Mon, 12 Jun 2023 20:45:43 -0400 Subject: [PATCH 42/49] Switch redis provision from map-based struct to string, because it allows for more control over the order in which the arguments are provisioned --- plugins/redis/provisioner.go | 16 ++++++++-------- plugins/redis/redis_cli.go | 2 +- plugins/redis/user_credentials.go | 10 ++++++---- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/plugins/redis/provisioner.go b/plugins/redis/provisioner.go index bf244bf2d..8e0674db3 100644 --- a/plugins/redis/provisioner.go +++ b/plugins/redis/provisioner.go @@ -2,28 +2,28 @@ package redis import ( "context" + "strings" "github.com/1Password/shell-plugins/sdk" "github.com/1Password/shell-plugins/sdk/schema/fieldname" ) type redisArgsProvisioner struct { - Schema map[string]sdk.FieldName } -func redisFlags(schema map[string]sdk.FieldName) sdk.Provisioner { - return redisArgsProvisioner{ - Schema: schema, - } +func redisProvisioner() sdk.Provisioner { + return redisArgsProvisioner{} } func (p redisArgsProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, out *sdk.ProvisionOutput) { if value, ok := in.ItemFields[fieldname.Password]; ok { out.AddEnvVar("REDISCLI_AUTH", value) } - for argName, fieldName := range p.Schema { - if value, ok := in.ItemFields[fieldName]; ok { - out.AddArgsAtIndex(1, argName, value) + for _, arg := range argsToProvision { + argName := strings.Split(arg, " ")[0] + fieldName := sdk.FieldName(strings.Split(arg, " ")[1]) + if fieldValue, ok := in.ItemFields[fieldName]; ok { + out.AddArgsAtIndex(1, argName, fieldValue) } } } diff --git a/plugins/redis/redis_cli.go b/plugins/redis/redis_cli.go index 7dfbebd23..d45b0469a 100644 --- a/plugins/redis/redis_cli.go +++ b/plugins/redis/redis_cli.go @@ -22,7 +22,7 @@ func RedisCLI() schema.Executable { Uses: []schema.CredentialUsage{ { Name: credname.UserCredentials, - Provisioner: redisFlags(argsToProvision), + Provisioner: redisProvisioner(), }, }, } diff --git a/plugins/redis/user_credentials.go b/plugins/redis/user_credentials.go index 77cdb4e5d..c6a04b816 100644 --- a/plugins/redis/user_credentials.go +++ b/plugins/redis/user_credentials.go @@ -1,6 +1,8 @@ package redis import ( + "fmt" + "github.com/1Password/shell-plugins/sdk" "github.com/1Password/shell-plugins/sdk/importer" "github.com/1Password/shell-plugins/sdk/provision" @@ -75,8 +77,8 @@ var defaultEnvVarMapping = map[string]sdk.FieldName{ "REDISCLI_AUTH": fieldname.Password, } -var argsToProvision = map[string]sdk.FieldName{ - "--user": fieldname.Username, - "-h": fieldname.Host, - "-p": fieldname.Port, +var argsToProvision = []string{ + fmt.Sprintf("--user %s", fieldname.Username), + fmt.Sprintf("-h %s", fieldname.Host), + fmt.Sprintf("-p %s", fieldname.Port), } From 6d6baf7a1e1d227b9e3695e325dd615663d55f2c Mon Sep 17 00:00:00 2001 From: Arun Date: Tue, 13 Jun 2023 00:31:12 -0400 Subject: [PATCH 43/49] Update test to verify the provisioned arguments and expected command line arguments but it's not working just yet due to slice range issues --- plugins/redis/user_credentials_test.go | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/plugins/redis/user_credentials_test.go b/plugins/redis/user_credentials_test.go index 655fa032b..7feb4de25 100644 --- a/plugins/redis/user_credentials_test.go +++ b/plugins/redis/user_credentials_test.go @@ -8,7 +8,25 @@ import ( "github.com/1Password/shell-plugins/sdk/schema/fieldname" ) -func TestPasswordProvisioner(t *testing.T) { +func TestUserCredentialsProvisioner(t *testing.T) { + plugintest.TestProvisioner(t, redisProvisioner(), map[string]plugintest.ProvisionCase{ + "default": { + ItemFields: map[sdk.FieldName]string{ + fieldname.Password: "pjtxpc2gaddifapjvalggspojexample", + fieldname.Username: "example", + fieldname.Host: "127.0.0.1", + fieldname.Port: "6379", + }, + ExpectedOutput: sdk.ProvisionOutput{ + Environment: map[string]string{ + "REDISCLI_AUTH": "pjtxpc2gaddifapjvalggspojexample", + }, + CommandLine: []string{"-p", "6379", "-h", "127.0.0.1", "--user", "example"}, + }, + }, + }) +} +func TestDefaultUserCredentialsProvisioner(t *testing.T) { plugintest.TestProvisioner(t, UserCredentials().DefaultProvisioner, map[string]plugintest.ProvisionCase{ "default": { ItemFields: map[sdk.FieldName]string{ @@ -23,7 +41,7 @@ func TestPasswordProvisioner(t *testing.T) { }) } -func TestPasswordImporter(t *testing.T) { +func TestUserCredentialsImporter(t *testing.T) { plugintest.TestImporter(t, UserCredentials().Importer, map[string]plugintest.ImportCase{ "environment": { Environment: map[string]string{ From cecb2d0c011e6592d81f2fa145023be77e58da2f Mon Sep 17 00:00:00 2001 From: Arun Date: Tue, 13 Jun 2023 12:17:30 -0400 Subject: [PATCH 44/49] Refactor argument provisioning logic to avoid string splitting --- plugins/redis/provisioner.go | 19 +++++++++++++------ plugins/redis/user_credentials.go | 8 -------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/plugins/redis/provisioner.go b/plugins/redis/provisioner.go index 8e0674db3..25604ef17 100644 --- a/plugins/redis/provisioner.go +++ b/plugins/redis/provisioner.go @@ -2,12 +2,17 @@ package redis import ( "context" - "strings" "github.com/1Password/shell-plugins/sdk" "github.com/1Password/shell-plugins/sdk/schema/fieldname" ) +var argsToProvision = []string{ + "--user", fieldname.Username.String(), + "-h", fieldname.Host.String(), + "-p", fieldname.Port.String(), +} + type redisArgsProvisioner struct { } @@ -19,11 +24,13 @@ func (p redisArgsProvisioner) Provision(ctx context.Context, in sdk.ProvisionInp if value, ok := in.ItemFields[fieldname.Password]; ok { out.AddEnvVar("REDISCLI_AUTH", value) } - for _, arg := range argsToProvision { - argName := strings.Split(arg, " ")[0] - fieldName := sdk.FieldName(strings.Split(arg, " ")[1]) - if fieldValue, ok := in.ItemFields[fieldName]; ok { - out.AddArgsAtIndex(1, argName, fieldValue) + for i, arg := range argsToProvision { + if i%2 == 0 { + argName := arg + fieldName := sdk.FieldName(argsToProvision[i+1]) + if fieldValue, ok := in.ItemFields[fieldName]; ok { + out.AddArgsAtIndex(1, argName, fieldValue) + } } } } diff --git a/plugins/redis/user_credentials.go b/plugins/redis/user_credentials.go index c6a04b816..f7f4b7346 100644 --- a/plugins/redis/user_credentials.go +++ b/plugins/redis/user_credentials.go @@ -1,8 +1,6 @@ package redis import ( - "fmt" - "github.com/1Password/shell-plugins/sdk" "github.com/1Password/shell-plugins/sdk/importer" "github.com/1Password/shell-plugins/sdk/provision" @@ -76,9 +74,3 @@ func UserCredentials() schema.CredentialType { var defaultEnvVarMapping = map[string]sdk.FieldName{ "REDISCLI_AUTH": fieldname.Password, } - -var argsToProvision = []string{ - fmt.Sprintf("--user %s", fieldname.Username), - fmt.Sprintf("-h %s", fieldname.Host), - fmt.Sprintf("-p %s", fieldname.Port), -} From c015cea1f787c8c92640feace6f0da84a7e6a181 Mon Sep 17 00:00:00 2001 From: Arun Date: Wed, 14 Jun 2023 12:52:28 -0400 Subject: [PATCH 45/49] Skip 1Password authentication when an user enters a custom host or port as well --- plugins/redis/redis_cli.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/redis/redis_cli.go b/plugins/redis/redis_cli.go index d45b0469a..0d8c86f8d 100644 --- a/plugins/redis/redis_cli.go +++ b/plugins/redis/redis_cli.go @@ -14,8 +14,10 @@ func RedisCLI() schema.Executable { DocsURL: sdk.URL("https://redis.io/docs/ui/cli"), NeedsAuth: needsauth.IfAll( needsauth.NotWhenContainsArgs("-u"), - needsauth.NotWhenContainsArgs("--user"), - needsauth.NotWhenContainsArgs("-a"), + needsauth.NotWhenContainsArgs("--user"), // skip when a custom username is provided + needsauth.NotWhenContainsArgs("-a"), // skip when a custom password is provided + needsauth.NotWhenContainsArgs("-h"), // skip when a custom host is provided + needsauth.NotWhenContainsArgs("-p"), // skip when a custom port is provided needsauth.NotWhenContainsArgs("--help"), needsauth.NotForVersion(), ), From c8a216db0a70920518eeef62d3aa8a1cddf1683c Mon Sep 17 00:00:00 2001 From: Arun Date: Thu, 15 Jun 2023 20:51:56 -0400 Subject: [PATCH 46/49] Fix provisoner test --- plugins/redis/user_credentials_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/redis/user_credentials_test.go b/plugins/redis/user_credentials_test.go index 7feb4de25..e5f7370f6 100644 --- a/plugins/redis/user_credentials_test.go +++ b/plugins/redis/user_credentials_test.go @@ -17,11 +17,12 @@ func TestUserCredentialsProvisioner(t *testing.T) { fieldname.Host: "127.0.0.1", fieldname.Port: "6379", }, + CommandLine: []string{"redis-cli"}, ExpectedOutput: sdk.ProvisionOutput{ Environment: map[string]string{ "REDISCLI_AUTH": "pjtxpc2gaddifapjvalggspojexample", }, - CommandLine: []string{"-p", "6379", "-h", "127.0.0.1", "--user", "example"}, + CommandLine: []string{"redis-cli", "-p", "6379", "-h", "127.0.0.1", "--user", "example"}, // Each argument is provisioned at index 1, pushing the existing arguments forward to the next index }, }, }) From 92e7f56e8fd0284c033f676c65457be010ee3678 Mon Sep 17 00:00:00 2001 From: Arun Date: Thu, 15 Jun 2023 20:53:21 -0400 Subject: [PATCH 47/49] Safeguard in the arguments injection code to prevent out of bounds errors, but this is not necessarily a concern in redis-cli because we always provision at index 1 and redis-cli is the minimum required command --- sdk/provisioner.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sdk/provisioner.go b/sdk/provisioner.go index db48364b7..d7359c776 100644 --- a/sdk/provisioner.go +++ b/sdk/provisioner.go @@ -109,6 +109,11 @@ func (out *ProvisionOutput) AddArgs(args ...string) { // AddArgsAtIndex can be used to add additional arguments to the command line of the provision output, at a specific index. func (out *ProvisionOutput) AddArgsAtIndex(index uint, args ...string) { + // Provision arguments at the end of the command line input to prevent out of bound errors. But, this isn't entirely a concern is the case of redis-cli where we always provision at index 1 and "redis-cli" is the minimum-required command + if index >= uint(len(out.CommandLine)) { + out.CommandLine = append(out.CommandLine, args...) + return + } newCommandLine := []string{} newCommandLine = append(newCommandLine, out.CommandLine[:index]...) newCommandLine = append(newCommandLine, args...) From c09b86441b849da61b4115692d028a0d08b9e7be Mon Sep 17 00:00:00 2001 From: Arun Date: Wed, 16 Aug 2023 23:29:09 -0400 Subject: [PATCH 48/49] Don't skip auth for certain flags, rather use them in conjunction with 1Password-stored secrets to provision --- plugins/redis/provisioner.go | 73 +++++++++++++++++++++++++++++------- plugins/redis/redis_cli.go | 5 --- 2 files changed, 59 insertions(+), 19 deletions(-) diff --git a/plugins/redis/provisioner.go b/plugins/redis/provisioner.go index 25604ef17..3d2a01f2e 100644 --- a/plugins/redis/provisioner.go +++ b/plugins/redis/provisioner.go @@ -7,12 +7,6 @@ import ( "github.com/1Password/shell-plugins/sdk/schema/fieldname" ) -var argsToProvision = []string{ - "--user", fieldname.Username.String(), - "-h", fieldname.Host.String(), - "-p", fieldname.Port.String(), -} - type redisArgsProvisioner struct { } @@ -21,18 +15,69 @@ func redisProvisioner() sdk.Provisioner { } func (p redisArgsProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, out *sdk.ProvisionOutput) { - if value, ok := in.ItemFields[fieldname.Password]; ok { - out.AddEnvVar("REDISCLI_AUTH", value) + listOfPossibleUserInputArguments := []string{ + "--user", + "-h", + "-p", + "--pass", + "-a", } - for i, arg := range argsToProvision { - if i%2 == 0 { - argName := arg - fieldName := sdk.FieldName(argsToProvision[i+1]) - if fieldValue, ok := in.ItemFields[fieldName]; ok { - out.AddArgsAtIndex(1, argName, fieldValue) + + var ( + commandLineContainsUserArgument bool = false + commandLineContainsHostArgument bool = false + commandLineContainsPortArgument bool = false + commandLineContainsPasswordArgument bool = false + ) + + for i, arg := range out.CommandLine { + for _, userArg := range listOfPossibleUserInputArguments { + if arg == userArg { + // Get the executable "redis-cli", the matched argument we are looking for and its value + commandLine := []string{out.CommandLine[0], out.CommandLine[i], out.CommandLine[i+1]} + // Remove the matched argument and its value as they will be added to the beginning of the command line + out.CommandLine = append(out.CommandLine[:i], out.CommandLine[i+2:]...) + // Add the executable "redis-cli", the matched argument and its value to the beginning of the command line + commandLine = append(commandLine, out.CommandLine[1:]...) + out.CommandLine = commandLine + // Controller to check if the user has already provided the argument + switch userArg { + case "--user": + commandLineContainsUserArgument = true + case "-h": + commandLineContainsHostArgument = true + case "-p": + commandLineContainsPortArgument = true + case "--pass", "-a": + commandLineContainsPasswordArgument = true + default: + break + } } } } + + if value, ok := in.ItemFields[fieldname.Password]; ok && !commandLineContainsPasswordArgument { + out.AddEnvVar("REDISCLI_AUTH", value) + } + + if value, ok := in.ItemFields[fieldname.Host]; ok && !commandLineContainsHostArgument { + commandLine := []string{out.CommandLine[0], "-h", value} + commandLine = append(commandLine, out.CommandLine[1:]...) + out.CommandLine = commandLine + } + + if value, ok := in.ItemFields[fieldname.Username]; ok && !commandLineContainsUserArgument { + commandLine := []string{out.CommandLine[0], "--user", value} + commandLine = append(commandLine, out.CommandLine[1:]...) + out.CommandLine = commandLine + } + + if value, ok := in.ItemFields[fieldname.Port]; ok && !commandLineContainsPortArgument { + commandLine := []string{out.CommandLine[0], "-p", value} + commandLine = append(commandLine, out.CommandLine[1:]...) + out.CommandLine = commandLine + } } func (p redisArgsProvisioner) Deprovision(ctx context.Context, in sdk.DeprovisionInput, out *sdk.DeprovisionOutput) { diff --git a/plugins/redis/redis_cli.go b/plugins/redis/redis_cli.go index 0d8c86f8d..486f1d21b 100644 --- a/plugins/redis/redis_cli.go +++ b/plugins/redis/redis_cli.go @@ -13,11 +13,6 @@ func RedisCLI() schema.Executable { Runs: []string{"redis-cli"}, DocsURL: sdk.URL("https://redis.io/docs/ui/cli"), NeedsAuth: needsauth.IfAll( - needsauth.NotWhenContainsArgs("-u"), - needsauth.NotWhenContainsArgs("--user"), // skip when a custom username is provided - needsauth.NotWhenContainsArgs("-a"), // skip when a custom password is provided - needsauth.NotWhenContainsArgs("-h"), // skip when a custom host is provided - needsauth.NotWhenContainsArgs("-p"), // skip when a custom port is provided needsauth.NotWhenContainsArgs("--help"), needsauth.NotForVersion(), ), From 913972225a494b90247bfc8fd0d6336bd65cafb7 Mon Sep 17 00:00:00 2001 From: Scott Lougheed Date: Tue, 2 Jun 2026 15:41:07 -0700 Subject: [PATCH 49/49] rebasing and making some fixes --- plugins/redis/provisioner.go | 119 +++++++++++++------------ plugins/redis/user_credentials.go | 1 - plugins/redis/user_credentials_test.go | 44 ++++++++- sdk/provisioner.go | 14 --- sdk/schema/fieldname/names.go | 2 + 5 files changed, 108 insertions(+), 72 deletions(-) diff --git a/plugins/redis/provisioner.go b/plugins/redis/provisioner.go index 3d2a01f2e..2788eb8f5 100644 --- a/plugins/redis/provisioner.go +++ b/plugins/redis/provisioner.go @@ -14,69 +14,41 @@ func redisProvisioner() sdk.Provisioner { return redisArgsProvisioner{} } -func (p redisArgsProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, out *sdk.ProvisionOutput) { - listOfPossibleUserInputArguments := []string{ - "--user", - "-h", - "-p", - "--pass", - "-a", - } - - var ( - commandLineContainsUserArgument bool = false - commandLineContainsHostArgument bool = false - commandLineContainsPortArgument bool = false - commandLineContainsPasswordArgument bool = false - ) +// Redis CLI flags that, when already supplied by the user, signal that we +// should not provision the corresponding field from the 1Password item. +var ( + hostFlags = []string{"-h"} + portFlags = []string{"-p"} + userFlags = []string{"--user"} + passwordFlags = []string{"-a", "--pass"} +) - for i, arg := range out.CommandLine { - for _, userArg := range listOfPossibleUserInputArguments { - if arg == userArg { - // Get the executable "redis-cli", the matched argument we are looking for and its value - commandLine := []string{out.CommandLine[0], out.CommandLine[i], out.CommandLine[i+1]} - // Remove the matched argument and its value as they will be added to the beginning of the command line - out.CommandLine = append(out.CommandLine[:i], out.CommandLine[i+2:]...) - // Add the executable "redis-cli", the matched argument and its value to the beginning of the command line - commandLine = append(commandLine, out.CommandLine[1:]...) - out.CommandLine = commandLine - // Controller to check if the user has already provided the argument - switch userArg { - case "--user": - commandLineContainsUserArgument = true - case "-h": - commandLineContainsHostArgument = true - case "-p": - commandLineContainsPortArgument = true - case "--pass", "-a": - commandLineContainsPasswordArgument = true - default: - break - } - } - } - } +func (p redisArgsProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, out *sdk.ProvisionOutput) { + suppliedFlags := flagSet(out.CommandLine) - if value, ok := in.ItemFields[fieldname.Password]; ok && !commandLineContainsPasswordArgument { + // The password is passed via an environment variable so it never appears in + // the process's argument list. Skip it if the user already authenticated on + // the command line. + if value, ok := in.ItemFields[fieldname.Password]; ok && !containsAny(suppliedFlags, passwordFlags) { out.AddEnvVar("REDISCLI_AUTH", value) } - if value, ok := in.ItemFields[fieldname.Host]; ok && !commandLineContainsHostArgument { - commandLine := []string{out.CommandLine[0], "-h", value} - commandLine = append(commandLine, out.CommandLine[1:]...) - out.CommandLine = commandLine + // Collect the flags to inject first, then prepend them in a single pass. + // Mutating out.CommandLine while ranging over it risks index-out-of-range + // panics and stale reads, so we never modify it during inspection. + var injected []string + if value, ok := in.ItemFields[fieldname.Host]; ok && !containsAny(suppliedFlags, hostFlags) { + injected = append(injected, "-h", value) } - - if value, ok := in.ItemFields[fieldname.Username]; ok && !commandLineContainsUserArgument { - commandLine := []string{out.CommandLine[0], "--user", value} - commandLine = append(commandLine, out.CommandLine[1:]...) - out.CommandLine = commandLine + if value, ok := in.ItemFields[fieldname.Port]; ok && !containsAny(suppliedFlags, portFlags) { + injected = append(injected, "-p", value) + } + if value, ok := in.ItemFields[fieldname.Username]; ok && !containsAny(suppliedFlags, userFlags) { + injected = append(injected, "--user", value) } - if value, ok := in.ItemFields[fieldname.Port]; ok && !commandLineContainsPortArgument { - commandLine := []string{out.CommandLine[0], "-p", value} - commandLine = append(commandLine, out.CommandLine[1:]...) - out.CommandLine = commandLine + if len(injected) > 0 { + out.CommandLine = prependArgs(out.CommandLine, injected) } } @@ -85,5 +57,40 @@ func (p redisArgsProvisioner) Deprovision(ctx context.Context, in sdk.Deprovisio } func (p redisArgsProvisioner) Description() string { - return "Provision redis secrets as command-line arguments." + return "Provision redis secrets as command-line arguments and the password as an environment variable." +} + +// flagSet returns the set of arguments present on the command line, excluding +// the executable name at index 0. +func flagSet(commandLine []string) map[string]bool { + set := make(map[string]bool, len(commandLine)) + for i, arg := range commandLine { + if i == 0 { + continue + } + set[arg] = true + } + return set +} + +func containsAny(set map[string]bool, flags []string) bool { + for _, f := range flags { + if set[f] { + return true + } + } + return false +} + +// prependArgs inserts args immediately after the executable name (index 0), +// leaving the rest of the user-supplied command line intact. +func prependArgs(commandLine []string, args []string) []string { + if len(commandLine) == 0 { + return append([]string{}, args...) + } + result := make([]string, 0, len(commandLine)+len(args)) + result = append(result, commandLine[0]) + result = append(result, args...) + result = append(result, commandLine[1:]...) + return result } diff --git a/plugins/redis/user_credentials.go b/plugins/redis/user_credentials.go index f7f4b7346..a882cf088 100644 --- a/plugins/redis/user_credentials.go +++ b/plugins/redis/user_credentials.go @@ -19,7 +19,6 @@ func UserCredentials() schema.CredentialType { MarkdownDescription: "Password used to authenticate to Redis server.", Secret: true, Composition: &schema.ValueComposition{ - Length: 32, Charset: schema.Charset{ Uppercase: true, Lowercase: true, diff --git a/plugins/redis/user_credentials_test.go b/plugins/redis/user_credentials_test.go index e5f7370f6..82783a544 100644 --- a/plugins/redis/user_credentials_test.go +++ b/plugins/redis/user_credentials_test.go @@ -22,7 +22,49 @@ func TestUserCredentialsProvisioner(t *testing.T) { Environment: map[string]string{ "REDISCLI_AUTH": "pjtxpc2gaddifapjvalggspojexample", }, - CommandLine: []string{"redis-cli", "-p", "6379", "-h", "127.0.0.1", "--user", "example"}, // Each argument is provisioned at index 1, pushing the existing arguments forward to the next index + CommandLine: []string{"redis-cli", "-h", "127.0.0.1", "-p", "6379", "--user", "example"}, + }, + }, + // User supplies some flags themselves: we must not duplicate them, but + // should still provision the fields they left out (and keep their args). + "user-supplied flags are respected": { + ItemFields: map[sdk.FieldName]string{ + fieldname.Password: "pjtxpc2gaddifapjvalggspojexample", + fieldname.Username: "example", + fieldname.Host: "127.0.0.1", + fieldname.Port: "6379", + }, + CommandLine: []string{"redis-cli", "-h", "myhost", "PING"}, + ExpectedOutput: sdk.ProvisionOutput{ + Environment: map[string]string{ + "REDISCLI_AUTH": "pjtxpc2gaddifapjvalggspojexample", + }, + CommandLine: []string{"redis-cli", "-p", "6379", "--user", "example", "-h", "myhost", "PING"}, + }, + }, + // A recognized flag as the final token used to cause an index-out-of-range + // panic; ensure it is handled gracefully. + "recognized flag as last token": { + ItemFields: map[sdk.FieldName]string{ + fieldname.Password: "pjtxpc2gaddifapjvalggspojexample", + fieldname.Host: "127.0.0.1", + }, + CommandLine: []string{"redis-cli", "-h"}, + ExpectedOutput: sdk.ProvisionOutput{ + Environment: map[string]string{ + "REDISCLI_AUTH": "pjtxpc2gaddifapjvalggspojexample", + }, + CommandLine: []string{"redis-cli", "-h"}, + }, + }, + // User authenticates on the command line: don't also set the env var. + "password flag skips env var": { + ItemFields: map[sdk.FieldName]string{ + fieldname.Password: "pjtxpc2gaddifapjvalggspojexample", + }, + CommandLine: []string{"redis-cli", "-a", "mypassword"}, + ExpectedOutput: sdk.ProvisionOutput{ + CommandLine: []string{"redis-cli", "-a", "mypassword"}, }, }, }) diff --git a/sdk/provisioner.go b/sdk/provisioner.go index d7359c776..99883ffae 100644 --- a/sdk/provisioner.go +++ b/sdk/provisioner.go @@ -107,20 +107,6 @@ func (out *ProvisionOutput) AddArgs(args ...string) { out.CommandLine = append(out.CommandLine, args...) } -// AddArgsAtIndex can be used to add additional arguments to the command line of the provision output, at a specific index. -func (out *ProvisionOutput) AddArgsAtIndex(index uint, args ...string) { - // Provision arguments at the end of the command line input to prevent out of bound errors. But, this isn't entirely a concern is the case of redis-cli where we always provision at index 1 and "redis-cli" is the minimum-required command - if index >= uint(len(out.CommandLine)) { - out.CommandLine = append(out.CommandLine, args...) - return - } - newCommandLine := []string{} - newCommandLine = append(newCommandLine, out.CommandLine[:index]...) - newCommandLine = append(newCommandLine, args...) - newCommandLine = append(newCommandLine, out.CommandLine[index:]...) - out.CommandLine = newCommandLine -} - // AddSecretFile can be used to add a file containing secrets to the provision output. func (out *ProvisionOutput) AddSecretFile(path string, contents []byte) { out.AddFile(path, OutputFile{ diff --git a/sdk/schema/fieldname/names.go b/sdk/schema/fieldname/names.go index 3899bac90..1cc0d992e 100644 --- a/sdk/schema/fieldname/names.go +++ b/sdk/schema/fieldname/names.go @@ -72,6 +72,7 @@ func ListAll() []sdk.FieldName { AccessToken, Account, AccountID, + AccountKey, AccountSID, Address, AppKey, @@ -111,6 +112,7 @@ func ListAll() []sdk.FieldName { Token, URL, User, + UserKey, Username, Website, }