From 85a416033edb8149818a1b6517a34b49cbb57b4f Mon Sep 17 00:00:00 2001 From: Maxime Lagresle Date: Tue, 1 Oct 2024 08:38:47 +0200 Subject: [PATCH] fix some issue with official Bitwarden servers --- .../crypto/keybuilder/encryption_key.go | 4 +- .../bitwarden/crypto/keybuilder/key_pair.go | 6 +- .../bitwarden/embedded/vault_base_test.go | 47 +- .../embedded/vault_mocked_webapi_test.go | 578 ++++++++++++++++++ .../bitwarden/embedded/vault_utils_test.go | 33 - internal/bitwarden/embedded/vault_webapi.go | 47 +- .../bitwarden/embedded/vault_webapi_test.go | 30 +- internal/bitwarden/webapi/client.go | 61 +- internal/provider/provider.go | 2 +- internal/provider/provider_utils_test.go | 4 +- 10 files changed, 730 insertions(+), 82 deletions(-) create mode 100644 internal/bitwarden/embedded/vault_mocked_webapi_test.go delete mode 100644 internal/bitwarden/embedded/vault_utils_test.go diff --git a/internal/bitwarden/crypto/keybuilder/encryption_key.go b/internal/bitwarden/crypto/keybuilder/encryption_key.go index f93d309..98c9a04 100644 --- a/internal/bitwarden/crypto/keybuilder/encryption_key.go +++ b/internal/bitwarden/crypto/keybuilder/encryption_key.go @@ -15,10 +15,10 @@ func GenerateEncryptionKey(key symmetrickey.Key) (*symmetrickey.Key, string, err return nil, "", fmt.Errorf("error generating random bytes: %w", err) } - return buildEncryptionKey(key, encryptionKey) + return EncryptEncryptionKey(key, encryptionKey) } -func buildEncryptionKey(key symmetrickey.Key, encryptionKey []byte) (newEncryptionKey *symmetrickey.Key, encryptedEncryptionKey string, err error) { +func EncryptEncryptionKey(key symmetrickey.Key, encryptionKey []byte) (newEncryptionKey *symmetrickey.Key, encryptedEncryptionKey string, err error) { if len(key.Key) == 32 { stretchedKey := key.StretchKey() encryptedEncryptionKey, err = crypto.EncryptAsString(encryptionKey, stretchedKey) diff --git a/internal/bitwarden/crypto/keybuilder/key_pair.go b/internal/bitwarden/crypto/keybuilder/key_pair.go index 07bd4a2..6ee10d8 100644 --- a/internal/bitwarden/crypto/keybuilder/key_pair.go +++ b/internal/bitwarden/crypto/keybuilder/key_pair.go @@ -11,12 +11,16 @@ import ( "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/crypto/symmetrickey" ) -func GenerateKeyPair(key symmetrickey.Key) (string, string, error) { +func GenerateRSAKeyPair(key symmetrickey.Key) (string, string, error) { privateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { return "", "", fmt.Errorf("error generating rsa key: %w", err) } + return EncryptRSAKeyPair(key, privateKey) +} + +func EncryptRSAKeyPair(key symmetrickey.Key, privateKey *rsa.PrivateKey) (string, string, error) { publicKeyBytes, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey) if err != nil { return "", "", fmt.Errorf("error marshalling PKIX public key: %w", err) diff --git a/internal/bitwarden/embedded/vault_base_test.go b/internal/bitwarden/embedded/vault_base_test.go index a953f97..c38a103 100644 --- a/internal/bitwarden/embedded/vault_base_test.go +++ b/internal/bitwarden/embedded/vault_base_test.go @@ -2,6 +2,9 @@ package embedded import ( "context" + "crypto/x509" + "encoding/pem" + "strings" "testing" "time" @@ -19,15 +22,55 @@ var ( ProtectedSymmetricKey: "2.lkAJiJtCKPHFPrZ96+j2Xg==|5XJtrKUndcGy28thFukrmgMcLp+BOVdkF+KcuOnfshq9AN1PFhna9Es96CVARCnjTcWuHuqvgnGmcOHTrf8fyfLv63VBsjLgLZk8rCXJoKE=|9dwgx4/13AD+elE2vE7vlSQoe8LbCGGlui345YrKvXY=", ProtectedRSAPrivateKey: "2.D2aLa8ne/DAkeSzctQISVw==|/xoGM5i5JGJTH/vohUuwTFrTx3hd/gt3kBD/FQdeLMtYjl1u96sh0ECmoERqGHeSfXj+iAb9kpTIOKG8LwmkZGUJBI90Mw0M7ODmf7E8eQ+aGF+bGqTSMQ1wtpunEyFVodlg92YN8Ddlb2V9J4uN8ykpHYNDmQiYLZ8bl6vCODRGPyzLvx5M8DbITVL5PhsjKDLLrVV8lFCgCcAL5YLfkghYhFELyX15zXA/KYEnwggDka3hG5+HHFOVZSeyk7Gi6M4TX2wADbTXz1/Wsho8oxFUrtNiOB3ZiY2cx9UWttpzMXoGfi2gJcP1db/nTfWenOLlzw6Od4VyRzsXsfyGwbqBqDnNFkjLvhjVw4JO+psF//xAMDs14101Tf2wFkB6toQ+zdnDphXUeKmiVPQ7gMnQlOWN5tWvjjmYOO4Y63sGpP24cDOdEScIdebZRSA8uOhTzadfKfOiH5zVYZzXs33FQ0li4nBrsj5xYa6PP4D1P7gqjxClPdguwkdLoZ7JvgIlyRIwEcORi5Ich8RWF/kqRBwk0QSzK1mTlHHU5xtSgi4MLNVx4qTYNaJVBtwL9d2MD9LeNn4Z2PL4A7qnszHqsERiQQDxNEgMxMBHDgSXqQbtQxRvsI6oY+yNbN7uVWw4o8AC3f5GBdxzIqcN1mgEM5ix5aDt15w3MhP2FHtf2neKI68TnL8WnT1fT8BVlbECIiUqK0tfq5aTjdSh3gCS15jvZ1H60h+K8+O/nDfquzVjY7UsTGwA+UtS8/JGiaUhc0VhxJo8P1V2VSCiu51d5q3De1vDg5R2VEgBmTchZyTIodC+3+7ACTOwkNCCdIJN7xKOcIGFA7QOuyJtBeXT4Rd9UGMHSL054IB/315WVDiwrP9W1aP0nHzFs+qAXbH5o1E+AmfMDyoHopjGgbUw22r837kHzf5Qe8QhRYPzQvDowfCPhdoy23cbN1VsNavNwTC9hcG5oYMwkK66xP3MEM9UfGD22pwxe7M8U4BRdLCCHbi95eklXE6Mg6DpWsAdMgokQbOvnlwgKfrlbltqXUE/vUQI8TB3AE1Nkt0ST4quTriMuuyiHdeeZV4UkV9jWt/5bTCfdrCYuGZP7g4shbfNcP7u1Zrdxv+EuUwGIOOTrNV5awmBnL3iHE5ya2MnqmRyfWiPIT5majZCk06yxj4XzyIPOpjYKFt0MOgLvG1GllmdtRqg7tMVvc5ZFo5KWIxLsIJD12UjA1GYYoFdX4+wsNbPjfnlE6D2PrtWUICnBFJzYpfyrKTe01k8G8hyyz+tVzBRfz8EA2ew1+hlVcAgSPCcBzhDgqPe+RSPi7ZSd66be1gDhGAftWFM8Z0MrMklXi2DyjjaKBNsZZD8qTcLcobm8nqHUQtnr5JCbmgP3rau8NY/fxeFHsvSiZQoB1aI/y+Sz/R4r+T9cg8hjmS/FUHDO+m6a6nuWNFwz8wIluM557oOTl+A9UGFF50Gpzmf97VdQjM3ZREazQ7la6AobzS3BHI6FNdxN9LTyMpYo+WODv52/VwU3ODH7wf5bz2OHZhk2NG5R7pSH7qg8jM+/MtJkFumENV0qMecozIkP6e4CyI9ua4YwI9n7G5OgKYMG1aj2PRSny2JSLS8aHF1TkRL8SD0nZFCox0=|muEtiwIuZxhuuLv0nouEdxHU2CO+I7JXKZuYHWiv/OE=", } - testPassword = "test12341234" - orgKey = "4.JW3mktbL7vpTVRweZdQBAirJuAEhSRn37zcXZjDjI47weFKkeZkvPxZWCqFYC/P5qCJwEYMbv7lTETkWDg6paevVfhJ35buGcTQdEbQxAJebzPahEcUstj11l4Y9T5RaDiAJR8+drrGJ3fKV3v3hymKz2o9fUfK1epuLFll2nnWSOjCcuRe/+zz5VwIVx4WJAPJHmiS6eofbj/DTIQCzG4JkR0UzT66ouLcgmPL1nGOqVI7KxRpL5yVj75UkjniHkWAcB7lfAxWXw2GhDJ/2L685uA3820ItTbxjCwLQOvjBttgrbURmkeP9BD+KkO4V6vb8bbTWNSvggXKk2h1CMw==" + rsaPrivateKey = `-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAlRbtt5Vyku3dUIkPR0Y3v94qvZReuwAijIwHQGQOuKw6lKVV +HL29rZ93TwCG2P5a+GKH2+2fIbT/wTMTK4K1ElxQZ/2yLN9Hfu2d/ITNTsfTzPXv +F3fao3Q0JmD7DNJS2bJqng3so28aFddOZ03H55m9T6+0ZJqrMdgE5Z1V/4I7LFF6 +JxGGZ4mg99OvfQ5K8GOBM6SCI6h5eMXM5EkSM9vol9sRxvVLZmvNKH3UP9riyQwt +dcD5IxmY1y34Bg4b+a8tYaP7v90xF73uKs+287yNPLhWE9i+/gXwvApVxENG9SCP +lSrAEHd1NZPfsZhHoG+LXhyCZu2COttZess44QIDAQABAoIBAAIXgN54+qMpJ+2M +yGsdvGj3vCy9+vSyWi8Tr3icdXMrKfTVgMUlvEurcOI/Mcp+v55MF3JF0kwylh3N +pSwbV3DBHN5Hp5xu8HmtahsoWRnXo98Z4oOB7U5gAj5kBmkMtKhB/fJW+UzF/C/b +I2906Tw59Uy2XIsROzvjMeGPnddh1LbvXUb9nAmhi7napdwCUbeqvatu56GyiXxV +03DTwhbfuU+nMi/M556WPEkPbJoG4bF82WqQ+6+a1NfE2wg//cc5CzXQehC6jGx6 +Pi+uNUtPhMSyTnJgpvg+Ob2r/LTiL0zic00ka29xUsi7EwXKUR2ih8JTkSPaTR06 +3ezrg3ECgYEAzi/MnXfWp49jRgb4bHE9Cy63hehgaBvIcEyhFKOz5OD6nI4Lg/3z +SNDQo9YhwMqschqQLHVEtjxDT0V/RHdX4icTF+zSCl/T79EtM/R1nMT0MSIXV7IE +NtPbnqXOjrbe3vgjLvBst/cWpGHiML+znCqukHOevSn7yUlg4b1aMVECgYEAuRvL +YnbNlps/nql1EW9DUKOEV7kBvehwGzYpFfZ7pRscl6RyISTipGMOzJmOSfscYwqR +HrFpTNMNxjnyXOuv4OTC7bCIUJc6N8AZ4jm4452ibxpzktlO8Im+TbsD6mZDT8zB +d+8o/8ST9j1zy43Sb+f5vxfB6fC9vUXpBW0+KpECgYEAzQKT9cJxSVv1/mvx2Ilj +g9nompmqOfnd+2MGCuqWdS4JoV5PLudzXeRaf3zrRLGAc1fcIIhdUMFsv8Y/O8la +NcBaaMCNO8l6hoo64tzfkIf4sV3PTd/v9sACL6V3U0mbIqIhAYwG3YguGDZHW+dQ +ZCfAOFrt6/Jxqvtt/CZ1JnECgYBOxmdNZeWj/Dmc2dy6KLFq9ctyUYdOPEbJLcla +UWTZJKqMVi1DsaDJ+GXp6EdHcJfqBisv9qwrR34LJ8nehWZ5vKC/6mp4cYMTCqt5 +PLtUEld4FLeufNA9SUE1bysBa7ellCuZUKwP/KZDGm/W5mnxubTs/71EQ3FbxQ6f +gpf8IQKBgGHK8j8rvxtszoQKUY+XpWFrP0x5pDLiAkmQ0bmF2KRIahq3anla6n3i +/LL5BrUdMjEnvAb+RASq+41rceq4rLcz0pA2yOWNjhbCAPFdU5MQMkJ4/zqHtzvd +GqwE00g9gizQ6CmsaNNJh7y6gNg0TBU2EGqTaQMz37fheAEt3NSt +-----END RSA PRIVATE KEY----- +` + + encryptionKey = "Vr+KA/il3QX4z7EqFnhQ3U8TtETlQPKkXHCE2PiR75wwzDVRutR4rib/jMtgZ1S/gPyOEXbwKFju2oJq3njVLg==" + orgKey = "4.JW3mktbL7vpTVRweZdQBAirJuAEhSRn37zcXZjDjI47weFKkeZkvPxZWCqFYC/P5qCJwEYMbv7lTETkWDg6paevVfhJ35buGcTQdEbQxAJebzPahEcUstj11l4Y9T5RaDiAJR8+drrGJ3fKV3v3hymKz2o9fUfK1epuLFll2nnWSOjCcuRe/+zz5VwIVx4WJAPJHmiS6eofbj/DTIQCzG4JkR0UzT66ouLcgmPL1nGOqVI7KxRpL5yVj75UkjniHkWAcB7lfAxWXw2GhDJ/2L685uA3820ItTbxjCwLQOvjBttgrbURmkeP9BD+KkO4V6vb8bbTWNSvggXKk2h1CMw==" + testPassword = "test12341234" ) func TestDecryptAccountSecret(t *testing.T) { accountSecrets, err := decryptAccountSecrets(testAccount, testPassword) assert.NoError(t, err) assert.Equal(t, "qOOJSiS6KGqePb+ZxBPD9G37cZjFfViArWiHCd0koK4=", accountSecrets.MasterPasswordHash) + + pemdata := pem.EncodeToMemory( + &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(accountSecrets.RSAPrivateKey), + }, + ) + + assert.Equal(t, rsaPrivateKey, strings.Replace(string(pemdata), "\\n", "\n", -1)) + assert.Contains(t, accountSecrets.MainKey.Summary(), encryptionKey) } func TestDecryptAccountSecretWrongPassword(t *testing.T) { diff --git a/internal/bitwarden/embedded/vault_mocked_webapi_test.go b/internal/bitwarden/embedded/vault_mocked_webapi_test.go new file mode 100644 index 0000000..e50c55f --- /dev/null +++ b/internal/bitwarden/embedded/vault_mocked_webapi_test.go @@ -0,0 +1,578 @@ +package embedded + +import ( + "context" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "net/http" + "testing" + + "github.com/jarcoal/httpmock" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/crypto" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/crypto/keybuilder" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/webapi" +) + +func NewWebAPIVaultWithMockedTransport(t *testing.T) webAPIVault { + vault := NewWebAPIVault("http://127.0.0.1:8081/").(*webAPIVault) + vault.client = mockedClient() + return *vault +} + +func mockedClient() webapi.Client { + client := http.Client{Transport: httpmock.DefaultTransport} + httpmock.RegisterResponder("POST", "http://127.0.0.1:8081/identity/accounts/prelogin", + httpmock.NewStringResponder(200, `{"kdf":0,"kdfIterations":600000,"kdfMemory":null,"kdfParallelism":null}`)) + + // Regexp match (could use httpmock.RegisterRegexpResponder instead) + httpmock.RegisterResponder("POST", `http://127.0.0.1:8081/identity/connect/token`, + httpmock.NewStringResponder(200, `{"ForcePasswordReset":false,"Kdf":0,"KdfIterations":600000,"KdfMemory":null,"KdfParallelism":null,"Key":"2.lkAJiJtCKPHFPrZ96+j2Xg==|5XJtrKUndcGy28thFukrmgMcLp+BOVdkF+KcuOnfshq9AN1PFhna9Es96CVARCnjTcWuHuqvgnGmcOHTrf8fyfLv63VBsjLgLZk8rCXJoKE=|9dwgx4/13AD+elE2vE7vlSQoe8LbCGGlui345YrKvXY=","MasterPasswordPolicy":{"object":"masterPasswordPolicy"},"PrivateKey":"2.D2aLa8ne/DAkeSzctQISVw==|/xoGM5i5JGJTH/vohUuwTFrTx3hd/gt3kBD/FQdeLMtYjl1u96sh0ECmoERqGHeSfXj+iAb9kpTIOKG8LwmkZGUJBI90Mw0M7ODmf7E8eQ+aGF+bGqTSMQ1wtpunEyFVodlg92YN8Ddlb2V9J4uN8ykpHYNDmQiYLZ8bl6vCODRGPyzLvx5M8DbITVL5PhsjKDLLrVV8lFCgCcAL5YLfkghYhFELyX15zXA/KYEnwggDka3hG5+HHFOVZSeyk7Gi6M4TX2wADbTXz1/Wsho8oxFUrtNiOB3ZiY2cx9UWttpzMXoGfi2gJcP1db/nTfWenOLlzw6Od4VyRzsXsfyGwbqBqDnNFkjLvhjVw4JO+psF//xAMDs14101Tf2wFkB6toQ+zdnDphXUeKmiVPQ7gMnQlOWN5tWvjjmYOO4Y63sGpP24cDOdEScIdebZRSA8uOhTzadfKfOiH5zVYZzXs33FQ0li4nBrsj5xYa6PP4D1P7gqjxClPdguwkdLoZ7JvgIlyRIwEcORi5Ich8RWF/kqRBwk0QSzK1mTlHHU5xtSgi4MLNVx4qTYNaJVBtwL9d2MD9LeNn4Z2PL4A7qnszHqsERiQQDxNEgMxMBHDgSXqQbtQxRvsI6oY+yNbN7uVWw4o8AC3f5GBdxzIqcN1mgEM5ix5aDt15w3MhP2FHtf2neKI68TnL8WnT1fT8BVlbECIiUqK0tfq5aTjdSh3gCS15jvZ1H60h+K8+O/nDfquzVjY7UsTGwA+UtS8/JGiaUhc0VhxJo8P1V2VSCiu51d5q3De1vDg5R2VEgBmTchZyTIodC+3+7ACTOwkNCCdIJN7xKOcIGFA7QOuyJtBeXT4Rd9UGMHSL054IB/315WVDiwrP9W1aP0nHzFs+qAXbH5o1E+AmfMDyoHopjGgbUw22r837kHzf5Qe8QhRYPzQvDowfCPhdoy23cbN1VsNavNwTC9hcG5oYMwkK66xP3MEM9UfGD22pwxe7M8U4BRdLCCHbi95eklXE6Mg6DpWsAdMgokQbOvnlwgKfrlbltqXUE/vUQI8TB3AE1Nkt0ST4quTriMuuyiHdeeZV4UkV9jWt/5bTCfdrCYuGZP7g4shbfNcP7u1Zrdxv+EuUwGIOOTrNV5awmBnL3iHE5ya2MnqmRyfWiPIT5majZCk06yxj4XzyIPOpjYKFt0MOgLvG1GllmdtRqg7tMVvc5ZFo5KWIxLsIJD12UjA1GYYoFdX4+wsNbPjfnlE6D2PrtWUICnBFJzYpfyrKTe01k8G8hyyz+tVzBRfz8EA2ew1+hlVcAgSPCcBzhDgqPe+RSPi7ZSd66be1gDhGAftWFM8Z0MrMklXi2DyjjaKBNsZZD8qTcLcobm8nqHUQtnr5JCbmgP3rau8NY/fxeFHsvSiZQoB1aI/y+Sz/R4r+T9cg8hjmS/FUHDO+m6a6nuWNFwz8wIluM557oOTl+A9UGFF50Gpzmf97VdQjM3ZREazQ7la6AobzS3BHI6FNdxN9LTyMpYo+WODv52/VwU3ODH7wf5bz2OHZhk2NG5R7pSH7qg8jM+/MtJkFumENV0qMecozIkP6e4CyI9ua4YwI9n7G5OgKYMG1aj2PRSny2JSLS8aHF1TkRL8SD0nZFCox0=|muEtiwIuZxhuuLv0nouEdxHU2CO+I7JXKZuYHWiv/OE=","ResetMasterPassword":false,"UserDecryptionOptions":{"HasMasterPassword":true,"Object":"userDecryptionOptions"},"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJuYmYiOjE3Mjc1MTI0NjIsImV4cCI6MTcyNzUxOTY2MiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdHxsb2dpbiIsInN1YiI6ImU4ZGFiYWJkLTI0MmUtNDkwMC1iZWNmLWU4OGJjMDIxZGRhOCIsInByZW1pdW0iOnRydWUsIm5hbWUiOiJUZXN0IExhdmVyc2UiLCJlbWFpbCI6InRlc3RAbGF2ZXJzZS5uZXQiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwic3N0YW1wIjoiNDAzM2NmMzUtYmM1ZC00ZWNkLTk1MWMtZGJlNTQ5ZjI3NzQwIiwiZGV2aWNlIjoiNWQ5MGI0NzAtNWQxZC00NTJkLTkzNWMtYjczMGMxNzdhOGQ2Iiwic2NvcGUiOlsiYXBpIiwib2ZmbGluZV9hY2Nlc3MiXSwiYW1yIjpbIkFwcGxpY2F0aW9uIl19.hKXO3p4am134f74wx5WqmuRCT__1CcqiZ5YkU5q8FvXNY-oe_pOrU7gxpf9TDBIOQVTjVkE09dgzEKco_qMnRTT7cvQ95o3k82gNDNgyi0Yjqtv5eS8K7m7MrSOUqe6LwZwpKy3G8vDnu1O2kiN-So44cCBKQzqOrR4oimmYIMt8IWqU2qa8ZaFe1E70nv5NjUxiE_NkT2FF6M8MA7z_aEkcBeleknlKRL-gaqYqAPWhKF_msW9e3SKvm_83wt1JRwr60T4aKXbl7rB4UrSqs_lqhdb3i1Ul5_TYQXbg4yfETgT8Uh37kXtnK_8MlxNBpA8pEAnp_29Odync3T7naQ","expires_in":7200,"refresh_token":"6wCk9N0MG-abyrmBUn5m1ACq-oRnhj6mbjCeuOz_HPyPERfK8DPeTJVf3c6KtP-bIbL6mErO-ggMs_MNS2GlIg==","scope":"api offline_access","token_type":"Bearer","unofficialServer":true}`)) + + httpmock.RegisterResponder("GET", `http://127.0.0.1:8081/api/sync?excludeDomains=true`, + httpmock.NewStringResponder(200, `{ + "ciphers": [ + { + "attachments": null, + "card": null, + "collectionIds": [], + "creationDate": "2024-09-22T11:13:40.346903Z", + "data": { + "autofillOnPageLoad": null, + "fields": [ + { + "linkedId": null, + "name": "2.svtF3aK9R1MLHt2GwH7Ykw==|PqVDa02T0Vzx6ogTNz1Xyg==|g9/Gd5OOLkVtyVAk9vkRCY2reWTZjdY1D3XsVpBfQA4=", + "type": 0, + "value": "2.K49DvcymvaQ/+V/3soCb1Q==|r+b2dz0YqFeA8BrHii5a0DIjkwxdwlY+aEEH2d2NdC0=|KEJAjqImC0Jit9oaFN2rSZHyetxuD7jEjMt07HPFLvU=" + } + ], + "name": "2.LjR8NxRtCB1noDILxNKmPQ==|s4e2AO4I3HqmPRRsbsYl0XYSuWu7+sn2K3+jNiFjsW0=|3NQW64/3RmPxe3hhUGeMTrSy9Ruh5hYRlJxIhhWhhSI=", + "notes": "2.ke3IFCPe50UCM2XdJDS/VQ==|ksO+dyEuRBgrpAelfhxNhw==|JrbMU58V5QyiTpdJXyWB1g2l8jPcqyeWDMqjOnaa9UA=", + "password": null, + "passwordHistory": [], + "passwordRevisionDate": null, + "totp": "2.mScxVU7uCx3fiPXYlgzWVA==|yxqIjknG71Qdwxq1wyVufWyB1Hb6qWowPgGoZNV2d4s=|uY7hrn7Eow81A76/n4VUrJxSRtC9VDnUdaesO6y0ivY=", + "uri": "2.7ihoxwsJH4HBkTzjFBwFIA==|5EkQHm4IRExHH/LAU6D/jw==|Hs6YgBIFuTQNnH9M3ejgUAVzsGjxAyaVPi1pl1NU9wg=", + "uris": [ + { + "match": null, + "uri": "2.7ihoxwsJH4HBkTzjFBwFIA==|5EkQHm4IRExHH/LAU6D/jw==|Hs6YgBIFuTQNnH9M3ejgUAVzsGjxAyaVPi1pl1NU9wg=", + "uriChecksum": "2.WfCD+h+rQ49rJDiTM7ycjw==|QgYnnlKzeymjRXmW15SC7bnWWvWaU1iki7rABdi0+RjPgRVGLluT/lQCKXkr5fXH|vaMZaBCKZySaycxVXjpDatKjrN6mBaq2ffRsygLTGzA=" + } + ], + "username": "2.9hmypXsnAjVxJ2e5kZQLKA==|8Jif+MQgdTuAlaCn7i/xig==|xy7zJh4qXutESyC02aKSYb/79EmfbGsYlxsYfKqneLA=" + }, + "deletedDate": null, + "edit": true, + "favorite": false, + "fields": [ + { + "linkedId": null, + "name": "2.svtF3aK9R1MLHt2GwH7Ykw==|PqVDa02T0Vzx6ogTNz1Xyg==|g9/Gd5OOLkVtyVAk9vkRCY2reWTZjdY1D3XsVpBfQA4=", + "type": 0, + "value": "2.K49DvcymvaQ/+V/3soCb1Q==|r+b2dz0YqFeA8BrHii5a0DIjkwxdwlY+aEEH2d2NdC0=|KEJAjqImC0Jit9oaFN2rSZHyetxuD7jEjMt07HPFLvU=" + } + ], + "folderId": null, + "id": "24d1c150-5dfd-4008-964c-01317d1f6b23", + "identity": null, + "key": null, + "login": { + "autofillOnPageLoad": null, + "password": null, + "passwordRevisionDate": null, + "totp": "2.mScxVU7uCx3fiPXYlgzWVA==|yxqIjknG71Qdwxq1wyVufWyB1Hb6qWowPgGoZNV2d4s=|uY7hrn7Eow81A76/n4VUrJxSRtC9VDnUdaesO6y0ivY=", + "uri": "2.7ihoxwsJH4HBkTzjFBwFIA==|5EkQHm4IRExHH/LAU6D/jw==|Hs6YgBIFuTQNnH9M3ejgUAVzsGjxAyaVPi1pl1NU9wg=", + "uris": [ + { + "match": null, + "uri": "2.7ihoxwsJH4HBkTzjFBwFIA==|5EkQHm4IRExHH/LAU6D/jw==|Hs6YgBIFuTQNnH9M3ejgUAVzsGjxAyaVPi1pl1NU9wg=", + "uriChecksum": "2.WfCD+h+rQ49rJDiTM7ycjw==|QgYnnlKzeymjRXmW15SC7bnWWvWaU1iki7rABdi0+RjPgRVGLluT/lQCKXkr5fXH|vaMZaBCKZySaycxVXjpDatKjrN6mBaq2ffRsygLTGzA=" + } + ], + "username": "2.9hmypXsnAjVxJ2e5kZQLKA==|8Jif+MQgdTuAlaCn7i/xig==|xy7zJh4qXutESyC02aKSYb/79EmfbGsYlxsYfKqneLA=" + }, + "name": "2.LjR8NxRtCB1noDILxNKmPQ==|s4e2AO4I3HqmPRRsbsYl0XYSuWu7+sn2K3+jNiFjsW0=|3NQW64/3RmPxe3hhUGeMTrSy9Ruh5hYRlJxIhhWhhSI=", + "notes": "2.ke3IFCPe50UCM2XdJDS/VQ==|ksO+dyEuRBgrpAelfhxNhw==|JrbMU58V5QyiTpdJXyWB1g2l8jPcqyeWDMqjOnaa9UA=", + "object": "cipherDetails", + "organizationId": null, + "organizationUseTotp": true, + "passwordHistory": [], + "reprompt": 0, + "revisionDate": "2024-09-22T11:13:40.347356Z", + "secureNote": null, + "type": 1, + "viewPassword": true + }, + { + "attachments": null, + "card": null, + "collectionIds": ["8d1a5611-5fd6-4728-a6f8-22bc03b50640"], + "creationDate": "2024-09-22T11:15:49.885670Z", + "data": { + "autofillOnPageLoad": null, + "fields": [], + "name": "2.kOK8qkac+p+c8mNjhkuO/w==|nP1hQA1/8rJt97NmWSarfchRF+XWAUflsYzq9X2lBPU=|J7AmiEiDRCWvlLjYBrwXxCgCLw4EQiXmvlKnJw0mTb8=", + "notes": null, + "password": "2.uiCrgwCc32t6jIaUcMiinw==|JJVfmpcdrekNqJLfJOVaGw==|1zpwpqlATK+6/av/6JfOoHJaOgtpNJA6LDqEqsR2C/A=", + "passwordHistory": [], + "passwordRevisionDate": null, + "totp": null, + "uri": null, + "uris": [], + "username": "2.6WVjMRz5qtfVkGJ40Ne+Fw==|LtPQSeBvHIEgmzTtpdq4lw==|BV/F2ZhCD1nsr/4RoETwpMnu8PPVYirxrDQXcF6gpg4=" + }, + "deletedDate": null, + "edit": true, + "favorite": false, + "fields": [], + "folderId": null, + "id": "94b0e53d-a194-493f-ad25-e6ca9b9abb75", + "identity": null, + "key": null, + "login": { + "autofillOnPageLoad": null, + "password": "2.uiCrgwCc32t6jIaUcMiinw==|JJVfmpcdrekNqJLfJOVaGw==|1zpwpqlATK+6/av/6JfOoHJaOgtpNJA6LDqEqsR2C/A=", + "passwordRevisionDate": null, + "totp": null, + "uri": null, + "uris": [], + "username": "2.6WVjMRz5qtfVkGJ40Ne+Fw==|LtPQSeBvHIEgmzTtpdq4lw==|BV/F2ZhCD1nsr/4RoETwpMnu8PPVYirxrDQXcF6gpg4=" + }, + "name": "2.kOK8qkac+p+c8mNjhkuO/w==|nP1hQA1/8rJt97NmWSarfchRF+XWAUflsYzq9X2lBPU=|J7AmiEiDRCWvlLjYBrwXxCgCLw4EQiXmvlKnJw0mTb8=", + "notes": null, + "object": "cipherDetails", + "organizationId": "81cc1652-dc80-472d-909f-9539d057068b", + "organizationUseTotp": true, + "passwordHistory": [], + "reprompt": 0, + "revisionDate": "2024-09-22T11:15:49.887811Z", + "secureNote": null, + "type": 1, + "viewPassword": true + }, + { + "attachments": null, + "card": null, + "collectionIds": ["15e18629-849f-4629-895b-54600c542a70"], + "creationDate": "2024-09-22T11:14:53.442012Z", + "data": { + "autofillOnPageLoad": null, + "fields": [], + "name": "2.sOEzwZwic+bzHlaUpnl43Q==|Hr5DUOPmBJiz1oT8o2uJNLKFhY8rcmsx8EGqxuoJC9tNBCv6WwJOotrr2bDoPPP/|ZgcnsODyuV2Zd+Nl7MGoAPj383d/fr8LCEIvidYFJYk=", + "notes": null, + "password": "2.Nw6WShTt6PTNzRvnLYp2oQ==|x2mPKtOtqPpJEbCPH4EBBw==|Ij7Gq+CCXpJgoZISsATzsZb7v7paSj8mryWKyU0t/VY=", + "passwordHistory": [], + "passwordRevisionDate": null, + "totp": null, + "uri": null, + "uris": [], + "username": "2.K7I2dQ8x82rPK2buV1dzUQ==|VdjhkLm2wlURH03+GnLTFw==|AtpKj+TYgu+/YSqPn2jkFzePKvz71JtCgn+w0MXuhoI=" + }, + "deletedDate": null, + "edit": true, + "favorite": false, + "fields": [], + "folderId": null, + "id": "adaef652-16e0-439a-9d67-07671ddc2a51", + "identity": null, + "key": null, + "login": { + "autofillOnPageLoad": null, + "password": "2.Nw6WShTt6PTNzRvnLYp2oQ==|x2mPKtOtqPpJEbCPH4EBBw==|Ij7Gq+CCXpJgoZISsATzsZb7v7paSj8mryWKyU0t/VY=", + "passwordRevisionDate": null, + "totp": null, + "uri": null, + "uris": [], + "username": "2.K7I2dQ8x82rPK2buV1dzUQ==|VdjhkLm2wlURH03+GnLTFw==|AtpKj+TYgu+/YSqPn2jkFzePKvz71JtCgn+w0MXuhoI=" + }, + "name": "2.sOEzwZwic+bzHlaUpnl43Q==|Hr5DUOPmBJiz1oT8o2uJNLKFhY8rcmsx8EGqxuoJC9tNBCv6WwJOotrr2bDoPPP/|ZgcnsODyuV2Zd+Nl7MGoAPj383d/fr8LCEIvidYFJYk=", + "notes": null, + "object": "cipherDetails", + "organizationId": "81cc1652-dc80-472d-909f-9539d057068b", + "organizationUseTotp": true, + "passwordHistory": [], + "reprompt": 0, + "revisionDate": "2024-09-22T11:14:53.443032Z", + "secureNote": null, + "type": 1, + "viewPassword": true + }, + { + "attachments": null, + "card": null, + "collectionIds": [], + "creationDate": "2024-09-26T14:26:27.436093Z", + "data": { + "autofillOnPageLoad": null, + "fields": [ + { + "linkedId": null, + "name": null, + "type": 2, + "value": "2.5ndrC0WqMJO6fH/2xv3AMw==|zY1/7UgW/MQUutZBixlyOA==|M4/tHU/s5w6ZygyXVUxpiEtAlFtoa9PlL5DY+S8K6gY=" + } + ], + "name": "2.4lqddV7PV1Uxn0qFk0JQLA==|svoCTgXohcpPJZirBg1PcQ==|AFIutATBeBvoGpfvDMqaDGlQXBFvIBKTyZqJNt+UPnE=", + "notes": null, + "password": null, + "passwordHistory": [], + "passwordRevisionDate": null, + "totp": null, + "uri": null, + "uris": [], + "username": "2.DkVdwPK5E5RAn6BLwzHXIg==|484sbAo5PFn6D50K9AToYw==|wjCnCnR+JunkRnoRjC/jwcJizdHCuzJ2RRp1z2uuJBg=" + }, + "deletedDate": null, + "edit": true, + "favorite": false, + "fields": [ + { + "linkedId": null, + "name": null, + "type": 2, + "value": "2.5ndrC0WqMJO6fH/2xv3AMw==|zY1/7UgW/MQUutZBixlyOA==|M4/tHU/s5w6ZygyXVUxpiEtAlFtoa9PlL5DY+S8K6gY=" + } + ], + "folderId": "3df04ed6-624d-45ae-b825-7ceb0cf6eb0e", + "id": "b4b2d9ad-1d86-41d7-a1f3-a249e9f96e8e", + "identity": null, + "key": null, + "login": { + "autofillOnPageLoad": null, + "password": null, + "passwordRevisionDate": null, + "totp": null, + "uri": null, + "uris": [], + "username": "2.DkVdwPK5E5RAn6BLwzHXIg==|484sbAo5PFn6D50K9AToYw==|wjCnCnR+JunkRnoRjC/jwcJizdHCuzJ2RRp1z2uuJBg=" + }, + "name": "2.4lqddV7PV1Uxn0qFk0JQLA==|svoCTgXohcpPJZirBg1PcQ==|AFIutATBeBvoGpfvDMqaDGlQXBFvIBKTyZqJNt+UPnE=", + "notes": null, + "object": "cipherDetails", + "organizationId": null, + "organizationUseTotp": true, + "passwordHistory": [], + "reprompt": 0, + "revisionDate": "2024-09-26T14:26:27.436534Z", + "secureNote": null, + "type": 1, + "viewPassword": true + }, + { + "attachments": null, + "card": null, + "collectionIds": [], + "creationDate": "2024-09-22T11:14:14.671495Z", + "data": { + "autofillOnPageLoad": null, + "fields": [], + "name": "2.aavpFIxp7xouEaflFBr0ug==|JEyMdNkR/1TaJ5lU5w8FfR4+yfV+JP+XsJd8np7/gY4=|BaHDWGBVNTdd5KsR2VLPrHA0caVgxszWFEwgmtR5iHI=", + "notes": null, + "password": "2.qjgHReWyJ0X9KlP3qFtB8w==|cHxcCMM2OTTdI3mt85KveQ==|/EuUwk9AzkUQqsJzJ3Z2MEvM4tQ9Ma8WDoUAXtNLE4E=", + "passwordHistory": [], + "passwordRevisionDate": null, + "totp": null, + "uri": null, + "uris": [], + "username": "2.K69oGBEuH9GzhSkzi2zzIg==|bbeKWbkuSc1cVJm2xDae8Q==|y+Z07fAT+zmlq2gSLFC6uLEfYIq3IT7HqNFJqlWQOPM=" + }, + "deletedDate": null, + "edit": true, + "favorite": false, + "fields": [], + "folderId": "e7098f5d-4d00-4bdf-8c66-56f9dca2129f", + "id": "b7441ea3-baf4-4f04-a445-0ad342eb56c4", + "identity": null, + "key": null, + "login": { + "autofillOnPageLoad": null, + "password": "2.qjgHReWyJ0X9KlP3qFtB8w==|cHxcCMM2OTTdI3mt85KveQ==|/EuUwk9AzkUQqsJzJ3Z2MEvM4tQ9Ma8WDoUAXtNLE4E=", + "passwordRevisionDate": null, + "totp": null, + "uri": null, + "uris": [], + "username": "2.K69oGBEuH9GzhSkzi2zzIg==|bbeKWbkuSc1cVJm2xDae8Q==|y+Z07fAT+zmlq2gSLFC6uLEfYIq3IT7HqNFJqlWQOPM=" + }, + "name": "2.aavpFIxp7xouEaflFBr0ug==|JEyMdNkR/1TaJ5lU5w8FfR4+yfV+JP+XsJd8np7/gY4=|BaHDWGBVNTdd5KsR2VLPrHA0caVgxszWFEwgmtR5iHI=", + "notes": null, + "object": "cipherDetails", + "organizationId": null, + "organizationUseTotp": true, + "passwordHistory": [], + "reprompt": 0, + "revisionDate": "2024-09-22T11:14:14.672096Z", + "secureNote": null, + "type": 1, + "viewPassword": true + } + ], + "collections": [ + { + "externalId": null, + "hidePasswords": false, + "id": "15e18629-849f-4629-895b-54600c542a70", + "name": "2.X7RK7wBZl+1pqo4yvAontQ==|Q+RD7xRWtXPC/Ijbl+863VSyQ4oYmHGG5EzNDtNYWW0=|369crUvCUpo5bID5wu0Q60eQJJv5+eJLxbsuZKmtVYA=", + "object": "collectionDetails", + "organizationId": "81cc1652-dc80-472d-909f-9539d057068b", + "readOnly": false + }, + { + "externalId": null, + "hidePasswords": false, + "id": "8d1a5611-5fd6-4728-a6f8-22bc03b50640", + "name": "2.4KNb9dUNUqfe2iqFZxBZJQ==|lrcBRuOjIpMAiBMHYDNbpn6ZmUz/OzHuBTr68ta0a/I=|OtEMhyhekHnARLATP8IWKAWieTaNQpbgO7/syW1KLK4=", + "object": "collectionDetails", + "organizationId": "81cc1652-dc80-472d-909f-9539d057068b", + "readOnly": false + } + ], + "domains": null, + "folders": [ + { + "id": "e7098f5d-4d00-4bdf-8c66-56f9dca2129f", + "name": "2.FTcL7PAe36ZQh0zIXPotSg==|ALhJRf/j9Wa83jg/tWbFSndiZ9JvZs2BwmVXmCUWhKc=|AEpDm/0LhllnW/Qzx9oWNYuSMaLQuR62MFUQuAaqQvc=", + "object": "folder", + "revisionDate": "2024-09-22T11:13:55.612053Z" + }, + { + "id": "3df04ed6-624d-45ae-b825-7ceb0cf6eb0e", + "name": "2.cybmeku3f53n3//scS4HfQ==|QEOsVN2jUF3+11/a8iISuQ==|PV2zCoVqNCX9KzyIUassEOjZE/f6bF+Y6Ft1ypRJ9lw=", + "object": "folder", + "revisionDate": "2024-09-22T17:00:11.566430Z" + } + ], + "object": "sync", + "policies": [], + "profile": { + "_status": 0, + "avatarColor": null, + "culture": "en-US", + "email": "test@laverse.net", + "emailVerified": true, + "forcePasswordReset": false, + "id": "e8dababd-242e-4900-becf-e88bc021dda8", + "key": "2.lkAJiJtCKPHFPrZ96+j2Xg==|5XJtrKUndcGy28thFukrmgMcLp+BOVdkF+KcuOnfshq9AN1PFhna9Es96CVARCnjTcWuHuqvgnGmcOHTrf8fyfLv63VBsjLgLZk8rCXJoKE=|9dwgx4/13AD+elE2vE7vlSQoe8LbCGGlui345YrKvXY=", + "masterPasswordHint": null, + "name": "Test Laverse", + "object": "profile", + "organizations": [ + { + "accessSecretsManager": false, + "allowAdminAccessToAllCollectionItems": true, + "enabled": true, + "familySponsorshipAvailable": false, + "familySponsorshipFriendlyName": null, + "familySponsorshipLastSyncDate": null, + "familySponsorshipToDelete": null, + "familySponsorshipValidUntil": null, + "flexibleCollections": false, + "hasPublicAndPrivateKeys": true, + "id": "81cc1652-dc80-472d-909f-9539d057068b", + "identifier": null, + "key": "4.JW3mktbL7vpTVRweZdQBAirJuAEhSRn37zcXZjDjI47weFKkeZkvPxZWCqFYC/P5qCJwEYMbv7lTETkWDg6paevVfhJ35buGcTQdEbQxAJebzPahEcUstj11l4Y9T5RaDiAJR8+drrGJ3fKV3v3hymKz2o9fUfK1epuLFll2nnWSOjCcuRe/+zz5VwIVx4WJAPJHmiS6eofbj/DTIQCzG4JkR0UzT66ouLcgmPL1nGOqVI7KxRpL5yVj75UkjniHkWAcB7lfAxWXw2GhDJ/2L685uA3820ItTbxjCwLQOvjBttgrbURmkeP9BD+KkO4V6vb8bbTWNSvggXKk2h1CMw==", + "keyConnectorEnabled": false, + "keyConnectorUrl": null, + "limitCollectionCreationDeletion": true, + "maxAutoscaleSeats": null, + "maxCollections": null, + "maxStorageGb": 32767, + "name": "My Test Organization", + "object": "profileOrganization", + "organizationUserId": "9e755d16-b500-48ac-b24c-4105b7d08796", + "permissions": { + "accessEventLogs": false, + "accessImportExport": false, + "accessReports": false, + "createNewCollections": false, + "deleteAnyCollection": false, + "deleteAssignedCollections": false, + "editAnyCollection": false, + "editAssignedCollections": false, + "manageGroups": false, + "managePolicies": false, + "manageResetPassword": false, + "manageScim": false, + "manageSso": false, + "manageUsers": false + }, + "planProductType": 3, + "productTierType": 3, + "providerId": null, + "providerName": null, + "providerType": null, + "resetPasswordEnrolled": false, + "seats": null, + "selfHost": true, + "ssoBound": false, + "status": 2, + "type": 0, + "use2fa": true, + "useActivateAutofillPolicy": false, + "useApi": true, + "useCustomPermissions": false, + "useDirectory": false, + "useEvents": false, + "useGroups": false, + "useKeyConnector": false, + "usePasswordManager": true, + "usePolicies": true, + "useResetPassword": false, + "useScim": false, + "useSecretsManager": false, + "useSso": false, + "useTotp": true, + "userId": "e8dababd-242e-4900-becf-e88bc021dda8", + "usersGetPremium": true + } + ], + "premium": true, + "premiumFromOrganization": false, + "privateKey": "2.D2aLa8ne/DAkeSzctQISVw==|/xoGM5i5JGJTH/vohUuwTFrTx3hd/gt3kBD/FQdeLMtYjl1u96sh0ECmoERqGHeSfXj+iAb9kpTIOKG8LwmkZGUJBI90Mw0M7ODmf7E8eQ+aGF+bGqTSMQ1wtpunEyFVodlg92YN8Ddlb2V9J4uN8ykpHYNDmQiYLZ8bl6vCODRGPyzLvx5M8DbITVL5PhsjKDLLrVV8lFCgCcAL5YLfkghYhFELyX15zXA/KYEnwggDka3hG5+HHFOVZSeyk7Gi6M4TX2wADbTXz1/Wsho8oxFUrtNiOB3ZiY2cx9UWttpzMXoGfi2gJcP1db/nTfWenOLlzw6Od4VyRzsXsfyGwbqBqDnNFkjLvhjVw4JO+psF//xAMDs14101Tf2wFkB6toQ+zdnDphXUeKmiVPQ7gMnQlOWN5tWvjjmYOO4Y63sGpP24cDOdEScIdebZRSA8uOhTzadfKfOiH5zVYZzXs33FQ0li4nBrsj5xYa6PP4D1P7gqjxClPdguwkdLoZ7JvgIlyRIwEcORi5Ich8RWF/kqRBwk0QSzK1mTlHHU5xtSgi4MLNVx4qTYNaJVBtwL9d2MD9LeNn4Z2PL4A7qnszHqsERiQQDxNEgMxMBHDgSXqQbtQxRvsI6oY+yNbN7uVWw4o8AC3f5GBdxzIqcN1mgEM5ix5aDt15w3MhP2FHtf2neKI68TnL8WnT1fT8BVlbECIiUqK0tfq5aTjdSh3gCS15jvZ1H60h+K8+O/nDfquzVjY7UsTGwA+UtS8/JGiaUhc0VhxJo8P1V2VSCiu51d5q3De1vDg5R2VEgBmTchZyTIodC+3+7ACTOwkNCCdIJN7xKOcIGFA7QOuyJtBeXT4Rd9UGMHSL054IB/315WVDiwrP9W1aP0nHzFs+qAXbH5o1E+AmfMDyoHopjGgbUw22r837kHzf5Qe8QhRYPzQvDowfCPhdoy23cbN1VsNavNwTC9hcG5oYMwkK66xP3MEM9UfGD22pwxe7M8U4BRdLCCHbi95eklXE6Mg6DpWsAdMgokQbOvnlwgKfrlbltqXUE/vUQI8TB3AE1Nkt0ST4quTriMuuyiHdeeZV4UkV9jWt/5bTCfdrCYuGZP7g4shbfNcP7u1Zrdxv+EuUwGIOOTrNV5awmBnL3iHE5ya2MnqmRyfWiPIT5majZCk06yxj4XzyIPOpjYKFt0MOgLvG1GllmdtRqg7tMVvc5ZFo5KWIxLsIJD12UjA1GYYoFdX4+wsNbPjfnlE6D2PrtWUICnBFJzYpfyrKTe01k8G8hyyz+tVzBRfz8EA2ew1+hlVcAgSPCcBzhDgqPe+RSPi7ZSd66be1gDhGAftWFM8Z0MrMklXi2DyjjaKBNsZZD8qTcLcobm8nqHUQtnr5JCbmgP3rau8NY/fxeFHsvSiZQoB1aI/y+Sz/R4r+T9cg8hjmS/FUHDO+m6a6nuWNFwz8wIluM557oOTl+A9UGFF50Gpzmf97VdQjM3ZREazQ7la6AobzS3BHI6FNdxN9LTyMpYo+WODv52/VwU3ODH7wf5bz2OHZhk2NG5R7pSH7qg8jM+/MtJkFumENV0qMecozIkP6e4CyI9ua4YwI9n7G5OgKYMG1aj2PRSny2JSLS8aHF1TkRL8SD0nZFCox0=|muEtiwIuZxhuuLv0nouEdxHU2CO+I7JXKZuYHWiv/OE=", + "providerOrganizations": [], + "providers": [], + "securityStamp": "4033cf35-bc5d-4ecd-951c-dbe549f27740", + "twoFactorEnabled": false, + "usesKeyConnector": false + }, + "sends": [], + "unofficialServer": true +} +`)) + + httpmock.RegisterResponder("GET", `http://127.0.0.1:8081/api/accounts/profile`, + httpmock.NewStringResponder(200, `{"_status":0,"avatarColor":null,"culture":"en-US","email":"test@laverse.net","emailVerified":true,"forcePasswordReset":false,"id":"e8dababd-242e-4900-becf-e88bc021dda8","key":"2.lkAJiJtCKPHFPrZ96+j2Xg==|5XJtrKUndcGy28thFukrmgMcLp+BOVdkF+KcuOnfshq9AN1PFhna9Es96CVARCnjTcWuHuqvgnGmcOHTrf8fyfLv63VBsjLgLZk8rCXJoKE=|9dwgx4/13AD+elE2vE7vlSQoe8LbCGGlui345YrKvXY=","masterPasswordHint":null,"name":"Test Laverse","object":"profile","organizations":[{"accessSecretsManager":false,"allowAdminAccessToAllCollectionItems":true,"enabled":true,"familySponsorshipAvailable":false,"familySponsorshipFriendlyName":null,"familySponsorshipLastSyncDate":null,"familySponsorshipToDelete":null,"familySponsorshipValidUntil":null,"flexibleCollections":false,"hasPublicAndPrivateKeys":true,"id":"81cc1652-dc80-472d-909f-9539d057068b","identifier":null,"key":"4.JW3mktbL7vpTVRweZdQBAirJuAEhSRn37zcXZjDjI47weFKkeZkvPxZWCqFYC/P5qCJwEYMbv7lTETkWDg6paevVfhJ35buGcTQdEbQxAJebzPahEcUstj11l4Y9T5RaDiAJR8+drrGJ3fKV3v3hymKz2o9fUfK1epuLFll2nnWSOjCcuRe/+zz5VwIVx4WJAPJHmiS6eofbj/DTIQCzG4JkR0UzT66ouLcgmPL1nGOqVI7KxRpL5yVj75UkjniHkWAcB7lfAxWXw2GhDJ/2L685uA3820ItTbxjCwLQOvjBttgrbURmkeP9BD+KkO4V6vb8bbTWNSvggXKk2h1CMw==","keyConnectorEnabled":false,"keyConnectorUrl":null,"limitCollectionCreationDeletion":true,"maxAutoscaleSeats":null,"maxCollections":null,"maxStorageGb":32767,"name":"My Test Organization","object":"profileOrganization","organizationUserId":"9e755d16-b500-48ac-b24c-4105b7d08796","permissions":{"accessEventLogs":false,"accessImportExport":false,"accessReports":false,"createNewCollections":false,"deleteAnyCollection":false,"deleteAssignedCollections":false,"editAnyCollection":false,"editAssignedCollections":false,"manageGroups":false,"managePolicies":false,"manageResetPassword":false,"manageScim":false,"manageSso":false,"manageUsers":false},"planProductType":3,"productTierType":3,"providerId":null,"providerName":null,"providerType":null,"resetPasswordEnrolled":false,"seats":null,"selfHost":true,"ssoBound":false,"status":2,"type":0,"use2fa":true,"useActivateAutofillPolicy":false,"useApi":true,"useCustomPermissions":false,"useDirectory":false,"useEvents":false,"useGroups":false,"useKeyConnector":false,"usePasswordManager":true,"usePolicies":true,"useResetPassword":false,"useScim":false,"useSecretsManager":false,"useSso":false,"useTotp":true,"userId":"e8dababd-242e-4900-becf-e88bc021dda8","usersGetPremium":true}],"premium":true,"premiumFromOrganization":false,"privateKey":"2.D2aLa8ne/DAkeSzctQISVw==|/xoGM5i5JGJTH/vohUuwTFrTx3hd/gt3kBD/FQdeLMtYjl1u96sh0ECmoERqGHeSfXj+iAb9kpTIOKG8LwmkZGUJBI90Mw0M7ODmf7E8eQ+aGF+bGqTSMQ1wtpunEyFVodlg92YN8Ddlb2V9J4uN8ykpHYNDmQiYLZ8bl6vCODRGPyzLvx5M8DbITVL5PhsjKDLLrVV8lFCgCcAL5YLfkghYhFELyX15zXA/KYEnwggDka3hG5+HHFOVZSeyk7Gi6M4TX2wADbTXz1/Wsho8oxFUrtNiOB3ZiY2cx9UWttpzMXoGfi2gJcP1db/nTfWenOLlzw6Od4VyRzsXsfyGwbqBqDnNFkjLvhjVw4JO+psF//xAMDs14101Tf2wFkB6toQ+zdnDphXUeKmiVPQ7gMnQlOWN5tWvjjmYOO4Y63sGpP24cDOdEScIdebZRSA8uOhTzadfKfOiH5zVYZzXs33FQ0li4nBrsj5xYa6PP4D1P7gqjxClPdguwkdLoZ7JvgIlyRIwEcORi5Ich8RWF/kqRBwk0QSzK1mTlHHU5xtSgi4MLNVx4qTYNaJVBtwL9d2MD9LeNn4Z2PL4A7qnszHqsERiQQDxNEgMxMBHDgSXqQbtQxRvsI6oY+yNbN7uVWw4o8AC3f5GBdxzIqcN1mgEM5ix5aDt15w3MhP2FHtf2neKI68TnL8WnT1fT8BVlbECIiUqK0tfq5aTjdSh3gCS15jvZ1H60h+K8+O/nDfquzVjY7UsTGwA+UtS8/JGiaUhc0VhxJo8P1V2VSCiu51d5q3De1vDg5R2VEgBmTchZyTIodC+3+7ACTOwkNCCdIJN7xKOcIGFA7QOuyJtBeXT4Rd9UGMHSL054IB/315WVDiwrP9W1aP0nHzFs+qAXbH5o1E+AmfMDyoHopjGgbUw22r837kHzf5Qe8QhRYPzQvDowfCPhdoy23cbN1VsNavNwTC9hcG5oYMwkK66xP3MEM9UfGD22pwxe7M8U4BRdLCCHbi95eklXE6Mg6DpWsAdMgokQbOvnlwgKfrlbltqXUE/vUQI8TB3AE1Nkt0ST4quTriMuuyiHdeeZV4UkV9jWt/5bTCfdrCYuGZP7g4shbfNcP7u1Zrdxv+EuUwGIOOTrNV5awmBnL3iHE5ya2MnqmRyfWiPIT5majZCk06yxj4XzyIPOpjYKFt0MOgLvG1GllmdtRqg7tMVvc5ZFo5KWIxLsIJD12UjA1GYYoFdX4+wsNbPjfnlE6D2PrtWUICnBFJzYpfyrKTe01k8G8hyyz+tVzBRfz8EA2ew1+hlVcAgSPCcBzhDgqPe+RSPi7ZSd66be1gDhGAftWFM8Z0MrMklXi2DyjjaKBNsZZD8qTcLcobm8nqHUQtnr5JCbmgP3rau8NY/fxeFHsvSiZQoB1aI/y+Sz/R4r+T9cg8hjmS/FUHDO+m6a6nuWNFwz8wIluM557oOTl+A9UGFF50Gpzmf97VdQjM3ZREazQ7la6AobzS3BHI6FNdxN9LTyMpYo+WODv52/VwU3ODH7wf5bz2OHZhk2NG5R7pSH7qg8jM+/MtJkFumENV0qMecozIkP6e4CyI9ua4YwI9n7G5OgKYMG1aj2PRSny2JSLS8aHF1TkRL8SD0nZFCox0=|muEtiwIuZxhuuLv0nouEdxHU2CO+I7JXKZuYHWiv/OE=","providerOrganizations":[],"providers":[],"securityStamp":"4033cf35-bc5d-4ecd-951c-dbe549f27740","twoFactorEnabled":false,"usesKeyConnector":false}`)) + + // We're changing the revisionDate on purpose in the response to mimick Bitwarden official server's behavior + httpmock.RegisterResponder("POST", `http://127.0.0.1:8081/api/ciphers`, + httpmock.NewStringResponder(200, ` + { + "attachments": null, + "card": null, + "collectionIds": [], + "creationDate": "2024-09-22T11:13:40.346903Z", + "data": { + "autofillOnPageLoad": null, + "fields": [ + { + "linkedId": null, + "name": "2.svtF3aK9R1MLHt2GwH7Ykw==|PqVDa02T0Vzx6ogTNz1Xyg==|g9/Gd5OOLkVtyVAk9vkRCY2reWTZjdY1D3XsVpBfQA4=", + "type": 0, + "value": "2.K49DvcymvaQ/+V/3soCb1Q==|r+b2dz0YqFeA8BrHii5a0DIjkwxdwlY+aEEH2d2NdC0=|KEJAjqImC0Jit9oaFN2rSZHyetxuD7jEjMt07HPFLvU=" + } + ], + "name": "2.LjR8NxRtCB1noDILxNKmPQ==|s4e2AO4I3HqmPRRsbsYl0XYSuWu7+sn2K3+jNiFjsW0=|3NQW64/3RmPxe3hhUGeMTrSy9Ruh5hYRlJxIhhWhhSI=", + "notes": "2.ke3IFCPe50UCM2XdJDS/VQ==|ksO+dyEuRBgrpAelfhxNhw==|JrbMU58V5QyiTpdJXyWB1g2l8jPcqyeWDMqjOnaa9UA=", + "password": null, + "passwordHistory": [], + "passwordRevisionDate": null, + "totp": "2.mScxVU7uCx3fiPXYlgzWVA==|yxqIjknG71Qdwxq1wyVufWyB1Hb6qWowPgGoZNV2d4s=|uY7hrn7Eow81A76/n4VUrJxSRtC9VDnUdaesO6y0ivY=", + "uri": "2.7ihoxwsJH4HBkTzjFBwFIA==|5EkQHm4IRExHH/LAU6D/jw==|Hs6YgBIFuTQNnH9M3ejgUAVzsGjxAyaVPi1pl1NU9wg=", + "uris": [ + { + "match": null, + "uri": "2.7ihoxwsJH4HBkTzjFBwFIA==|5EkQHm4IRExHH/LAU6D/jw==|Hs6YgBIFuTQNnH9M3ejgUAVzsGjxAyaVPi1pl1NU9wg=", + "uriChecksum": "2.WfCD+h+rQ49rJDiTM7ycjw==|QgYnnlKzeymjRXmW15SC7bnWWvWaU1iki7rABdi0+RjPgRVGLluT/lQCKXkr5fXH|vaMZaBCKZySaycxVXjpDatKjrN6mBaq2ffRsygLTGzA=" + } + ], + "username": "2.9hmypXsnAjVxJ2e5kZQLKA==|8Jif+MQgdTuAlaCn7i/xig==|xy7zJh4qXutESyC02aKSYb/79EmfbGsYlxsYfKqneLA=" + }, + "deletedDate": null, + "edit": true, + "favorite": false, + "fields": [ + { + "linkedId": null, + "name": "2.svtF3aK9R1MLHt2GwH7Ykw==|PqVDa02T0Vzx6ogTNz1Xyg==|g9/Gd5OOLkVtyVAk9vkRCY2reWTZjdY1D3XsVpBfQA4=", + "type": 0, + "value": "2.K49DvcymvaQ/+V/3soCb1Q==|r+b2dz0YqFeA8BrHii5a0DIjkwxdwlY+aEEH2d2NdC0=|KEJAjqImC0Jit9oaFN2rSZHyetxuD7jEjMt07HPFLvU=" + } + ], + "folderId": null, + "id": "24d1c150-5dfd-4008-964c-01317d1f6b23", + "identity": null, + "key": null, + "login": { + "autofillOnPageLoad": null, + "password": null, + "passwordRevisionDate": null, + "totp": "2.mScxVU7uCx3fiPXYlgzWVA==|yxqIjknG71Qdwxq1wyVufWyB1Hb6qWowPgGoZNV2d4s=|uY7hrn7Eow81A76/n4VUrJxSRtC9VDnUdaesO6y0ivY=", + "uri": "2.7ihoxwsJH4HBkTzjFBwFIA==|5EkQHm4IRExHH/LAU6D/jw==|Hs6YgBIFuTQNnH9M3ejgUAVzsGjxAyaVPi1pl1NU9wg=", + "uris": [ + { + "match": null, + "uri": "2.7ihoxwsJH4HBkTzjFBwFIA==|5EkQHm4IRExHH/LAU6D/jw==|Hs6YgBIFuTQNnH9M3ejgUAVzsGjxAyaVPi1pl1NU9wg=", + "uriChecksum": "2.WfCD+h+rQ49rJDiTM7ycjw==|QgYnnlKzeymjRXmW15SC7bnWWvWaU1iki7rABdi0+RjPgRVGLluT/lQCKXkr5fXH|vaMZaBCKZySaycxVXjpDatKjrN6mBaq2ffRsygLTGzA=" + } + ], + "username": "2.9hmypXsnAjVxJ2e5kZQLKA==|8Jif+MQgdTuAlaCn7i/xig==|xy7zJh4qXutESyC02aKSYb/79EmfbGsYlxsYfKqneLA=" + }, + "name": "2.LjR8NxRtCB1noDILxNKmPQ==|s4e2AO4I3HqmPRRsbsYl0XYSuWu7+sn2K3+jNiFjsW0=|3NQW64/3RmPxe3hhUGeMTrSy9Ruh5hYRlJxIhhWhhSI=", + "notes": "2.ke3IFCPe50UCM2XdJDS/VQ==|ksO+dyEuRBgrpAelfhxNhw==|JrbMU58V5QyiTpdJXyWB1g2l8jPcqyeWDMqjOnaa9UA=", + "object": "cipherDetails", + "organizationId": null, + "organizationUseTotp": true, + "passwordHistory": [], + "reprompt": 0, + "revisionDate": "2024-09-22T11:13:40.345356Z", + "secureNote": null, + "type": 1, + "viewPassword": true + }`)) + + return webapi.NewClient("http://127.0.0.1:8081/", webapi.WithCustomClient(client), webapi.DisableRetries()) +} + +func createTestAccount(t *testing.T) { + ctx := context.Background() + preloginKey, err := keybuilder.BuildPreloginKey(testPassword, testAccount.Email, testAccount.KdfIterations) + if err != nil { + t.Fatal(err) + } + + hashedPassword := crypto.HashPassword(testPassword, *preloginKey, false) + + block, _ := pem.Decode([]byte(rsaPrivateKey)) + if block == nil { + t.Fatal(err) + } + + privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + t.Fatal(err) + } + + encryptionKeyBytes, err := base64.StdEncoding.DecodeString(encryptionKey) + if err != nil { + t.Fatal(err) + } + + newEncryptionKey, encryptedEncryptionKey, err := keybuilder.EncryptEncryptionKey(*preloginKey, encryptionKeyBytes) + if err != nil { + t.Fatal(err) + } + + publicKey, encryptedPrivateKey, err := keybuilder.EncryptRSAKeyPair(*newEncryptionKey, privateKey) + if err != nil { + t.Fatal(err) + } + + signupRequest := webapi.SignupRequest{ + Email: testAccount.Email, + Name: testAccount.Email, + MasterPasswordHash: hashedPassword, + Key: encryptedEncryptionKey, + KdfIterations: testAccount.KdfIterations, + Keys: webapi.KeyPair{ + PublicKey: publicKey, + EncryptedPrivateKey: encryptedPrivateKey, + }, + } + + client := webapi.NewClient("http://127.0.0.1:8080") + err = client.RegisterUser(ctx, signupRequest) + if err != nil { + t.Fatal(err) + } +} diff --git a/internal/bitwarden/embedded/vault_utils_test.go b/internal/bitwarden/embedded/vault_utils_test.go deleted file mode 100644 index 8471a4c..0000000 --- a/internal/bitwarden/embedded/vault_utils_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package embedded - -import ( - "net/http" - "testing" - - "github.com/jarcoal/httpmock" - "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/webapi" -) - -func NewWebAPIVaultWithMockedTransport(t *testing.T) webAPIVault { - vault := NewWebAPIVault("http://127.0.0.1:8081/").(*webAPIVault) - vault.client = mockedClient() - return *vault -} - -func mockedClient() webapi.Client { - client := http.Client{Transport: httpmock.DefaultTransport} - httpmock.RegisterResponder("POST", "http://127.0.0.1:8081/identity/accounts/prelogin", - httpmock.NewStringResponder(200, `{"kdf":0,"kdfIterations":600000,"kdfMemory":null,"kdfParallelism":null}`)) - - // Regexp match (could use httpmock.RegisterRegexpResponder instead) - httpmock.RegisterResponder("POST", `http://127.0.0.1:8081/identity/connect/token`, - httpmock.NewStringResponder(200, `{"ForcePasswordReset":false,"Kdf":0,"KdfIterations":600000,"KdfMemory":null,"KdfParallelism":null,"Key":"2.lkAJiJtCKPHFPrZ96+j2Xg==|5XJtrKUndcGy28thFukrmgMcLp+BOVdkF+KcuOnfshq9AN1PFhna9Es96CVARCnjTcWuHuqvgnGmcOHTrf8fyfLv63VBsjLgLZk8rCXJoKE=|9dwgx4/13AD+elE2vE7vlSQoe8LbCGGlui345YrKvXY=","MasterPasswordPolicy":{"object":"masterPasswordPolicy"},"PrivateKey":"2.D2aLa8ne/DAkeSzctQISVw==|/xoGM5i5JGJTH/vohUuwTFrTx3hd/gt3kBD/FQdeLMtYjl1u96sh0ECmoERqGHeSfXj+iAb9kpTIOKG8LwmkZGUJBI90Mw0M7ODmf7E8eQ+aGF+bGqTSMQ1wtpunEyFVodlg92YN8Ddlb2V9J4uN8ykpHYNDmQiYLZ8bl6vCODRGPyzLvx5M8DbITVL5PhsjKDLLrVV8lFCgCcAL5YLfkghYhFELyX15zXA/KYEnwggDka3hG5+HHFOVZSeyk7Gi6M4TX2wADbTXz1/Wsho8oxFUrtNiOB3ZiY2cx9UWttpzMXoGfi2gJcP1db/nTfWenOLlzw6Od4VyRzsXsfyGwbqBqDnNFkjLvhjVw4JO+psF//xAMDs14101Tf2wFkB6toQ+zdnDphXUeKmiVPQ7gMnQlOWN5tWvjjmYOO4Y63sGpP24cDOdEScIdebZRSA8uOhTzadfKfOiH5zVYZzXs33FQ0li4nBrsj5xYa6PP4D1P7gqjxClPdguwkdLoZ7JvgIlyRIwEcORi5Ich8RWF/kqRBwk0QSzK1mTlHHU5xtSgi4MLNVx4qTYNaJVBtwL9d2MD9LeNn4Z2PL4A7qnszHqsERiQQDxNEgMxMBHDgSXqQbtQxRvsI6oY+yNbN7uVWw4o8AC3f5GBdxzIqcN1mgEM5ix5aDt15w3MhP2FHtf2neKI68TnL8WnT1fT8BVlbECIiUqK0tfq5aTjdSh3gCS15jvZ1H60h+K8+O/nDfquzVjY7UsTGwA+UtS8/JGiaUhc0VhxJo8P1V2VSCiu51d5q3De1vDg5R2VEgBmTchZyTIodC+3+7ACTOwkNCCdIJN7xKOcIGFA7QOuyJtBeXT4Rd9UGMHSL054IB/315WVDiwrP9W1aP0nHzFs+qAXbH5o1E+AmfMDyoHopjGgbUw22r837kHzf5Qe8QhRYPzQvDowfCPhdoy23cbN1VsNavNwTC9hcG5oYMwkK66xP3MEM9UfGD22pwxe7M8U4BRdLCCHbi95eklXE6Mg6DpWsAdMgokQbOvnlwgKfrlbltqXUE/vUQI8TB3AE1Nkt0ST4quTriMuuyiHdeeZV4UkV9jWt/5bTCfdrCYuGZP7g4shbfNcP7u1Zrdxv+EuUwGIOOTrNV5awmBnL3iHE5ya2MnqmRyfWiPIT5majZCk06yxj4XzyIPOpjYKFt0MOgLvG1GllmdtRqg7tMVvc5ZFo5KWIxLsIJD12UjA1GYYoFdX4+wsNbPjfnlE6D2PrtWUICnBFJzYpfyrKTe01k8G8hyyz+tVzBRfz8EA2ew1+hlVcAgSPCcBzhDgqPe+RSPi7ZSd66be1gDhGAftWFM8Z0MrMklXi2DyjjaKBNsZZD8qTcLcobm8nqHUQtnr5JCbmgP3rau8NY/fxeFHsvSiZQoB1aI/y+Sz/R4r+T9cg8hjmS/FUHDO+m6a6nuWNFwz8wIluM557oOTl+A9UGFF50Gpzmf97VdQjM3ZREazQ7la6AobzS3BHI6FNdxN9LTyMpYo+WODv52/VwU3ODH7wf5bz2OHZhk2NG5R7pSH7qg8jM+/MtJkFumENV0qMecozIkP6e4CyI9ua4YwI9n7G5OgKYMG1aj2PRSny2JSLS8aHF1TkRL8SD0nZFCox0=|muEtiwIuZxhuuLv0nouEdxHU2CO+I7JXKZuYHWiv/OE=","ResetMasterPassword":false,"UserDecryptionOptions":{"HasMasterPassword":true,"Object":"userDecryptionOptions"},"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJuYmYiOjE3Mjc1MTI0NjIsImV4cCI6MTcyNzUxOTY2MiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdHxsb2dpbiIsInN1YiI6ImU4ZGFiYWJkLTI0MmUtNDkwMC1iZWNmLWU4OGJjMDIxZGRhOCIsInByZW1pdW0iOnRydWUsIm5hbWUiOiJUZXN0IExhdmVyc2UiLCJlbWFpbCI6InRlc3RAbGF2ZXJzZS5uZXQiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwic3N0YW1wIjoiNDAzM2NmMzUtYmM1ZC00ZWNkLTk1MWMtZGJlNTQ5ZjI3NzQwIiwiZGV2aWNlIjoiNWQ5MGI0NzAtNWQxZC00NTJkLTkzNWMtYjczMGMxNzdhOGQ2Iiwic2NvcGUiOlsiYXBpIiwib2ZmbGluZV9hY2Nlc3MiXSwiYW1yIjpbIkFwcGxpY2F0aW9uIl19.hKXO3p4am134f74wx5WqmuRCT__1CcqiZ5YkU5q8FvXNY-oe_pOrU7gxpf9TDBIOQVTjVkE09dgzEKco_qMnRTT7cvQ95o3k82gNDNgyi0Yjqtv5eS8K7m7MrSOUqe6LwZwpKy3G8vDnu1O2kiN-So44cCBKQzqOrR4oimmYIMt8IWqU2qa8ZaFe1E70nv5NjUxiE_NkT2FF6M8MA7z_aEkcBeleknlKRL-gaqYqAPWhKF_msW9e3SKvm_83wt1JRwr60T4aKXbl7rB4UrSqs_lqhdb3i1Ul5_TYQXbg4yfETgT8Uh37kXtnK_8MlxNBpA8pEAnp_29Odync3T7naQ","expires_in":7200,"refresh_token":"6wCk9N0MG-abyrmBUn5m1ACq-oRnhj6mbjCeuOz_HPyPERfK8DPeTJVf3c6KtP-bIbL6mErO-ggMs_MNS2GlIg==","scope":"api offline_access","token_type":"Bearer","unofficialServer":true}`)) - - httpmock.RegisterResponder("GET", `http://127.0.0.1:8081/api/sync?excludeDomains=true`, - httpmock.NewStringResponder(200, `{"ciphers":[{"attachments":null,"card":null,"collectionIds":[],"creationDate":"2024-09-22T11:13:40.346903Z","data":{"autofillOnPageLoad":null,"fields":[{"linkedId":null,"name":"2.svtF3aK9R1MLHt2GwH7Ykw==|PqVDa02T0Vzx6ogTNz1Xyg==|g9/Gd5OOLkVtyVAk9vkRCY2reWTZjdY1D3XsVpBfQA4=","type":0,"value":"2.K49DvcymvaQ/+V/3soCb1Q==|r+b2dz0YqFeA8BrHii5a0DIjkwxdwlY+aEEH2d2NdC0=|KEJAjqImC0Jit9oaFN2rSZHyetxuD7jEjMt07HPFLvU="}],"name":"2.LjR8NxRtCB1noDILxNKmPQ==|s4e2AO4I3HqmPRRsbsYl0XYSuWu7+sn2K3+jNiFjsW0=|3NQW64/3RmPxe3hhUGeMTrSy9Ruh5hYRlJxIhhWhhSI=","notes":"2.ke3IFCPe50UCM2XdJDS/VQ==|ksO+dyEuRBgrpAelfhxNhw==|JrbMU58V5QyiTpdJXyWB1g2l8jPcqyeWDMqjOnaa9UA=","password":null,"passwordHistory":[],"passwordRevisionDate":null,"totp":"2.mScxVU7uCx3fiPXYlgzWVA==|yxqIjknG71Qdwxq1wyVufWyB1Hb6qWowPgGoZNV2d4s=|uY7hrn7Eow81A76/n4VUrJxSRtC9VDnUdaesO6y0ivY=","uri":"2.7ihoxwsJH4HBkTzjFBwFIA==|5EkQHm4IRExHH/LAU6D/jw==|Hs6YgBIFuTQNnH9M3ejgUAVzsGjxAyaVPi1pl1NU9wg=","uris":[{"match":null,"uri":"2.7ihoxwsJH4HBkTzjFBwFIA==|5EkQHm4IRExHH/LAU6D/jw==|Hs6YgBIFuTQNnH9M3ejgUAVzsGjxAyaVPi1pl1NU9wg=","uriChecksum":"2.WfCD+h+rQ49rJDiTM7ycjw==|QgYnnlKzeymjRXmW15SC7bnWWvWaU1iki7rABdi0+RjPgRVGLluT/lQCKXkr5fXH|vaMZaBCKZySaycxVXjpDatKjrN6mBaq2ffRsygLTGzA="}],"username":"2.9hmypXsnAjVxJ2e5kZQLKA==|8Jif+MQgdTuAlaCn7i/xig==|xy7zJh4qXutESyC02aKSYb/79EmfbGsYlxsYfKqneLA="},"deletedDate":null,"edit":true,"favorite":false,"fields":[{"linkedId":null,"name":"2.svtF3aK9R1MLHt2GwH7Ykw==|PqVDa02T0Vzx6ogTNz1Xyg==|g9/Gd5OOLkVtyVAk9vkRCY2reWTZjdY1D3XsVpBfQA4=","type":0,"value":"2.K49DvcymvaQ/+V/3soCb1Q==|r+b2dz0YqFeA8BrHii5a0DIjkwxdwlY+aEEH2d2NdC0=|KEJAjqImC0Jit9oaFN2rSZHyetxuD7jEjMt07HPFLvU="}],"folderId":null,"id":"24d1c150-5dfd-4008-964c-01317d1f6b23","identity":null,"key":null,"login":{"autofillOnPageLoad":null,"password":null,"passwordRevisionDate":null,"totp":"2.mScxVU7uCx3fiPXYlgzWVA==|yxqIjknG71Qdwxq1wyVufWyB1Hb6qWowPgGoZNV2d4s=|uY7hrn7Eow81A76/n4VUrJxSRtC9VDnUdaesO6y0ivY=","uri":"2.7ihoxwsJH4HBkTzjFBwFIA==|5EkQHm4IRExHH/LAU6D/jw==|Hs6YgBIFuTQNnH9M3ejgUAVzsGjxAyaVPi1pl1NU9wg=","uris":[{"match":null,"uri":"2.7ihoxwsJH4HBkTzjFBwFIA==|5EkQHm4IRExHH/LAU6D/jw==|Hs6YgBIFuTQNnH9M3ejgUAVzsGjxAyaVPi1pl1NU9wg=","uriChecksum":"2.WfCD+h+rQ49rJDiTM7ycjw==|QgYnnlKzeymjRXmW15SC7bnWWvWaU1iki7rABdi0+RjPgRVGLluT/lQCKXkr5fXH|vaMZaBCKZySaycxVXjpDatKjrN6mBaq2ffRsygLTGzA="}],"username":"2.9hmypXsnAjVxJ2e5kZQLKA==|8Jif+MQgdTuAlaCn7i/xig==|xy7zJh4qXutESyC02aKSYb/79EmfbGsYlxsYfKqneLA="},"name":"2.LjR8NxRtCB1noDILxNKmPQ==|s4e2AO4I3HqmPRRsbsYl0XYSuWu7+sn2K3+jNiFjsW0=|3NQW64/3RmPxe3hhUGeMTrSy9Ruh5hYRlJxIhhWhhSI=","notes":"2.ke3IFCPe50UCM2XdJDS/VQ==|ksO+dyEuRBgrpAelfhxNhw==|JrbMU58V5QyiTpdJXyWB1g2l8jPcqyeWDMqjOnaa9UA=","object":"cipherDetails","organizationId":null,"organizationUseTotp":true,"passwordHistory":[],"reprompt":0,"revisionDate":"2024-09-22T11:13:40.347356Z","secureNote":null,"type":1,"viewPassword":true},{"attachments":null,"card":null,"collectionIds":["8d1a5611-5fd6-4728-a6f8-22bc03b50640"],"creationDate":"2024-09-22T11:15:49.885670Z","data":{"autofillOnPageLoad":null,"fields":[],"name":"2.kOK8qkac+p+c8mNjhkuO/w==|nP1hQA1/8rJt97NmWSarfchRF+XWAUflsYzq9X2lBPU=|J7AmiEiDRCWvlLjYBrwXxCgCLw4EQiXmvlKnJw0mTb8=","notes":null,"password":"2.uiCrgwCc32t6jIaUcMiinw==|JJVfmpcdrekNqJLfJOVaGw==|1zpwpqlATK+6/av/6JfOoHJaOgtpNJA6LDqEqsR2C/A=","passwordHistory":[],"passwordRevisionDate":null,"totp":null,"uri":null,"uris":[],"username":"2.6WVjMRz5qtfVkGJ40Ne+Fw==|LtPQSeBvHIEgmzTtpdq4lw==|BV/F2ZhCD1nsr/4RoETwpMnu8PPVYirxrDQXcF6gpg4="},"deletedDate":null,"edit":true,"favorite":false,"fields":[],"folderId":null,"id":"94b0e53d-a194-493f-ad25-e6ca9b9abb75","identity":null,"key":null,"login":{"autofillOnPageLoad":null,"password":"2.uiCrgwCc32t6jIaUcMiinw==|JJVfmpcdrekNqJLfJOVaGw==|1zpwpqlATK+6/av/6JfOoHJaOgtpNJA6LDqEqsR2C/A=","passwordRevisionDate":null,"totp":null,"uri":null,"uris":[],"username":"2.6WVjMRz5qtfVkGJ40Ne+Fw==|LtPQSeBvHIEgmzTtpdq4lw==|BV/F2ZhCD1nsr/4RoETwpMnu8PPVYirxrDQXcF6gpg4="},"name":"2.kOK8qkac+p+c8mNjhkuO/w==|nP1hQA1/8rJt97NmWSarfchRF+XWAUflsYzq9X2lBPU=|J7AmiEiDRCWvlLjYBrwXxCgCLw4EQiXmvlKnJw0mTb8=","notes":null,"object":"cipherDetails","organizationId":"81cc1652-dc80-472d-909f-9539d057068b","organizationUseTotp":true,"passwordHistory":[],"reprompt":0,"revisionDate":"2024-09-22T11:15:49.887811Z","secureNote":null,"type":1,"viewPassword":true},{"attachments":null,"card":null,"collectionIds":["15e18629-849f-4629-895b-54600c542a70"],"creationDate":"2024-09-22T11:14:53.442012Z","data":{"autofillOnPageLoad":null,"fields":[],"name":"2.sOEzwZwic+bzHlaUpnl43Q==|Hr5DUOPmBJiz1oT8o2uJNLKFhY8rcmsx8EGqxuoJC9tNBCv6WwJOotrr2bDoPPP/|ZgcnsODyuV2Zd+Nl7MGoAPj383d/fr8LCEIvidYFJYk=","notes":null,"password":"2.Nw6WShTt6PTNzRvnLYp2oQ==|x2mPKtOtqPpJEbCPH4EBBw==|Ij7Gq+CCXpJgoZISsATzsZb7v7paSj8mryWKyU0t/VY=","passwordHistory":[],"passwordRevisionDate":null,"totp":null,"uri":null,"uris":[],"username":"2.K7I2dQ8x82rPK2buV1dzUQ==|VdjhkLm2wlURH03+GnLTFw==|AtpKj+TYgu+/YSqPn2jkFzePKvz71JtCgn+w0MXuhoI="},"deletedDate":null,"edit":true,"favorite":false,"fields":[],"folderId":null,"id":"adaef652-16e0-439a-9d67-07671ddc2a51","identity":null,"key":null,"login":{"autofillOnPageLoad":null,"password":"2.Nw6WShTt6PTNzRvnLYp2oQ==|x2mPKtOtqPpJEbCPH4EBBw==|Ij7Gq+CCXpJgoZISsATzsZb7v7paSj8mryWKyU0t/VY=","passwordRevisionDate":null,"totp":null,"uri":null,"uris":[],"username":"2.K7I2dQ8x82rPK2buV1dzUQ==|VdjhkLm2wlURH03+GnLTFw==|AtpKj+TYgu+/YSqPn2jkFzePKvz71JtCgn+w0MXuhoI="},"name":"2.sOEzwZwic+bzHlaUpnl43Q==|Hr5DUOPmBJiz1oT8o2uJNLKFhY8rcmsx8EGqxuoJC9tNBCv6WwJOotrr2bDoPPP/|ZgcnsODyuV2Zd+Nl7MGoAPj383d/fr8LCEIvidYFJYk=","notes":null,"object":"cipherDetails","organizationId":"81cc1652-dc80-472d-909f-9539d057068b","organizationUseTotp":true,"passwordHistory":[],"reprompt":0,"revisionDate":"2024-09-22T11:14:53.443032Z","secureNote":null,"type":1,"viewPassword":true},{"attachments":null,"card":null,"collectionIds":[],"creationDate":"2024-09-26T14:26:27.436093Z","data":{"autofillOnPageLoad":null,"fields":[{"linkedId":null,"name":null,"type":2,"value":"2.5ndrC0WqMJO6fH/2xv3AMw==|zY1/7UgW/MQUutZBixlyOA==|M4/tHU/s5w6ZygyXVUxpiEtAlFtoa9PlL5DY+S8K6gY="}],"name":"2.4lqddV7PV1Uxn0qFk0JQLA==|svoCTgXohcpPJZirBg1PcQ==|AFIutATBeBvoGpfvDMqaDGlQXBFvIBKTyZqJNt+UPnE=","notes":null,"password":null,"passwordHistory":[],"passwordRevisionDate":null,"totp":null,"uri":null,"uris":[],"username":"2.DkVdwPK5E5RAn6BLwzHXIg==|484sbAo5PFn6D50K9AToYw==|wjCnCnR+JunkRnoRjC/jwcJizdHCuzJ2RRp1z2uuJBg="},"deletedDate":null,"edit":true,"favorite":false,"fields":[{"linkedId":null,"name":null,"type":2,"value":"2.5ndrC0WqMJO6fH/2xv3AMw==|zY1/7UgW/MQUutZBixlyOA==|M4/tHU/s5w6ZygyXVUxpiEtAlFtoa9PlL5DY+S8K6gY="}],"folderId":"3df04ed6-624d-45ae-b825-7ceb0cf6eb0e","id":"b4b2d9ad-1d86-41d7-a1f3-a249e9f96e8e","identity":null,"key":null,"login":{"autofillOnPageLoad":null,"password":null,"passwordRevisionDate":null,"totp":null,"uri":null,"uris":[],"username":"2.DkVdwPK5E5RAn6BLwzHXIg==|484sbAo5PFn6D50K9AToYw==|wjCnCnR+JunkRnoRjC/jwcJizdHCuzJ2RRp1z2uuJBg="},"name":"2.4lqddV7PV1Uxn0qFk0JQLA==|svoCTgXohcpPJZirBg1PcQ==|AFIutATBeBvoGpfvDMqaDGlQXBFvIBKTyZqJNt+UPnE=","notes":null,"object":"cipherDetails","organizationId":null,"organizationUseTotp":true,"passwordHistory":[],"reprompt":0,"revisionDate":"2024-09-26T14:26:27.436534Z","secureNote":null,"type":1,"viewPassword":true},{"attachments":null,"card":null,"collectionIds":[],"creationDate":"2024-09-22T11:14:14.671495Z","data":{"autofillOnPageLoad":null,"fields":[],"name":"2.aavpFIxp7xouEaflFBr0ug==|JEyMdNkR/1TaJ5lU5w8FfR4+yfV+JP+XsJd8np7/gY4=|BaHDWGBVNTdd5KsR2VLPrHA0caVgxszWFEwgmtR5iHI=","notes":null,"password":"2.qjgHReWyJ0X9KlP3qFtB8w==|cHxcCMM2OTTdI3mt85KveQ==|/EuUwk9AzkUQqsJzJ3Z2MEvM4tQ9Ma8WDoUAXtNLE4E=","passwordHistory":[],"passwordRevisionDate":null,"totp":null,"uri":null,"uris":[],"username":"2.K69oGBEuH9GzhSkzi2zzIg==|bbeKWbkuSc1cVJm2xDae8Q==|y+Z07fAT+zmlq2gSLFC6uLEfYIq3IT7HqNFJqlWQOPM="},"deletedDate":null,"edit":true,"favorite":false,"fields":[],"folderId":"e7098f5d-4d00-4bdf-8c66-56f9dca2129f","id":"b7441ea3-baf4-4f04-a445-0ad342eb56c4","identity":null,"key":null,"login":{"autofillOnPageLoad":null,"password":"2.qjgHReWyJ0X9KlP3qFtB8w==|cHxcCMM2OTTdI3mt85KveQ==|/EuUwk9AzkUQqsJzJ3Z2MEvM4tQ9Ma8WDoUAXtNLE4E=","passwordRevisionDate":null,"totp":null,"uri":null,"uris":[],"username":"2.K69oGBEuH9GzhSkzi2zzIg==|bbeKWbkuSc1cVJm2xDae8Q==|y+Z07fAT+zmlq2gSLFC6uLEfYIq3IT7HqNFJqlWQOPM="},"name":"2.aavpFIxp7xouEaflFBr0ug==|JEyMdNkR/1TaJ5lU5w8FfR4+yfV+JP+XsJd8np7/gY4=|BaHDWGBVNTdd5KsR2VLPrHA0caVgxszWFEwgmtR5iHI=","notes":null,"object":"cipherDetails","organizationId":null,"organizationUseTotp":true,"passwordHistory":[],"reprompt":0,"revisionDate":"2024-09-22T11:14:14.672096Z","secureNote":null,"type":1,"viewPassword":true}],"collections":[{"externalId":null,"hidePasswords":false,"id":"15e18629-849f-4629-895b-54600c542a70","name":"2.X7RK7wBZl+1pqo4yvAontQ==|Q+RD7xRWtXPC/Ijbl+863VSyQ4oYmHGG5EzNDtNYWW0=|369crUvCUpo5bID5wu0Q60eQJJv5+eJLxbsuZKmtVYA=","object":"collectionDetails","organizationId":"81cc1652-dc80-472d-909f-9539d057068b","readOnly":false},{"externalId":null,"hidePasswords":false,"id":"8d1a5611-5fd6-4728-a6f8-22bc03b50640","name":"2.4KNb9dUNUqfe2iqFZxBZJQ==|lrcBRuOjIpMAiBMHYDNbpn6ZmUz/OzHuBTr68ta0a/I=|OtEMhyhekHnARLATP8IWKAWieTaNQpbgO7/syW1KLK4=","object":"collectionDetails","organizationId":"81cc1652-dc80-472d-909f-9539d057068b","readOnly":false}],"domains":null,"folders":[{"id":"e7098f5d-4d00-4bdf-8c66-56f9dca2129f","name":"2.FTcL7PAe36ZQh0zIXPotSg==|ALhJRf/j9Wa83jg/tWbFSndiZ9JvZs2BwmVXmCUWhKc=|AEpDm/0LhllnW/Qzx9oWNYuSMaLQuR62MFUQuAaqQvc=","object":"folder","revisionDate":"2024-09-22T11:13:55.612053Z"},{"id":"3df04ed6-624d-45ae-b825-7ceb0cf6eb0e","name":"2.cybmeku3f53n3//scS4HfQ==|QEOsVN2jUF3+11/a8iISuQ==|PV2zCoVqNCX9KzyIUassEOjZE/f6bF+Y6Ft1ypRJ9lw=","object":"folder","revisionDate":"2024-09-22T17:00:11.566430Z"}],"object":"sync","policies":[],"profile":{"_status":0,"avatarColor":null,"culture":"en-US","email":"test@laverse.net","emailVerified":true,"forcePasswordReset":false,"id":"e8dababd-242e-4900-becf-e88bc021dda8","key":"2.lkAJiJtCKPHFPrZ96+j2Xg==|5XJtrKUndcGy28thFukrmgMcLp+BOVdkF+KcuOnfshq9AN1PFhna9Es96CVARCnjTcWuHuqvgnGmcOHTrf8fyfLv63VBsjLgLZk8rCXJoKE=|9dwgx4/13AD+elE2vE7vlSQoe8LbCGGlui345YrKvXY=","masterPasswordHint":null,"name":"Test Laverse","object":"profile","organizations":[{"accessSecretsManager":false,"allowAdminAccessToAllCollectionItems":true,"enabled":true,"familySponsorshipAvailable":false,"familySponsorshipFriendlyName":null,"familySponsorshipLastSyncDate":null,"familySponsorshipToDelete":null,"familySponsorshipValidUntil":null,"flexibleCollections":false,"hasPublicAndPrivateKeys":true,"id":"81cc1652-dc80-472d-909f-9539d057068b","identifier":null,"key":"4.JW3mktbL7vpTVRweZdQBAirJuAEhSRn37zcXZjDjI47weFKkeZkvPxZWCqFYC/P5qCJwEYMbv7lTETkWDg6paevVfhJ35buGcTQdEbQxAJebzPahEcUstj11l4Y9T5RaDiAJR8+drrGJ3fKV3v3hymKz2o9fUfK1epuLFll2nnWSOjCcuRe/+zz5VwIVx4WJAPJHmiS6eofbj/DTIQCzG4JkR0UzT66ouLcgmPL1nGOqVI7KxRpL5yVj75UkjniHkWAcB7lfAxWXw2GhDJ/2L685uA3820ItTbxjCwLQOvjBttgrbURmkeP9BD+KkO4V6vb8bbTWNSvggXKk2h1CMw==","keyConnectorEnabled":false,"keyConnectorUrl":null,"limitCollectionCreationDeletion":true,"maxAutoscaleSeats":null,"maxCollections":null,"maxStorageGb":32767,"name":"My Test Organization","object":"profileOrganization","organizationUserId":"9e755d16-b500-48ac-b24c-4105b7d08796","permissions":{"accessEventLogs":false,"accessImportExport":false,"accessReports":false,"createNewCollections":false,"deleteAnyCollection":false,"deleteAssignedCollections":false,"editAnyCollection":false,"editAssignedCollections":false,"manageGroups":false,"managePolicies":false,"manageResetPassword":false,"manageScim":false,"manageSso":false,"manageUsers":false},"planProductType":3,"productTierType":3,"providerId":null,"providerName":null,"providerType":null,"resetPasswordEnrolled":false,"seats":null,"selfHost":true,"ssoBound":false,"status":2,"type":0,"use2fa":true,"useActivateAutofillPolicy":false,"useApi":true,"useCustomPermissions":false,"useDirectory":false,"useEvents":false,"useGroups":false,"useKeyConnector":false,"usePasswordManager":true,"usePolicies":true,"useResetPassword":false,"useScim":false,"useSecretsManager":false,"useSso":false,"useTotp":true,"userId":"e8dababd-242e-4900-becf-e88bc021dda8","usersGetPremium":true}],"premium":true,"premiumFromOrganization":false,"privateKey":"2.D2aLa8ne/DAkeSzctQISVw==|/xoGM5i5JGJTH/vohUuwTFrTx3hd/gt3kBD/FQdeLMtYjl1u96sh0ECmoERqGHeSfXj+iAb9kpTIOKG8LwmkZGUJBI90Mw0M7ODmf7E8eQ+aGF+bGqTSMQ1wtpunEyFVodlg92YN8Ddlb2V9J4uN8ykpHYNDmQiYLZ8bl6vCODRGPyzLvx5M8DbITVL5PhsjKDLLrVV8lFCgCcAL5YLfkghYhFELyX15zXA/KYEnwggDka3hG5+HHFOVZSeyk7Gi6M4TX2wADbTXz1/Wsho8oxFUrtNiOB3ZiY2cx9UWttpzMXoGfi2gJcP1db/nTfWenOLlzw6Od4VyRzsXsfyGwbqBqDnNFkjLvhjVw4JO+psF//xAMDs14101Tf2wFkB6toQ+zdnDphXUeKmiVPQ7gMnQlOWN5tWvjjmYOO4Y63sGpP24cDOdEScIdebZRSA8uOhTzadfKfOiH5zVYZzXs33FQ0li4nBrsj5xYa6PP4D1P7gqjxClPdguwkdLoZ7JvgIlyRIwEcORi5Ich8RWF/kqRBwk0QSzK1mTlHHU5xtSgi4MLNVx4qTYNaJVBtwL9d2MD9LeNn4Z2PL4A7qnszHqsERiQQDxNEgMxMBHDgSXqQbtQxRvsI6oY+yNbN7uVWw4o8AC3f5GBdxzIqcN1mgEM5ix5aDt15w3MhP2FHtf2neKI68TnL8WnT1fT8BVlbECIiUqK0tfq5aTjdSh3gCS15jvZ1H60h+K8+O/nDfquzVjY7UsTGwA+UtS8/JGiaUhc0VhxJo8P1V2VSCiu51d5q3De1vDg5R2VEgBmTchZyTIodC+3+7ACTOwkNCCdIJN7xKOcIGFA7QOuyJtBeXT4Rd9UGMHSL054IB/315WVDiwrP9W1aP0nHzFs+qAXbH5o1E+AmfMDyoHopjGgbUw22r837kHzf5Qe8QhRYPzQvDowfCPhdoy23cbN1VsNavNwTC9hcG5oYMwkK66xP3MEM9UfGD22pwxe7M8U4BRdLCCHbi95eklXE6Mg6DpWsAdMgokQbOvnlwgKfrlbltqXUE/vUQI8TB3AE1Nkt0ST4quTriMuuyiHdeeZV4UkV9jWt/5bTCfdrCYuGZP7g4shbfNcP7u1Zrdxv+EuUwGIOOTrNV5awmBnL3iHE5ya2MnqmRyfWiPIT5majZCk06yxj4XzyIPOpjYKFt0MOgLvG1GllmdtRqg7tMVvc5ZFo5KWIxLsIJD12UjA1GYYoFdX4+wsNbPjfnlE6D2PrtWUICnBFJzYpfyrKTe01k8G8hyyz+tVzBRfz8EA2ew1+hlVcAgSPCcBzhDgqPe+RSPi7ZSd66be1gDhGAftWFM8Z0MrMklXi2DyjjaKBNsZZD8qTcLcobm8nqHUQtnr5JCbmgP3rau8NY/fxeFHsvSiZQoB1aI/y+Sz/R4r+T9cg8hjmS/FUHDO+m6a6nuWNFwz8wIluM557oOTl+A9UGFF50Gpzmf97VdQjM3ZREazQ7la6AobzS3BHI6FNdxN9LTyMpYo+WODv52/VwU3ODH7wf5bz2OHZhk2NG5R7pSH7qg8jM+/MtJkFumENV0qMecozIkP6e4CyI9ua4YwI9n7G5OgKYMG1aj2PRSny2JSLS8aHF1TkRL8SD0nZFCox0=|muEtiwIuZxhuuLv0nouEdxHU2CO+I7JXKZuYHWiv/OE=","providerOrganizations":[],"providers":[],"securityStamp":"4033cf35-bc5d-4ecd-951c-dbe549f27740","twoFactorEnabled":false,"usesKeyConnector":false},"sends":[],"unofficialServer":true}`)) - - httpmock.RegisterResponder("GET", `http://127.0.0.1:8081/api/accounts/profile`, - httpmock.NewStringResponder(200, `{"_status":0,"avatarColor":null,"culture":"en-US","email":"test@laverse.net","emailVerified":true,"forcePasswordReset":false,"id":"e8dababd-242e-4900-becf-e88bc021dda8","key":"2.lkAJiJtCKPHFPrZ96+j2Xg==|5XJtrKUndcGy28thFukrmgMcLp+BOVdkF+KcuOnfshq9AN1PFhna9Es96CVARCnjTcWuHuqvgnGmcOHTrf8fyfLv63VBsjLgLZk8rCXJoKE=|9dwgx4/13AD+elE2vE7vlSQoe8LbCGGlui345YrKvXY=","masterPasswordHint":null,"name":"Test Laverse","object":"profile","organizations":[{"accessSecretsManager":false,"allowAdminAccessToAllCollectionItems":true,"enabled":true,"familySponsorshipAvailable":false,"familySponsorshipFriendlyName":null,"familySponsorshipLastSyncDate":null,"familySponsorshipToDelete":null,"familySponsorshipValidUntil":null,"flexibleCollections":false,"hasPublicAndPrivateKeys":true,"id":"81cc1652-dc80-472d-909f-9539d057068b","identifier":null,"key":"4.JW3mktbL7vpTVRweZdQBAirJuAEhSRn37zcXZjDjI47weFKkeZkvPxZWCqFYC/P5qCJwEYMbv7lTETkWDg6paevVfhJ35buGcTQdEbQxAJebzPahEcUstj11l4Y9T5RaDiAJR8+drrGJ3fKV3v3hymKz2o9fUfK1epuLFll2nnWSOjCcuRe/+zz5VwIVx4WJAPJHmiS6eofbj/DTIQCzG4JkR0UzT66ouLcgmPL1nGOqVI7KxRpL5yVj75UkjniHkWAcB7lfAxWXw2GhDJ/2L685uA3820ItTbxjCwLQOvjBttgrbURmkeP9BD+KkO4V6vb8bbTWNSvggXKk2h1CMw==","keyConnectorEnabled":false,"keyConnectorUrl":null,"limitCollectionCreationDeletion":true,"maxAutoscaleSeats":null,"maxCollections":null,"maxStorageGb":32767,"name":"My Test Organization","object":"profileOrganization","organizationUserId":"9e755d16-b500-48ac-b24c-4105b7d08796","permissions":{"accessEventLogs":false,"accessImportExport":false,"accessReports":false,"createNewCollections":false,"deleteAnyCollection":false,"deleteAssignedCollections":false,"editAnyCollection":false,"editAssignedCollections":false,"manageGroups":false,"managePolicies":false,"manageResetPassword":false,"manageScim":false,"manageSso":false,"manageUsers":false},"planProductType":3,"productTierType":3,"providerId":null,"providerName":null,"providerType":null,"resetPasswordEnrolled":false,"seats":null,"selfHost":true,"ssoBound":false,"status":2,"type":0,"use2fa":true,"useActivateAutofillPolicy":false,"useApi":true,"useCustomPermissions":false,"useDirectory":false,"useEvents":false,"useGroups":false,"useKeyConnector":false,"usePasswordManager":true,"usePolicies":true,"useResetPassword":false,"useScim":false,"useSecretsManager":false,"useSso":false,"useTotp":true,"userId":"e8dababd-242e-4900-becf-e88bc021dda8","usersGetPremium":true}],"premium":true,"premiumFromOrganization":false,"privateKey":"2.D2aLa8ne/DAkeSzctQISVw==|/xoGM5i5JGJTH/vohUuwTFrTx3hd/gt3kBD/FQdeLMtYjl1u96sh0ECmoERqGHeSfXj+iAb9kpTIOKG8LwmkZGUJBI90Mw0M7ODmf7E8eQ+aGF+bGqTSMQ1wtpunEyFVodlg92YN8Ddlb2V9J4uN8ykpHYNDmQiYLZ8bl6vCODRGPyzLvx5M8DbITVL5PhsjKDLLrVV8lFCgCcAL5YLfkghYhFELyX15zXA/KYEnwggDka3hG5+HHFOVZSeyk7Gi6M4TX2wADbTXz1/Wsho8oxFUrtNiOB3ZiY2cx9UWttpzMXoGfi2gJcP1db/nTfWenOLlzw6Od4VyRzsXsfyGwbqBqDnNFkjLvhjVw4JO+psF//xAMDs14101Tf2wFkB6toQ+zdnDphXUeKmiVPQ7gMnQlOWN5tWvjjmYOO4Y63sGpP24cDOdEScIdebZRSA8uOhTzadfKfOiH5zVYZzXs33FQ0li4nBrsj5xYa6PP4D1P7gqjxClPdguwkdLoZ7JvgIlyRIwEcORi5Ich8RWF/kqRBwk0QSzK1mTlHHU5xtSgi4MLNVx4qTYNaJVBtwL9d2MD9LeNn4Z2PL4A7qnszHqsERiQQDxNEgMxMBHDgSXqQbtQxRvsI6oY+yNbN7uVWw4o8AC3f5GBdxzIqcN1mgEM5ix5aDt15w3MhP2FHtf2neKI68TnL8WnT1fT8BVlbECIiUqK0tfq5aTjdSh3gCS15jvZ1H60h+K8+O/nDfquzVjY7UsTGwA+UtS8/JGiaUhc0VhxJo8P1V2VSCiu51d5q3De1vDg5R2VEgBmTchZyTIodC+3+7ACTOwkNCCdIJN7xKOcIGFA7QOuyJtBeXT4Rd9UGMHSL054IB/315WVDiwrP9W1aP0nHzFs+qAXbH5o1E+AmfMDyoHopjGgbUw22r837kHzf5Qe8QhRYPzQvDowfCPhdoy23cbN1VsNavNwTC9hcG5oYMwkK66xP3MEM9UfGD22pwxe7M8U4BRdLCCHbi95eklXE6Mg6DpWsAdMgokQbOvnlwgKfrlbltqXUE/vUQI8TB3AE1Nkt0ST4quTriMuuyiHdeeZV4UkV9jWt/5bTCfdrCYuGZP7g4shbfNcP7u1Zrdxv+EuUwGIOOTrNV5awmBnL3iHE5ya2MnqmRyfWiPIT5majZCk06yxj4XzyIPOpjYKFt0MOgLvG1GllmdtRqg7tMVvc5ZFo5KWIxLsIJD12UjA1GYYoFdX4+wsNbPjfnlE6D2PrtWUICnBFJzYpfyrKTe01k8G8hyyz+tVzBRfz8EA2ew1+hlVcAgSPCcBzhDgqPe+RSPi7ZSd66be1gDhGAftWFM8Z0MrMklXi2DyjjaKBNsZZD8qTcLcobm8nqHUQtnr5JCbmgP3rau8NY/fxeFHsvSiZQoB1aI/y+Sz/R4r+T9cg8hjmS/FUHDO+m6a6nuWNFwz8wIluM557oOTl+A9UGFF50Gpzmf97VdQjM3ZREazQ7la6AobzS3BHI6FNdxN9LTyMpYo+WODv52/VwU3ODH7wf5bz2OHZhk2NG5R7pSH7qg8jM+/MtJkFumENV0qMecozIkP6e4CyI9ua4YwI9n7G5OgKYMG1aj2PRSny2JSLS8aHF1TkRL8SD0nZFCox0=|muEtiwIuZxhuuLv0nouEdxHU2CO+I7JXKZuYHWiv/OE=","providerOrganizations":[],"providers":[],"securityStamp":"4033cf35-bc5d-4ecd-951c-dbe549f27740","twoFactorEnabled":false,"usesKeyConnector":false}`)) - - return webapi.NewClient("http://127.0.0.1:8081/", webapi.WithCustomClient(client)) -} diff --git a/internal/bitwarden/embedded/vault_webapi.go b/internal/bitwarden/embedded/vault_webapi.go index 5024670..3c7ecd1 100644 --- a/internal/bitwarden/embedded/vault_webapi.go +++ b/internal/bitwarden/embedded/vault_webapi.go @@ -28,6 +28,7 @@ type WebAPIVault interface { GetAttachment(ctx context.Context, itemId, attachmentId string) ([]byte, error) LoginWithAPIKey(ctx context.Context, password, clientId, clientSecret string) error LoginWithPassword(ctx context.Context, username, password string) error + RegisterUser(ctx context.Context, name, username, password string, kdfIterations int) error Sync(ctx context.Context) error Unlock(ctx context.Context, password string) error } @@ -229,6 +230,12 @@ func (v *webAPIVault) CreateObject(ctx context.Context, obj models.Object) (*mod return nil, fmt.Errorf("error getting object after creation (sync-after-write): %w", err) } + // NOTE: The official Bitwarden server returns dates that are a few milliseconds apart + // between the object's creation call and a later retrieval. We need to ignore + // these differences in the diff. + resObj.CreationDate = remoteObj.CreationDate + resObj.RevisionDate = remoteObj.RevisionDate + return remoteObj, compareObjects(*resObj, *remoteObj) } return resObj, nil @@ -249,7 +256,7 @@ func (v *webAPIVault) CreateOrganization(ctx context.Context, organizationName, return "", fmt.Errorf("error encryption collection label: %w", err) } - publicKey, encryptedPrivateKey, err := keybuilder.GenerateKeyPair(*sharedKey) + publicKey, encryptedPrivateKey, err := keybuilder.GenerateRSAKeyPair(*sharedKey) if err != nil { return "", fmt.Errorf("error generating key pair: %w", err) } @@ -399,6 +406,11 @@ func (v *webAPIVault) EditObject(ctx context.Context, obj models.Object) (*model return nil, fmt.Errorf("error getting object after edition (sync-after-write): %w", err) } + // NOTE: The official Bitwarden server returns dates that are a few milliseconds apart + // between the object's creation call and a later retrieval. We need to ignore + // these differences in the diff. + resObj.RevisionDate = remoteObj.RevisionDate + return remoteObj, compareObjects(*resObj, *remoteObj) } return resObj, nil @@ -476,6 +488,39 @@ func (v *webAPIVault) LoginWithPassword(ctx context.Context, username, password return v.continueLoginWithTokens(ctx, *tokenResp, password) } +func (v *webAPIVault) RegisterUser(ctx context.Context, name, username, password string, kdfIterations int) error { + preloginKey, err := keybuilder.BuildPreloginKey(password, username, kdfIterations) + if err != nil { + return fmt.Errorf("error building prelogin key: %w", err) + } + + hashedPassword := crypto.HashPassword(password, *preloginKey, false) + + encryptionKey, encryptedEncryptionKey, err := keybuilder.GenerateEncryptionKey(*preloginKey) + if err != nil { + return fmt.Errorf("error generating encryption key: %w", err) + } + + publicKey, encryptedPrivateKey, err := keybuilder.GenerateRSAKeyPair(*encryptionKey) + if err != nil { + return fmt.Errorf("error generating key pair: %w", err) + } + + signupRequest := webapi.SignupRequest{ + Email: username, + Name: name, + MasterPasswordHash: hashedPassword, + Key: encryptedEncryptionKey, + KdfIterations: kdfIterations, + Keys: webapi.KeyPair{ + PublicKey: publicKey, + EncryptedPrivateKey: encryptedPrivateKey, + }, + } + + return v.client.RegisterUser(ctx, signupRequest) +} + func (v *webAPIVault) Sync(ctx context.Context) error { ciphersRaw, err := v.client.Sync(ctx) if err != nil { diff --git a/internal/bitwarden/embedded/vault_webapi_test.go b/internal/bitwarden/embedded/vault_webapi_test.go index 2581e32..31c6648 100644 --- a/internal/bitwarden/embedded/vault_webapi_test.go +++ b/internal/bitwarden/embedded/vault_webapi_test.go @@ -5,16 +5,18 @@ import ( "testing" "github.com/jarcoal/httpmock" + "github.com/maxlaverse/terraform-provider-bitwarden/internal/bitwarden/models" "github.com/stretchr/testify/assert" ) func TestLoginAsPasswordLoadsAccountInformation(t *testing.T) { + ctx := context.Background() httpmock.Activate() defer httpmock.DeactivateAndReset() vault := NewWebAPIVaultWithMockedTransport(t) - err := vault.LoginWithPassword(context.Background(), "test@laverse.net", "test12341234") + err := vault.LoginWithPassword(ctx, "test@laverse.net", testPassword) if err != nil { t.Fatalf("vault unlock failed: %v", err) } @@ -27,3 +29,29 @@ func TestLoginAsPasswordLoadsAccountInformation(t *testing.T) { assert.Equal(t, vault.loginAccount.ProtectedRSAPrivateKey, "2.D2aLa8ne/DAkeSzctQISVw==|/xoGM5i5JGJTH/vohUuwTFrTx3hd/gt3kBD/FQdeLMtYjl1u96sh0ECmoERqGHeSfXj+iAb9kpTIOKG8LwmkZGUJBI90Mw0M7ODmf7E8eQ+aGF+bGqTSMQ1wtpunEyFVodlg92YN8Ddlb2V9J4uN8ykpHYNDmQiYLZ8bl6vCODRGPyzLvx5M8DbITVL5PhsjKDLLrVV8lFCgCcAL5YLfkghYhFELyX15zXA/KYEnwggDka3hG5+HHFOVZSeyk7Gi6M4TX2wADbTXz1/Wsho8oxFUrtNiOB3ZiY2cx9UWttpzMXoGfi2gJcP1db/nTfWenOLlzw6Od4VyRzsXsfyGwbqBqDnNFkjLvhjVw4JO+psF//xAMDs14101Tf2wFkB6toQ+zdnDphXUeKmiVPQ7gMnQlOWN5tWvjjmYOO4Y63sGpP24cDOdEScIdebZRSA8uOhTzadfKfOiH5zVYZzXs33FQ0li4nBrsj5xYa6PP4D1P7gqjxClPdguwkdLoZ7JvgIlyRIwEcORi5Ich8RWF/kqRBwk0QSzK1mTlHHU5xtSgi4MLNVx4qTYNaJVBtwL9d2MD9LeNn4Z2PL4A7qnszHqsERiQQDxNEgMxMBHDgSXqQbtQxRvsI6oY+yNbN7uVWw4o8AC3f5GBdxzIqcN1mgEM5ix5aDt15w3MhP2FHtf2neKI68TnL8WnT1fT8BVlbECIiUqK0tfq5aTjdSh3gCS15jvZ1H60h+K8+O/nDfquzVjY7UsTGwA+UtS8/JGiaUhc0VhxJo8P1V2VSCiu51d5q3De1vDg5R2VEgBmTchZyTIodC+3+7ACTOwkNCCdIJN7xKOcIGFA7QOuyJtBeXT4Rd9UGMHSL054IB/315WVDiwrP9W1aP0nHzFs+qAXbH5o1E+AmfMDyoHopjGgbUw22r837kHzf5Qe8QhRYPzQvDowfCPhdoy23cbN1VsNavNwTC9hcG5oYMwkK66xP3MEM9UfGD22pwxe7M8U4BRdLCCHbi95eklXE6Mg6DpWsAdMgokQbOvnlwgKfrlbltqXUE/vUQI8TB3AE1Nkt0ST4quTriMuuyiHdeeZV4UkV9jWt/5bTCfdrCYuGZP7g4shbfNcP7u1Zrdxv+EuUwGIOOTrNV5awmBnL3iHE5ya2MnqmRyfWiPIT5majZCk06yxj4XzyIPOpjYKFt0MOgLvG1GllmdtRqg7tMVvc5ZFo5KWIxLsIJD12UjA1GYYoFdX4+wsNbPjfnlE6D2PrtWUICnBFJzYpfyrKTe01k8G8hyyz+tVzBRfz8EA2ew1+hlVcAgSPCcBzhDgqPe+RSPi7ZSd66be1gDhGAftWFM8Z0MrMklXi2DyjjaKBNsZZD8qTcLcobm8nqHUQtnr5JCbmgP3rau8NY/fxeFHsvSiZQoB1aI/y+Sz/R4r+T9cg8hjmS/FUHDO+m6a6nuWNFwz8wIluM557oOTl+A9UGFF50Gpzmf97VdQjM3ZREazQ7la6AobzS3BHI6FNdxN9LTyMpYo+WODv52/VwU3ODH7wf5bz2OHZhk2NG5R7pSH7qg8jM+/MtJkFumENV0qMecozIkP6e4CyI9ua4YwI9n7G5OgKYMG1aj2PRSny2JSLS8aHF1TkRL8SD0nZFCox0=|muEtiwIuZxhuuLv0nouEdxHU2CO+I7JXKZuYHWiv/OE=") assert.Equal(t, vault.loginAccount.ProtectedSymmetricKey, "2.lkAJiJtCKPHFPrZ96+j2Xg==|5XJtrKUndcGy28thFukrmgMcLp+BOVdkF+KcuOnfshq9AN1PFhna9Es96CVARCnjTcWuHuqvgnGmcOHTrf8fyfLv63VBsjLgLZk8rCXJoKE=|9dwgx4/13AD+elE2vE7vlSQoe8LbCGGlui345YrKvXY=") } + +func TestObjectCreation(t *testing.T) { + ctx := context.Background() + httpmock.Activate() + defer httpmock.DeactivateAndReset() + + vault := NewWebAPIVaultWithMockedTransport(t) + + err := vault.LoginWithPassword(ctx, "test@laverse.net", testPassword) + if err != nil { + t.Fatalf("vault unlock failed: %v", err) + } + + obj, err := vault.CreateObject(ctx, models.Object{ + Object: models.ObjectTypeItem, + Type: models.ItemTypeLogin, + Name: "test", + }) + assert.NoError(t, err) + if !assert.NotNil(t, obj) { + return + } + + assert.Equal(t, obj.Name, "Item in own Vault") + assert.Equal(t, obj.Login.Username, "my-username") +} diff --git a/internal/bitwarden/webapi/client.go b/internal/bitwarden/webapi/client.go index 0d3652b..fde5c9f 100644 --- a/internal/bitwarden/webapi/client.go +++ b/internal/bitwarden/webapi/client.go @@ -43,7 +43,7 @@ type Client interface { LoginWithPassword(ctx context.Context, username, password string, kdfIterations int) (*TokenResponse, error) PreLogin(context.Context, string) (*PreloginResponse, error) Profile(context.Context) (*Profile, error) - RegisterUser(ctx context.Context, name, username, password string, kdfIterations int) error + RegisterUser(ctx context.Context, req SignupRequest) error Sync(ctx context.Context) (*SyncResponse, error) } @@ -288,9 +288,12 @@ func (c *client) LoginWithPassword(ctx context.Context, username, password strin form.Add("grant_type", "password") form.Add("username", username) form.Add("password", hashedPassword) - form.Add("device_type", c.deviceType) - form.Add("device_identifier", c.deviceIdentifier) - form.Add("device_name", c.deviceName) + + // NOTE: The following fields are not documented on the Bitwarden API, but seem to be required + // in order to avoid a "No device information provided." error. + form.Add("deviceType", c.deviceType) + form.Add("deviceIdentifier", c.deviceIdentifier) + form.Add("deviceName", c.deviceName) httpReq, err := c.prepareRequest(ctx, "POST", fmt.Sprintf("%s/identity/connect/token", c.serverURL), form) if err != nil { @@ -323,9 +326,12 @@ func (c *client) LoginWithAPIKey(ctx context.Context, clientId, clientSecret str form.Add("client_id", clientId) form.Add("client_secret", clientSecret) form.Add("grant_type", "client_credentials") - form.Add("device_type", c.deviceType) - form.Add("device_identifier", c.deviceIdentifier) - form.Add("device_name", c.deviceName) + + // NOTE: The following fields are not documented on the Bitwarden API, but seem to be required + // in order to avoid a "No device information provided." error. + form.Add("deviceType", c.deviceType) + form.Add("deviceIdentifier", c.deviceIdentifier) + form.Add("deviceName", c.deviceName) httpReq, err := c.prepareRequest(ctx, "POST", fmt.Sprintf("%s/identity/connect/token", c.serverURL), form) if err != nil { @@ -358,36 +364,7 @@ func (c *client) Profile(ctx context.Context) (*Profile, error) { return doRequest[Profile](ctx, c.httpClient, httpReq) } -func (c *client) RegisterUser(ctx context.Context, name, username, password string, kdfIterations int) error { - preloginKey, err := keybuilder.BuildPreloginKey(password, username, kdfIterations) - if err != nil { - return fmt.Errorf("error building prelogin key: %w", err) - } - - hashedPassword := crypto.HashPassword(password, *preloginKey, false) - - encryptionKey, encryptedEncryptionKey, err := keybuilder.GenerateEncryptionKey(*preloginKey) - if err != nil { - return fmt.Errorf("error generating encryption key: %w", err) - } - - publicKey, encryptedPrivateKey, err := keybuilder.GenerateKeyPair(*encryptionKey) - if err != nil { - return fmt.Errorf("error generating key pair: %w", err) - } - - signupRequest := SignupRequest{ - Email: username, - Name: name, - MasterPasswordHash: hashedPassword, - Key: encryptedEncryptionKey, - KdfIterations: kdfIterations, - Keys: KeyPair{ - PublicKey: publicKey, - EncryptedPrivateKey: encryptedPrivateKey, - }, - } - +func (c *client) RegisterUser(ctx context.Context, signupRequest SignupRequest) error { httpReq, err := c.prepareRequest(ctx, "POST", fmt.Sprintf("%s/api/accounts/register", c.serverURL), signupRequest) if err != nil { return fmt.Errorf("error preparing registration request: %w", err) @@ -410,6 +387,7 @@ func (c *client) prepareRequest(ctx context.Context, reqMethod, reqUrl string, r var httpReq *retryablehttp.Request var err error contentType := "" + debugInfo := map[string]interface{}{"url": reqUrl, "method": reqMethod} if reqBody != nil { var bodyBytes []byte if v, ok := reqBody.(url.Values); ok { @@ -425,7 +403,7 @@ func (c *client) prepareRequest(ctx context.Context, reqMethod, reqUrl string, r } } httpReq, err = retryablehttp.NewRequestWithContext(ctx, reqMethod, reqUrl, bytes.NewBuffer(bodyBytes)) - tflog.Trace(ctx, "Request to Bitwarden server", map[string]interface{}{"url": reqUrl, "method": reqMethod, "body": string(bodyBytes)}) + debugInfo["body"] = string(bodyBytes) } else { httpReq, err = retryablehttp.NewRequestWithContext(ctx, reqMethod, reqUrl, nil) } @@ -433,11 +411,16 @@ func (c *client) prepareRequest(ctx context.Context, reqMethod, reqUrl string, r if err != nil { return nil, fmt.Errorf("error preparing http request: %w", err) } - httpReq.Header.Add("authorization", fmt.Sprintf("Bearer %s", c.sessionAccessToken)) + if len(c.sessionAccessToken) > 0 { + httpReq.Header.Add("authorization", fmt.Sprintf("Bearer %s", c.sessionAccessToken)) + } if len(contentType) > 0 { httpReq.Header.Add("Content-Type", contentType) } + debugInfo["headers"] = httpReq.Header + tflog.Trace(ctx, "Request to Bitwarden server", debugInfo) + return httpReq, nil } diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 1b9be36..c6166b6 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -337,7 +337,7 @@ func getOrGenerateDeviceIdentifier(ctx context.Context) (string, error) { deviceId := embedded.NewDeviceIdentifier() err = os.Mkdir(".bitwarden", 0700) - if err != nil { + if err != nil && !os.IsExist(err) { tflog.Error(ctx, "Failed to create .bitwarden directory", map[string]interface{}{"error": err}) return "", err } diff --git a/internal/provider/provider_utils_test.go b/internal/provider/provider_utils_test.go index 9da4f21..ca7be95 100644 --- a/internal/provider/provider_utils_test.go +++ b/internal/provider/provider_utils_test.go @@ -133,10 +133,10 @@ func ensureVaultwardenHasUser(t *testing.T) { clearTestVault(t) - webapiClient := webapi.NewClient(testServerURL) + client := embedded.NewWebAPIVault(testServerURL) testUsername = fmt.Sprintf("test-%s", testUniqueIdentifier) testEmail = fmt.Sprintf("test-%s@laverse.net", testUniqueIdentifier) - err := webapiClient.RegisterUser(context.Background(), testUsername, testEmail, testPassword, kdfIterations) + err := client.RegisterUser(context.Background(), testUsername, testEmail, testPassword, kdfIterations) if err != nil && !strings.Contains(strings.ToLower(err.Error()), "user already exists") { t.Fatal(err) }