From 88a569c184d77fce0d8c01664929cc3c840ef949 Mon Sep 17 00:00:00 2001 From: Maxime Lagresle Date: Tue, 16 Apr 2024 15:49:08 +0200 Subject: [PATCH 01/11] implement support for filters in data sources --- GNUmakefile | 4 +- docs/data-sources/item_login.md | 10 ++- docs/data-sources/item_secure_note.md | 10 ++- docs/resources/item_login.md | 2 +- docs/resources/item_secure_note.md | 2 +- internal/bitwarden/bw/client.go | 32 +++++++- internal/bitwarden/bw/client_options.go | 34 +++++++++ internal/provider/data_source.go | 24 ++++++ .../provider/data_source_item_login_test.go | 14 ++++ internal/provider/object.go | 43 ++++++++++- internal/provider/schema.go | 27 +++++-- internal/provider/schema_attributes.go | 74 ++++++++++--------- 12 files changed, 222 insertions(+), 54 deletions(-) create mode 100644 internal/bitwarden/bw/client_options.go diff --git a/GNUmakefile b/GNUmakefile index 5bf72aa..a6a2c3c 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -9,8 +9,8 @@ tffmt: terraform fmt -recursive examples docs: - go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs - find docs -type f -exec sed -i '' '/INTERNAL USE/d' {} \; + go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs@v0.19.0 + find docs -type f -name '*.md' -exec sed -i '' '/INTERNAL USE/d' {} \; clean: rm internal/provider/.bitwarden/data.json || true \ No newline at end of file diff --git a/docs/data-sources/item_login.md b/docs/data-sources/item_login.md index 06433b6..e5ea2c1 100644 --- a/docs/data-sources/item_login.md +++ b/docs/data-sources/item_login.md @@ -42,22 +42,24 @@ data "bitwarden_item_login" "database_credentials" { ## Schema -### Required +### Optional +- `collection_ids` (List of String) Identifier of the collections the item belongs to. +- `folder_id` (String) Identifier of the folder. - `id` (String) Identifier. +- `organization_id` (String) Identifier of the organization. +- `search` (String) Occurence to search for. +- `url` (String) URL to filter results by. ### Read-Only - `attachments` (List of Object) List of item attachments. (see [below for nested schema](#nestedatt--attachments)) -- `collection_ids` (List of String) Identifier of the collections the item belongs to. - `creation_date` (String) Date the item was created. - `deleted_date` (String) Date the item was deleted. - `favorite` (Boolean) Mark as a Favorite to have item appear at the top of your Vault in the UI. - `field` (List of Object, Sensitive) Extra fields. (see [below for nested schema](#nestedatt--field)) -- `folder_id` (String) Identifier of the folder. - `name` (String) Name. - `notes` (String, Sensitive) Notes. -- `organization_id` (String) Identifier of the organization. - `password` (String, Sensitive) Login password. - `reprompt` (Boolean) Require master password “re-prompt” when displaying secret in the UI. - `revision_date` (String) Last time the item was updated. diff --git a/docs/data-sources/item_secure_note.md b/docs/data-sources/item_secure_note.md index 16d6ef1..d75474b 100644 --- a/docs/data-sources/item_secure_note.md +++ b/docs/data-sources/item_secure_note.md @@ -41,22 +41,24 @@ data "bitwarden_item_secure_note" "ssh_notes" { ## Schema -### Required +### Optional +- `collection_ids` (List of String) Identifier of the collections the item belongs to. +- `folder_id` (String) Identifier of the folder. - `id` (String) Identifier. +- `organization_id` (String) Identifier of the organization. +- `search` (String) Occurence to search for. +- `url` (String) URL to filter results by. ### Read-Only - `attachments` (List of Object) List of item attachments. (see [below for nested schema](#nestedatt--attachments)) -- `collection_ids` (List of String) Identifier of the collections the item belongs to. - `creation_date` (String) Date the item was created. - `deleted_date` (String) Date the item was deleted. - `favorite` (Boolean) Mark as a Favorite to have item appear at the top of your Vault in the UI. - `field` (List of Object, Sensitive) Extra fields. (see [below for nested schema](#nestedatt--field)) -- `folder_id` (String) Identifier of the folder. - `name` (String) Name. - `notes` (String, Sensitive) Notes. -- `organization_id` (String) Identifier of the organization. - `reprompt` (Boolean) Require master password “re-prompt” when displaying secret in the UI. - `revision_date` (String) Last time the item was updated. diff --git a/docs/resources/item_login.md b/docs/resources/item_login.md index aef1e20..310e24d 100644 --- a/docs/resources/item_login.md +++ b/docs/resources/item_login.md @@ -54,6 +54,7 @@ resource "bitwarden_item_login" "administrative-user" { - `favorite` (Boolean) Mark as a Favorite to have item appear at the top of your Vault in the UI. - `field` (Block List) Extra fields. (see [below for nested schema](#nestedblock--field)) - `folder_id` (String) Identifier of the folder. +- `id` (String) Identifier. - `notes` (String, Sensitive) Notes. - `organization_id` (String) Identifier of the organization. - `password` (String, Sensitive) Login password. @@ -67,7 +68,6 @@ resource "bitwarden_item_login" "administrative-user" { - `attachments` (List of Object) List of item attachments. (see [below for nested schema](#nestedatt--attachments)) - `creation_date` (String) Date the item was created. - `deleted_date` (String) Date the item was deleted. -- `id` (String) Identifier. - `revision_date` (String) Last time the item was updated. diff --git a/docs/resources/item_secure_note.md b/docs/resources/item_secure_note.md index 8baef84..ae9746d 100644 --- a/docs/resources/item_secure_note.md +++ b/docs/resources/item_secure_note.md @@ -55,6 +55,7 @@ EOT - `favorite` (Boolean) Mark as a Favorite to have item appear at the top of your Vault in the UI. - `field` (Block List) Extra fields. (see [below for nested schema](#nestedblock--field)) - `folder_id` (String) Identifier of the folder. +- `id` (String) Identifier. - `notes` (String, Sensitive) Notes. - `organization_id` (String) Identifier of the organization. - `reprompt` (Boolean) Require master password “re-prompt” when displaying secret in the UI. @@ -64,7 +65,6 @@ EOT - `attachments` (List of Object) List of item attachments. (see [below for nested schema](#nestedatt--attachments)) - `creation_date` (String) Date the item was created. - `deleted_date` (String) Date the item was deleted. -- `id` (String) Identifier. - `revision_date` (String) Last time the item was updated. diff --git a/internal/bitwarden/bw/client.go b/internal/bitwarden/bw/client.go index e7f8221..0654872 100644 --- a/internal/bitwarden/bw/client.go +++ b/internal/bitwarden/bw/client.go @@ -13,9 +13,10 @@ type Client interface { CreateObject(Object) (*Object, error) EditObject(Object) (*Object, error) GetAttachment(itemId, attachmentId string) ([]byte, error) - GetObject(objType, itemId string) (*Object, error) + GetObject(objType, itemOrSearch string) (*Object, error) GetSessionKey() string HasSessionKey() bool + ListObjects(objType string, options ...ListObjectsOption) ([]Object, error) LoginWithAPIKey(password, clientId, clientSecret string) error LoginWithPassword(username, password string) error Logout() error @@ -137,8 +138,8 @@ func (c *client) EditObject(obj Object) (*Object, error) { return &obj, nil } -func (c *client) GetObject(objType, itemId string) (*Object, error) { - out, err := c.cmdWithSession("get", objType, itemId).Run() +func (c *client) GetObject(objType, itemOrSearch string) (*Object, error) { + out, err := c.cmdWithSession("get", objType, itemOrSearch).Run() if err != nil { return nil, remapError(err) } @@ -165,6 +166,31 @@ func (c *client) GetSessionKey() string { return c.sessionKey } +// ListObjects returns objects of a given type matching given filters. +func (c *client) ListObjects(objType string, options ...ListObjectsOption) ([]Object, error) { + args := []string{ + "list", + objType, + } + + for _, applyOption := range options { + applyOption(&args) + } + + out, err := c.cmdWithSession(args...).Run() + if err != nil { + return nil, remapError(err) + } + + var obj []Object + err = json.Unmarshal(out, &obj) + if err != nil { + return nil, newUnmarshallError(err, "list object", out) + } + + return obj, nil +} + // LoginWithPassword logs in using a password and retrieves the session key, // allowing authenticated requests using the client. func (c *client) LoginWithPassword(username, password string) error { diff --git a/internal/bitwarden/bw/client_options.go b/internal/bitwarden/bw/client_options.go new file mode 100644 index 0000000..9b12dcc --- /dev/null +++ b/internal/bitwarden/bw/client_options.go @@ -0,0 +1,34 @@ +package bw + +type ListObjectsOption func(args *[]string) +type ListObjectsOptionGenerator func(id string) ListObjectsOption + +func WithCollectionID(id string) ListObjectsOption { + return func(args *[]string) { + *args = append(*args, "--collectionid", id) + } +} + +func WithFolderID(id string) ListObjectsOption { + return func(args *[]string) { + *args = append(*args, "--folderid", id) + } +} + +func WithOrganizationID(id string) ListObjectsOption { + return func(args *[]string) { + *args = append(*args, "--organizationid", id) + } +} + +func WithSearch(search string) ListObjectsOption { + return func(args *[]string) { + *args = append(*args, "--search", search) + } +} + +func WithUrl(url string) ListObjectsOption { + return func(args *[]string) { + *args = append(*args, "--url", url) + } +} diff --git a/internal/provider/data_source.go b/internal/provider/data_source.go index d5ad905..85efe8b 100644 --- a/internal/provider/data_source.go +++ b/internal/provider/data_source.go @@ -22,3 +22,27 @@ func readDataSource(attrObject bw.ObjectType, attrType bw.ItemType) schema.ReadC return objectRead(ctx, d, meta) } } + +func listOptionsFromData(d *schema.ResourceData) []bw.ListObjectsOption { + filters := []bw.ListObjectsOption{} + + filterMap := map[string]bw.ListObjectsOptionGenerator{ + attributeFilterSearch: bw.WithSearch, + attributeFilterCollectionId: bw.WithCollectionID, + attributeFilterFolderID: bw.WithFolderID, + attributeFilterOrganizationID: bw.WithOrganizationID, + attributeFilterURL: bw.WithUrl, + } + + for attribute, optionFunc := range filterMap { + v, ok := d.GetOk(attribute) + if !ok { + continue + } + + if v, ok := v.(string); ok && len(v) > 0 { + filters = append(filters, optionFunc(v)) + } + } + return filters +} diff --git a/internal/provider/data_source_item_login_test.go b/internal/provider/data_source_item_login_test.go index 7eb36c4..3d71cfd 100644 --- a/internal/provider/data_source_item_login_test.go +++ b/internal/provider/data_source_item_login_test.go @@ -52,6 +52,10 @@ func TestAccDataSourceItemLoginDeleted(t *testing.T) { Config: tfConfigProvider() + tfConfigResourceItemLoginSmall(), Check: getObjectID("bitwarden_item_login.foo", &objectID), }, + { + Config: tfConfigProvider() + tfConfigDataItemLoginWithURLFilter("https://start_with/something"), + Check: getObjectID("bitwarden_item_login.foo", &objectID), + }, { Config: tfConfigProvider() + tfConfigResourceItemLoginSmall() + tfConfigDataItemLoginWithId(objectID), PreConfig: func() { @@ -74,6 +78,16 @@ data "bitwarden_item_login" "foo_data" { `, id) } +func tfConfigDataItemLoginWithURLFilter(url string) string { + return fmt.Sprintf(` +data "bitwarden_item_login" "foo_data" { + provider = bitwarden + + url = "%s" +} +`, url) +} + func tfConfigDataItemLogin() string { return ` data "bitwarden_item_login" "foo_data" { diff --git a/internal/provider/object.go b/internal/provider/object.go index 86d5c39..70ad816 100644 --- a/internal/provider/object.go +++ b/internal/provider/object.go @@ -3,6 +3,7 @@ package provider import ( "context" "errors" + "fmt" "log" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" @@ -15,6 +16,10 @@ func objectCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) } func objectRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + if _, areFiltersSet := d.GetOk(attributeID); !areFiltersSet { + return diag.FromErr(objectSearch(d, meta)) + } + return diag.FromErr(objectOperation(ctx, d, func(secret bw.Object) (*bw.Object, error) { obj, err := meta.(bw.Client).GetObject(string(secret.Object), secret.ID) @@ -23,10 +28,46 @@ func objectRead(ctx context.Context, d *schema.ResourceData, meta interface{}) d if obj != nil && obj.DeletedDate != nil { return nil, errors.New("object is soft deleted") } + + if obj != nil && obj.ID != secret.ID { + return nil, errors.New("returned object ID does not match requested object ID") + } return obj, err })) } +func objectSearch(d *schema.ResourceData, meta interface{}) error { + objType, ok := d.GetOk(attributeObject) + if !ok { + return fmt.Errorf("BUG: object type not set in the resource data") + } + + objs, err := meta.(bw.Client).ListObjects(fmt.Sprintf("%ss", objType), listOptionsFromData(d)...) + if err != nil { + return err + } + + if len(objs) == 0 { + return fmt.Errorf("no object found matching the filter") + } else if len(objs) > 1 { + log.Print("[WARN] Too many objects found:") + for _, obj := range objs { + log.Printf("[WARN] * %s (%s)", obj.Name, obj.ID) + } + return fmt.Errorf("too many objects found") + } + + obj := objs[0] + + // If the object exists but is marked as soft deleted, we return an error, because relying + // on an object in the 'trash' sounds like a bad idea. + if obj.DeletedDate != nil { + return errors.New("object is soft deleted") + } + + return objectDataFromStruct(d, &obj) +} + func objectReadIgnoreMissing(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { err := objectOperation(ctx, d, func(secret bw.Object) (*bw.Object, error) { return meta.(bw.Client).GetObject(string(secret.Object), secret.ID) @@ -57,7 +98,7 @@ func objectDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) })) } -func objectOperation(ctx context.Context, d *schema.ResourceData, operation func(secret bw.Object) (*bw.Object, error)) error { +func objectOperation(_ context.Context, d *schema.ResourceData, operation func(secret bw.Object) (*bw.Object, error)) error { obj, err := operation(objectStructFromData(d)) if err != nil { return err diff --git a/internal/provider/schema.go b/internal/provider/schema.go index 300a52b..8fcd6ef 100644 --- a/internal/provider/schema.go +++ b/internal/provider/schema.go @@ -48,7 +48,7 @@ func loginSchema(schemaType schemaTypeEnum) map[string]*schema.Schema { func baseSchema(schemaType schemaTypeEnum) map[string]*schema.Schema { - return map[string]*schema.Schema{ + base := map[string]*schema.Schema{ /* * Attributes that can be required */ @@ -56,7 +56,7 @@ func baseSchema(schemaType schemaTypeEnum) map[string]*schema.Schema { Description: descriptionIdentifier, Type: schema.TypeString, Computed: schemaType == Resource, - Required: schemaType == DataSource, + Optional: true, }, attributeName: { Description: descriptionName, @@ -73,7 +73,7 @@ func baseSchema(schemaType schemaTypeEnum) map[string]*schema.Schema { Type: schema.TypeList, Elem: &schema.Schema{Type: schema.TypeString}, Computed: schemaType == DataSource, - Optional: schemaType == Resource, + Optional: true, }, attributeFavorite: { Description: descriptionFavorite, @@ -121,7 +121,7 @@ func baseSchema(schemaType schemaTypeEnum) map[string]*schema.Schema { Description: descriptionFolderID, Type: schema.TypeString, Computed: schemaType == DataSource, - Optional: schemaType == Resource, + Optional: true, }, attributeNotes: { @@ -135,7 +135,7 @@ func baseSchema(schemaType schemaTypeEnum) map[string]*schema.Schema { Description: descriptionOrganizationID, Type: schema.TypeString, Computed: schemaType == DataSource, - Optional: schemaType == Resource, + Optional: true, }, attributeReprompt: { Description: descriptionReprompt, @@ -181,6 +181,23 @@ func baseSchema(schemaType schemaTypeEnum) map[string]*schema.Schema { Computed: true, }, } + + if schemaType == DataSource { + base[attributeFilterSearch] = &schema.Schema{ + Description: descriptionFilterSearch, + Type: schema.TypeString, + Optional: true, + Computed: true, + } + + base[attributeFilterURL] = &schema.Schema{ + Description: descriptionFilterURL, + Type: schema.TypeString, + Optional: true, + Computed: true, + } + } + return base } func uriElem() *schema.Resource { diff --git a/internal/provider/schema_attributes.go b/internal/provider/schema_attributes.go index 7839500..25b2ec3 100644 --- a/internal/provider/schema_attributes.go +++ b/internal/provider/schema_attributes.go @@ -2,39 +2,45 @@ package provider const ( // Datasource and Resource field attributes - attributeAttachments = "attachments" - attributeCollectionIDs = "collection_ids" - attributeCreationDate = "creation_date" - attributeDeletedDate = "deleted_date" - attributeID = "id" - attributeFavorite = "favorite" - attributeField = "field" - attributeFieldName = "name" - attributeFieldBoolean = "boolean" - attributeFieldHidden = "hidden" - attributeFieldLinked = "linked" - attributeFieldText = "text" - attributeFolderID = "folder_id" - attributeAttachmentContent = "content" - attributeAttachmentItemID = "item_id" - attributeAttachmentFile = "file" - attributeAttachmentSize = "size" - attributeAttachmentSizeName = "size_name" - attributeAttachmentFileName = "file_name" - attributeAttachmentURL = "url" - attributeLoginPassword = "password" - attributeLoginUsername = "username" - attributeLoginURIs = "uri" - attributeLoginURIsMatch = "match" - attributeLoginURIsValue = "value" - attributeLoginTotp = "totp" - attributeName = "name" - attributeNotes = "notes" - attributeObject = "object" - attributeOrganizationID = "organization_id" - attributeReprompt = "reprompt" - attributeRevisionDate = "revision_date" - attributeType = "type" + attributeAttachments = "attachments" + attributeCollectionIDs = "collection_ids" + attributeCreationDate = "creation_date" + attributeDeletedDate = "deleted_date" + attributeID = "id" + attributeFavorite = "favorite" + attributeField = "field" + attributeFieldName = "name" + attributeFieldBoolean = "boolean" + attributeFieldHidden = "hidden" + attributeFieldLinked = "linked" + attributeFieldText = "text" + attributeFilterValues = "values" + attributeFolderID = "folder_id" + attributeAttachmentContent = "content" + attributeAttachmentItemID = "item_id" + attributeAttachmentFile = "file" + attributeAttachmentSize = "size" + attributeAttachmentSizeName = "size_name" + attributeAttachmentFileName = "file_name" + attributeAttachmentURL = "url" + attributeFilterSearch = "search" + attributeFilterCollectionId = "collection_id" + attributeFilterFolderID = "folder_id" + attributeFilterOrganizationID = "organization_id" + attributeFilterURL = "url" + attributeLoginPassword = "password" + attributeLoginUsername = "username" + attributeLoginURIs = "uri" + attributeLoginURIsMatch = "match" + attributeLoginURIsValue = "value" + attributeLoginTotp = "totp" + attributeName = "name" + attributeNotes = "notes" + attributeObject = "object" + attributeOrganizationID = "organization_id" + attributeReprompt = "reprompt" + attributeRevisionDate = "revision_date" + attributeType = "type" // Datasource and Resource field descriptions descriptionAttachments = "List of item attachments." @@ -48,6 +54,8 @@ const ( descriptionFieldLinked = "Value of a linked field." descriptionFieldName = "Name of the field." descriptionFieldText = "Value of a text field." + descriptionFilterSearch = "Occurence to search for." + descriptionFilterURL = "URL to filter results by." descriptionFolderID = "Identifier of the folder." descriptionIdentifier = "Identifier." descriptionInternal = "INTERNAL USE" // TODO: Manage to hide this from the users From 21afdb878ef34288e4519496f47badc8ae0339f3 Mon Sep 17 00:00:00 2001 From: Maxime Lagresle Date: Fri, 19 Apr 2024 19:07:44 +0200 Subject: [PATCH 02/11] add tests --- .github/workflows/tests.yml | 5 +- README.md | 8 +- go.mod | 32 +++--- go.sum | 105 ++++++++++-------- .../provider/data_source_item_login_test.go | 67 +++++++++-- internal/provider/provider_utils_test.go | 3 +- 6 files changed, 146 insertions(+), 74 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 02fb85d..9b75b21 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -27,6 +27,7 @@ jobs: env: ADMIN_TOKEN: test1234 I_REALLY_WANT_VOLATILE_STORAGE: "true" + DISABLE_ICON_DOWNLOAD: "false" LOGIN_RATELIMIT_SECONDS: "1" LOGIN_RATELIMIT_MAX_BURST: "1000000" ADMIN_RATELIMIT_SECONDS: "1" @@ -49,7 +50,7 @@ jobs: - id: binaries run: | echo "LOCAL_BINARIES=$GITHUB_WORKSPACE/bin" >> $GITHUB_OUTPUT - echo "BWCLI_VERSION=2023.1.0" >> $GITHUB_OUTPUT + echo "BWCLI_VERSION=2024.3.1" >> $GITHUB_OUTPUT - name: Check out repository code uses: actions/checkout@v3 @@ -57,7 +58,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: '>=1.19.0' + go-version: '>=1.22.0' # Cache go build cache, used to speedup go test - name: Go Build Cache diff --git a/README.md b/README.md index 35a3a9c..266e2dd 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,9 @@ This project is not associated with the Bitwarden project nor 8bit Solutions LLC ## Supported Versions The plugin has been tested and built with the following components: -- [Terraform] v1.5.2 -- [Bitwarden CLI] v2023.7.0 -- [Go] 1.20.7 (for development) +- [Terraform] v1.6.1 +- [Bitwarden CLI] v2024.3.1 +- [Go] 1.22.0 (for development) - [Docker] 23.0.5 (for development) The provider likely works with older versions but those haven't been tested. @@ -141,11 +141,13 @@ In order to run the full suite of Acceptance tests, start a Vaultwarden server: ```sh $ docker run -ti \ -e I_REALLY_WANT_VOLATILE_STORAGE=true \ + -e DISABLE_ICON_DOWNLOAD=false \ -e ADMIN_TOKEN=test1234 \ -e LOGIN_RATELIMIT_SECONDS=1 \ -e LOGIN_RATELIMIT_MAX_BURST=1000000 \ -e ADMIN_RATELIMIT_SECONDS=1 \ -e ADMIN_RATELIMIT_MAX_BURST=1000000 \ + --mount type=tmpfs,destination=/data \ -p 8080:80 vaultwarden/server ``` diff --git a/go.mod b/go.mod index 9d7c69d..22a511a 100644 --- a/go.mod +++ b/go.mod @@ -1,25 +1,27 @@ module github.com/maxlaverse/terraform-provider-bitwarden -go 1.20 +go 1.22 require ( github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 - github.com/hashicorp/terraform-plugin-docs v0.18.0 - github.com/hashicorp/terraform-plugin-sdk/v2 v2.32.0 - github.com/stretchr/testify v1.8.4 - golang.org/x/crypto v0.19.0 + github.com/hashicorp/terraform-plugin-docs v0.19.0 + github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0 + github.com/stretchr/testify v1.9.0 + golang.org/x/crypto v0.22.0 ) require ( + github.com/BurntSushi/toml v1.2.1 // indirect github.com/Kunde21/markdownfmt/v3 v3.1.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.2.0 // indirect github.com/Masterminds/sprig/v3 v3.2.3 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect + github.com/ProtonMail/go-crypto v1.1.0-alpha.2 // indirect github.com/agext/levenshtein v1.2.2 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/armon/go-radix v1.0.0 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect + github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect github.com/cloudflare/circl v1.3.7 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fatih/color v1.16.0 // indirect @@ -35,12 +37,12 @@ require ( github.com/hashicorp/go-plugin v1.6.0 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/go-version v1.6.0 // indirect - github.com/hashicorp/hc-install v0.6.2 // indirect + github.com/hashicorp/hc-install v0.6.4 // indirect github.com/hashicorp/hcl/v2 v2.19.1 // indirect github.com/hashicorp/logutils v1.0.0 // indirect github.com/hashicorp/terraform-exec v0.20.0 // indirect github.com/hashicorp/terraform-json v0.21.0 // indirect - github.com/hashicorp/terraform-plugin-go v0.21.0 // indirect + github.com/hashicorp/terraform-plugin-go v0.22.0 // indirect github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect github.com/hashicorp/terraform-registry-address v0.2.3 // indirect github.com/hashicorp/terraform-svchost v0.1.1 // indirect @@ -58,23 +60,23 @@ require ( github.com/oklog/run v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/posener/complete v1.2.3 // indirect - github.com/russross/blackfriday v1.6.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - github.com/yuin/goldmark v1.6.0 // indirect + github.com/yuin/goldmark v1.7.0 // indirect github.com/yuin/goldmark-meta v1.1.0 // indirect - github.com/zclconf/go-cty v1.14.2 // indirect + github.com/zclconf/go-cty v1.14.4 // indirect + go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect - golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.18.0 // indirect - golang.org/x/sys v0.17.0 // indirect + golang.org/x/mod v0.16.0 // indirect + golang.org/x/net v0.22.0 // indirect + golang.org/x/sys v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect - google.golang.org/grpc v1.61.0 // indirect + google.golang.org/grpc v1.61.1 // indirect google.golang.org/protobuf v1.32.0 // indirect gopkg.in/yaml.v2 v2.3.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 22d150e..d0e3871 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,7 @@ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/Kunde21/markdownfmt/v3 v3.1.0 h1:KiZu9LKs+wFFBQKhrZJrFZwtLnCCWJahL+S+E/3VnM0= github.com/Kunde21/markdownfmt/v3 v3.1.0/go.mod h1:tPXN1RTyOzJwhfHoon9wUr4HGYmWgVxSQN6VBJDkrVc= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= @@ -8,8 +11,9 @@ github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYr github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= -github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/ProtonMail/go-crypto v1.1.0-alpha.2 h1:bkyFVUP+ROOARdgCiJzNQo2V2kiB97LyUpzH9P6Hrlg= +github.com/ProtonMail/go-crypto v1.1.0-alpha.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE= github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= @@ -19,25 +23,34 @@ github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I= +github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= -github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= +github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= +github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= -github.com/go-git/go-git/v5 v5.10.1 h1:tu8/D8i+TWxgKpzQ3Vc43e+kkhXqtsZCKI/egajKnxk= +github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= +github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= +github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= +github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= @@ -74,8 +87,8 @@ github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/C github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/hc-install v0.6.2 h1:V1k+Vraqz4olgZ9UzKiAcbman9i9scg9GgSt/U3mw/M= -github.com/hashicorp/hc-install v0.6.2/go.mod h1:2JBpd+NCFKiHiu/yYCGaPyPHhZLxXTpz8oreHa/a3Ps= +github.com/hashicorp/hc-install v0.6.4 h1:QLqlM56/+SIIGvGcfFiwMY3z5WGXT066suo/v9Km8e0= +github.com/hashicorp/hc-install v0.6.4/go.mod h1:05LWLy8TD842OtgcfBbOT0WMoInBMUSHjmDx10zuBIA= github.com/hashicorp/hcl/v2 v2.19.1 h1://i05Jqznmb2EXqa39Nsvyan2o5XyMowW5fnCKW5RPI= github.com/hashicorp/hcl/v2 v2.19.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= @@ -84,14 +97,14 @@ github.com/hashicorp/terraform-exec v0.20.0 h1:DIZnPsqzPGuUnq6cH8jWcPunBfY+C+M8J github.com/hashicorp/terraform-exec v0.20.0/go.mod h1:ckKGkJWbsNqFKV1itgMnE0hY9IYf1HoiekpuN0eWoDw= github.com/hashicorp/terraform-json v0.21.0 h1:9NQxbLNqPbEMze+S6+YluEdXgJmhQykRyRNd+zTI05U= github.com/hashicorp/terraform-json v0.21.0/go.mod h1:qdeBs11ovMzo5puhrRibdD6d2Dq6TyE/28JiU4tIQxk= -github.com/hashicorp/terraform-plugin-docs v0.18.0 h1:2bINhzXc+yDeAcafurshCrIjtdu1XHn9zZ3ISuEhgpk= -github.com/hashicorp/terraform-plugin-docs v0.18.0/go.mod h1:iIUfaJpdUmpi+rI42Kgq+63jAjI8aZVTyxp3Bvk9Hg8= -github.com/hashicorp/terraform-plugin-go v0.21.0 h1:VSjdVQYNDKR0l2pi3vsFK1PdMQrw6vGOshJXMNFeVc0= -github.com/hashicorp/terraform-plugin-go v0.21.0/go.mod h1:piJp8UmO1uupCvC9/H74l2C6IyKG0rW4FDedIpwW5RQ= +github.com/hashicorp/terraform-plugin-docs v0.19.0 h1:ufXLte5Kx20LazYmGN2UZG2bN4aF0PmlDyuS1iKWSXo= +github.com/hashicorp/terraform-plugin-docs v0.19.0/go.mod h1:NPfKCSfzTtq+YCFHr2qTAMknWUxR8C4KgTbGkHULSV8= +github.com/hashicorp/terraform-plugin-go v0.22.0 h1:1OS1Jk5mO0f5hrziWJGXXIxBrMe2j/B8E+DVGw43Xmc= +github.com/hashicorp/terraform-plugin-go v0.22.0/go.mod h1:mPULV91VKss7sik6KFEcEu7HuTogMLLO/EvWCuFkRVE= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.32.0 h1:7xdO9aOXVmhvMxNAq8UloyyqW0EEzyAY37llSTHJgjo= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.32.0/go.mod h1:LxQzs7AQl/5JE1IGFd6LX8E4A0InRJ/7s245gOmsejA= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0 h1:qHprzXy/As0rxedphECBEQAh3R4yp6pKksKHcqZx5G8= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0/go.mod h1:H+8tjs9TjV2w57QFVSMBQacf8k/E1XwLXGCARgViC6A= github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI= github.com/hashicorp/terraform-registry-address v0.2.3/go.mod h1:lFHA76T8jfQteVfT7caREqguFrW3c4MFSPhZB7HHgUM= github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= @@ -104,14 +117,20 @@ github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= +github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -138,18 +157,20 @@ github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= +github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= -github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= -github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= +github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A= +github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= @@ -158,8 +179,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= @@ -168,39 +189,36 @@ github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21 github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68= -github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark v1.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA= +github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc= github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0= -github.com/zclconf/go-cty v1.14.2 h1:kTG7lqmBou0Zkx35r6HJHUQTvaRPr5bIAf3AoHS0izI= -github.com/zclconf/go-cty v1.14.2/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty v1.14.4 h1:uXXczd9QDGsgu0i/QFR/hzI5NYCHLf6NQw/atrbnhq8= +github.com/zclconf/go-cty v1.14.4/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +go.abhg.dev/goldmark/frontmatter v0.2.0 h1:P8kPG0YkL12+aYk2yU3xHv4tcXzeVnN+gU0tJ5JnxRw= +go.abhg.dev/goldmark/frontmatter v0.2.0/go.mod h1:XqrEkZuM57djk7zrlRUB02x8I5J0px76YjkOzhB4YlU= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= -golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -213,31 +231,25 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= @@ -245,8 +257,8 @@ google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAs google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14= google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= -google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= -google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= +google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY= +google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= @@ -255,6 +267,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal/provider/data_source_item_login_test.go b/internal/provider/data_source_item_login_test.go index 3d71cfd..df500d4 100644 --- a/internal/provider/data_source_item_login_test.go +++ b/internal/provider/data_source_item_login_test.go @@ -52,10 +52,6 @@ func TestAccDataSourceItemLoginDeleted(t *testing.T) { Config: tfConfigProvider() + tfConfigResourceItemLoginSmall(), Check: getObjectID("bitwarden_item_login.foo", &objectID), }, - { - Config: tfConfigProvider() + tfConfigDataItemLoginWithURLFilter("https://start_with/something"), - Check: getObjectID("bitwarden_item_login.foo", &objectID), - }, { Config: tfConfigProvider() + tfConfigResourceItemLoginSmall() + tfConfigDataItemLoginWithId(objectID), PreConfig: func() { @@ -68,6 +64,42 @@ func TestAccDataSourceItemLoginDeleted(t *testing.T) { }) } +func TestAccDataSourceItemLoginBySearch(t *testing.T) { + var objectID string + resourceName := "bitwarden_item_login.foo" + + ensureVaultwardenConfigured(t) + + resource.UnitTest(t, resource.TestCase{ + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: tfConfigProvider() + tfConfigResourceFolder() + tfConfigResourceItemLogin(), + Check: resource.ComposeTestCheckFunc( + checkItemLogin(resourceName), + getObjectID(resourceName, &objectID), + ), + }, + { + Config: tfConfigProvider() + tfConfigResourceFolder() + tfConfigResourceItemLogin() + tfConfigDataItemLoginWithSearchAndOrg("test-username"), + Check: checkItemLogin("data.bitwarden_item_login.foo_data"), + }, + { + Config: tfConfigProvider() + tfConfigResourceFolder() + tfConfigResourceItemLogin() + tfConfigResourceItemLoginDuplicate() + tfConfigDataItemLoginWithSearchAndOrg("test-username"), + Check: checkItemLogin("data.bitwarden_item_login.foo_data"), + }, + { + Config: tfConfigProvider() + tfConfigResourceFolder() + tfConfigResourceItemLogin() + tfConfigResourceItemLoginDuplicate() + tfConfigDataItemLoginWithSearchOnly("test-username"), + ExpectError: regexp.MustCompile("Error: too many objects found"), + }, + { + Config: tfConfigProvider() + tfConfigResourceFolder() + tfConfigResourceItemLogin() + tfConfigDataItemLoginWithSearchAndOrg("missing-item"), + ExpectError: regexp.MustCompile("Error: no object found matching the filter"), + }, + }, + }) +} + func tfConfigDataItemLoginWithId(id string) string { return fmt.Sprintf(` data "bitwarden_item_login" "foo_data" { @@ -78,16 +110,37 @@ data "bitwarden_item_login" "foo_data" { `, id) } -func tfConfigDataItemLoginWithURLFilter(url string) string { +func tfConfigDataItemLoginWithSearchAndOrg(search string) string { + return fmt.Sprintf(` +data "bitwarden_item_login" "foo_data" { + provider = bitwarden + + search = "%s" + organization_id = "%s" +} +`, search, testOrganizationID) +} + +func tfConfigDataItemLoginWithSearchOnly(search string) string { return fmt.Sprintf(` data "bitwarden_item_login" "foo_data" { provider = bitwarden - url = "%s" + search = "%s" } -`, url) +`, search) } +func tfConfigResourceItemLoginDuplicate() string { + return ` + resource "bitwarden_item_login" "foo_duplicate" { + provider = bitwarden + + name = "another item with username 'test-username'" + username = "test-username" + } + ` +} func tfConfigDataItemLogin() string { return ` data "bitwarden_item_login" "foo_data" { diff --git a/internal/provider/provider_utils_test.go b/internal/provider/provider_utils_test.go index 1e19f6e..206f440 100644 --- a/internal/provider/provider_utils_test.go +++ b/internal/provider/provider_utils_test.go @@ -97,7 +97,8 @@ func ensureVaultwardenConfigured(t *testing.T) { t.Fatal(err) } } - testOrganizationID, err = webapiClient.CreateOrganization(fmt.Sprintf("org-%d", time.Now().Unix()), fmt.Sprintf("coll-%d", time.Now().Unix()), testEmail) + dateTimeStr := fmt.Sprintf("%d-%d-%d", time.Now().Hour(), time.Now().Minute(), time.Now().Second()) + testOrganizationID, err = webapiClient.CreateOrganization(fmt.Sprintf("org-%s", dateTimeStr), fmt.Sprintf("coll-%s", dateTimeStr), testEmail) if err != nil { t.Fatal(err) } From d667b03a8892b1e6c01d23e55198cb5684cfbc92 Mon Sep 17 00:00:00 2001 From: Maxime Lagresle Date: Fri, 19 Apr 2024 19:11:29 +0200 Subject: [PATCH 03/11] more doc --- docs/data-sources/item_login.md | 7 ++++++- docs/data-sources/item_secure_note.md | 7 ++++++- examples/data-sources/bitwarden_item_login/data-source.tf | 7 ++++++- .../data-sources/bitwarden_item_secure_note/data-source.tf | 7 ++++++- examples/quick/main.tf | 2 +- 5 files changed, 25 insertions(+), 5 deletions(-) diff --git a/docs/data-sources/item_login.md b/docs/data-sources/item_login.md index e5ea2c1..b173abc 100644 --- a/docs/data-sources/item_login.md +++ b/docs/data-sources/item_login.md @@ -13,7 +13,7 @@ Use this data source to get information on an existing Login. ## Example Usage ```terraform -# Find the identifier of the resource you want to import: +# Option 1: Find the identifier of the resource you want to import: # # $ bw list items --search "Mysql Root Credentials" | jq '.[] .id' # ? Master password: [hidden] @@ -25,6 +25,11 @@ data "bitwarden_item_login" "database_credentials" { id = "ec4e447f-9aed-4203-b834-c8f3848828f7" } +# Option 2: Use filters directly in the resource declaration +data "bitwarden_item_secure_note" "ssh_notes" { + search = "SSH Private Key" +} + # Later to be accessed as # ... = data.bitwarden_item_login.database_credentials.username # ... = data.bitwarden_item_login.database_credentials.password diff --git a/docs/data-sources/item_secure_note.md b/docs/data-sources/item_secure_note.md index d75474b..af78c17 100644 --- a/docs/data-sources/item_secure_note.md +++ b/docs/data-sources/item_secure_note.md @@ -13,7 +13,7 @@ Use this data source to get information on an existing Secure Note. ## Example Usage ```terraform -# Find the identifier of the resource you want to import: +# Option 1: Find the identifier of the resource you want to import: # # $ bw list items --search "SSH Private Key" | jq '.[] .id' # ? Master password: [hidden] @@ -25,6 +25,11 @@ data "bitwarden_item_secure_note" "ssh_notes" { id = "a9e19f26-1b8c-4568-bc09-191e2cf56ed6" } +# Option 2: Use filters directly in the resource declaration +data "bitwarden_item_secure_note" "ssh_notes" { + search = "SSH Private Key" +} + # Later to be accessed as # ... = data.bitwarden_item_secure_note.ssh_notes.notes # diff --git a/examples/data-sources/bitwarden_item_login/data-source.tf b/examples/data-sources/bitwarden_item_login/data-source.tf index 482d174..56fb11b 100644 --- a/examples/data-sources/bitwarden_item_login/data-source.tf +++ b/examples/data-sources/bitwarden_item_login/data-source.tf @@ -1,4 +1,4 @@ -# Find the identifier of the resource you want to import: +# Option 1: Find the identifier of the resource you want to import: # # $ bw list items --search "Mysql Root Credentials" | jq '.[] .id' # ? Master password: [hidden] @@ -10,6 +10,11 @@ data "bitwarden_item_login" "database_credentials" { id = "ec4e447f-9aed-4203-b834-c8f3848828f7" } +# Option 2: Use filters directly in the resource declaration +data "bitwarden_item_secure_note" "ssh_notes" { + search = "SSH Private Key" +} + # Later to be accessed as # ... = data.bitwarden_item_login.database_credentials.username # ... = data.bitwarden_item_login.database_credentials.password diff --git a/examples/data-sources/bitwarden_item_secure_note/data-source.tf b/examples/data-sources/bitwarden_item_secure_note/data-source.tf index 6c92678..030a913 100644 --- a/examples/data-sources/bitwarden_item_secure_note/data-source.tf +++ b/examples/data-sources/bitwarden_item_secure_note/data-source.tf @@ -1,4 +1,4 @@ -# Find the identifier of the resource you want to import: +# Option 1: Find the identifier of the resource you want to import: # # $ bw list items --search "SSH Private Key" | jq '.[] .id' # ? Master password: [hidden] @@ -10,6 +10,11 @@ data "bitwarden_item_secure_note" "ssh_notes" { id = "a9e19f26-1b8c-4568-bc09-191e2cf56ed6" } +# Option 2: Use filters directly in the resource declaration +data "bitwarden_item_secure_note" "ssh_notes" { + search = "SSH Private Key" +} + # Later to be accessed as # ... = data.bitwarden_item_secure_note.ssh_notes.notes # diff --git a/examples/quick/main.tf b/examples/quick/main.tf index 990ff34..40bfb84 100644 --- a/examples/quick/main.tf +++ b/examples/quick/main.tf @@ -30,7 +30,7 @@ resource "bitwarden_item_secure_note" "vpn_note" { # Read sensitive information from Bitwarden # Using Login information data "bitwarden_item_login" "mysql_credentials" { - id = "ec4e447f-9aed-4203-b834-c8f3848828f7" + search = "mysql/server-1" } # Later to be accessed as From baff5126867dbb1d0d9089484a483dee88e1a53c Mon Sep 17 00:00:00 2001 From: Maxime Lagresle Date: Fri, 19 Apr 2024 19:41:59 +0200 Subject: [PATCH 04/11] rollback cli upgrade --- .github/workflows/tests.yml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9b75b21..c1aabe3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -50,7 +50,7 @@ jobs: - id: binaries run: | echo "LOCAL_BINARIES=$GITHUB_WORKSPACE/bin" >> $GITHUB_OUTPUT - echo "BWCLI_VERSION=2024.3.1" >> $GITHUB_OUTPUT + echo "BWCLI_VERSION=2023.2.0" >> $GITHUB_OUTPUT - name: Check out repository code uses: actions/checkout@v3 diff --git a/README.md b/README.md index 266e2dd..dfc8c38 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ This project is not associated with the Bitwarden project nor 8bit Solutions LLC ## Supported Versions The plugin has been tested and built with the following components: - [Terraform] v1.6.1 -- [Bitwarden CLI] v2024.3.1 +- [Bitwarden CLI] v2023.2.0 - [Go] 1.22.0 (for development) - [Docker] 23.0.5 (for development) From f11883e6d8cf604a8c7e2bc566f6a4182a1f4f85 Mon Sep 17 00:00:00 2001 From: Maxime Lagresle Date: Fri, 19 Apr 2024 20:11:31 +0200 Subject: [PATCH 05/11] remove broken test --- examples/quick/provider.tf | 5 +++ internal/provider/data_source.go | 24 ----------- .../provider/data_source_item_login_test.go | 41 +------------------ internal/provider/object.go | 26 +++++++++++- 4 files changed, 31 insertions(+), 65 deletions(-) diff --git a/examples/quick/provider.tf b/examples/quick/provider.tf index 307ce20..07f41c6 100644 --- a/examples/quick/provider.tf +++ b/examples/quick/provider.tf @@ -17,4 +17,9 @@ resource "bitwarden_item_login" "example" { name = "Example" username = "service-account" password = "" +} + +# Use Bitwarden Resource +data "bitwarden_item_login" "example" { + search = "Example" } \ No newline at end of file diff --git a/internal/provider/data_source.go b/internal/provider/data_source.go index 85efe8b..d5ad905 100644 --- a/internal/provider/data_source.go +++ b/internal/provider/data_source.go @@ -22,27 +22,3 @@ func readDataSource(attrObject bw.ObjectType, attrType bw.ItemType) schema.ReadC return objectRead(ctx, d, meta) } } - -func listOptionsFromData(d *schema.ResourceData) []bw.ListObjectsOption { - filters := []bw.ListObjectsOption{} - - filterMap := map[string]bw.ListObjectsOptionGenerator{ - attributeFilterSearch: bw.WithSearch, - attributeFilterCollectionId: bw.WithCollectionID, - attributeFilterFolderID: bw.WithFolderID, - attributeFilterOrganizationID: bw.WithOrganizationID, - attributeFilterURL: bw.WithUrl, - } - - for attribute, optionFunc := range filterMap { - v, ok := d.GetOk(attribute) - if !ok { - continue - } - - if v, ok := v.(string); ok && len(v) > 0 { - filters = append(filters, optionFunc(v)) - } - } - return filters -} diff --git a/internal/provider/data_source_item_login_test.go b/internal/provider/data_source_item_login_test.go index df500d4..a58a38a 100644 --- a/internal/provider/data_source_item_login_test.go +++ b/internal/provider/data_source_item_login_test.go @@ -6,7 +6,6 @@ import ( "testing" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/stretchr/testify/assert" ) func TestAccDataSourceItemLoginAttributes(t *testing.T) { @@ -40,32 +39,7 @@ func TestAccDataSourceItemLoginFailsOnInexistentItem(t *testing.T) { }) } -func TestAccDataSourceItemLoginDeleted(t *testing.T) { - var objectID string - - ensureVaultwardenConfigured(t) - - resource.UnitTest(t, resource.TestCase{ - ProviderFactories: providerFactories, - Steps: []resource.TestStep{ - { - Config: tfConfigProvider() + tfConfigResourceItemLoginSmall(), - Check: getObjectID("bitwarden_item_login.foo", &objectID), - }, - { - Config: tfConfigProvider() + tfConfigResourceItemLoginSmall() + tfConfigDataItemLoginWithId(objectID), - PreConfig: func() { - err := bwTestClient(t).DeleteObject("item", objectID) - assert.NoError(t, err) - }, - ExpectError: regexp.MustCompile("Error: object not found"), - }, - }, - }) -} - func TestAccDataSourceItemLoginBySearch(t *testing.T) { - var objectID string resourceName := "bitwarden_item_login.foo" ensureVaultwardenConfigured(t) @@ -75,10 +49,7 @@ func TestAccDataSourceItemLoginBySearch(t *testing.T) { Steps: []resource.TestStep{ { Config: tfConfigProvider() + tfConfigResourceFolder() + tfConfigResourceItemLogin(), - Check: resource.ComposeTestCheckFunc( - checkItemLogin(resourceName), - getObjectID(resourceName, &objectID), - ), + Check: checkItemLogin(resourceName), }, { Config: tfConfigProvider() + tfConfigResourceFolder() + tfConfigResourceItemLogin() + tfConfigDataItemLoginWithSearchAndOrg("test-username"), @@ -100,16 +71,6 @@ func TestAccDataSourceItemLoginBySearch(t *testing.T) { }) } -func tfConfigDataItemLoginWithId(id string) string { - return fmt.Sprintf(` -data "bitwarden_item_login" "foo_data" { - provider = bitwarden - - id = "%s" -} -`, id) -} - func tfConfigDataItemLoginWithSearchAndOrg(search string) string { return fmt.Sprintf(` data "bitwarden_item_login" "foo_data" { diff --git a/internal/provider/object.go b/internal/provider/object.go index 70ad816..76dc59b 100644 --- a/internal/provider/object.go +++ b/internal/provider/object.go @@ -16,7 +16,7 @@ func objectCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) } func objectRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - if _, areFiltersSet := d.GetOk(attributeID); !areFiltersSet { + if _, idProvided := d.GetOk(attributeID); !idProvided { return diag.FromErr(objectSearch(d, meta)) } @@ -68,6 +68,30 @@ func objectSearch(d *schema.ResourceData, meta interface{}) error { return objectDataFromStruct(d, &obj) } +func listOptionsFromData(d *schema.ResourceData) []bw.ListObjectsOption { + filters := []bw.ListObjectsOption{} + + filterMap := map[string]bw.ListObjectsOptionGenerator{ + attributeFilterSearch: bw.WithSearch, + attributeFilterCollectionId: bw.WithCollectionID, + attributeFilterFolderID: bw.WithFolderID, + attributeFilterOrganizationID: bw.WithOrganizationID, + attributeFilterURL: bw.WithUrl, + } + + for attribute, optionFunc := range filterMap { + v, ok := d.GetOk(attribute) + if !ok { + continue + } + + if v, ok := v.(string); ok && len(v) > 0 { + filters = append(filters, optionFunc(v)) + } + } + return filters +} + func objectReadIgnoreMissing(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { err := objectOperation(ctx, d, func(secret bw.Object) (*bw.Object, error) { return meta.(bw.Client).GetObject(string(secret.Object), secret.ID) From ca7f66dbacdc9dc49bd05663710cb7d42350294e Mon Sep 17 00:00:00 2001 From: Maxime Lagresle Date: Fri, 19 Apr 2024 20:45:06 +0200 Subject: [PATCH 06/11] use dedicated attributes --- docs/data-sources/item_login.md | 13 +++++--- docs/data-sources/item_secure_note.md | 13 +++++--- docs/index.md | 5 +++ .../provider/data_source_item_login_test.go | 2 +- internal/provider/provider.go | 2 +- internal/provider/schema.go | 31 ++++++++++++++----- internal/provider/schema_attributes.go | 15 +++++---- 7 files changed, 56 insertions(+), 25 deletions(-) diff --git a/docs/data-sources/item_login.md b/docs/data-sources/item_login.md index b173abc..65759cd 100644 --- a/docs/data-sources/item_login.md +++ b/docs/data-sources/item_login.md @@ -49,22 +49,25 @@ data "bitwarden_item_secure_note" "ssh_notes" { ### Optional -- `collection_ids` (List of String) Identifier of the collections the item belongs to. -- `folder_id` (String) Identifier of the folder. +- `filter_collection_id` (String) Filter search results by collection ID +- `filter_folder_id` (String) Filter search results by folder ID +- `filter_organization_id` (String) Filter search results by organization ID +- `filter_url` (String) Filter search results by URL - `id` (String) Identifier. -- `organization_id` (String) Identifier of the organization. -- `search` (String) Occurence to search for. -- `url` (String) URL to filter results by. +- `search` (String) Search items matching the search string. Can be combined with filters to narrow down the search. ### Read-Only - `attachments` (List of Object) List of item attachments. (see [below for nested schema](#nestedatt--attachments)) +- `collection_ids` (List of String) Identifier of the collections the item belongs to. - `creation_date` (String) Date the item was created. - `deleted_date` (String) Date the item was deleted. - `favorite` (Boolean) Mark as a Favorite to have item appear at the top of your Vault in the UI. - `field` (List of Object, Sensitive) Extra fields. (see [below for nested schema](#nestedatt--field)) +- `folder_id` (String) Identifier of the folder. - `name` (String) Name. - `notes` (String, Sensitive) Notes. +- `organization_id` (String) Identifier of the organization. - `password` (String, Sensitive) Login password. - `reprompt` (Boolean) Require master password “re-prompt” when displaying secret in the UI. - `revision_date` (String) Last time the item was updated. diff --git a/docs/data-sources/item_secure_note.md b/docs/data-sources/item_secure_note.md index af78c17..978f413 100644 --- a/docs/data-sources/item_secure_note.md +++ b/docs/data-sources/item_secure_note.md @@ -48,22 +48,25 @@ data "bitwarden_item_secure_note" "ssh_notes" { ### Optional -- `collection_ids` (List of String) Identifier of the collections the item belongs to. -- `folder_id` (String) Identifier of the folder. +- `filter_collection_id` (String) Filter search results by collection ID +- `filter_folder_id` (String) Filter search results by folder ID +- `filter_organization_id` (String) Filter search results by organization ID +- `filter_url` (String) Filter search results by URL - `id` (String) Identifier. -- `organization_id` (String) Identifier of the organization. -- `search` (String) Occurence to search for. -- `url` (String) URL to filter results by. +- `search` (String) Search items matching the search string. Can be combined with filters to narrow down the search. ### Read-Only - `attachments` (List of Object) List of item attachments. (see [below for nested schema](#nestedatt--attachments)) +- `collection_ids` (List of String) Identifier of the collections the item belongs to. - `creation_date` (String) Date the item was created. - `deleted_date` (String) Date the item was deleted. - `favorite` (Boolean) Mark as a Favorite to have item appear at the top of your Vault in the UI. - `field` (List of Object, Sensitive) Extra fields. (see [below for nested schema](#nestedatt--field)) +- `folder_id` (String) Identifier of the folder. - `name` (String) Name. - `notes` (String, Sensitive) Notes. +- `organization_id` (String) Identifier of the organization. - `reprompt` (Boolean) Require master password “re-prompt” when displaying secret in the UI. - `revision_date` (String) Last time the item was updated. diff --git a/docs/index.md b/docs/index.md index 6f17f9f..1942d60 100644 --- a/docs/index.md +++ b/docs/index.md @@ -33,6 +33,11 @@ resource "bitwarden_item_login" "example" { username = "service-account" password = "" } + +# Use Bitwarden Resource +data "bitwarden_item_login" "example" { + search = "Example" +} ``` ## Authentication diff --git a/internal/provider/data_source_item_login_test.go b/internal/provider/data_source_item_login_test.go index a58a38a..1b094b3 100644 --- a/internal/provider/data_source_item_login_test.go +++ b/internal/provider/data_source_item_login_test.go @@ -77,7 +77,7 @@ data "bitwarden_item_login" "foo_data" { provider = bitwarden search = "%s" - organization_id = "%s" + filter_organization_id = "%s" } `, search, testOrganizationID) } diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 1c2cabf..f8ecb80 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -108,7 +108,7 @@ func New(version string) func() *schema.Provider { } } -func providerConfigure(version string, p *schema.Provider) func(context.Context, *schema.ResourceData) (interface{}, diag.Diagnostics) { +func providerConfigure(version string, _ *schema.Provider) func(context.Context, *schema.ResourceData) (interface{}, diag.Diagnostics) { return func(_ context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { bwClient, err := newBitwardenClient(d, version) diff --git a/internal/provider/schema.go b/internal/provider/schema.go index 8fcd6ef..6c87e54 100644 --- a/internal/provider/schema.go +++ b/internal/provider/schema.go @@ -73,7 +73,7 @@ func baseSchema(schemaType schemaTypeEnum) map[string]*schema.Schema { Type: schema.TypeList, Elem: &schema.Schema{Type: schema.TypeString}, Computed: schemaType == DataSource, - Optional: true, + Optional: schemaType == Resource, }, attributeFavorite: { Description: descriptionFavorite, @@ -121,7 +121,7 @@ func baseSchema(schemaType schemaTypeEnum) map[string]*schema.Schema { Description: descriptionFolderID, Type: schema.TypeString, Computed: schemaType == DataSource, - Optional: true, + Optional: schemaType == Resource, }, attributeNotes: { @@ -135,7 +135,7 @@ func baseSchema(schemaType schemaTypeEnum) map[string]*schema.Schema { Description: descriptionOrganizationID, Type: schema.TypeString, Computed: schemaType == DataSource, - Optional: true, + Optional: schemaType == Resource, }, attributeReprompt: { Description: descriptionReprompt, @@ -183,18 +183,35 @@ func baseSchema(schemaType schemaTypeEnum) map[string]*schema.Schema { } if schemaType == DataSource { - base[attributeFilterSearch] = &schema.Schema{ - Description: descriptionFilterSearch, + base[attributeFilterCollectionId] = &schema.Schema{ + Description: descriptionFilterCollectionID, + Type: schema.TypeString, + Optional: true, + } + + base[attributeFilterFolderID] = &schema.Schema{ + Description: descriptionFilterFolderID, + Type: schema.TypeString, + Optional: true, + } + + base[attributeFilterOrganizationID] = &schema.Schema{ + Description: descriptionFilterOrganizationID, Type: schema.TypeString, Optional: true, - Computed: true, + } + + base[attributeFilterSearch] = &schema.Schema{ + Description: descriptionFilterSearch, + Type: schema.TypeString, + Optional: true, + AtLeastOneOf: []string{attributeFilterSearch, attributeID}, } base[attributeFilterURL] = &schema.Schema{ Description: descriptionFilterURL, Type: schema.TypeString, Optional: true, - Computed: true, } } return base diff --git a/internal/provider/schema_attributes.go b/internal/provider/schema_attributes.go index 25b2ec3..08b326c 100644 --- a/internal/provider/schema_attributes.go +++ b/internal/provider/schema_attributes.go @@ -23,11 +23,11 @@ const ( attributeAttachmentSizeName = "size_name" attributeAttachmentFileName = "file_name" attributeAttachmentURL = "url" + attributeFilterCollectionId = "filter_collection_id" + attributeFilterFolderID = "filter_folder_id" + attributeFilterOrganizationID = "filter_organization_id" attributeFilterSearch = "search" - attributeFilterCollectionId = "collection_id" - attributeFilterFolderID = "folder_id" - attributeFilterOrganizationID = "organization_id" - attributeFilterURL = "url" + attributeFilterURL = "filter_url" attributeLoginPassword = "password" attributeLoginUsername = "username" attributeLoginURIs = "uri" @@ -54,8 +54,11 @@ const ( descriptionFieldLinked = "Value of a linked field." descriptionFieldName = "Name of the field." descriptionFieldText = "Value of a text field." - descriptionFilterSearch = "Occurence to search for." - descriptionFilterURL = "URL to filter results by." + descriptionFilterCollectionID = "Filter search results by collection ID" + descriptionFilterFolderID = "Filter search results by folder ID" + descriptionFilterOrganizationID = "Filter search results by organization ID" + descriptionFilterSearch = "Search items matching the search string. Can be combined with filters to narrow down the search." + descriptionFilterURL = "Filter search results by URL" descriptionFolderID = "Identifier of the folder." descriptionIdentifier = "Identifier." descriptionInternal = "INTERNAL USE" // TODO: Manage to hide this from the users From 2f96b3fa70672dd28594cb85ccc32c0408bd5294 Mon Sep 17 00:00:00 2001 From: Maxime Lagresle Date: Fri, 19 Apr 2024 20:58:37 +0200 Subject: [PATCH 07/11] filter by right item type --- internal/bitwarden/bw/filter.go | 15 +++++++++++++++ internal/provider/object.go | 14 ++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 internal/bitwarden/bw/filter.go diff --git a/internal/bitwarden/bw/filter.go b/internal/bitwarden/bw/filter.go new file mode 100644 index 0000000..27feed5 --- /dev/null +++ b/internal/bitwarden/bw/filter.go @@ -0,0 +1,15 @@ +package bw + +func FilterObjectsByType(objs []Object, itemType ItemType) []Object { + if itemType == 0 { + return objs + } + + filtered := make([]Object, 0, len(objs)) + for _, obj := range objs { + if obj.Type == itemType { + filtered = append(filtered, obj) + } + } + return filtered +} diff --git a/internal/provider/object.go b/internal/provider/object.go index 76dc59b..c032632 100644 --- a/internal/provider/object.go +++ b/internal/provider/object.go @@ -47,6 +47,16 @@ func objectSearch(d *schema.ResourceData, meta interface{}) error { return err } + // If the object is an item, also filter by type to avoid returning a login when a secure note is expected. + if objType == bw.ObjectTypeItem { + itemType, ok := d.GetOk(attributeType) + if !ok { + return fmt.Errorf("BUG: item type not set in the resource data") + } + + objs = bw.FilterObjectsByType(objs, bw.ItemType(itemType.(int))) + } + if len(objs) == 0 { return fmt.Errorf("no object found matching the filter") } else if len(objs) > 1 { @@ -59,8 +69,8 @@ func objectSearch(d *schema.ResourceData, meta interface{}) error { obj := objs[0] - // If the object exists but is marked as soft deleted, we return an error, because relying - // on an object in the 'trash' sounds like a bad idea. + // If the object exists but is marked as soft deleted, we return an error. This shouldn't happen + // in theory since we never pass the --trash flag to the Bitwarden CLI when listing objects. if obj.DeletedDate != nil { return errors.New("object is soft deleted") } From 932328974b61d199cf8558678664fd73cbaf5c3a Mon Sep 17 00:00:00 2001 From: Maxime Lagresle Date: Fri, 19 Apr 2024 21:13:08 +0200 Subject: [PATCH 08/11] only allow filtering logins by url --- docs/data-sources/item_secure_note.md | 1 - internal/provider/schema.go | 17 ++++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/data-sources/item_secure_note.md b/docs/data-sources/item_secure_note.md index 978f413..b0a1cba 100644 --- a/docs/data-sources/item_secure_note.md +++ b/docs/data-sources/item_secure_note.md @@ -51,7 +51,6 @@ data "bitwarden_item_secure_note" "ssh_notes" { - `filter_collection_id` (String) Filter search results by collection ID - `filter_folder_id` (String) Filter search results by folder ID - `filter_organization_id` (String) Filter search results by organization ID -- `filter_url` (String) Filter search results by URL - `id` (String) Identifier. - `search` (String) Search items matching the search string. Can be combined with filters to narrow down the search. diff --git a/internal/provider/schema.go b/internal/provider/schema.go index 6c87e54..92824ca 100644 --- a/internal/provider/schema.go +++ b/internal/provider/schema.go @@ -13,7 +13,7 @@ const ( ) func loginSchema(schemaType schemaTypeEnum) map[string]*schema.Schema { - return map[string]*schema.Schema{ + base := map[string]*schema.Schema{ attributeLoginPassword: { Description: descriptionLoginPassword, Type: schema.TypeString, @@ -44,6 +44,15 @@ func loginSchema(schemaType schemaTypeEnum) map[string]*schema.Schema { Sensitive: false, }, } + + if schemaType == DataSource { + base[attributeFilterURL] = &schema.Schema{ + Description: descriptionFilterURL, + Type: schema.TypeString, + Optional: true, + } + } + return base } func baseSchema(schemaType schemaTypeEnum) map[string]*schema.Schema { @@ -207,12 +216,6 @@ func baseSchema(schemaType schemaTypeEnum) map[string]*schema.Schema { Optional: true, AtLeastOneOf: []string{attributeFilterSearch, attributeID}, } - - base[attributeFilterURL] = &schema.Schema{ - Description: descriptionFilterURL, - Type: schema.TypeString, - Optional: true, - } } return base } From 23b77b726960c7fd1de482fbaa978b756727221f Mon Sep 17 00:00:00 2001 From: Maxime Lagresle Date: Fri, 19 Apr 2024 21:33:21 +0200 Subject: [PATCH 09/11] add one more test and adjust doc --- docs/index.md | 2 +- examples/quick/provider.tf | 2 +- internal/bitwarden/bw/client_test.go | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/docs/index.md b/docs/index.md index 1942d60..5822497 100644 --- a/docs/index.md +++ b/docs/index.md @@ -36,7 +36,7 @@ resource "bitwarden_item_login" "example" { # Use Bitwarden Resource data "bitwarden_item_login" "example" { - search = "Example" + search = "Example" } ``` diff --git a/examples/quick/provider.tf b/examples/quick/provider.tf index 07f41c6..52f5180 100644 --- a/examples/quick/provider.tf +++ b/examples/quick/provider.tf @@ -21,5 +21,5 @@ resource "bitwarden_item_login" "example" { # Use Bitwarden Resource data "bitwarden_item_login" "example" { - search = "Example" + search = "Example" } \ No newline at end of file diff --git a/internal/bitwarden/bw/client_test.go b/internal/bitwarden/bw/client_test.go index 2f6a9fb..af9b969 100644 --- a/internal/bitwarden/bw/client_test.go +++ b/internal/bitwarden/bw/client_test.go @@ -32,3 +32,18 @@ func TestCreateObjectEncoding(t *testing.T) { assert.Equal(t, "create e30K", commandsExecuted()[1]) } } + +func TestListObjects(t *testing.T) { + removeMocks, commandsExecuted := test_command.MockCommands(t, map[string]string{ + "list item --folderid folder-id --collectionid collection-id --search search": `[]`, + }) + defer removeMocks(t) + + b := NewClient("dummy") + _, err := b.ListObjects("item", WithFolderID("folder-id"), WithCollectionID("collection-id"), WithSearch("search")) + + assert.NoError(t, err) + if assert.Len(t, commandsExecuted(), 1) { + assert.Equal(t, "list item --folderid folder-id --collectionid collection-id --search search", commandsExecuted()[0]) + } +} From b188ee60136438668a5f615144e59e9de943b1e1 Mon Sep 17 00:00:00 2001 From: Maxime Lagresle Date: Fri, 19 Apr 2024 21:42:57 +0200 Subject: [PATCH 10/11] add a test for the item type filtering --- internal/provider/data_source_item_login_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/internal/provider/data_source_item_login_test.go b/internal/provider/data_source_item_login_test.go index 1b094b3..150e0ea 100644 --- a/internal/provider/data_source_item_login_test.go +++ b/internal/provider/data_source_item_login_test.go @@ -67,6 +67,13 @@ func TestAccDataSourceItemLoginBySearch(t *testing.T) { Config: tfConfigProvider() + tfConfigResourceFolder() + tfConfigResourceItemLogin() + tfConfigDataItemLoginWithSearchAndOrg("missing-item"), ExpectError: regexp.MustCompile("Error: no object found matching the filter"), }, + { + Config: tfConfigProvider() + tfConfigResourceFolder() + tfConfigResourceItemSecureNote(), + }, + { + Config: tfConfigProvider() + tfConfigResourceFolder() + tfConfigResourceItemSecureNote() + tfConfigDataItemLoginWithSearchAndOrg("secure-bar"), + ExpectError: regexp.MustCompile("Error: no object found matching the filter"), + }, }, }) } From cc65e9a833b78ac13b78a861dbec51d0837af7b4 Mon Sep 17 00:00:00 2001 From: Maxime Lagresle Date: Fri, 19 Apr 2024 22:11:51 +0200 Subject: [PATCH 11/11] fix mixing item types --- .../provider/data_source_item_login_test.go | 28 +++++++++++++++++++ internal/provider/object.go | 22 +++++++++------ 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/internal/provider/data_source_item_login_test.go b/internal/provider/data_source_item_login_test.go index 150e0ea..deb7e9d 100644 --- a/internal/provider/data_source_item_login_test.go +++ b/internal/provider/data_source_item_login_test.go @@ -39,6 +39,23 @@ func TestAccDataSourceItemLoginFailsOnInexistentItem(t *testing.T) { }) } +func TestAccDataSourceItemLoginFailsOnWrongResourceType(t *testing.T) { + ensureVaultwardenConfigured(t) + + resource.UnitTest(t, resource.TestCase{ + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: tfConfigProvider() + tfConfigResourceFolder() + tfConfigResourceItemSecureNote(), + }, + { + Config: tfConfigProvider() + tfConfigResourceFolder() + tfConfigResourceItemSecureNote() + tfConfigDataItemLoginCrossReference(), + ExpectError: regexp.MustCompile("Error: returned object type does not match requested object type"), + }, + }, + }) +} + func TestAccDataSourceItemLoginBySearch(t *testing.T) { resourceName := "bitwarden_item_login.foo" @@ -109,6 +126,7 @@ func tfConfigResourceItemLoginDuplicate() string { } ` } + func tfConfigDataItemLogin() string { return ` data "bitwarden_item_login" "foo_data" { @@ -119,6 +137,16 @@ data "bitwarden_item_login" "foo_data" { ` } +func tfConfigDataItemLoginCrossReference() string { + return ` +data "bitwarden_item_login" "foo_data" { + provider = bitwarden + + id = bitwarden_item_secure_note.foo.id +} +` +} + func tfConfigInexistentDataItemLogin() string { return ` data "bitwarden_item_login" "foo_data" { diff --git a/internal/provider/object.go b/internal/provider/object.go index c032632..76f4a59 100644 --- a/internal/provider/object.go +++ b/internal/provider/object.go @@ -22,16 +22,22 @@ func objectRead(ctx context.Context, d *schema.ResourceData, meta interface{}) d return diag.FromErr(objectOperation(ctx, d, func(secret bw.Object) (*bw.Object, error) { obj, err := meta.(bw.Client).GetObject(string(secret.Object), secret.ID) + if obj != nil { + // If the object exists but is marked as soft deleted, we return an error, because relying + // on an object in the 'trash' sounds like a bad idea. + if obj.DeletedDate != nil { + return nil, errors.New("object is soft deleted") + } - // If the object exists but is marked as soft deleted, we return an error, because relying - // on an object in the 'trash' sounds like a bad idea. - if obj != nil && obj.DeletedDate != nil { - return nil, errors.New("object is soft deleted") - } + if obj.ID != secret.ID { + return nil, errors.New("returned object ID does not match requested object ID") + } - if obj != nil && obj.ID != secret.ID { - return nil, errors.New("returned object ID does not match requested object ID") + if obj.Type != secret.Type { + return nil, errors.New("returned object type does not match requested object type") + } } + return obj, err })) } @@ -48,7 +54,7 @@ func objectSearch(d *schema.ResourceData, meta interface{}) error { } // If the object is an item, also filter by type to avoid returning a login when a secure note is expected. - if objType == bw.ObjectTypeItem { + if bw.ObjectType(objType.(string)) == bw.ObjectTypeItem { itemType, ok := d.GetOk(attributeType) if !ok { return fmt.Errorf("BUG: item type not set in the resource data")