Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
be92d38
Working shell plugin with environment variable support for the passwo…
arunsathiya May 31, 2023
0e6b425
Use NotForVersion check instead of defining each version flag one by …
arunsathiya May 31, 2023
a7bc372
Update CLI docs URL
arunsathiya May 31, 2023
9892e54
Add test for environment variable importer
arunsathiya May 31, 2023
bed61f7
Add support for host and port, in conjunction with password as enviro…
arunsathiya Jun 1, 2023
7365c78
Better description for the Redis environment variable and flags provi…
arunsathiya Jun 1, 2023
c065d86
Add username to the credential as well, thus allowing scoped access t…
arunsathiya Jun 2, 2023
1e158f8
Use shell plugin even without args, if one wants to enter redis shell…
arunsathiya Jun 2, 2023
634c9dc
Introduce second credential that'd be used only for authenticating to…
arunsathiya Jun 5, 2023
90c5674
Do not use 1Password authentication when --help flag is used
arunsathiya Jun 5, 2023
55ac590
Define ID for the credential selection and description for the creden…
arunsathiya Jun 5, 2023
14cb2e4
Switch from credential selection to credential reference with just Us…
arunsathiya Jun 6, 2023
f242d43
Mark host and port as optional fields
arunsathiya Jun 6, 2023
94a056f
Rename argument provision functions to AddArgsFirst and AddArgsLast
arunsathiya Jun 6, 2023
68d35a5
Introduce flags provision and chained provision at the SDK level and …
arunsathiya Jun 7, 2023
d93ce0a
Rename flags to args
arunsathiya Jun 7, 2023
1f9b41f
Describe the new arguments and chained provisioners, and better handl…
arunsathiya Jun 7, 2023
26fea29
Better handling of the arguments index using a map that maps the argu…
arunsathiya Jun 8, 2023
81293a9
Describe what ArgsProvisioner does better
arunsathiya Jun 8, 2023
e538aae
Remove unnecessary provisioner field in the ChainedProvisioner
arunsathiya Jun 8, 2023
a980f0c
Remove validation check that checks if multiple credentials are defin…
arunsathiya Jun 8, 2023
080d6db
Switch from provisioning at a specific index to either immediately af…
arunsathiya Jun 8, 2023
588f520
Address feedback to rename credential name to APIKey and credential f…
arunsathiya Jun 8, 2023
1c666ed
Add ManagementURL for the Redis Cloud API Keys credential
arunsathiya Jun 8, 2023
c94621d
Remove length for account key and user key fields, because they don't…
arunsathiya Jun 8, 2023
4911721
Remove the description that the default username will be default, bec…
arunsathiya Jun 8, 2023
3f26c9d
Revert "Switch from provisioning at a specific index to either immedi…
arunsathiya Jun 8, 2023
5a15a10
Rename AddArgs helper function to AddArgsAtIndex, and in the ArgsProv…
arunsathiya Jun 8, 2023
ca6978b
Swap around the index and args params in ArgsAtIndex function for bet…
arunsathiya Jun 8, 2023
26e621c
Reintroduce AddArgs helper function and fix FileProvisioner breakage
arunsathiya Jun 9, 2023
1286d92
Remove Arguments Provisioner completely and introduce redis-specific …
arunsathiya Jun 9, 2023
681375e
Update redis Enterprise Cloud platform credential fields to the moder…
arunsathiya Jun 9, 2023
d96f43a
Remove unused AccessKey and SecretKey field names
arunsathiya Jun 12, 2023
510fe41
Move executable code from redis-cli to redis_cli as snake case is mor…
arunsathiya Jun 12, 2023
c430598
Remove optional field in certain field name structs because the defau…
arunsathiya Jun 12, 2023
3507031
Since redis CLI requires injecting arguments only at index 1, indexTo…
arunsathiya Jun 12, 2023
3875a13
Remove chained provisioner and add REDISCLI_AUTH directly in custom r…
arunsathiya Jun 12, 2023
472076e
Branch out rediscloud as a separate shell plugin
arunsathiya Jun 12, 2023
80ad3bd
Update rediscloud platform homepage address
arunsathiya Jun 12, 2023
72c737c
Since rediscloud is a separate shell plugin now, rename credential fu…
arunsathiya Jun 12, 2023
3a138cd
Revert "Remove validation check that checks if multiple credentials a…
arunsathiya Jun 12, 2023
8a1f3ca
Switch redis provision from map-based struct to string, because it al…
arunsathiya Jun 13, 2023
6d6baf7
Update test to verify the provisioned arguments and expected command …
arunsathiya Jun 13, 2023
cecb2d0
Refactor argument provisioning logic to avoid string splitting
arunsathiya Jun 13, 2023
c015cea
Skip 1Password authentication when an user enters a custom host or po…
arunsathiya Jun 14, 2023
c8a216d
Fix provisoner test
arunsathiya Jun 16, 2023
92e7f56
Safeguard in the arguments injection code to prevent out of bounds er…
arunsathiya Jun 16, 2023
c09b864
Don't skip auth for certain flags, rather use them in conjunction wit…
arunsathiya Aug 17, 2023
9139722
rebasing and making some fixes
scottisloud Jun 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions plugins/redis/plugin.go
Original file line number Diff line number Diff line change
@@ -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{
UserCredentials(),
},
Executables: []schema.Executable{
RedisCLI(),
},
}
}
96 changes: 96 additions & 0 deletions plugins/redis/provisioner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package redis

import (
"context"

"github.com/1Password/shell-plugins/sdk"
"github.com/1Password/shell-plugins/sdk/schema/fieldname"
)

type redisArgsProvisioner struct {
}

func redisProvisioner() sdk.Provisioner {
return redisArgsProvisioner{}
}

// 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"}
)

func (p redisArgsProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, out *sdk.ProvisionOutput) {
Comment thread
arunsathiya marked this conversation as resolved.
suppliedFlags := flagSet(out.CommandLine)

// 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)
}

// 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.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 len(injected) > 0 {
out.CommandLine = prependArgs(out.CommandLine, injected)
}
Comment thread
arunsathiya marked this conversation as resolved.
}

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 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
}
26 changes: 26 additions & 0 deletions plugins/redis/redis_cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
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/ui/cli"),
NeedsAuth: needsauth.IfAll(
needsauth.NotWhenContainsArgs("--help"),
needsauth.NotForVersion(),
),
Uses: []schema.CredentialUsage{
{
Name: credname.UserCredentials,
Provisioner: redisProvisioner(),
},
},
}
}
75 changes: 75 additions & 0 deletions plugins/redis/user_credentials.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
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 UserCredentials() schema.CredentialType {
return schema.CredentialType{
Name: credname.UserCredentials,
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 server.",
Secret: true,
Composition: &schema.ValueComposition{
Charset: schema.Charset{
Uppercase: true,
Lowercase: true,
Digits: true,
},
},
},
{
Name: fieldname.Username,
MarkdownDescription: "Username used to authenticate to Redis server.",
Secret: false,
Optional: true,
Composition: &schema.ValueComposition{
Charset: schema.Charset{
Uppercase: true,
Lowercase: true,
Digits: true,
},
},
},
{
Name: fieldname.Host,
Comment thread
arunsathiya marked this conversation as resolved.
MarkdownDescription: "Host address for the Redis server.",
Secret: false,
Optional: true,
Composition: &schema.ValueComposition{
Charset: schema.Charset{
Lowercase: true,
Symbols: true,
Digits: true,
},
},
},
{
Name: fieldname.Port,
MarkdownDescription: "Port for the Redis server.",
Secret: false,
Optional: true,
Composition: &schema.ValueComposition{
Charset: schema.Charset{
Digits: true,
},
Comment thread
arunsathiya marked this conversation as resolved.
},
},
},
DefaultProvisioner: provision.EnvVars(defaultEnvVarMapping),
Importer: importer.TryAll(
importer.TryEnvVarPair(defaultEnvVarMapping),
)}
}

var defaultEnvVarMapping = map[string]sdk.FieldName{
"REDISCLI_AUTH": fieldname.Password,
}
102 changes: 102 additions & 0 deletions plugins/redis/user_credentials_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
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 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",
},
CommandLine: []string{"redis-cli"},
ExpectedOutput: sdk.ProvisionOutput{
Environment: map[string]string{
"REDISCLI_AUTH": "pjtxpc2gaddifapjvalggspojexample",
},
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"},
},
},
})
}
func TestDefaultUserCredentialsProvisioner(t *testing.T) {
plugintest.TestProvisioner(t, UserCredentials().DefaultProvisioner, map[string]plugintest.ProvisionCase{
"default": {
ItemFields: map[sdk.FieldName]string{
fieldname.Password: "pjtxpc2gaddifapjvalggspojexample",
},
ExpectedOutput: sdk.ProvisionOutput{
Environment: map[string]string{
Comment thread
AndyTitu marked this conversation as resolved.
"REDISCLI_AUTH": "pjtxpc2gaddifapjvalggspojexample",
},
},
},
})
}

func TestUserCredentialsImporter(t *testing.T) {
plugintest.TestImporter(t, UserCredentials().Importer, map[string]plugintest.ImportCase{
"environment": {
Environment: map[string]string{
"REDISCLI_AUTH": "pjtxpc2gaddifapjvalggspojexample",
},
ExpectedCandidates: []sdk.ImportCandidate{
{
Fields: map[sdk.FieldName]string{
fieldname.Password: "pjtxpc2gaddifapjvalggspojexample",
},
},
},
},
})
}
52 changes: 52 additions & 0 deletions plugins/rediscloud/api_key.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package rediscloud

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 APIKey() schema.CredentialType {
return schema.CredentialType{
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.AccountKey,
MarkdownDescription: "API Account key (also known as Access Key, or just API Key) to authenticate to Redis Enterprise Cloud.",
Secret: true,
Composition: &schema.ValueComposition{
Charset: schema.Charset{
Uppercase: true,
Lowercase: true,
Digits: true,
},
},
},
{
Name: fieldname.UserKey,
MarkdownDescription: "API user key (also known as Secret Key) to authenticate to Redis Enterprise Cloud.",
Secret: true,
Composition: &schema.ValueComposition{
Charset: schema.Charset{
Uppercase: true,
Lowercase: true,
Digits: true,
},
},
},
},
DefaultProvisioner: provision.EnvVars(envVarMappingForRedisCloud),
Importer: importer.TryAll(
importer.TryEnvVarPair(envVarMappingForRedisCloud),
)}
}

var envVarMappingForRedisCloud = map[string]sdk.FieldName{
"REDISCLOUD_ACCESS_KEY": fieldname.AccountKey,
"REDISCLOUD_SECRET_KEY": fieldname.UserKey,
}
Loading