From cd29af8c779b196b049d9c637e858fcbf0f544d7 Mon Sep 17 00:00:00 2001 From: Kevin Franklin Kim Date: Mon, 2 May 2022 15:59:43 +0200 Subject: [PATCH 1/4] feat: switch to ConfigureContextFunc --- onepassword/provider.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/onepassword/provider.go b/onepassword/provider.go index 1c152905..5b6b0084 100644 --- a/onepassword/provider.go +++ b/onepassword/provider.go @@ -1,11 +1,14 @@ package onepassword import ( + "context" "fmt" "github.com/1Password/connect-sdk-go/connect" - "github.com/1Password/terraform-provider-onepassword/version" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/1Password/terraform-provider-onepassword/version" ) const ( @@ -54,7 +57,8 @@ func Provider() *schema.Provider { "onepassword_item": resourceOnepasswordItem(), }, } - provider.ConfigureFunc = func(d *schema.ResourceData) (interface{}, error) { + provider.ConfigureContextFunc = func(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { + var diags diag.Diagnostics url := d.Get("url").(string) token := d.Get("token").(string) @@ -64,7 +68,12 @@ func Provider() *schema.Provider { // is defined in the code. This confusing user-experience can be avoided by handling the // requirement of one of the attributes manually. if url == "" { - return nil, fmt.Errorf("URL for Connect API is not set. Either provide the \"url\" field in the provider configuration or set the OP_CONNECT_HOST environment variable") + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "URL for Connect API is not set", + Detail: "Either provide the \"url\" field in the provider configuration or set the OP_CONNECT_HOST environment variable", + }) + return nil, diags } return connect.NewClientWithUserAgent(url, token, providerUserAgent), nil From 1ab1ef5b86bd008de6f79ba9fdb56faeffb51eb9 Mon Sep 17 00:00:00 2001 From: Kevin Franklin Kim Date: Mon, 2 May 2022 16:02:52 +0200 Subject: [PATCH 2/4] feat: add token check --- onepassword/provider.go | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/onepassword/provider.go b/onepassword/provider.go index 5b6b0084..dbe3c7a4 100644 --- a/onepassword/provider.go +++ b/onepassword/provider.go @@ -60,13 +60,6 @@ func Provider() *schema.Provider { provider.ConfigureContextFunc = func(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { var diags diag.Diagnostics url := d.Get("url").(string) - token := d.Get("token").(string) - - // This is not handled by setting Required to true because Terraform does not handle - // multiple required attributes well. If only one is set in the provider configuration, - // the other one is prompted for, but Terraform then forgets the value for the one that - // is defined in the code. This confusing user-experience can be avoided by handling the - // requirement of one of the attributes manually. if url == "" { diags = append(diags, diag.Diagnostic{ Severity: diag.Error, @@ -76,6 +69,20 @@ func Provider() *schema.Provider { return nil, diags } + token := d.Get("token").(string) + if token == "" { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "TOKEN for Connect API is not set", + Detail: "Either provide the \"token\" field in the provider configuration or set the OP_CONNECT_TOKEN environment variable", + }) + return nil, diags + } + + if len(diags) > 0 { + return nil, diags + } + return connect.NewClientWithUserAgent(url, token, providerUserAgent), nil } return provider From 935043bf2b64f24bed44427c9056ed28aa736505 Mon Sep 17 00:00:00 2001 From: Kevin Franklin Kim Date: Thu, 5 May 2022 10:41:42 +0200 Subject: [PATCH 3/4] feat: add op cli support --- onepassword/client.go | 196 ++++++++++++++++++++++++++++++++++++++++ onepassword/provider.go | 69 +++++++++----- 2 files changed, 244 insertions(+), 21 deletions(-) create mode 100644 onepassword/client.go diff --git a/onepassword/client.go b/onepassword/client.go new file mode 100644 index 00000000..29234c92 --- /dev/null +++ b/onepassword/client.go @@ -0,0 +1,196 @@ +package onepassword + +import ( + "encoding/json" + "os/exec" + "time" + + "github.com/1Password/connect-sdk-go/onepassword" +) + +type cliClient struct{} + +type ( + // Item represents an item returned to the consumer + Item struct { + ID string `json:"id"` + Title string `json:"title"` + + URLs []onepassword.ItemURL `json:"urls,omitempty"` + Favorite bool `json:"favorite,omitempty"` + Tags []string `json:"tags,omitempty"` + Version int `json:"version,omitempty"` + Trashed bool `json:"trashed,omitempty"` + + Vault onepassword.ItemVault `json:"vault"` + Category onepassword.ItemCategory `json:"category,omitempty"` + + Sections []*onepassword.ItemSection `json:"sections,omitempty"` + Fields []*ItemField `json:"fields,omitempty"` + Files []*onepassword.File `json:"files,omitempty"` + + LastEditedBy string `json:"last_edited_by,omitempty"` + CreatedAt time.Time `json:"created_at,omitempty"` + UpdatedAt time.Time `json:"updated_at,omitempty"` + } + ItemField struct { + ID string `json:"id"` + Section *onepassword.ItemSection `json:"section,omitempty"` + Type string `json:"type"` + Purpose string `json:"purpose,omitempty"` + Label string `json:"label,omitempty"` + Value string `json:"value,omitempty"` + Generate bool `json:"generate,omitempty"` + Recipe *GeneratorRecipe `json:"recipe,omitempty"` + Entropy float64 `json:"entropy,omitempty"` + } + GeneratorRecipe struct { + Length int `json:"length,omitempty"` + CharacterSets []string `json:"character_sets,omitempty"` + } +) + +func NewClient() *cliClient { + return &cliClient{} +} + +func NewItemFields(itemFields []*ItemField) []*onepassword.ItemField { + if itemFields == nil { + return nil + } + ret := make([]*onepassword.ItemField, 0, len(itemFields)) + for _, itemField := range itemFields { + ret = append(ret, NewItemField(itemField)) + } + return ret +} + +func NewItemField(itemField *ItemField) *onepassword.ItemField { + if itemField == nil { + return nil + } + return &onepassword.ItemField{ + ID: itemField.ID, + Section: itemField.Section, + Type: itemField.Type, + Purpose: itemField.Purpose, + Label: itemField.Label, + Value: itemField.Value, + Generate: itemField.Generate, + Recipe: NewGeneratorRecipe(itemField.Recipe), + Entropy: itemField.Entropy, + } +} + +func NewGeneratorRecipe(generatorRecipe *GeneratorRecipe) *onepassword.GeneratorRecipe { + if generatorRecipe == nil { + return nil + } + return &onepassword.GeneratorRecipe{ + Length: generatorRecipe.Length, + CharacterSets: generatorRecipe.CharacterSets, + } +} + +func NewItem(item *Item) *onepassword.Item { + if item == nil { + return nil + } + return &onepassword.Item{ + ID: item.ID, + Title: item.Title, + URLs: item.URLs, + Favorite: item.Favorite, + Tags: item.Tags, + Version: item.Version, + Trashed: item.Trashed, + Vault: item.Vault, + Category: item.Category, + Sections: item.Sections, + Fields: NewItemFields(item.Fields), + Files: item.Files, + LastEditedBy: item.LastEditedBy, + CreatedAt: item.CreatedAt, + UpdatedAt: item.UpdatedAt, + } +} + +func (c *cliClient) GetVaults() ([]onepassword.Vault, error) { + //TODO implement me + panic("implement me") +} + +func (c *cliClient) GetVault(uuid string) (*onepassword.Vault, error) { + //TODO implement me + panic("implement me") +} + +func (c *cliClient) GetVaultsByTitle(uuid string) ([]onepassword.Vault, error) { + //TODO implement me + panic("implement me") +} + +func (c *cliClient) GetItem(uuid string, vaultUUID string) (*onepassword.Item, error) { + var temp *Item + if out, err := exec.Command( + "op", "item", "get", uuid, + "--vault", vaultUUID, + "--format", "json", + ).Output(); err != nil { + return nil, err + } else if err := json.Unmarshal(out, &temp); err != nil { + return nil, err + } else { + return NewItem(temp), nil + } +} + +func (c *cliClient) GetItems(vaultUUID string) ([]onepassword.Item, error) { + //TODO implement me + panic("implement me") +} + +func (c *cliClient) GetItemsByTitle(title string, vaultUUID string) ([]onepassword.Item, error) { + //TODO implement me + panic("implement me") +} + +func (c *cliClient) GetItemByTitle(title string, vaultUUID string) (*onepassword.Item, error) { + var temp *Item + if out, err := exec.Command( + "op", "item", "get", "\""+title+"\"", + "--vault", vaultUUID, + "--format", "json", + ).Output(); err != nil { + return nil, err + } else if err := json.Unmarshal(out, &temp); err != nil { + return nil, err + } else { + return NewItem(temp), nil + } +} + +func (c *cliClient) CreateItem(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error) { + //TODO implement me + panic("implement me") +} + +func (c *cliClient) UpdateItem(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error) { + //TODO implement me + panic("implement me") +} + +func (c *cliClient) DeleteItem(item *onepassword.Item, vaultUUID string) error { + //TODO implement me + panic("implement me") +} + +func (c *cliClient) GetFile(fileUUID string, itemUUID string, vaultUUID string) (*onepassword.File, error) { + //TODO implement me + panic("implement me") +} + +func (c *cliClient) GetFileContent(file *onepassword.File) ([]byte, error) { + //TODO implement me + panic("implement me") +} diff --git a/onepassword/provider.go b/onepassword/provider.go index dbe3c7a4..bf0411fa 100644 --- a/onepassword/provider.go +++ b/onepassword/provider.go @@ -3,6 +3,8 @@ package onepassword import ( "context" "fmt" + "os" + "os/exec" "github.com/1Password/connect-sdk-go/connect" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" @@ -36,6 +38,12 @@ func Provider() *schema.Provider { providerUserAgent := fmt.Sprintf(terraformProviderUserAgent, version.ProviderVersion) provider := &schema.Provider{ Schema: map[string]*schema.Schema{ + "account": { + Type: schema.TypeString, + Optional: true, + DefaultFunc: schema.EnvDefaultFunc("OP_ACCOUNT", nil), + Description: "The account to execute the command by account shorthand, sign-in address, account UUID, or user UUID.", + }, "url": { Type: schema.TypeString, Optional: true, @@ -58,32 +66,51 @@ func Provider() *schema.Provider { }, } provider.ConfigureContextFunc = func(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { - var diags diag.Diagnostics + var op bool url := d.Get("url").(string) - if url == "" { - diags = append(diags, diag.Diagnostic{ - Severity: diag.Error, - Summary: "URL for Connect API is not set", - Detail: "Either provide the \"url\" field in the provider configuration or set the OP_CONNECT_HOST environment variable", - }) - return nil, diags - } - token := d.Get("token").(string) - if token == "" { - diags = append(diags, diag.Diagnostic{ - Severity: diag.Error, - Summary: "TOKEN for Connect API is not set", - Detail: "Either provide the \"token\" field in the provider configuration or set the OP_CONNECT_TOKEN environment variable", - }) - return nil, diags + account := d.Get("account").(string) + if _, err := exec.LookPath("op"); err == nil { + op = true } - if len(diags) > 0 { - return nil, diags + if url != "" || token != "" { + if url == "" { + return nil, diag.Diagnostics{{ + Severity: diag.Error, + Summary: "URL for Connect API is not set", + Detail: "Either provide the \"url\" field in the provider configuration or set the OP_CONNECT_HOST environment variable", + }} + } + if token == "" { + return nil, diag.Diagnostics{{ + Severity: diag.Error, + Summary: "TOKEN for Connect API is not set", + Detail: "Either provide the \"token\" field in the provider configuration or set the OP_CONNECT_TOKEN environment variable", + }} + } + return connect.NewClientWithUserAgent(url, token, providerUserAgent), nil + } else if account == "" { + return nil, diag.Diagnostics{{ + Severity: diag.Error, + Summary: "ACCOUNT is not set", + Detail: "Either provide the \"account\" field in the provider configuration or set the OP_ACCOUNT environment variable", + }} + } else if !op { + return nil, diag.Diagnostics{{ + Severity: diag.Error, + Summary: "op executable not found", + Detail: "Please ensure you have the 1password-cli >= 2.0.0 installed in your $PATH.", + }} + } else if session := os.Getenv("OP_SESSION_" + account); session == "" { + return nil, diag.Diagnostics{{ + Severity: diag.Error, + Summary: "SESSION is not set", + Detail: "Provide the OP_SESSION_" + account + " environment variable.", + }} + } else { + return NewClient(), nil } - - return connect.NewClientWithUserAgent(url, token, providerUserAgent), nil } return provider } From 4d7a048a9500cbd44d2dc5781413da643f86fa84 Mon Sep 17 00:00:00 2001 From: Stefan Martinov Date: Wed, 25 May 2022 11:35:20 +0200 Subject: [PATCH 4/4] feat: implement 1Password CLI client for read-only operations --- Makefile | 19 +++++++ docs/index.md | 25 +++++++-- examples/cli/main.tf | 30 +++++++++++ onepassword/provider.go | 29 +++++++--- opcli/cli.go | 83 +++++++++++++++++++++++++++++ {onepassword => opcli}/client.go | 90 ++++++++++++++++---------------- 6 files changed, 221 insertions(+), 55 deletions(-) create mode 100644 examples/cli/main.tf create mode 100644 opcli/cli.go rename {onepassword => opcli}/client.go (66%) diff --git a/Makefile b/Makefile index 72dbd36b..03590268 100644 --- a/Makefile +++ b/Makefile @@ -54,3 +54,22 @@ endif ifneq ($(WORKTREE_CLEAN), 0) @echo "[ERROR] Uncommitted changes found in worktree. Address them and try again."; exit 1; endif + +## Local Installation +# Example Provider For Local Testing +# terraform { +# required_providers { +# onepassword = { +# source = "terraform.example.com/local/onepassword" +# version = "~> 1.0.2" +# } +# } +#} +local: VERSION = "1.0.2" +local: PLUGIN_PATH = ~/.terraform.d/plugins/terraform.example.com/local/onepassword/${VERSION}/darwin_amd64 +local: build + mkdir -p $(PLUGIN_PATH) + cp ./dist/terraform-provider-onepassword $(PLUGIN_PATH) + rm ./examples/cli/.terraform.lock.hcl + rm -r ./examples/cli/.terraform + cd ./examples/cli/ && terraform init \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index cdad5fee..fcb088c0 100644 --- a/docs/index.md +++ b/docs/index.md @@ -11,18 +11,37 @@ Use the 1Password Connect Terraform Provider to reference, create, or update ite ## Example Usage +Connecting to 1Password Connect + ```terraform provider "onepassword" { url = "http://localhost:8080" } ``` +Connecting to 1Password local CLI + +```terraform +provider "onepassword" { + account = "username" + password = "password" +} +``` + ## Schema -### Required +### Optional + +- **token** (String, Optional) A valid token for your 1Password Connect API. Can also be sourced from OP_CONNECT_TOKEN. + +### Optional + +- **url** (String, Optional) The HTTP(S) URL where your 1Password Connect API can be found. Must be provided through the OP_CONNECT_HOST environment variable if this attribute is not set. + +### Optional -- **token** (String, Required) A valid token for your 1Password Connect API. Can also be sourced from OP_CONNECT_TOKEN. +- **account** (String, Optional) Account to use for the 1Password CLI. Can also be sourced from OP_ACCOUNT ### Optional -- **url** (String, Optional) The HTTP(S) URL where your 1Password Connect API can be found. Must be provided through the the OP_CONNECT_HOST environment variable if this attribute is not set. +- **password** (String, Optional) Password to use for the 1Password CLI. Can also be sourced from OP_PASSWORD \ No newline at end of file diff --git a/examples/cli/main.tf b/examples/cli/main.tf new file mode 100644 index 00000000..d1eabc75 --- /dev/null +++ b/examples/cli/main.tf @@ -0,0 +1,30 @@ +variable "op_account" { + type = string +} +variable "op_password" { + type = string +} + +terraform { + required_providers { + onepassword = { + source = "terraform.example.com/local/onepassword" + version = "~> 1.0.2" + } + } +} + + +provider "onepassword" { + account = var.op_account + password = var.op_password +} + +data "onepassword_item" "item" { + vault = "Private" + title = "Example" +} + +output "password" { + value = data.onepassword_item.item.section +} diff --git a/onepassword/provider.go b/onepassword/provider.go index bf0411fa..fa780812 100644 --- a/onepassword/provider.go +++ b/onepassword/provider.go @@ -3,10 +3,10 @@ package onepassword import ( "context" "fmt" - "os" "os/exec" "github.com/1Password/connect-sdk-go/connect" + "github.com/1Password/terraform-provider-onepassword/opcli" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -44,6 +44,12 @@ func Provider() *schema.Provider { DefaultFunc: schema.EnvDefaultFunc("OP_ACCOUNT", nil), Description: "The account to execute the command by account shorthand, sign-in address, account UUID, or user UUID.", }, + "password": { + Type: schema.TypeString, + Optional: true, + DefaultFunc: schema.EnvDefaultFunc("OP_PASSWORD", nil), + Description: "The password to interact with the CLI", + }, "url": { Type: schema.TypeString, Optional: true, @@ -52,7 +58,7 @@ func Provider() *schema.Provider { }, "token": { Type: schema.TypeString, - Required: true, + Optional: true, DefaultFunc: schema.EnvDefaultFunc("OP_CONNECT_TOKEN", nil), Description: "A valid token for your 1Password Connect API. Can also be sourced from OP_CONNECT_TOKEN.", }, @@ -70,6 +76,8 @@ func Provider() *schema.Provider { url := d.Get("url").(string) token := d.Get("token").(string) account := d.Get("account").(string) + password := d.Get("password").(string) + if _, err := exec.LookPath("op"); err == nil { op = true } @@ -102,14 +110,23 @@ func Provider() *schema.Provider { Summary: "op executable not found", Detail: "Please ensure you have the 1password-cli >= 2.0.0 installed in your $PATH.", }} - } else if session := os.Getenv("OP_SESSION_" + account); session == "" { + } else if password == "" { return nil, diag.Diagnostics{{ Severity: diag.Error, - Summary: "SESSION is not set", - Detail: "Provide the OP_SESSION_" + account + " environment variable.", + Summary: "Password is not set", + Detail: "Provide the OP_PASSWORD environment variable.", }} } else { - return NewClient(), nil + provider, err := opcli.NewCLIClient(account, password) + if err != nil { + return nil, diag.Diagnostics{{ + Severity: diag.Error, + Summary: "Could not initialize CLI provider", + Detail: err.Error(), + }} + } + + return provider, nil } } return provider diff --git a/opcli/cli.go b/opcli/cli.go new file mode 100644 index 00000000..4f28d707 --- /dev/null +++ b/opcli/cli.go @@ -0,0 +1,83 @@ +package opcli + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "os" + "os/exec" + + "github.com/1Password/connect-sdk-go/onepassword" +) + +type OnePasswordCLI struct { + account string + token string +} + +func (cli OnePasswordCLI) GetItem(item, vault string) (*onepassword.Item, error) { + output, err := cli.command( + defaultOnePasswordPath, + "item", "get", item, + "--vault", vault, + "--format", "json", + ) + if err != nil { + return nil, err + } + + var value *onepassword.Item + err = json.Unmarshal(output, &value) + if err != nil { + return nil, fmt.Errorf("failed to unmarshlal: %v data: %v", err, string(output)) + } + return value, nil +} + +func (cli OnePasswordCLI) command(name string, args ...string) (output []byte, err error) { + if cli.token == "" { + return nil, errors.New("OP client not authenticated") + } + args = append(args, + "--session", cli.token, + "--account", cli.account, + "--no-color", + ) + cmd := exec.Command(name, args...) + var stdout, stdin, stderr bytes.Buffer + + cmd.Stdin = &stdin + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + err = cmd.Run() + if err != nil { + stdout.Write(stderr.Bytes()) + return nil, fmt.Errorf(string(stdout.Bytes())) + } + return stdout.Bytes(), nil +} + +func getOnePasswordSessionToken(account, password string) (string, error) { + cmd := exec.Command(defaultOnePasswordPath, + "signin", + "--account", account, + "--raw", + ) + var stdout, stdin, stderr bytes.Buffer + cmd.Env = os.Environ() + + stdin.Write([]byte(password)) + cmd.Stdin = &stdin + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + err := cmd.Run() + if err != nil { + stdout.Write(stderr.Bytes()) + return "", fmt.Errorf("failed to sign in: %v", string(stdout.Bytes())) + } + + return string(stdout.Bytes()), nil +} diff --git a/onepassword/client.go b/opcli/client.go similarity index 66% rename from onepassword/client.go rename to opcli/client.go index 29234c92..3db78534 100644 --- a/onepassword/client.go +++ b/opcli/client.go @@ -1,14 +1,18 @@ -package onepassword +package opcli import ( - "encoding/json" - "os/exec" "time" "github.com/1Password/connect-sdk-go/onepassword" ) -type cliClient struct{} +const ( + defaultOnePasswordPath = "/usr/local/bin/op" +) + +type cliProvider struct { + cli OnePasswordCLI +} type ( // Item represents an item returned to the consumer @@ -50,8 +54,14 @@ type ( } ) -func NewClient() *cliClient { - return &cliClient{} +func NewCLIClient(account, password string) (*cliProvider, error) { + cli, err := NewOnePasswordCLI(account, password) + if err != nil { + return nil, err + } + return &cliProvider{ + cli: cli, + }, nil } func NewItemFields(itemFields []*ItemField) []*onepassword.ItemField { @@ -115,82 +125,70 @@ func NewItem(item *Item) *onepassword.Item { } } -func (c *cliClient) GetVaults() ([]onepassword.Vault, error) { +func (c *cliProvider) GetVaults() ([]onepassword.Vault, error) { //TODO implement me panic("implement me") } -func (c *cliClient) GetVault(uuid string) (*onepassword.Vault, error) { +func (c *cliProvider) GetVault(uuid string) (*onepassword.Vault, error) { //TODO implement me panic("implement me") } -func (c *cliClient) GetVaultsByTitle(uuid string) ([]onepassword.Vault, error) { +func (c *cliProvider) GetVaultsByTitle(uuid string) ([]onepassword.Vault, error) { //TODO implement me panic("implement me") } -func (c *cliClient) GetItem(uuid string, vaultUUID string) (*onepassword.Item, error) { - var temp *Item - if out, err := exec.Command( - "op", "item", "get", uuid, - "--vault", vaultUUID, - "--format", "json", - ).Output(); err != nil { - return nil, err - } else if err := json.Unmarshal(out, &temp); err != nil { - return nil, err - } else { - return NewItem(temp), nil - } +func (c *cliProvider) GetItem(uuid string, vaultUUID string) (*onepassword.Item, error) { + return c.cli.GetItem(uuid, vaultUUID) } -func (c *cliClient) GetItems(vaultUUID string) ([]onepassword.Item, error) { - //TODO implement me - panic("implement me") +func (c *cliProvider) GetItems(vaultUUID string) ([]onepassword.Item, error) { + return []onepassword.Item{}, nil } -func (c *cliClient) GetItemsByTitle(title string, vaultUUID string) ([]onepassword.Item, error) { - //TODO implement me - panic("implement me") +func (c *cliProvider) GetItemsByTitle(title string, vaultUUID string) ([]onepassword.Item, error) { + return []onepassword.Item{}, nil } -func (c *cliClient) GetItemByTitle(title string, vaultUUID string) (*onepassword.Item, error) { - var temp *Item - if out, err := exec.Command( - "op", "item", "get", "\""+title+"\"", - "--vault", vaultUUID, - "--format", "json", - ).Output(); err != nil { - return nil, err - } else if err := json.Unmarshal(out, &temp); err != nil { - return nil, err - } else { - return NewItem(temp), nil - } +func (c *cliProvider) GetItemByTitle(title string, vaultUUID string) (*onepassword.Item, error) { + return c.cli.GetItem(title, vaultUUID) } -func (c *cliClient) CreateItem(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error) { +func (c *cliProvider) CreateItem(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error) { //TODO implement me panic("implement me") } -func (c *cliClient) UpdateItem(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error) { +func (c *cliProvider) UpdateItem(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error) { //TODO implement me panic("implement me") } -func (c *cliClient) DeleteItem(item *onepassword.Item, vaultUUID string) error { +func (c *cliProvider) DeleteItem(item *onepassword.Item, vaultUUID string) error { //TODO implement me panic("implement me") } -func (c *cliClient) GetFile(fileUUID string, itemUUID string, vaultUUID string) (*onepassword.File, error) { +func (c *cliProvider) GetFile(fileUUID string, itemUUID string, vaultUUID string) (*onepassword.File, error) { //TODO implement me panic("implement me") } -func (c *cliClient) GetFileContent(file *onepassword.File) ([]byte, error) { +func (c *cliProvider) GetFileContent(file *onepassword.File) ([]byte, error) { //TODO implement me panic("implement me") } + +func NewOnePasswordCLI(account, password string) (OnePasswordCLI, error) { + token, err := getOnePasswordSessionToken(account, password) + if err != nil { + return OnePasswordCLI{}, err + } + + return OnePasswordCLI{ + account: account, + token: token, + }, nil +}