From 7c76162262918f3eb4c4161a9fdbd3a23b2e9694 Mon Sep 17 00:00:00 2001 From: Maxime Lagresle Date: Mon, 30 Sep 2024 11:11:40 +0200 Subject: [PATCH 1/6] proper error when session key is used --- README.md | 4 +++- internal/provider/provider.go | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 17099e1..14d1896 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,9 @@ Currently, the Terraform SDK doesn't offer a way to remove the encrypted Vault o The issue [hashicorp/terraform-plugin-sdk#63] tracks discussions for adding such a feature. If you want find out more about this file, you can read [Terraform's documentation on Data Storage]. -Please note that this file is stored at `/.bitwarden/` by default, in order to not interfer with your local Vaults. +Please note that this file is stored at `/.bitwarden/` by default, in order to not interfere with your local Vaults. + +NOTE: This whole paragraph doesn't apply to the experimental client, as nothing is stored on disk. ## Developing the Provider diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 8e2b4f9..8e6f30a 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -139,7 +139,7 @@ func New(version string) func() *schema.Provider { } } -func experimentalEmbeddedClient(d *schema.ResourceData) bool { +func useExperimentalEmbeddedClient(d *schema.ResourceData) bool { experimentalFeatures, hasExperimentalFeatures := d.GetOk(attributeExperimental) if hasExperimentalFeatures { if experimentalFeatures.(*schema.Set).Len() > 0 { @@ -154,7 +154,11 @@ func experimentalEmbeddedClient(d *schema.ResourceData) bool { func providerConfigure(version string, _ *schema.Provider) func(context.Context, *schema.ResourceData) (interface{}, diag.Diagnostics) { return func(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { - if experimentalEmbeddedClient(d) { + if useExperimentalEmbeddedClient(d) { + if _, hasSessionKey := d.GetOk(attributeSessionKey); hasSessionKey { + return nil, diag.Errorf("session key is not supported with the embedded client") + } + bwClient, err := newBitwardenEmbeddedClient(ctx, d, version) if err != nil { return nil, diag.FromErr(err) From b91b99b8bc16d4256e4475655b8749d448d275f6 Mon Sep 17 00:00:00 2001 From: Maxime Lagresle Date: Mon, 30 Sep 2024 11:15:32 +0200 Subject: [PATCH 2/6] store device identifier on disk --- internal/provider/provider.go | 32 +++++++++++--------------- internal/provider/schema_attributes.go | 1 - 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 8e6f30a..307a7a4 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -3,8 +3,10 @@ package provider import ( "context" "fmt" + "os" "os/exec" "path/filepath" + "strings" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" @@ -108,14 +110,6 @@ func New(version string) func() *schema.Provider { }, }, }, - - // Internal - attributeDeviceIdentifier: { - Description: descriptionInternal, - Type: schema.TypeString, - Computed: true, - Optional: true, - }, }, DataSourcesMap: map[string]*schema.Resource{ "bitwarden_attachment": dataSourceAttachment(), @@ -315,7 +309,7 @@ func newBitwardenClient(d *schema.ResourceData, version string) (bwcli.CLIClient } func newBitwardenEmbeddedClient(ctx context.Context, d *schema.ResourceData, version string) (bitwarden.Client, error) { - deviceId, err := getOrGenerateDeviceIdentifier(ctx, d) + deviceId, err := getOrGenerateDeviceIdentifier(ctx) if err != nil { return nil, err } @@ -333,18 +327,18 @@ func newBitwardenEmbeddedClient(ctx context.Context, d *schema.ResourceData, ver return embedded.NewWebAPIVault(serverURL, opts...), nil } -func getOrGenerateDeviceIdentifier(ctx context.Context, d *schema.ResourceData) (string, error) { - deviceId, hasDeviceID := d.GetOk(attributeDeviceIdentifier) - if hasDeviceID { - return deviceId.(string), nil - } - deviceId = embedded.NewDeviceIdentifier() - err := d.Set(attributeDeviceIdentifier, embedded.NewDeviceIdentifier()) - if err != nil { - return "", err +func getOrGenerateDeviceIdentifier(ctx context.Context) (string, error) { + deviceIdBytes, err := os.ReadFile(".bitwarden/device_identifier") + if err == nil { + deviceId := string(deviceIdBytes) + tflog.Info(ctx, "Read device identifier from disk", map[string]interface{}{"device_id": deviceId}) + return strings.TrimSpace(deviceId), nil } + + deviceId := embedded.NewDeviceIdentifier() + os.WriteFile(".bitwarden/device_identifier", []byte(deviceId), 0600) tflog.Info(ctx, "Generated device identifier", map[string]interface{}{"device_id": deviceId}) - return deviceId.(string), nil + return deviceId, nil } func ensureLoggedInEmbedded(ctx context.Context, d *schema.ResourceData, bwClient bitwarden.Client) error { diff --git a/internal/provider/schema_attributes.go b/internal/provider/schema_attributes.go index 4ab59d8..736acab 100644 --- a/internal/provider/schema_attributes.go +++ b/internal/provider/schema_attributes.go @@ -92,7 +92,6 @@ const ( attributeExtraCACertsPath = "extra_ca_certs" attributeExperimental = "experimental" attributeExperimentalEmbeddedClient = "embedded_client" - attributeDeviceIdentifier = "device_id" // Provider field descriptions descriptionClientSecret = "Client Secret (env: `BW_CLIENTSECRET`). Do not commit this information in Git unless you know what you're doing. Prefer using a Terraform `variable {}` in order to inject this value from the environment." From 6da1776e693ce78f5b9a2160d0e94ebcfd6876a6 Mon Sep 17 00:00:00 2001 From: Maxime Lagresle Date: Mon, 30 Sep 2024 11:16:38 +0200 Subject: [PATCH 3/6] rename device name --- internal/bitwarden/webapi/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/bitwarden/webapi/client.go b/internal/bitwarden/webapi/client.go index c27ea6f..9b0cb11 100644 --- a/internal/bitwarden/webapi/client.go +++ b/internal/bitwarden/webapi/client.go @@ -19,7 +19,7 @@ import ( ) const ( - deviceName = "Terraform Provider Bitwarden" + deviceName = "Bitwarden Terraform Provider" ) type Client interface { From 0b025e7d272c1a8283799a5063cfb495039811b3 Mon Sep 17 00:00:00 2001 From: Maxime Lagresle Date: Mon, 30 Sep 2024 17:46:33 +0200 Subject: [PATCH 4/6] don't read access token on errors --- internal/bitwarden/webapi/client.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/bitwarden/webapi/client.go b/internal/bitwarden/webapi/client.go index 9b0cb11..0d3652b 100644 --- a/internal/bitwarden/webapi/client.go +++ b/internal/bitwarden/webapi/client.go @@ -333,6 +333,9 @@ func (c *client) LoginWithAPIKey(ctx context.Context, clientId, clientSecret str } tokenResp, err := doRequest[TokenResponse](ctx, c.httpClient, httpReq) + if err != nil { + return nil, err + } c.sessionAccessToken = tokenResp.AccessToken return tokenResp, err } From c9817a78a28f745afd93277272cc41541d1c49e4 Mon Sep 17 00:00:00 2001 From: Maxime Lagresle Date: Mon, 30 Sep 2024 17:46:51 +0200 Subject: [PATCH 5/6] move vaultwarden start in makefile --- GNUmakefile | 14 +++++++++++++- README.md | 11 +---------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index a6a2c3c..053c567 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -13,4 +13,16 @@ docs: 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 + rm internal/provider/.bitwarden/data.json || true + +server: + docker run -ti \ + -e I_REALLY_WANT_VOLATILE_STORAGE=true \ + -e DISABLE_ICON_DOWNLOAD=true \ + -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/README.md b/README.md index 14d1896..9ec7c10 100644 --- a/README.md +++ b/README.md @@ -92,16 +92,7 @@ To generate or update documentation, run `go generate`. 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=true \ - -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 +$ make server ``` Then run `make testacc`. From edf75ae2df1c7a9952c95f0ce7e3a4b4e02fd6f3 Mon Sep 17 00:00:00 2001 From: Maxime Lagresle Date: Mon, 30 Sep 2024 17:48:57 +0200 Subject: [PATCH 6/6] better handling of device identifier storing errors --- internal/provider/provider.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 307a7a4..1b9be36 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -336,7 +336,17 @@ func getOrGenerateDeviceIdentifier(ctx context.Context) (string, error) { } deviceId := embedded.NewDeviceIdentifier() - os.WriteFile(".bitwarden/device_identifier", []byte(deviceId), 0600) + err = os.Mkdir(".bitwarden", 0700) + if err != nil { + tflog.Error(ctx, "Failed to create .bitwarden directory", map[string]interface{}{"error": err}) + return "", err + } + err = os.WriteFile(".bitwarden/device_identifier", []byte(deviceId), 0600) + if err != nil { + tflog.Error(ctx, "Failed to store device identifier", map[string]interface{}{"error": err}) + return "", err + } + tflog.Info(ctx, "Generated device identifier", map[string]interface{}{"device_id": deviceId}) return deviceId, nil }