From f6d745339f309fd25620018467c71ed6726540cb Mon Sep 17 00:00:00 2001 From: Maxime Lagresle Date: Sun, 29 Sep 2024 15:14:07 +0200 Subject: [PATCH 1/2] move and rename models --- internal/bitwarden/bw/client_options.go | 34 ---- internal/bitwarden/bw/filter.go | 15 -- internal/bitwarden/{bw => bwcli}/client.go | 85 ++++++---- .../bitwarden/{bw => bwcli}/client_test.go | 20 +-- internal/bitwarden/{bw => bwcli}/errors.go | 11 +- internal/bitwarden/bwcli/filter.go | 17 ++ internal/bitwarden/bwcli/models.go | 38 +++++ .../bitwarden/{bw => bwcli}/models_test.go | 2 +- .../bitwarden/{bw => bwcli}/retry_handler.go | 2 +- internal/bitwarden/client.go | 25 +++ internal/bitwarden/client_options.go | 56 +++++++ .../{webapi => }/crypto/aes_encoding.go | 0 .../crypto/encryptedstring/encryptedstring.go | 2 +- .../{webapi => }/crypto/encryption.go | 4 +- .../{webapi => }/crypto/encryption_test.go | 2 +- .../{webapi => }/crypto/hash_password.go | 2 +- .../{webapi => }/crypto/hash_password_test.go | 2 +- .../crypto/keybuilder/encryption_key.go | 4 +- .../crypto/keybuilder/key_pair.go | 4 +- .../crypto/keybuilder/prelogin_key.go | 2 +- .../crypto/keybuilder/share_key.go | 2 +- .../crypto/symmetrickey/symmetrickey.go | 0 internal/bitwarden/{bw => models}/models.go | 8 +- internal/bitwarden/webapi/client.go | 4 +- internal/provider/attachment.go | 25 +-- internal/provider/data_source.go | 6 +- internal/provider/data_source_folder.go | 4 +- internal/provider/data_source_item_login.go | 4 +- .../provider/data_source_item_secure_note.go | 4 +- .../provider/data_source_org_collection.go | 4 +- internal/provider/data_source_organization.go | 4 +- internal/provider/object.go | 148 +++++++++--------- internal/provider/provider.go | 31 ++-- internal/provider/provider_utils_test.go | 15 +- internal/provider/resource.go | 6 +- internal/provider/resource_folder.go | 6 +- internal/provider/resource_item_login.go | 6 +- internal/provider/resource_item_login_test.go | 4 +- .../provider/resource_item_secure_note.go | 6 +- internal/provider/resource_org_collection.go | 6 +- 40 files changed, 367 insertions(+), 253 deletions(-) delete mode 100644 internal/bitwarden/bw/client_options.go delete mode 100644 internal/bitwarden/bw/filter.go rename internal/bitwarden/{bw => bwcli}/client.go (72%) rename internal/bitwarden/{bw => bwcli}/client_test.go (76%) rename internal/bitwarden/{bw => bwcli}/errors.go (80%) create mode 100644 internal/bitwarden/bwcli/filter.go create mode 100644 internal/bitwarden/bwcli/models.go rename internal/bitwarden/{bw => bwcli}/models_test.go (99%) rename internal/bitwarden/{bw => bwcli}/retry_handler.go (97%) create mode 100644 internal/bitwarden/client.go create mode 100644 internal/bitwarden/client_options.go rename internal/bitwarden/{webapi => }/crypto/aes_encoding.go (100%) rename internal/bitwarden/{webapi => }/crypto/encryptedstring/encryptedstring.go (98%) rename internal/bitwarden/{webapi => }/crypto/encryption.go (98%) rename internal/bitwarden/{webapi => }/crypto/encryption_test.go (99%) rename internal/bitwarden/{webapi => }/crypto/hash_password.go (92%) rename internal/bitwarden/{webapi => }/crypto/hash_password_test.go (93%) rename internal/bitwarden/{webapi => }/crypto/keybuilder/encryption_key.go (95%) rename internal/bitwarden/{webapi => }/crypto/keybuilder/key_pair.go (94%) rename internal/bitwarden/{webapi => }/crypto/keybuilder/prelogin_key.go (93%) rename internal/bitwarden/{webapi => }/crypto/keybuilder/share_key.go (96%) rename internal/bitwarden/{webapi => }/crypto/symmetrickey/symmetrickey.go (100%) rename internal/bitwarden/{bw => models}/models.go (96%) diff --git a/internal/bitwarden/bw/client_options.go b/internal/bitwarden/bw/client_options.go deleted file mode 100644 index 9b12dcc..0000000 --- a/internal/bitwarden/bw/client_options.go +++ /dev/null @@ -1,34 +0,0 @@ -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/bitwarden/bw/filter.go b/internal/bitwarden/bw/filter.go deleted file mode 100644 index 27feed5..0000000 --- a/internal/bitwarden/bw/filter.go +++ /dev/null @@ -1,15 +0,0 @@ -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/bitwarden/bw/client.go b/internal/bitwarden/bwcli/client.go similarity index 72% rename from internal/bitwarden/bw/client.go rename to internal/bitwarden/bwcli/client.go index 6729ec5..2896f16 100644 --- a/internal/bitwarden/bw/client.go +++ b/internal/bitwarden/bwcli/client.go @@ -1,4 +1,4 @@ -package bw +package bwcli import ( "context" @@ -7,23 +7,25 @@ import ( "fmt" "os" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/models" "github.com/maxlaverse/terraform-provider-bitwarden/internal/command" ) -type Client interface { - CreateAttachment(ctx context.Context, itemId, filePath string) (*Object, error) - CreateObject(context.Context, Object) (*Object, error) - EditObject(context.Context, Object) (*Object, error) +type CLIClient interface { + CreateAttachment(ctx context.Context, itemId, filePath string) (*models.Object, error) + CreateObject(context.Context, models.Object) (*models.Object, error) + EditObject(context.Context, models.Object) (*models.Object, error) GetAttachment(ctx context.Context, itemId, attachmentId string) ([]byte, error) - GetObject(context.Context, Object) (*Object, error) + GetObject(context.Context, models.Object) (*models.Object, error) GetSessionKey() string HasSessionKey() bool - ListObjects(ctx context.Context, objType string, options ...ListObjectsOption) ([]Object, error) + ListObjects(ctx context.Context, objType models.ObjectType, options ...bitwarden.ListObjectsOption) ([]models.Object, error) LoginWithAPIKey(ctx context.Context, password, clientId, clientSecret string) error LoginWithPassword(ctx context.Context, username, password string) error Logout(context.Context) error DeleteAttachment(ctx context.Context, itemId, attachmentId string) error - DeleteObject(context.Context, Object) error + DeleteObject(context.Context, models.Object) error SetServer(context.Context, string) error SetSessionKey(string) Status(context.Context) (*Status, error) @@ -31,7 +33,7 @@ type Client interface { Unlock(ctx context.Context, password string) error } -func NewClient(execPath string, opts ...Options) Client { +func NewClient(execPath string, opts ...Options) CLIClient { c := &client{ execPath: execPath, } @@ -55,33 +57,33 @@ type client struct { sessionKey string } -type Options func(c Client) +type Options func(c bitwarden.Client) func WithAppDataDir(appDataDir string) Options { - return func(c Client) { + return func(c bitwarden.Client) { c.(*client).appDataDir = appDataDir } } func WithExtraCACertsPath(extraCACertsPath string) Options { - return func(c Client) { + return func(c bitwarden.Client) { c.(*client).extraCACertsPath = extraCACertsPath } } func DisableSync() Options { - return func(c Client) { + return func(c bitwarden.Client) { c.(*client).disableSync = true } } func DisableRetryBackoff() Options { - return func(c Client) { + return func(c bitwarden.Client) { c.(*client).disableRetryBackoff = true } } -func (c *client) CreateObject(ctx context.Context, obj Object) (*Object, error) { +func (c *client) CreateObject(ctx context.Context, obj models.Object) (*models.Object, error) { objEncoded, err := c.encode(obj) if err != nil { return nil, err @@ -93,7 +95,7 @@ func (c *client) CreateObject(ctx context.Context, obj Object) (*Object, error) objEncoded, } - if obj.Object == ObjectTypeOrgCollection { + if obj.Object == models.ObjectTypeOrgCollection { args = append(args, "--organizationid", obj.OrganizationID) } @@ -111,13 +113,13 @@ func (c *client) CreateObject(ctx context.Context, obj Object) (*Object, error) return &obj, nil } -func (c *client) CreateAttachment(ctx context.Context, itemId string, filePath string) (*Object, error) { - out, err := c.cmdWithSession("create", string(ObjectTypeAttachment), "--itemid", itemId, "--file", filePath).Run(ctx) +func (c *client) CreateAttachment(ctx context.Context, itemId string, filePath string) (*models.Object, error) { + out, err := c.cmdWithSession("create", string(models.ObjectTypeAttachment), "--itemid", itemId, "--file", filePath).Run(ctx) if err != nil { return nil, err } - var obj Object + var obj models.Object err = json.Unmarshal(out, &obj) if err != nil { return nil, err @@ -128,7 +130,7 @@ func (c *client) CreateAttachment(ctx context.Context, itemId string, filePath s return &obj, nil } -func (c *client) EditObject(ctx context.Context, obj Object) (*Object, error) { +func (c *client) EditObject(ctx context.Context, obj models.Object) (*models.Object, error) { objEncoded, err := c.encode(obj) if err != nil { return nil, err @@ -157,14 +159,14 @@ func (c *client) EditObject(ctx context.Context, obj Object) (*Object, error) { return &obj, nil } -func (c *client) GetObject(ctx context.Context, obj Object) (*Object, error) { +func (c *client) GetObject(ctx context.Context, obj models.Object) (*models.Object, error) { args := []string{ "get", string(obj.Object), obj.ID, } - if obj.Object == ObjectTypeOrgCollection { + if obj.Object == models.ObjectTypeOrgCollection { args = append(args, "--organizationid", obj.OrganizationID) } @@ -182,7 +184,7 @@ func (c *client) GetObject(ctx context.Context, obj Object) (*Object, error) { } func (c *client) GetAttachment(ctx context.Context, itemId, attachmentId string) ([]byte, error) { - out, err := c.cmdWithSession("get", string(ObjectTypeAttachment), attachmentId, "--itemid", itemId, "--raw").Run(ctx) + out, err := c.cmdWithSession("get", string(models.ObjectTypeAttachment), attachmentId, "--itemid", itemId, "--raw").Run(ctx) if err != nil { return nil, remapError(err) } @@ -195,22 +197,20 @@ func (c *client) GetSessionKey() string { } // ListObjects returns objects of a given type matching given filters. -func (c *client) ListObjects(ctx context.Context, objType string, options ...ListObjectsOption) ([]Object, error) { +func (c *client) ListObjects(ctx context.Context, objType models.ObjectType, options ...bitwarden.ListObjectsOption) ([]models.Object, error) { args := []string{ "list", - objType, + string(objType), } - for _, applyOption := range options { - applyOption(&args) - } + applyFiltersToArgs(&args, options...) out, err := c.cmdWithSession(args...).Run(ctx) if err != nil { return nil, remapError(err) } - var obj []Object + var obj []models.Object err = json.Unmarshal(out, &obj) if err != nil { return nil, newUnmarshallError(err, args[0:2], out) @@ -245,14 +245,14 @@ func (c *client) Logout(ctx context.Context) error { return err } -func (c *client) DeleteObject(ctx context.Context, obj Object) error { +func (c *client) DeleteObject(ctx context.Context, obj models.Object) error { args := []string{ "delete", string(obj.Object), obj.ID, } - if obj.Object == ObjectTypeOrgCollection { + if obj.Object == models.ObjectTypeOrgCollection { args = append(args, "--organizationid", obj.OrganizationID) } @@ -261,7 +261,7 @@ func (c *client) DeleteObject(ctx context.Context, obj Object) error { } func (c *client) DeleteAttachment(ctx context.Context, itemId, attachmentId string) error { - _, err := c.cmdWithSession("delete", string(ObjectTypeAttachment), attachmentId, "--itemid", itemId).Run(ctx) + _, err := c.cmdWithSession("delete", string(models.ObjectTypeAttachment), attachmentId, "--itemid", itemId).Run(ctx) return err } @@ -331,10 +331,29 @@ func (c *client) env() []string { return defaultEnv } -func (c *client) encode(item Object) (string, error) { +func (c *client) encode(item models.Object) (string, error) { newOut, err := json.Marshal(item) if err != nil { return "", fmt.Errorf("marshalling error: %v, %v", err, string(newOut)) } return base64.RawStdEncoding.EncodeToString(newOut), nil } + +func applyFiltersToArgs(args *[]string, options ...bitwarden.ListObjectsOption) { + filters := bitwarden.ListObjectsOptionsToFilterOptions(options...) + if filters.OrganizationFilter != "" { + *args = append(*args, "--organizationid", filters.OrganizationFilter) + } + if filters.FolderFilter != "" { + *args = append(*args, "--folderid", filters.FolderFilter) + } + if filters.CollectionFilter != "" { + *args = append(*args, "--collectionid", filters.CollectionFilter) + } + if filters.SearchFilter != "" { + *args = append(*args, "--search", filters.SearchFilter) + } + if filters.UrlFilter != "" { + *args = append(*args, "--url", filters.UrlFilter) + } +} diff --git a/internal/bitwarden/bw/client_test.go b/internal/bitwarden/bwcli/client_test.go similarity index 76% rename from internal/bitwarden/bw/client_test.go rename to internal/bitwarden/bwcli/client_test.go index 445e55b..fa7d362 100644 --- a/internal/bitwarden/bw/client_test.go +++ b/internal/bitwarden/bwcli/client_test.go @@ -1,9 +1,11 @@ -package bw +package bwcli import ( "context" "testing" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/models" test_command "github.com/maxlaverse/terraform-provider-bitwarden/internal/command/test" "github.com/stretchr/testify/assert" ) @@ -15,10 +17,10 @@ func TestCreateObjectEncoding(t *testing.T) { defer removeMocks(t) b := NewClient("dummy") - _, err := b.CreateObject(context.Background(), Object{ - Type: ItemTypeLogin, - Object: ObjectTypeItem, - Fields: []Field{ + _, err := b.CreateObject(context.Background(), models.Object{ + Type: models.ItemTypeLogin, + Object: models.ObjectTypeItem, + Fields: []models.Field{ { Name: "test", Value: "passed", @@ -40,7 +42,7 @@ func TestListObjects(t *testing.T) { defer removeMocks(t) b := NewClient("dummy") - _, err := b.ListObjects(context.Background(), "item", WithFolderID("folder-id"), WithCollectionID("collection-id"), WithSearch("search")) + _, err := b.ListObjects(context.Background(), "item", bitwarden.WithFolderID("folder-id"), bitwarden.WithCollectionID("collection-id"), bitwarden.WithSearch("search")) assert.NoError(t, err) if assert.Len(t, commandsExecuted(), 1) { @@ -55,7 +57,7 @@ func TestGetItem(t *testing.T) { defer removeMocks(t) b := NewClient("dummy") - _, err := b.GetObject(context.Background(), Object{ID: "object-id", Object: ObjectTypeItem, Type: ItemTypeLogin}) + _, err := b.GetObject(context.Background(), models.Object{ID: "object-id", Object: models.ObjectTypeItem, Type: models.ItemTypeLogin}) assert.NoError(t, err) if assert.Len(t, commandsExecuted(), 1) { @@ -70,7 +72,7 @@ func TestGetOrgCollection(t *testing.T) { defer removeMocks(t) b := NewClient("dummy") - _, err := b.GetObject(context.Background(), Object{ID: "object-id", Object: ObjectTypeOrgCollection, OrganizationID: "org-id"}) + _, err := b.GetObject(context.Background(), models.Object{ID: "object-id", Object: models.ObjectTypeOrgCollection, OrganizationID: "org-id"}) assert.NoError(t, err) if assert.Len(t, commandsExecuted(), 1) { @@ -85,7 +87,7 @@ func TestErrorContainsCommand(t *testing.T) { defer removeMocks(t) b := NewClient("dummy") - _, err := b.ListObjects(context.Background(), "org-collection", WithSearch("search")) + _, err := b.ListObjects(context.Background(), "org-collection", bitwarden.WithSearch("search")) if assert.Error(t, err) { assert.ErrorContains(t, err, "unable to parse result of 'list org-collection', error: 'unexpected end of JSON input', output: ''") diff --git a/internal/bitwarden/bw/errors.go b/internal/bitwarden/bwcli/errors.go similarity index 80% rename from internal/bitwarden/bw/errors.go rename to internal/bitwarden/bwcli/errors.go index 11fd0c5..959bce9 100644 --- a/internal/bitwarden/bw/errors.go +++ b/internal/bitwarden/bwcli/errors.go @@ -1,18 +1,15 @@ -package bw +package bwcli import ( - "errors" "fmt" "regexp" "strings" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/models" "github.com/maxlaverse/terraform-provider-bitwarden/internal/command" ) var ( - ErrObjectNotFound = errors.New("object not found") - ErrAttachmentNotFound = errors.New("attachment not found") - attachmentNotFoundRegexp = regexp.MustCompile(`^Attachment .* was not found.$`) ) @@ -25,9 +22,9 @@ func remapError(err error) error { if ok { switch { case isObjectNotFoundError(v): - return ErrObjectNotFound + return models.ErrObjectNotFound case isAttachmentNotFoundError(v): - return ErrAttachmentNotFound + return models.ErrAttachmentNotFound } } return err diff --git a/internal/bitwarden/bwcli/filter.go b/internal/bitwarden/bwcli/filter.go new file mode 100644 index 0000000..8145c24 --- /dev/null +++ b/internal/bitwarden/bwcli/filter.go @@ -0,0 +1,17 @@ +package bwcli + +import "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/models" + +func FilterObjectsByType(objs []models.Object, itemType models.ItemType) []models.Object { + if itemType == 0 { + return objs + } + + filtered := make([]models.Object, 0, len(objs)) + for _, obj := range objs { + if obj.Type == itemType { + filtered = append(filtered, obj) + } + } + return filtered +} diff --git a/internal/bitwarden/bwcli/models.go b/internal/bitwarden/bwcli/models.go new file mode 100644 index 0000000..dc82536 --- /dev/null +++ b/internal/bitwarden/bwcli/models.go @@ -0,0 +1,38 @@ +package bwcli + +import ( + "strings" + "time" + + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden" +) + +type VaultStatus string + +const ( + StatusLocked VaultStatus = "locked" + StatusUnauthenticated VaultStatus = "unauthenticated" + StatusUnlocked VaultStatus = "unlocked" +) + +type Status struct { + ServerURL string `json:"serverURL,omitempty"` + LastSync time.Time `json:"lastSync,omitempty"` + UserEmail string `json:"userEmail,omitempty"` + UserID string `json:"userID,omitempty"` + Status VaultStatus `json:"status,omitempty"` +} + +func (s *Status) VaultFromServer(serverUrl string) bool { + providerServerUrl := trimSlashSuffix(serverUrl) + vaultServerUrl := trimSlashSuffix(s.ServerURL) + return vaultServerUrl == providerServerUrl || len(vaultServerUrl) == 0 && providerServerUrl == bitwarden.DefaultBitwardenServerURL +} + +func (s *Status) VaultOfUser(email string) bool { + return s.UserEmail == email +} + +func trimSlashSuffix(serverUrl string) string { + return strings.TrimSuffix(serverUrl, "/") +} diff --git a/internal/bitwarden/bw/models_test.go b/internal/bitwarden/bwcli/models_test.go similarity index 99% rename from internal/bitwarden/bw/models_test.go rename to internal/bitwarden/bwcli/models_test.go index d2f0994..3d1dc56 100644 --- a/internal/bitwarden/bw/models_test.go +++ b/internal/bitwarden/bwcli/models_test.go @@ -1,4 +1,4 @@ -package bw +package bwcli import ( "testing" diff --git a/internal/bitwarden/bw/retry_handler.go b/internal/bitwarden/bwcli/retry_handler.go similarity index 97% rename from internal/bitwarden/bw/retry_handler.go rename to internal/bitwarden/bwcli/retry_handler.go index 6040487..d17f75d 100644 --- a/internal/bitwarden/bw/retry_handler.go +++ b/internal/bitwarden/bwcli/retry_handler.go @@ -1,4 +1,4 @@ -package bw +package bwcli import ( "math" diff --git a/internal/bitwarden/client.go b/internal/bitwarden/client.go new file mode 100644 index 0000000..4899d10 --- /dev/null +++ b/internal/bitwarden/client.go @@ -0,0 +1,25 @@ +package bitwarden + +import ( + "context" + + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/models" +) + +const ( + DefaultBitwardenServerURL = "https://vault.bitwarden.com" +) + +type Client interface { + CreateAttachment(ctx context.Context, itemId, filePath string) (*models.Object, error) + CreateObject(context.Context, models.Object) (*models.Object, error) + DeleteAttachment(ctx context.Context, itemId, attachmentId string) error + DeleteObject(context.Context, models.Object) error + EditObject(context.Context, models.Object) (*models.Object, error) + GetAttachment(ctx context.Context, itemId, attachmentId string) ([]byte, error) + GetObject(context.Context, models.Object) (*models.Object, error) + ListObjects(ctx context.Context, objType models.ObjectType, options ...ListObjectsOption) ([]models.Object, error) + LoginWithAPIKey(ctx context.Context, password, clientId, clientSecret string) error + LoginWithPassword(ctx context.Context, username, password string) error + Sync(context.Context) error +} diff --git a/internal/bitwarden/client_options.go b/internal/bitwarden/client_options.go new file mode 100644 index 0000000..80e52af --- /dev/null +++ b/internal/bitwarden/client_options.go @@ -0,0 +1,56 @@ +package bitwarden + +type ListObjectsOptionGenerator func(id string) ListObjectsOption +type ListObjectsFilterOptions struct { + CollectionFilter string + FolderFilter string + OrganizationFilter string + SearchFilter string + UrlFilter string +} + +func (f *ListObjectsFilterOptions) IsValid() bool { + return f.SearchFilter != "" +} + +type ListObjectsOption func(filters *ListObjectsFilterOptions) + +func WithCollectionID(id string) ListObjectsOption { + return func(f *ListObjectsFilterOptions) { + f.CollectionFilter = id + } +} + +func WithFolderID(id string) ListObjectsOption { + return func(f *ListObjectsFilterOptions) { + f.FolderFilter = id + } +} + +func WithOrganizationID(id string) ListObjectsOption { + return func(f *ListObjectsFilterOptions) { + f.OrganizationFilter = id + } +} + +func WithSearch(search string) ListObjectsOption { + return func(f *ListObjectsFilterOptions) { + f.SearchFilter = search + } +} + +func WithUrl(url string) ListObjectsOption { + return func(f *ListObjectsFilterOptions) { + f.UrlFilter = url + } +} + +func ListObjectsOptionsToFilterOptions(options ...ListObjectsOption) ListObjectsFilterOptions { + filter := ListObjectsFilterOptions{} + + for _, option := range options { + option(&filter) + } + + return filter +} diff --git a/internal/bitwarden/webapi/crypto/aes_encoding.go b/internal/bitwarden/crypto/aes_encoding.go similarity index 100% rename from internal/bitwarden/webapi/crypto/aes_encoding.go rename to internal/bitwarden/crypto/aes_encoding.go diff --git a/internal/bitwarden/webapi/crypto/encryptedstring/encryptedstring.go b/internal/bitwarden/crypto/encryptedstring/encryptedstring.go similarity index 98% rename from internal/bitwarden/webapi/crypto/encryptedstring/encryptedstring.go rename to internal/bitwarden/crypto/encryptedstring/encryptedstring.go index 7d860bb..9790bd8 100644 --- a/internal/bitwarden/webapi/crypto/encryptedstring/encryptedstring.go +++ b/internal/bitwarden/crypto/encryptedstring/encryptedstring.go @@ -6,7 +6,7 @@ import ( "strconv" "strings" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/webapi/crypto/symmetrickey" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/crypto/symmetrickey" ) type EncryptedString struct { diff --git a/internal/bitwarden/webapi/crypto/encryption.go b/internal/bitwarden/crypto/encryption.go similarity index 98% rename from internal/bitwarden/webapi/crypto/encryption.go rename to internal/bitwarden/crypto/encryption.go index f6e7e2d..638a250 100644 --- a/internal/bitwarden/webapi/crypto/encryption.go +++ b/internal/bitwarden/crypto/encryption.go @@ -10,8 +10,8 @@ import ( "fmt" "hash" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/webapi/crypto/encryptedstring" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/webapi/crypto/symmetrickey" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/crypto/encryptedstring" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/crypto/symmetrickey" ) func Encrypt(plainValue []byte, key symmetrickey.Key) (string, error) { diff --git a/internal/bitwarden/webapi/crypto/encryption_test.go b/internal/bitwarden/crypto/encryption_test.go similarity index 99% rename from internal/bitwarden/webapi/crypto/encryption_test.go rename to internal/bitwarden/crypto/encryption_test.go index 96647a2..6828048 100644 --- a/internal/bitwarden/webapi/crypto/encryption_test.go +++ b/internal/bitwarden/crypto/encryption_test.go @@ -5,7 +5,7 @@ import ( "encoding/base64" "testing" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/webapi/crypto/symmetrickey" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/crypto/symmetrickey" "github.com/stretchr/testify/assert" ) diff --git a/internal/bitwarden/webapi/crypto/hash_password.go b/internal/bitwarden/crypto/hash_password.go similarity index 92% rename from internal/bitwarden/webapi/crypto/hash_password.go rename to internal/bitwarden/crypto/hash_password.go index cea0c5a..38c9256 100644 --- a/internal/bitwarden/webapi/crypto/hash_password.go +++ b/internal/bitwarden/crypto/hash_password.go @@ -4,7 +4,7 @@ import ( "crypto/sha256" "encoding/base64" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/webapi/crypto/symmetrickey" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/crypto/symmetrickey" "golang.org/x/crypto/pbkdf2" ) diff --git a/internal/bitwarden/webapi/crypto/hash_password_test.go b/internal/bitwarden/crypto/hash_password_test.go similarity index 93% rename from internal/bitwarden/webapi/crypto/hash_password_test.go rename to internal/bitwarden/crypto/hash_password_test.go index a779807..170d06e 100644 --- a/internal/bitwarden/webapi/crypto/hash_password_test.go +++ b/internal/bitwarden/crypto/hash_password_test.go @@ -4,7 +4,7 @@ import ( "crypto/sha256" "testing" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/webapi/crypto/symmetrickey" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/crypto/symmetrickey" "github.com/stretchr/testify/assert" "golang.org/x/crypto/pbkdf2" ) diff --git a/internal/bitwarden/webapi/crypto/keybuilder/encryption_key.go b/internal/bitwarden/crypto/keybuilder/encryption_key.go similarity index 95% rename from internal/bitwarden/webapi/crypto/keybuilder/encryption_key.go rename to internal/bitwarden/crypto/keybuilder/encryption_key.go index 48e8e64..29a7c86 100644 --- a/internal/bitwarden/webapi/crypto/keybuilder/encryption_key.go +++ b/internal/bitwarden/crypto/keybuilder/encryption_key.go @@ -4,8 +4,8 @@ import ( "crypto/rand" "fmt" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/webapi/crypto" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/webapi/crypto/symmetrickey" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/crypto" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/crypto/symmetrickey" ) func GenerateEncryptionKey(key symmetrickey.Key) (*symmetrickey.Key, string, error) { diff --git a/internal/bitwarden/webapi/crypto/keybuilder/key_pair.go b/internal/bitwarden/crypto/keybuilder/key_pair.go similarity index 94% rename from internal/bitwarden/webapi/crypto/keybuilder/key_pair.go rename to internal/bitwarden/crypto/keybuilder/key_pair.go index 066e1f0..8ded6d2 100644 --- a/internal/bitwarden/webapi/crypto/keybuilder/key_pair.go +++ b/internal/bitwarden/crypto/keybuilder/key_pair.go @@ -7,8 +7,8 @@ import ( "encoding/base64" "fmt" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/webapi/crypto" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/webapi/crypto/symmetrickey" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/crypto" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/crypto/symmetrickey" ) func GenerateKeyPair(key symmetrickey.Key) (string, string, error) { diff --git a/internal/bitwarden/webapi/crypto/keybuilder/prelogin_key.go b/internal/bitwarden/crypto/keybuilder/prelogin_key.go similarity index 93% rename from internal/bitwarden/webapi/crypto/keybuilder/prelogin_key.go rename to internal/bitwarden/crypto/keybuilder/prelogin_key.go index 2fc4ac8..29351d6 100644 --- a/internal/bitwarden/webapi/crypto/keybuilder/prelogin_key.go +++ b/internal/bitwarden/crypto/keybuilder/prelogin_key.go @@ -3,7 +3,7 @@ package keybuilder import ( "crypto/sha256" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/webapi/crypto/symmetrickey" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/crypto/symmetrickey" "golang.org/x/crypto/pbkdf2" ) diff --git a/internal/bitwarden/webapi/crypto/keybuilder/share_key.go b/internal/bitwarden/crypto/keybuilder/share_key.go similarity index 96% rename from internal/bitwarden/webapi/crypto/keybuilder/share_key.go rename to internal/bitwarden/crypto/keybuilder/share_key.go index f71ab0f..e75f726 100644 --- a/internal/bitwarden/webapi/crypto/keybuilder/share_key.go +++ b/internal/bitwarden/crypto/keybuilder/share_key.go @@ -7,7 +7,7 @@ import ( "encoding/base64" "fmt" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/webapi/crypto/symmetrickey" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/crypto/symmetrickey" ) func GenerateShareKey(publicKey *rsa.PublicKey) (string, *symmetrickey.Key, error) { diff --git a/internal/bitwarden/webapi/crypto/symmetrickey/symmetrickey.go b/internal/bitwarden/crypto/symmetrickey/symmetrickey.go similarity index 100% rename from internal/bitwarden/webapi/crypto/symmetrickey/symmetrickey.go rename to internal/bitwarden/crypto/symmetrickey/symmetrickey.go diff --git a/internal/bitwarden/bw/models.go b/internal/bitwarden/models/models.go similarity index 96% rename from internal/bitwarden/bw/models.go rename to internal/bitwarden/models/models.go index eeb9edf..02c8cef 100644 --- a/internal/bitwarden/bw/models.go +++ b/internal/bitwarden/models/models.go @@ -1,10 +1,16 @@ -package bw +package models import ( + "errors" "strings" "time" ) +var ( + ErrObjectNotFound = errors.New("object not found") + ErrAttachmentNotFound = errors.New("attachment not found") +) + type ItemType int const ( diff --git a/internal/bitwarden/webapi/client.go b/internal/bitwarden/webapi/client.go index a168a38..6177e5b 100644 --- a/internal/bitwarden/webapi/client.go +++ b/internal/bitwarden/webapi/client.go @@ -11,8 +11,8 @@ import ( "net/url" "strings" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/webapi/crypto" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/webapi/crypto/keybuilder" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/crypto" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/crypto/keybuilder" ) /* diff --git a/internal/provider/attachment.go b/internal/provider/attachment.go index fa48719..9a6895c 100644 --- a/internal/provider/attachment.go +++ b/internal/provider/attachment.go @@ -10,19 +10,20 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/bw" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/models" ) func attachmentCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { itemId := d.Get(attributeAttachmentItemID).(string) - existingAttachments, err := listExistingAttachments(ctx, meta.(bw.Client), itemId) + existingAttachments, err := listExistingAttachments(ctx, meta.(bitwarden.Client), itemId) if err != nil { return diag.FromErr(err) } filePath := d.Get(attributeAttachmentFile).(string) - obj, err := meta.(bw.Client).CreateAttachment(ctx, itemId, filePath) + obj, err := meta.(bitwarden.Client).CreateAttachment(ctx, itemId, filePath) if err != nil { return diag.FromErr(err) } @@ -42,7 +43,7 @@ func attachmentCreate(ctx context.Context, d *schema.ResourceData, meta interfac func attachmentRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { itemId := d.Get(attributeAttachmentItemID).(string) - obj, err := meta.(bw.Client).GetObject(ctx, bw.Object{ID: itemId, Object: bw.ObjectTypeItem}) + obj, err := meta.(bitwarden.Client).GetObject(ctx, models.Object{ID: itemId, Object: models.ObjectTypeItem}) if err != nil { // If the item is not found, we can't simply consider the attachment as // deleted, because we won't have an item to attach it to. @@ -66,10 +67,10 @@ func attachmentRead(ctx context.Context, d *schema.ResourceData, meta interface{ func attachmentDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { itemId := d.Get(attributeAttachmentItemID).(string) - return diag.FromErr(meta.(bw.Client).DeleteAttachment(ctx, itemId, d.Id())) + return diag.FromErr(meta.(bitwarden.Client).DeleteAttachment(ctx, itemId, d.Id())) } -func attachmentDataFromStruct(d *schema.ResourceData, attachment bw.Attachment) error { +func attachmentDataFromStruct(d *schema.ResourceData, attachment models.Attachment) error { d.SetId(attachment.ID) err := d.Set(attributeAttachmentFileName, attachment.FileName) @@ -100,7 +101,7 @@ func readDataSourceAttachment() schema.ReadContextFunc { attachmentId := d.Get(attributeID).(string) - content, err := meta.(bw.Client).GetAttachment(ctx, itemId, attachmentId) + content, err := meta.(bitwarden.Client).GetAttachment(ctx, itemId, attachmentId) if err != nil { return diag.FromErr(err) } @@ -111,20 +112,20 @@ func readDataSourceAttachment() schema.ReadContextFunc { } } -func listExistingAttachments(ctx context.Context, client bw.Client, itemId string) ([]bw.Attachment, error) { - obj, err := client.GetObject(ctx, bw.Object{ID: itemId, Object: bw.ObjectTypeItem}) +func listExistingAttachments(ctx context.Context, client bitwarden.Client, itemId string) ([]models.Attachment, error) { + obj, err := client.GetObject(ctx, models.Object{ID: itemId, Object: models.ObjectTypeItem}) if err != nil { return nil, err } return obj.Attachments, nil } -func compareLists(listA []bw.Attachment, listB []bw.Attachment) ([]bw.Attachment, []bw.Attachment) { +func compareLists(listA []models.Attachment, listB []models.Attachment) ([]models.Attachment, []models.Attachment) { return itemsOnlyInSecondList(listB, listA), itemsOnlyInSecondList(listA, listB) } -func itemsOnlyInSecondList(firstList []bw.Attachment, secondList []bw.Attachment) []bw.Attachment { - result := []bw.Attachment{} +func itemsOnlyInSecondList(firstList []models.Attachment, secondList []models.Attachment) []models.Attachment { + result := []models.Attachment{} for _, secondAttachment := range secondList { found := false for _, firstAttachment := range firstList { diff --git a/internal/provider/data_source.go b/internal/provider/data_source.go index 6b25bc8..f950c60 100644 --- a/internal/provider/data_source.go +++ b/internal/provider/data_source.go @@ -5,10 +5,10 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/bw" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/models" ) -func readDataSourceItem(attrObject bw.ObjectType, attrType bw.ItemType) schema.ReadContextFunc { +func readDataSourceItem(attrObject models.ObjectType, attrType models.ItemType) schema.ReadContextFunc { return func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { d.SetId(d.Get(attributeID).(string)) err := d.Set(attributeObject, attrObject) @@ -23,7 +23,7 @@ func readDataSourceItem(attrObject bw.ObjectType, attrType bw.ItemType) schema.R } } -func readDataSourceObject(objType bw.ObjectType) schema.ReadContextFunc { +func readDataSourceObject(objType models.ObjectType) schema.ReadContextFunc { return func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { d.SetId(d.Get(attributeID).(string)) err := d.Set(attributeObject, objType) diff --git a/internal/provider/data_source_folder.go b/internal/provider/data_source_folder.go index 857a39e..b837486 100644 --- a/internal/provider/data_source_folder.go +++ b/internal/provider/data_source_folder.go @@ -2,13 +2,13 @@ package provider import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/bw" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/models" ) func dataSourceFolder() *schema.Resource { return &schema.Resource{ Description: "Use this data source to get information on an existing folder.", - ReadContext: readDataSourceObject(bw.ObjectTypeFolder), + ReadContext: readDataSourceObject(models.ObjectTypeFolder), Schema: folderSchema(DataSource), } } diff --git a/internal/provider/data_source_item_login.go b/internal/provider/data_source_item_login.go index 6ae82fd..082cd7a 100644 --- a/internal/provider/data_source_item_login.go +++ b/internal/provider/data_source_item_login.go @@ -2,7 +2,7 @@ package provider import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/bw" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/models" ) func dataSourceItemLogin() *schema.Resource { @@ -13,7 +13,7 @@ func dataSourceItemLogin() *schema.Resource { return &schema.Resource{ Description: "Use this data source to get information on an existing login item.", - ReadContext: readDataSourceItem(bw.ObjectTypeItem, bw.ItemTypeLogin), + ReadContext: readDataSourceItem(models.ObjectTypeItem, models.ItemTypeLogin), Schema: dataSourceItemLoginSchema, } } diff --git a/internal/provider/data_source_item_secure_note.go b/internal/provider/data_source_item_secure_note.go index 8f246e0..933b04e 100644 --- a/internal/provider/data_source_item_secure_note.go +++ b/internal/provider/data_source_item_secure_note.go @@ -2,7 +2,7 @@ package provider import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/bw" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/models" ) func dataSourceItemSecureNote() *schema.Resource { @@ -10,7 +10,7 @@ func dataSourceItemSecureNote() *schema.Resource { return &schema.Resource{ Description: "Use this data source to get information on an existing secure note item.", - ReadContext: readDataSourceItem(bw.ObjectTypeItem, bw.ItemTypeSecureNote), + ReadContext: readDataSourceItem(models.ObjectTypeItem, models.ItemTypeSecureNote), Schema: dataSourceItemSecureNoteSchema, } } diff --git a/internal/provider/data_source_org_collection.go b/internal/provider/data_source_org_collection.go index 6ba5591..e4473d3 100644 --- a/internal/provider/data_source_org_collection.go +++ b/internal/provider/data_source_org_collection.go @@ -2,13 +2,13 @@ package provider import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/bw" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/models" ) func dataSourceOrgCollection() *schema.Resource { return &schema.Resource{ Description: "Use this data source to get information on an existing organization collection.", - ReadContext: readDataSourceObject(bw.ObjectTypeOrgCollection), + ReadContext: readDataSourceObject(models.ObjectTypeOrgCollection), Schema: orgCollectionSchema(DataSource), } } diff --git a/internal/provider/data_source_organization.go b/internal/provider/data_source_organization.go index b9ac10f..2730c9e 100644 --- a/internal/provider/data_source_organization.go +++ b/internal/provider/data_source_organization.go @@ -2,13 +2,13 @@ package provider import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/bw" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/models" ) func dataSourceOrganization() *schema.Resource { return &schema.Resource{ Description: "Use this data source to get information on an existing organization.", - ReadContext: readDataSourceObject(bw.ObjectTypeOrganization), + ReadContext: readDataSourceObject(models.ObjectTypeOrganization), Schema: organizationSchema(), } } diff --git a/internal/provider/object.go b/internal/provider/object.go index 8ddfe6f..dde40e7 100644 --- a/internal/provider/object.go +++ b/internal/provider/object.go @@ -8,11 +8,13 @@ import ( "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/bw" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/bwcli" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/models" ) func objectCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - return diag.FromErr(objectOperation(ctx, d, meta.(bw.Client).CreateObject)) + return diag.FromErr(objectOperation(ctx, d, meta.(bitwarden.Client).CreateObject)) } func objectRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -20,8 +22,8 @@ func objectRead(ctx context.Context, d *schema.ResourceData, meta interface{}) d return diag.FromErr(objectSearch(ctx, d, meta)) } - return diag.FromErr(objectOperation(ctx, d, func(ctx context.Context, secret bw.Object) (*bw.Object, error) { - obj, err := meta.(bw.Client).GetObject(ctx, secret) + return diag.FromErr(objectOperation(ctx, d, func(ctx context.Context, secret models.Object) (*models.Object, error) { + obj, err := meta.(bitwarden.Client).GetObject(ctx, secret) 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. @@ -48,19 +50,19 @@ func objectSearch(ctx context.Context, d *schema.ResourceData, meta interface{}) return fmt.Errorf("BUG: object type not set in the resource data") } - objs, err := meta.(bw.Client).ListObjects(ctx, fmt.Sprintf("%ss", objType), listOptionsFromData(d)...) + objs, err := meta.(bitwarden.Client).ListObjects(ctx, models.ObjectType(objType.(string)), listOptionsFromData(d)...) if err != nil { return err } // If the object is an item, also filter by type to avoid returning a login when a secure note is expected. - if bw.ObjectType(objType.(string)) == bw.ObjectTypeItem { + if models.ObjectType(objType.(string)) == models.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))) + objs = bwcli.FilterObjectsByType(objs, models.ItemType(itemType.(int))) } if len(objs) == 0 { @@ -86,16 +88,16 @@ func objectSearch(ctx context.Context, d *schema.ResourceData, meta interface{}) return objectDataFromStruct(ctx, d, &obj) } -func listOptionsFromData(d *schema.ResourceData) []bw.ListObjectsOption { - filters := []bw.ListObjectsOption{} +func listOptionsFromData(d *schema.ResourceData) []bitwarden.ListObjectsOption { + filters := []bitwarden.ListObjectsOption{} - filterMap := map[string]bw.ListObjectsOptionGenerator{ - attributeFilterSearch: bw.WithSearch, - attributeFilterCollectionId: bw.WithCollectionID, - attributeOrganizationID: bw.WithOrganizationID, - attributeFilterFolderID: bw.WithFolderID, - attributeFilterOrganizationID: bw.WithOrganizationID, - attributeFilterURL: bw.WithUrl, + filterMap := map[string]bitwarden.ListObjectsOptionGenerator{ + attributeFilterSearch: bitwarden.WithSearch, + attributeFilterCollectionId: bitwarden.WithCollectionID, + attributeOrganizationID: bitwarden.WithOrganizationID, + attributeFilterFolderID: bitwarden.WithFolderID, + attributeFilterOrganizationID: bitwarden.WithOrganizationID, + attributeFilterURL: bitwarden.WithUrl, } for attribute, optionFunc := range filterMap { @@ -112,11 +114,11 @@ func listOptionsFromData(d *schema.ResourceData) []bw.ListObjectsOption { } func objectReadIgnoreMissing(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - err := objectOperation(ctx, d, func(ctx context.Context, secret bw.Object) (*bw.Object, error) { - return meta.(bw.Client).GetObject(ctx, secret) + err := objectOperation(ctx, d, func(ctx context.Context, secret models.Object) (*models.Object, error) { + return meta.(bitwarden.Client).GetObject(ctx, secret) }) - if errors.Is(err, bw.ErrObjectNotFound) { + if errors.Is(err, models.ErrObjectNotFound) { d.SetId("") tflog.Warn(ctx, "Object not found, removing from state") return diag.Diagnostics{} @@ -132,16 +134,16 @@ func objectReadIgnoreMissing(ctx context.Context, d *schema.ResourceData, meta i } func objectUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - return diag.FromErr(objectOperation(ctx, d, meta.(bw.Client).EditObject)) + return diag.FromErr(objectOperation(ctx, d, meta.(bitwarden.Client).EditObject)) } func objectDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - return diag.FromErr(objectOperation(ctx, d, func(ctx context.Context, secret bw.Object) (*bw.Object, error) { - return nil, meta.(bw.Client).DeleteObject(ctx, secret) + return diag.FromErr(objectOperation(ctx, d, func(ctx context.Context, secret models.Object) (*models.Object, error) { + return nil, meta.(bitwarden.Client).DeleteObject(ctx, secret) })) } -func objectOperation(ctx context.Context, d *schema.ResourceData, operation func(ctx context.Context, secret bw.Object) (*bw.Object, error)) error { +func objectOperation(ctx context.Context, d *schema.ResourceData, operation func(ctx context.Context, secret models.Object) (*models.Object, error)) error { obj, err := operation(ctx, objectStructFromData(ctx, d)) if err != nil { return err @@ -150,7 +152,7 @@ func objectOperation(ctx context.Context, d *schema.ResourceData, operation func return objectDataFromStruct(ctx, d, obj) } -func objectDataFromStruct(ctx context.Context, d *schema.ResourceData, obj *bw.Object) error { +func objectDataFromStruct(ctx context.Context, d *schema.ResourceData, obj *models.Object) error { if obj == nil { // Object has been deleted return nil @@ -170,13 +172,13 @@ func objectDataFromStruct(ctx context.Context, d *schema.ResourceData, obj *bw.O // Object-specific fields switch obj.Object { - case bw.ObjectTypeOrgCollection: + case models.ObjectTypeOrgCollection: err = d.Set(attributeOrganizationID, obj.OrganizationID) if err != nil { return err } - case bw.ObjectTypeItem: + case models.ObjectTypeItem: err = d.Set(attributeFolderID, obj.FolderID) if err != nil { return err @@ -223,27 +225,27 @@ func objectDataFromStruct(ctx context.Context, d *schema.ResourceData, obj *bw.O } if obj.RevisionDate != nil { - err = d.Set(attributeRevisionDate, obj.RevisionDate.Format(bw.DateLayout)) + err = d.Set(attributeRevisionDate, obj.RevisionDate.Format(models.DateLayout)) if err != nil { return err } } if obj.CreationDate != nil { - err = d.Set(attributeCreationDate, obj.CreationDate.Format(bw.DateLayout)) + err = d.Set(attributeCreationDate, obj.CreationDate.Format(models.DateLayout)) if err != nil { return err } } if obj.DeletedDate != nil { - err = d.Set(attributeDeletedDate, obj.DeletedDate.Format(bw.DateLayout)) + err = d.Set(attributeDeletedDate, obj.DeletedDate.Format(models.DateLayout)) if err != nil { return err } } - if obj.Type == bw.ItemTypeLogin { + if obj.Type == models.ItemTypeLogin { err = d.Set(attributeLoginPassword, obj.Login.Password) if err != nil { return err @@ -269,8 +271,8 @@ func objectDataFromStruct(ctx context.Context, d *schema.ResourceData, obj *bw.O return nil } -func objectStructFromData(ctx context.Context, d *schema.ResourceData) bw.Object { - var obj bw.Object +func objectStructFromData(ctx context.Context, d *schema.ResourceData) models.Object { + var obj models.Object obj.ID = d.Id() if v, ok := d.Get(attributeName).(string); ok { @@ -278,21 +280,19 @@ func objectStructFromData(ctx context.Context, d *schema.ResourceData) bw.Object } if v, ok := d.Get(attributeObject).(string); ok { - obj.Object = bw.ObjectType(v) + obj.Object = models.ObjectType(v) } // Object-specific fields switch obj.Object { - case bw.ObjectTypeOrgCollection: + case models.ObjectTypeOrgCollection: if v, ok := d.Get(attributeOrganizationID).(string); ok { obj.OrganizationID = v } - obj.Groups = []interface{}{} - - case bw.ObjectTypeItem: + case models.ObjectTypeItem: if v, ok := d.Get(attributeType).(int); ok { - obj.Type = bw.ItemType(v) + obj.Type = models.ItemType(v) } if v, ok := d.Get(attributeFolderID).(string); ok { @@ -330,7 +330,7 @@ func objectStructFromData(ctx context.Context, d *schema.ResourceData) bw.Object obj.Fields = objectFieldStructFromData(v) } - if obj.Type == bw.ItemTypeLogin { + if obj.Type == models.ItemTypeLogin { if v, ok := d.Get(attributeLoginPassword).(string); ok { obj.Login.Password = v } @@ -349,19 +349,19 @@ func objectStructFromData(ctx context.Context, d *schema.ResourceData) bw.Object return obj } -func objectFieldDataFromStruct(obj *bw.Object) []interface{} { +func objectFieldDataFromStruct(obj *models.Object) []interface{} { fields := make([]interface{}, len(obj.Fields)) for k, f := range obj.Fields { field := map[string]interface{}{ attributeFieldName: f.Name, } - if f.Type == bw.FieldTypeText { + if f.Type == models.FieldTypeText { field[attributeFieldText] = f.Value - } else if f.Type == bw.FieldTypeBoolean { + } else if f.Type == models.FieldTypeBoolean { field[attributeFieldBoolean] = (f.Value == "true") - } else if f.Type == bw.FieldTypeHidden { + } else if f.Type == models.FieldTypeHidden { field[attributeFieldHidden] = f.Value - } else if f.Type == bw.FieldTypeLinked { + } else if f.Type == models.FieldTypeLinked { field[attributeFieldLinked] = f.Value } fields[k] = field @@ -369,11 +369,11 @@ func objectFieldDataFromStruct(obj *bw.Object) []interface{} { return fields } -func objectAttachmentStructFromData(vList []interface{}) []bw.Attachment { - attachments := make([]bw.Attachment, len(vList)) +func objectAttachmentStructFromData(vList []interface{}) []models.Attachment { + attachments := make([]models.Attachment, len(vList)) for k, v := range vList { vc := v.(map[string]interface{}) - attachments[k] = bw.Attachment{ + attachments[k] = models.Attachment{ ID: vc[attributeID].(string), FileName: vc[attributeAttachmentFileName].(string), Size: vc[attributeAttachmentSize].(string), @@ -384,7 +384,7 @@ func objectAttachmentStructFromData(vList []interface{}) []bw.Attachment { return attachments } -func objectAttachmentsFromStruct(objAttachments []bw.Attachment) []interface{} { +func objectAttachmentsFromStruct(objAttachments []models.Attachment) []interface{} { attachments := make([]interface{}, len(objAttachments)) for k, f := range objAttachments { attachments[k] = map[string]interface{}{ @@ -398,24 +398,24 @@ func objectAttachmentsFromStruct(objAttachments []bw.Attachment) []interface{} { return attachments } -func objectFieldStructFromData(vList []interface{}) []bw.Field { - fields := make([]bw.Field, len(vList)) +func objectFieldStructFromData(vList []interface{}) []models.Field { + fields := make([]models.Field, len(vList)) for k, v := range vList { vc := v.(map[string]interface{}) - fields[k] = bw.Field{ + fields[k] = models.Field{ Name: vc[attributeFieldName].(string), } if vs, ok := vc[attributeFieldText].(string); ok && len(vs) > 0 { - fields[k].Type = bw.FieldTypeText + fields[k].Type = models.FieldTypeText fields[k].Value = vs } else if vs, ok := vc[attributeFieldHidden].(string); ok && len(vs) > 0 { - fields[k].Type = bw.FieldTypeHidden + fields[k].Type = models.FieldTypeHidden fields[k].Value = vs } else if vs, ok := vc[attributeFieldLinked].(string); ok && len(vs) > 0 { - fields[k].Type = bw.FieldTypeLinked + fields[k].Type = models.FieldTypeLinked fields[k].Value = vs } else if vs, ok := vc[attributeFieldBoolean].(bool); ok { - fields[k].Type = bw.FieldTypeBoolean + fields[k].Type = models.FieldTypeBoolean if vs { fields[k].Value = "true" } else { @@ -426,11 +426,11 @@ func objectFieldStructFromData(vList []interface{}) []bw.Field { return fields } -func objectLoginURIsFromData(ctx context.Context, vList []interface{}) []bw.LoginURI { - uris := make([]bw.LoginURI, len(vList)) +func objectLoginURIsFromData(ctx context.Context, vList []interface{}) []models.LoginURI { + uris := make([]models.LoginURI, len(vList)) for k, v := range vList { vc := v.(map[string]interface{}) - uris[k] = bw.LoginURI{ + uris[k] = models.LoginURI{ Match: strMatchToInt(ctx, vc[attributeLoginURIsMatch].(string)), URI: vc[attributeLoginURIsValue].(string), } @@ -438,7 +438,7 @@ func objectLoginURIsFromData(ctx context.Context, vList []interface{}) []bw.Logi return uris } -func objectLoginURIsFromStruct(ctx context.Context, objUris []bw.LoginURI) []interface{} { +func objectLoginURIsFromStruct(ctx context.Context, objUris []models.LoginURI) []interface{} { uris := make([]interface{}, len(objUris)) for k, f := range objUris { uris[k] = map[string]interface{}{ @@ -449,23 +449,23 @@ func objectLoginURIsFromStruct(ctx context.Context, objUris []bw.LoginURI) []int return uris } -func intMatchToStr(ctx context.Context, match *bw.URIMatch) URIMatchStr { +func intMatchToStr(ctx context.Context, match *models.URIMatch) URIMatchStr { if match == nil { return URIMatchDefault } switch *match { - case bw.URIMatchBaseDomain: + case models.URIMatchBaseDomain: return URIMatchBaseDomain - case bw.URIMatchHost: + case models.URIMatchHost: return URIMatchHost - case bw.URIMatchStartWith: + case models.URIMatchStartWith: return URIMatchStartWith - case bw.URIMatchExact: + case models.URIMatchExact: return URIMatchExact - case bw.URIMatchRegExp: + case models.URIMatchRegExp: return URIMatchRegExp - case bw.URIMatchNever: + case models.URIMatchNever: return URIMatchNever default: tflog.Warn(ctx, "unsupported integer value for URI match - Falling back to default", map[string]interface{}{"match": *match}) @@ -473,23 +473,23 @@ func intMatchToStr(ctx context.Context, match *bw.URIMatch) URIMatchStr { } } -func strMatchToInt(ctx context.Context, match string) *bw.URIMatch { - var v bw.URIMatch +func strMatchToInt(ctx context.Context, match string) *models.URIMatch { + var v models.URIMatch switch match { case string(URIMatchDefault): return nil case string(URIMatchBaseDomain): - v = bw.URIMatchBaseDomain + v = models.URIMatchBaseDomain case string(URIMatchHost): - v = bw.URIMatchHost + v = models.URIMatchHost case string(URIMatchStartWith): - v = bw.URIMatchStartWith + v = models.URIMatchStartWith case string(URIMatchExact): - v = bw.URIMatchExact + v = models.URIMatchExact case string(URIMatchRegExp): - v = bw.URIMatchRegExp + v = models.URIMatchRegExp case string(URIMatchNever): - v = bw.URIMatchNever + v = models.URIMatchNever default: tflog.Warn(ctx, "unsupported string value for URI match - Falling back to default", map[string]interface{}{"match": match}) return nil diff --git a/internal/provider/provider.go b/internal/provider/provider.go index a40b92c..d7e547a 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -9,7 +9,8 @@ import ( "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/bw" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/bwcli" ) type LoginMethod int @@ -69,7 +70,7 @@ func New(version string) func() *schema.Provider { Type: schema.TypeString, Description: descriptionServer, Required: true, - DefaultFunc: schema.EnvDefaultFunc("BW_URL", bw.DefaultBitwardenServerURL), + DefaultFunc: schema.EnvDefaultFunc("BW_URL", bitwarden.DefaultBitwardenServerURL), }, attributeEmail: { Type: schema.TypeString, @@ -134,7 +135,7 @@ func providerConfigure(version string, _ *schema.Provider) func(context.Context, } } -func ensureLoggedIn(ctx context.Context, d *schema.ResourceData, bwClient bw.Client) error { +func ensureLoggedIn(ctx context.Context, d *schema.ResourceData, bwClient bwcli.CLIClient) error { status, err := bwClient.Status(ctx) if err != nil { return err @@ -148,7 +149,7 @@ func ensureLoggedIn(ctx context.Context, d *schema.ResourceData, bwClient bw.Cli // Scenario 1: The Vault is already *unlocked*, there is nothing else to // be done. This should happen when a session key is provided. // => return - if status.Status == bw.StatusUnlocked { + if status.Status == bwcli.StatusUnlocked { return bwClient.Sync(ctx) } @@ -156,7 +157,7 @@ func ensureLoggedIn(ctx context.Context, d *schema.ResourceData, bwClient bw.Cli // happens when the Vault is already cached locally. // => unlock and return masterPassword, hasMasterPassword := d.GetOk(attributeMasterPassword) - if hasMasterPassword && status.Status == bw.StatusLocked { + if hasMasterPassword && status.Status == bwcli.StatusLocked { err = bwClient.Unlock(ctx, masterPassword.(string)) if err != nil { return err @@ -208,12 +209,12 @@ func loginMethod(d *schema.ResourceData) LoginMethod { return LoginMethodNone } -func logoutIfIdentityChanged(ctx context.Context, d *schema.ResourceData, bwClient bw.Client, status *bw.Status) error { +func logoutIfIdentityChanged(ctx context.Context, d *schema.ResourceData, bwClient bwcli.CLIClient, status *bwcli.Status) error { email := d.Get(attributeEmail).(string) serverURL := d.Get(attributeServer).(string) - if (status.Status == bw.StatusLocked || status.Status == bw.StatusUnlocked) && (!status.VaultOfUser(email) || !status.VaultFromServer(serverURL)) { - status.Status = bw.StatusUnauthenticated + if (status.Status == bwcli.StatusLocked || status.Status == bwcli.StatusUnlocked) && (!status.VaultOfUser(email) || !status.VaultFromServer(serverURL)) { + status.Status = bwcli.StatusUnauthenticated tflog.Warn(ctx, "Logging out as the local Vault belongs to a different identity", map[string]interface{}{"vault_email": status.UserEmail, "vault_server": status.ServerURL, "provider_email": email, "provider_server": serverURL}) err := bwClient.Logout(ctx) @@ -231,30 +232,30 @@ func logoutIfIdentityChanged(ctx context.Context, d *schema.ResourceData, bwClie return nil } -func newBitwardenClient(d *schema.ResourceData, version string) (bw.Client, error) { - opts := []bw.Options{} +func newBitwardenClient(d *schema.ResourceData, version string) (bwcli.CLIClient, error) { + opts := []bwcli.Options{} if vaultPath, exists := d.GetOk(attributeVaultPath); exists { abs, err := filepath.Abs(vaultPath.(string)) if err != nil { return nil, err } - opts = append(opts, bw.WithAppDataDir(abs)) + opts = append(opts, bwcli.WithAppDataDir(abs)) } if extraCACertsPath, exists := d.GetOk(attributeExtraCACertsPath); exists { - opts = append(opts, bw.WithExtraCACertsPath(extraCACertsPath.(string))) + opts = append(opts, bwcli.WithExtraCACertsPath(extraCACertsPath.(string))) } if version == versionDev { // During development, we disable Vault synchronization and retry backoffs to make some // operations faster. - opts = append(opts, bw.DisableSync()) - opts = append(opts, bw.DisableRetryBackoff()) + opts = append(opts, bwcli.DisableSync()) + opts = append(opts, bwcli.DisableRetryBackoff()) } bwExecutable, err := exec.LookPath("bw") if err != nil { return nil, err } - return bw.NewClient(bwExecutable, opts...), nil + return bwcli.NewClient(bwExecutable, opts...), nil } diff --git a/internal/provider/provider_utils_test.go b/internal/provider/provider_utils_test.go index 03a2294..ecd329c 100644 --- a/internal/provider/provider_utils_test.go +++ b/internal/provider/provider_utils_test.go @@ -14,7 +14,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/bw" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/bwcli" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/models" "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/webapi" ) @@ -129,8 +130,8 @@ func createTestUserResources(t *testing.T) { testFolderName := fmt.Sprintf("folder-%s-bar", testUniqueIdentifier) bwClient := bwTestClient(t) t.Logf("Creating Folder") - folder, err := bwClient.CreateObject(context.Background(), bw.Object{ - Object: bw.ObjectTypeFolder, + folder, err := bwClient.CreateObject(context.Background(), models.Object{ + Object: models.ObjectTypeFolder, Name: testFolderName, }) if err != nil { @@ -150,7 +151,7 @@ func createTestUserResources(t *testing.T) { t.Log("Synced test client") } -func bwTestClient(t *testing.T) bw.Client { +func bwTestClient(t *testing.T) bwcli.CLIClient { vault, err := filepath.Abs("./.bitwarden") if err != nil { t.Fatal(err) @@ -161,7 +162,7 @@ func bwTestClient(t *testing.T) bw.Client { t.Fatal(err) } - client := bw.NewClient(bwExec, bw.DisableRetryBackoff(), bw.WithAppDataDir(vault)) + client := bwcli.NewClient(bwExec, bwcli.DisableRetryBackoff(), bwcli.WithAppDataDir(vault)) status, err := client.Status(context.Background()) if err != nil { t.Fatal(err) @@ -173,7 +174,7 @@ func bwTestClient(t *testing.T) bw.Client { t.Fatal(err) } } - if status.Status == bw.StatusUnauthenticated { + if status.Status == bwcli.StatusUnauthenticated { retries := 0 for { @@ -190,7 +191,7 @@ func bwTestClient(t *testing.T) bw.Client { } break } - } else if status.Status == bw.StatusLocked { + } else if status.Status == bwcli.StatusLocked { err = client.Unlock(context.Background(), testPassword) if err != nil { t.Fatal(err) diff --git a/internal/provider/resource.go b/internal/provider/resource.go index 6e10bd0..2903acd 100644 --- a/internal/provider/resource.go +++ b/internal/provider/resource.go @@ -5,10 +5,10 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/bw" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/models" ) -func createResource(attrObject bw.ObjectType, attrType bw.ItemType) schema.CreateContextFunc { +func createResource(attrObject models.ObjectType, attrType models.ItemType) schema.CreateContextFunc { return func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { err := d.Set(attributeObject, attrObject) if err != nil { @@ -22,7 +22,7 @@ func createResource(attrObject bw.ObjectType, attrType bw.ItemType) schema.Creat } } -func importItemResource(attrObject bw.ObjectType, attrType bw.ItemType) *schema.ResourceImporter { +func importItemResource(attrObject models.ObjectType, attrType models.ItemType) *schema.ResourceImporter { return &schema.ResourceImporter{ StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { d.SetId(d.Id()) diff --git a/internal/provider/resource_folder.go b/internal/provider/resource_folder.go index ff06ab2..2649247 100644 --- a/internal/provider/resource_folder.go +++ b/internal/provider/resource_folder.go @@ -5,7 +5,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/bw" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/models" ) func resourceFolder() *schema.Resource { @@ -23,7 +23,7 @@ func resourceFolder() *schema.Resource { } func resourceFolderCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - err := d.Set(attributeObject, bw.ObjectTypeFolder) + err := d.Set(attributeObject, models.ObjectTypeFolder) if err != nil { return diag.FromErr(err) } @@ -34,7 +34,7 @@ func importFolderResource() *schema.ResourceImporter { return &schema.ResourceImporter{ StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { d.SetId(d.Id()) - err := d.Set(attributeObject, bw.ObjectTypeFolder) + err := d.Set(attributeObject, models.ObjectTypeFolder) if err != nil { return nil, err } diff --git a/internal/provider/resource_item_login.go b/internal/provider/resource_item_login.go index b7cd7da..fba921d 100644 --- a/internal/provider/resource_item_login.go +++ b/internal/provider/resource_item_login.go @@ -2,7 +2,7 @@ package provider import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/bw" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/models" ) func resourceItemLogin() *schema.Resource { @@ -13,11 +13,11 @@ func resourceItemLogin() *schema.Resource { return &schema.Resource{ Description: "Manages a login item.", - CreateContext: createResource(bw.ObjectTypeItem, bw.ItemTypeLogin), + CreateContext: createResource(models.ObjectTypeItem, models.ItemTypeLogin), ReadContext: objectReadIgnoreMissing, UpdateContext: objectUpdate, DeleteContext: objectDelete, - Importer: importItemResource(bw.ObjectTypeItem, bw.ItemTypeLogin), + Importer: importItemResource(models.ObjectTypeItem, models.ItemTypeLogin), Schema: dataSourceItemSecureNoteSchema, } } diff --git a/internal/provider/resource_item_login_test.go b/internal/provider/resource_item_login_test.go index dbac459..34c8015 100644 --- a/internal/provider/resource_item_login_test.go +++ b/internal/provider/resource_item_login_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/bw" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/models" "github.com/stretchr/testify/assert" ) @@ -59,7 +59,7 @@ func TestAccMissingResourceItemLoginIsRecreated(t *testing.T) { { Config: tfConfigProvider() + tfConfigResourceItemLoginSmall(), PreConfig: func() { - obj := bw.Object{ID: objectID, Object: bw.ObjectTypeItem} + obj := models.Object{ID: objectID, Object: models.ObjectTypeItem} err := bwTestClient(t).DeleteObject(context.Background(), obj) assert.NoError(t, err) }, diff --git a/internal/provider/resource_item_secure_note.go b/internal/provider/resource_item_secure_note.go index f604509..2541525 100644 --- a/internal/provider/resource_item_secure_note.go +++ b/internal/provider/resource_item_secure_note.go @@ -2,7 +2,7 @@ package provider import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/bw" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/models" ) func resourceItemSecureNote() *schema.Resource { @@ -10,11 +10,11 @@ func resourceItemSecureNote() *schema.Resource { return &schema.Resource{ Description: "Manages a secure note item.", - CreateContext: createResource(bw.ObjectTypeItem, bw.ItemTypeSecureNote), + CreateContext: createResource(models.ObjectTypeItem, models.ItemTypeSecureNote), ReadContext: objectReadIgnoreMissing, UpdateContext: objectUpdate, DeleteContext: objectDelete, - Importer: importItemResource(bw.ObjectTypeItem, bw.ItemTypeSecureNote), + Importer: importItemResource(models.ObjectTypeItem, models.ItemTypeSecureNote), Schema: dataSourceItemSecureNoteSchema, } } diff --git a/internal/provider/resource_org_collection.go b/internal/provider/resource_org_collection.go index 57a48a9..9424963 100644 --- a/internal/provider/resource_org_collection.go +++ b/internal/provider/resource_org_collection.go @@ -7,7 +7,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/bw" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/models" ) func resourceOrgCollection() *schema.Resource { @@ -25,7 +25,7 @@ func resourceOrgCollection() *schema.Resource { } func resourceOrgCollectionCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - err := d.Set(attributeObject, bw.ObjectTypeOrgCollection) + err := d.Set(attributeObject, models.ObjectTypeOrgCollection) if err != nil { return diag.FromErr(err) } @@ -41,7 +41,7 @@ func importOrgCollectionResource() *schema.ResourceImporter { } d.SetId(split[1]) d.Set(attributeOrganizationID, split[0]) - err := d.Set(attributeObject, bw.ObjectTypeOrgCollection) + err := d.Set(attributeObject, models.ObjectTypeOrgCollection) if err != nil { return nil, err } From a8661f05dbd27d7274f3db3069448ae30ac58c76 Mon Sep 17 00:00:00 2001 From: Maxime Lagresle Date: Sun, 29 Sep 2024 15:25:37 +0200 Subject: [PATCH 2/2] set group for org collections --- .github/workflows/tests.yml | 2 +- internal/bitwarden/bwcli/client.go | 3 ++- internal/bitwarden/bwcli/client_test.go | 16 ++++++++-------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1c02eb4..7d28c2f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,7 +23,7 @@ jobs: services: vaultwarden: - image: vaultwarden/server:latest + image: vaultwarden/server:1.32.0 env: ADMIN_TOKEN: test1234 I_REALLY_WANT_VOLATILE_STORAGE: "true" diff --git a/internal/bitwarden/bwcli/client.go b/internal/bitwarden/bwcli/client.go index 2896f16..c1c768b 100644 --- a/internal/bitwarden/bwcli/client.go +++ b/internal/bitwarden/bwcli/client.go @@ -84,6 +84,7 @@ func DisableRetryBackoff() Options { } func (c *client) CreateObject(ctx context.Context, obj models.Object) (*models.Object, error) { + obj.Groups = []interface{}{} objEncoded, err := c.encode(obj) if err != nil { return nil, err @@ -200,7 +201,7 @@ func (c *client) GetSessionKey() string { func (c *client) ListObjects(ctx context.Context, objType models.ObjectType, options ...bitwarden.ListObjectsOption) ([]models.Object, error) { args := []string{ "list", - string(objType), + fmt.Sprintf("%ss", objType), } applyFiltersToArgs(&args, options...) diff --git a/internal/bitwarden/bwcli/client_test.go b/internal/bitwarden/bwcli/client_test.go index fa7d362..c68e112 100644 --- a/internal/bitwarden/bwcli/client_test.go +++ b/internal/bitwarden/bwcli/client_test.go @@ -12,7 +12,7 @@ import ( func TestCreateObjectEncoding(t *testing.T) { removeMocks, commandsExecuted := test_command.MockCommands(t, map[string]string{ - "create item eyJncm91cHMiOm51bGwsImxvZ2luIjp7fSwib2JqZWN0IjoiaXRlbSIsInNlY3VyZU5vdGUiOnt9LCJ0eXBlIjoxLCJmaWVsZHMiOlt7Im5hbWUiOiJ0ZXN0IiwidmFsdWUiOiJwYXNzZWQiLCJ0eXBlIjowLCJsaW5rZWRJZCI6bnVsbH1dfQ": `{}`, + "create item eyJncm91cHMiOltdLCJsb2dpbiI6e30sIm9iamVjdCI6Iml0ZW0iLCJzZWN1cmVOb3RlIjp7fSwidHlwZSI6MSwiZmllbGRzIjpbeyJuYW1lIjoidGVzdCIsInZhbHVlIjoicGFzc2VkIiwidHlwZSI6MCwibGlua2VkSWQiOm51bGx9XX0": `{}`, }) defer removeMocks(t) @@ -31,22 +31,22 @@ func TestCreateObjectEncoding(t *testing.T) { assert.NoError(t, err) if assert.Len(t, commandsExecuted(), 1) { - assert.Equal(t, "create item eyJncm91cHMiOm51bGwsImxvZ2luIjp7fSwib2JqZWN0IjoiaXRlbSIsInNlY3VyZU5vdGUiOnt9LCJ0eXBlIjoxLCJmaWVsZHMiOlt7Im5hbWUiOiJ0ZXN0IiwidmFsdWUiOiJwYXNzZWQiLCJ0eXBlIjowLCJsaW5rZWRJZCI6bnVsbH1dfQ", commandsExecuted()[0]) + assert.Equal(t, "create item eyJncm91cHMiOltdLCJsb2dpbiI6e30sIm9iamVjdCI6Iml0ZW0iLCJzZWN1cmVOb3RlIjp7fSwidHlwZSI6MSwiZmllbGRzIjpbeyJuYW1lIjoidGVzdCIsInZhbHVlIjoicGFzc2VkIiwidHlwZSI6MCwibGlua2VkSWQiOm51bGx9XX0", commandsExecuted()[0]) } } func TestListObjects(t *testing.T) { removeMocks, commandsExecuted := test_command.MockCommands(t, map[string]string{ - "list item --folderid folder-id --collectionid collection-id --search search": `[]`, + "list items --folderid folder-id --collectionid collection-id --search search": `[]`, }) defer removeMocks(t) b := NewClient("dummy") - _, err := b.ListObjects(context.Background(), "item", bitwarden.WithFolderID("folder-id"), bitwarden.WithCollectionID("collection-id"), bitwarden.WithSearch("search")) + _, err := b.ListObjects(context.Background(), models.ObjectTypeItem, bitwarden.WithFolderID("folder-id"), bitwarden.WithCollectionID("collection-id"), bitwarden.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]) + assert.Equal(t, "list items --folderid folder-id --collectionid collection-id --search search", commandsExecuted()[0]) } } @@ -82,14 +82,14 @@ func TestGetOrgCollection(t *testing.T) { func TestErrorContainsCommand(t *testing.T) { removeMocks, _ := test_command.MockCommands(t, map[string]string{ - "list org-collection --search search": ``, + "list org-collections --search search": ``, }) defer removeMocks(t) b := NewClient("dummy") - _, err := b.ListObjects(context.Background(), "org-collection", bitwarden.WithSearch("search")) + _, err := b.ListObjects(context.Background(), models.ObjectTypeOrgCollection, bitwarden.WithSearch("search")) if assert.Error(t, err) { - assert.ErrorContains(t, err, "unable to parse result of 'list org-collection', error: 'unexpected end of JSON input', output: ''") + assert.ErrorContains(t, err, "unable to parse result of 'list org-collections', error: 'unexpected end of JSON input', output: ''") } }