diff --git a/example/main.go b/example/main.go index 128b8c2..feda7c2 100644 --- a/example/main.go +++ b/example/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "fmt" "os" onepassword "github.com/1password/onepassword-sdk-go" @@ -20,9 +21,83 @@ func main() { panic(err) } + item := createAndGetItem(client) + updateItem(client, item.VaultID, item.ID) + getAndUpdateItem(client, item.VaultID, item.ID) + resolveSecretReference(client, item.VaultID, item.ID, "username") +} + +func getAndUpdateItem(client *onepassword.Client, existingVaultID, existingItemID string) { + // Retrieves the newly created item + item, err := client.Items.Get(context.Background(), existingVaultID, existingItemID) + if err != nil { + panic(err) + } + + // Finds the field named "Details" and edits its value + for i := range item.Fields { + if item.Fields[i].Title == "Details" { + item.Fields[i].Value = "updated details" + } + } + item.Title = "New Title" + + updatedItem, err := client.Items.Update(context.Background(), item) + if err != nil { + panic(err) + } + + for _, f := range updatedItem.Fields { + if f.Title == "Details" { + doSomethingSecret(f.Value) + } + } +} + +func updateItem(client *onepassword.Client, existingVaultID, existingID string) { + sectionID := "extraDetails" + newItem := onepassword.Item{ + ID: existingID, + Title: "My Login SDK 1234", + Category: onepassword.ItemCategoryLogin, + VaultID: existingVaultID, + Fields: []onepassword.ItemField{ + { + ID: "username", + Value: "Wendy_Appleseed1234", + FieldType: onepassword.ItemFieldTypeText, + }, + { + ID: "password", + Value: "my_weak_password1234", + FieldType: onepassword.ItemFieldTypeConcealed, + }, + { + Title: "Details", + ID: "myDetailsID", + Value: "Test Item", + FieldType: onepassword.ItemFieldTypeText, + SectionID: §ionID, + }, + }, + Sections: []onepassword.ItemSection{ + { + ID: sectionID, + Title: "Extra Details", + }, + }, + } + + _, err := client.Items.Update(context.Background(), newItem) + if err != nil { + panic(err) + } +} + +func resolveSecretReference(client *onepassword.Client, vaultID, itemID, fieldID string) { // Retrieves a secret from 1Password. // Takes a secret reference as input and returns the secret to which it points. - secret, err := client.Secrets.Resolve(context.Background(), "op://vault/item/field") + secret, err := client.Secrets.Resolve(context.Background(), fmt.Sprintf("op://%s/%s/%s", vaultID, itemID, fieldID)) if err != nil { panic(err) } @@ -30,6 +105,56 @@ func main() { doSomethingSecret(secret) } +func createAndGetItem(client *onepassword.Client) onepassword.Item { + sectionID := "extraDetails" + item := onepassword.Item{ + Title: "Login created with the SDK", + Category: onepassword.ItemCategoryLogin, + VaultID: "xw33qlvug6moegr3wkk5zkenoa", + Fields: []onepassword.ItemField{ + { + ID: "username", + Title: "username", + Value: "Wendy_Appleseed", + FieldType: onepassword.ItemFieldTypeText, + }, + { + ID: "password", + Title: "password", + Value: "my_weak_password123", + FieldType: onepassword.ItemFieldTypeConcealed, + }, + { + ID: "uniqueId", + Title: "Web address", + Value: "1password.com", + FieldType: onepassword.ItemFieldTypeText, + SectionID: §ionID, + }, + }, + Sections: []onepassword.ItemSection{ + { + ID: sectionID, + Title: "Extra Details", + }, + }, + } + + // Creates a new item based on the structure definition above + createdItem, err := client.Items.Create(context.Background(), item) + if err != nil { + panic(err) + } + + // Retrieves the newly created item + login, err := client.Items.Get(context.Background(), createdItem.VaultID, createdItem.ID) + if err != nil { + panic(err) + } + + return login +} + // Exports the secret to the SECRET_ENV_VAR environment variable. func doSomethingSecret(secret string) { err := os.Setenv("SECRET_ENV_VAR", secret) diff --git a/internal/wasm/core.wasm b/internal/wasm/core.wasm index 7272a48..baa959f 100644 Binary files a/internal/wasm/core.wasm and b/internal/wasm/core.wasm differ diff --git a/items.go b/items.go index 12dbac9..32be794 100644 --- a/items.go +++ b/items.go @@ -8,8 +8,16 @@ import ( "github.com/1password/onepassword-sdk-go/internal" ) +// ItemsAPI contains all operations the SDK client can perform on 1Password items. type ItemsAPI interface { - Get(ctx context.Context, vaultID string, itemID string) (Item, error) + // Create a new item + Create(ctx context.Context, item Item) (Item, error) + // Get an item by vault and item ID + Get(ctx context.Context, vaultId string, itemId string) (Item, error) + // Update an existing item. Warning: Only text and concealed fields are currently supported. Other fields will be permanently lost when you update an item. + Update(ctx context.Context, item Item) (Item, error) + // Delete an item. Warning: Information saved in fields other than text and concealed fields will be permanently lost. + Delete(ctx context.Context, vaultId string, itemId string) error } type ItemsSource struct { @@ -20,11 +28,43 @@ func NewItemsSource(inner internal.InnerClient) *ItemsSource { return &ItemsSource{inner} } -func (s ItemsSource) Get(ctx context.Context, vaultID string, itemID string) (Item, error) { +func (s ItemsSource) Create(ctx context.Context, item Item) (Item, error) { + + resultString, err := clientInvoke(ctx, s.InnerClient, "Create", map[string]interface{}{ + "item": item, + }) + if err != nil { + return Item{}, err + } + var result Item + err = json.Unmarshal([]byte(*resultString), &result) + if err != nil { + return Item{}, err + } + return result, nil +} + +func (s ItemsSource) Get(ctx context.Context, vaultId string, itemId string) (Item, error) { resultString, err := clientInvoke(ctx, s.InnerClient, "Get", map[string]interface{}{ - "vault_id": vaultID, - "item_id": itemID, + "vault_id": vaultId, + "item_id": itemId, + }) + if err != nil { + return Item{}, err + } + var result Item + err = json.Unmarshal([]byte(*resultString), &result) + if err != nil { + return Item{}, err + } + return result, nil +} + +func (s ItemsSource) Update(ctx context.Context, item Item) (Item, error) { + + resultString, err := clientInvoke(ctx, s.InnerClient, "Update", map[string]interface{}{ + "item": item, }) if err != nil { return Item{}, err @@ -36,3 +76,12 @@ func (s ItemsSource) Get(ctx context.Context, vaultID string, itemID string) (It } return result, nil } + +func (s ItemsSource) Delete(ctx context.Context, vaultId string, itemId string) error { + + _, err := clientInvoke(ctx, s.InnerClient, "Delete", map[string]interface{}{ + "vault_id": vaultId, + "item_id": itemId, + }) + return err +} diff --git a/secrets.go b/secrets.go index 75943b9..417d251 100644 --- a/secrets.go +++ b/secrets.go @@ -9,6 +9,9 @@ import ( ) type SecretsAPI interface { + // Resolve returns the secret the provided secret reference points to. + // Secret references point to fields in 1Password. They have the following format: op:///[/]/ + // Read more about secret references: https://developer.1password.com/docs/cli/secret-references Resolve(ctx context.Context, secretReference string) (string, error) } @@ -20,6 +23,9 @@ func NewSecretsSource(inner internal.InnerClient) *SecretsSource { return &SecretsSource{inner} } +// Resolve returns the secret the provided secret reference points to. +// Secret references point to fields in 1Password. They have the following format: op:///[/]/ +// Read more about secret references: https://developer.1password.com/docs/cli/secret-references func (s SecretsSource) Resolve(ctx context.Context, secretReference string) (string, error) { resultString, err := clientInvoke(ctx, s.InnerClient, "Resolve", map[string]interface{}{ diff --git a/types.go b/types.go index 856270e..0e94a73 100644 --- a/types.go +++ b/types.go @@ -38,22 +38,40 @@ const ( ItemFieldTypeUnsupported ItemFieldType = "Unsupported" ) +// Stores a field's title and value. type ItemField struct { - ID string `json:"id"` - Title string `json:"title"` - SectionID *string `json:"section_id,omitempty"` + // The field's ID + ID string `json:"id"` + // The field's title + Title string `json:"title"` + // The ID of the section containing the field. Designated fields such as usernames and passwords don't need to specify a section. + SectionID *string `json:"section_id,omitempty"` + // The type of value stored in the field FieldType ItemFieldType `json:"field_type"` - Value string `json:"value"` + // The string representation of the field's value + Value string `json:"value"` } + +// A section groups together multiple fields in an item. type ItemSection struct { - ID string `json:"id"` + // The section's ID + ID string `json:"id"` + // The section's name Title string `json:"title"` } + +// Represents a 1Password item. type Item struct { - ID string `json:"id"` - Title string `json:"title"` - Category ItemCategory `json:"category"` - VaultID string `json:"vault_id"` - Fields []ItemField `json:"fields"` + // The item's unique ID + ID string `json:"id"` + // The item's title + Title string `json:"title"` + // The item's category + Category ItemCategory `json:"category"` + // The ID of the vault where the item is saved + VaultID string `json:"vault_id"` + // The item's fields + Fields []ItemField `json:"fields"` + // The item's sections Sections []ItemSection `json:"sections"` }