diff --git a/plugins/ansiblevault/ansible_vault.go b/plugins/ansiblevault/ansible_vault.go new file mode 100644 index 000000000..35c17fc53 --- /dev/null +++ b/plugins/ansiblevault/ansible_vault.go @@ -0,0 +1,25 @@ +package ansiblevault + +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 AnsibleVaultCLI() schema.Executable { + return schema.Executable{ + Name: "Ansible Vault CLI", + Runs: []string{"ansible-vault"}, + DocsURL: sdk.URL("https://docs.ansible.com/ansible/latest/cli/ansible-vault.html"), + NeedsAuth: needsauth.IfAll( + needsauth.NotForHelpOrVersion(), + needsauth.NotWithoutArgs(), + ), + Uses: []schema.CredentialUsage{ + { + Name: credname.Credential, + }, + }, + } +} diff --git a/plugins/ansiblevault/password.go b/plugins/ansiblevault/password.go new file mode 100644 index 000000000..c8d1f92f9 --- /dev/null +++ b/plugins/ansiblevault/password.go @@ -0,0 +1,63 @@ +package ansiblevault + +import ( + "context" + "fmt" + "os" + "strings" + + "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.Credential, + DocsURL: sdk.URL("https://docs.ansible.com/ansible/latest/cli/ansible-vault.html"), + ManagementURL: nil, + Fields: []schema.CredentialField{ + { + Name: fieldname.Password, + MarkdownDescription: "Password to use when encrypting or decrypting.", + Secret: true, + }, + }, + DefaultProvisioner: provision.TempFile( + passwordFile, + provision.Filename(".ansible-vault"), + provision.SetPathAsEnvVar("ANSIBLE_VAULT_PASSWORD_FILE"), + ), + Importer: importer.TryAll( + TryPasswordFile(), + )} +} + +func passwordFile(in sdk.ProvisionInput) ([]byte, error) { + if password, ok := in.ItemFields[fieldname.Password]; ok { + return []byte(password + "\n"), nil + } + return nil, fmt.Errorf("unable to find password field") +} + +func TryPasswordFile() sdk.Importer { + file := os.Getenv("ANSIBLE_VAULT_PASSWORD_FILE") + if file == "" { + file = "~/.ansible-vault" + } + return importer.TryFile(file, func(ctx context.Context, contents importer.FileContents, in sdk.ImportInput, out *sdk.ImportAttempt) { + password := strings.TrimSuffix(contents.ToString(), "\n") + if password == "" { + return + } + + out.AddCandidate(sdk.ImportCandidate{ + Fields: map[sdk.FieldName]string{ + fieldname.Password: password, + }, + }) + }) +} diff --git a/plugins/ansiblevault/password_test.go b/plugins/ansiblevault/password_test.go new file mode 100644 index 000000000..1433ae6a3 --- /dev/null +++ b/plugins/ansiblevault/password_test.go @@ -0,0 +1,46 @@ +package ansiblevault + +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: "Sl9WhfECfUn43o2qEcN2GnAw2EXAMPLE", + }, + ExpectedOutput: sdk.ProvisionOutput{ + Files: map[string]sdk.OutputFile{ + "/tmp/.ansible-vault": { + Contents: []byte(plugintest.LoadFixture(t, ".ansible-vault")), + }, + }, + Environment: map[string]string{ + "ANSIBLE_VAULT_PASSWORD_FILE": "/tmp/.ansible-vault", + }, + }, + }, + }) +} + +func TestPasswordImporter(t *testing.T) { + plugintest.TestImporter(t, Password().Importer, map[string]plugintest.ImportCase{ + "default": { + Files: map[string]string{ + "~/.ansible-vault": plugintest.LoadFixture(t, ".ansible-vault"), + }, + ExpectedCandidates: []sdk.ImportCandidate{ + { + Fields: map[sdk.FieldName]string{ + fieldname.Password: "Sl9WhfECfUn43o2qEcN2GnAw2EXAMPLE", + }, + }, + }, + }, + }) +} diff --git a/plugins/ansiblevault/plugin.go b/plugins/ansiblevault/plugin.go new file mode 100644 index 000000000..21b9cb49b --- /dev/null +++ b/plugins/ansiblevault/plugin.go @@ -0,0 +1,22 @@ +package ansiblevault + +import ( + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/schema" +) + +func New() schema.Plugin { + return schema.Plugin{ + Name: "ansiblevault", + Platform: schema.PlatformInfo{ + Name: "Ansible Vault", + Homepage: sdk.URL("https://docs.ansible.com/ansible/latest/cli/ansible-vault.html"), + }, + Credentials: []schema.CredentialType{ + Password(), + }, + Executables: []schema.Executable{ + AnsibleVaultCLI(), + }, + } +} diff --git a/plugins/ansiblevault/test-fixtures/.ansible-vault b/plugins/ansiblevault/test-fixtures/.ansible-vault new file mode 100644 index 000000000..93d8f6f3b --- /dev/null +++ b/plugins/ansiblevault/test-fixtures/.ansible-vault @@ -0,0 +1 @@ +Sl9WhfECfUn43o2qEcN2GnAw2EXAMPLE