Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for document data source #161

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 13 additions & 1 deletion docs/data-sources/item.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ data "onepassword_item" "example" {

### Read-Only

- `category` (String) The category of the item. One of ["login" "password" "database"]
- `category` (String) The category of the item. One of ["login" "password" "database" "document"]
- `database` (String) (Only applies to the database category) The name of the database.
- `file` (List of Object) A list of files in the section. (see [below for nested schema](#nestedatt--file))
- `hostname` (String) (Only applies to the database category) The address where the database can be found
- `id` (String) The Terraform resource identifier for this item in the format `vaults/<vault_id>/items/<item_id>`
- `password` (String, Sensitive) Password for this item.
Expand All @@ -46,6 +47,17 @@ data "onepassword_item" "example" {
- `url` (String) The primary URL for the item.
- `username` (String) Username for this item.

<a id="nestedatt--file"></a>
### Nested Schema for `file`

Read-Only:

- `content` (String)
- `content_base64` (String)
- `id` (String)
- `name` (String)


<a id="nestedatt--section"></a>
### Nested Schema for `section`

Expand Down
2 changes: 1 addition & 1 deletion docs/resources/item.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ resource "onepassword_item" "demo_db" {

### Optional

- `category` (String) The category of the item. One of ["login" "password" "database"]
- `category` (String) The category of the item. One of ["login" "password" "database" "document"]
- `database` (String) (Only applies to the database category) The name of the database.
- `hostname` (String) (Only applies to the database category) The address where the database can be found
- `password` (String, Sensitive) Password for this item.
Expand Down
13 changes: 13 additions & 0 deletions onepassword/cli/op.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,19 @@ func (op *OP) GetItemByTitle(ctx context.Context, title string, vaultUuid string
return op.GetItem(ctx, title, vaultUuid)
}

func (op *OP) GetFileContent(ctx context.Context, file *onepassword.File, itemUuid, vaultUuid string) ([]byte, error) {
versionErr := op.checkCliVersion(ctx)
if versionErr != nil {
return nil, versionErr
}
ref := fmt.Sprintf("op://%s/%s/%s", vaultUuid, itemUuid, file.ID)
res, err := op.execRaw(ctx, nil, p("read"), p(ref))
if err != nil {
return nil, err
}
return res, nil
}

func (op *OP) CreateItem(ctx context.Context, item *onepassword.Item, vaultUuid string) (*onepassword.Item, error) {
versionErr := op.checkCliVersion(ctx)
if versionErr != nil {
Expand Down
4 changes: 4 additions & 0 deletions onepassword/connectctx/wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ func (w *Wrapper) DeleteItem(_ context.Context, item *onepassword.Item, vaultUui
return w.client.DeleteItem(item, vaultUuid)
}

func (w *Wrapper) GetFileContent(_ context.Context, file *onepassword.File, itemUUID, vaultUUID string) ([]byte, error) {
return w.client.GetFileContent(file)
}

func Wrap(client connect.Client) *Wrapper {
return &Wrapper{client: client}
}
66 changes: 66 additions & 0 deletions onepassword/data_source_onepassword_item.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package onepassword

import (
"context"
"encoding/base64"
"errors"
"fmt"
"strings"
Expand All @@ -11,6 +12,22 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

type FileSchema struct {
Name string
Id string
Content string
ContentBase64 string
}

func (f FileSchema) toResourceData() map[string]string {
return map[string]string {
"name": f.Name,
"id": f.Id,
"content": f.Content,
"content_base64": f.ContentBase64,
}
}

func dataSourceOnepasswordItem() *schema.Resource {
exactlyOneOfUUIDAndTitle := []string{"uuid", "title"}

Expand Down Expand Up @@ -154,6 +171,39 @@ func dataSourceOnepasswordItem() *schema.Resource {
},
},
},
"file": {
Description: filesListDescription,
Type: schema.TypeList,
Computed: true,
MinItems: 0,
Elem: &schema.Resource{
Description: fileDescription,
Schema: map[string]*schema.Schema{
"id": {
Description: fileIDDescription,
Type: schema.TypeString,
Computed: true,
},
"name": {
Description: fileNameDescription,
Type: schema.TypeString,
Computed: true,
},
"content": {
Description: fileContentDescription,
Type: schema.TypeString,
Computed: true,
Sensitive: true,
},
"content_base64": {
Description: fileContentBase64Description,
Type: schema.TypeString,
Computed: true,
Sensitive: true,
},
},
},
},
},
}
}
Expand All @@ -177,6 +227,22 @@ func dataSourceOnepasswordItemRead(ctx context.Context, data *schema.ResourceDat
}
}

dataFiles := []map[string]string{}
for _, f := range item.Files {
file := FileSchema{}
file.Name = f.Name
file.Id = f.ID
data, err := client.GetFileContent(ctx, f, item.ID, item.Vault.ID)
if err != nil {
return diag.FromErr(err)
}
file.Content = string(data)
file.ContentBase64 = base64.StdEncoding.EncodeToString(data)

dataFiles = append(dataFiles, file.toResourceData())
}
data.Set("file", dataFiles)

data.Set("tags", item.Tags)
data.Set("category", strings.ToLower(string(item.Category)))

Expand Down
44 changes: 44 additions & 0 deletions onepassword/data_source_onepassword_item_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,34 @@ func TestDataSourceOnePasswordItemRead(t *testing.T) {
compareItemToSource(t, dataSourceData, expectedItem)
}

func TestDataSourceOnePasswordItemDocumentRead(t *testing.T) {
expectedItem := generateItem()
expectedItem.Category = "DOCUMENT"
expectedItem.Files = []*onepassword.File{
{
Name: "test_file",
},
}
expectedItem.Files[0].SetContent([]byte("test_content"))
meta := &testClient{
GetItemFunc: func(uuid string, vaultUUID string) (*onepassword.Item, error) {
return expectedItem, nil
},
GetFileFunc: func(file *onepassword.File, itemUUID, vaultUUID string) ([]byte, error) {
return []byte("test_content"), nil
},
}

dataSourceData := generateDataSource(t, expectedItem)
dataSourceData.Set("uuid", expectedItem.ID)

err := dataSourceOnepasswordItemRead(context.Background(), dataSourceData, meta)
if err != nil {
t.Errorf("Unexpected error occured")
}
compareItemToSource(t, dataSourceData, expectedItem)
}

func TestDataSourceOnePasswordItemReadByTitle(t *testing.T) {
expectedItem := generateItem()
meta := &testClient{
Expand Down Expand Up @@ -126,6 +154,22 @@ func compareItemToSource(t *testing.T, dataSourceData *schema.ResourceData, item
t.Errorf("Expected field %v to be %v got %v", f.Label, f.Value, dataSourceData.Get(path))
}
}
if files := dataSourceData.Get("file"); files != nil && len(item.Files) != len(files.([]interface{})) {
got := len(files.([]interface{}))
t.Errorf("Expected %v files got %v", len(item.Files), got)
}
for i, file := range item.Files {
if dataSourceData.Get(fmt.Sprintf("file.%s", file.Name)) == nil {
t.Errorf("Expected file %v to be present", file.Name)
}
want, err := file.Content()
if err != nil {
t.Errorf("Unexpected error occured")
}
if dataSourceData.Get(fmt.Sprintf("file.%d.content", i)).(string) != string(want) {
t.Errorf("Expected file %v to have content %v, got %v", file.Name, string(want), dataSourceData.Get(fmt.Sprintf("file.%d.content", i)))
}
}
}

func generateDataSource(t *testing.T, item *onepassword.Item) *schema.ResourceData {
Expand Down
5 changes: 5 additions & 0 deletions onepassword/mock_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type testClient struct {
CreateItemFunc func(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error)
UpdateItemFunc func(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error)
DeleteItemFunc func(item *onepassword.Item, vaultUUID string) error
GetFileFunc func(file *onepassword.File, itemUUID, vaultUUID string) ([]byte, error)
}

var _ Client = (*testClient)(nil)
Expand Down Expand Up @@ -45,3 +46,7 @@ func (m *testClient) DeleteItem(_ context.Context, item *onepassword.Item, vault
func (m *testClient) UpdateItem(_ context.Context, item *onepassword.Item, vaultUUID string) (*onepassword.Item, error) {
return m.UpdateItemFunc(item, vaultUUID)
}

func (m *testClient) GetFileContent(_ context.Context, file *onepassword.File, itemUUID, vaultUUID string) ([]byte, error) {
return m.GetFileFunc(file, itemUUID, vaultUUID)
}
1 change: 1 addition & 0 deletions onepassword/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,5 @@ type Client interface {
CreateItem(ctx context.Context, item *onepassword.Item, vaultUuid string) (*onepassword.Item, error)
UpdateItem(ctx context.Context, item *onepassword.Item, vaultUuid string) (*onepassword.Item, error)
DeleteItem(ctx context.Context, item *onepassword.Item, vaultUuid string) error
GetFileContent(ctx context.Context, file *onepassword.File, itemUUid, vaultUuid string) ([]byte, error)
}
30 changes: 23 additions & 7 deletions onepassword/resource_onepassword_item.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,21 @@ const (
sectionLabelDescription = "The label for the section."
sectionFieldsDescription = "A list of custom fields in the section."

filesListDescription = "A list of files in an item."

fieldDescription = "A custom field."
fieldIDDescription = "A unique identifier for the field."
fieldLabelDescription = "The label for the field."
fieldPurposeDescription = "Purpose indicates this is a special field: a username, password, or notes field."
fieldTypeDescription = "The type of value stored in the field."
fieldValueDescription = "The value of the field."

fileDescription = "A file attached to the item."
fileIDDescription = "A UUID for the file."
fileNameDescription = "The name of the file."
fileContentDescription = "The content of the file."
fileContentBase64Description = "The content of the file in base64 encoding. (Use this for binary files.)"

passwordRecipeDescription = "The recipe used to generate a new value for a password."
passwordElementDescription = "The kinds of characters to include in the password."
passwordLengthDescription = "The length of the password to be generated."
Expand All @@ -55,7 +63,7 @@ const (
enumDescription = "%s One of %q"
)

var categories = []string{"login", "password", "database"}
var categories = []string{"login", "password", "database", "document"}
var dbTypes = []string{"db2", "filemaker", "msaccess", "mssql", "mysql", "oracle", "postgresql", "sqlite", "other"}
var fieldPurposes = []string{"USERNAME", "PASSWORD", "NOTES"}
var fieldTypes = []string{"STRING", "EMAIL", "CONCEALED", "URL", "OTP", "DATE", "MONTH_YEAR", "MENU"}
Expand Down Expand Up @@ -133,12 +141,20 @@ func resourceOnepasswordItem() *schema.Resource {
ForceNew: true,
},
"category": {
Description: fmt.Sprintf(enumDescription, categoryDescription, categories),
Type: schema.TypeString,
Optional: true,
Default: "login",
ValidateFunc: validation.StringInSlice(categories, true),
ForceNew: true,
Description: fmt.Sprintf(enumDescription, categoryDescription, categories),
Type: schema.TypeString,
Optional: true,
Default: "login",
ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) {
v := val.(string)
f := validation.StringInSlice(categories, true)
warns, errs = f(v, key)
if v == "document" {
errs = append(errs, fmt.Errorf("cannot create document category, connect api do not support it"))
}
return warns, errs
},
ForceNew: true,
},
"title": {
Description: itemTitleDescription,
Expand Down