toru-vault 0.1.2__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. {toru_vault-0.1.2/toru_vault.egg-info → toru_vault-0.2.0}/PKG-INFO +22 -17
  2. {toru_vault-0.1.2 → toru_vault-0.2.0}/README.md +19 -16
  3. {toru_vault-0.1.2 → toru_vault-0.2.0}/pyproject.toml +4 -1
  4. {toru_vault-0.1.2 → toru_vault-0.2.0}/setup.py +5 -3
  5. toru_vault-0.2.0/tests/test_env_load.py +101 -0
  6. toru_vault-0.2.0/tests/test_vault_encryption.py +124 -0
  7. toru_vault-0.2.0/tests/test_vault_jit.py +79 -0
  8. {toru_vault-0.1.2 → toru_vault-0.2.0}/toru_vault/__init__.py +1 -0
  9. {toru_vault-0.1.2 → toru_vault-0.2.0}/toru_vault/__main__.py +29 -6
  10. toru_vault-0.2.0/toru_vault/in_env.py +173 -0
  11. toru_vault-0.2.0/toru_vault/in_memory.py +379 -0
  12. {toru_vault-0.1.2 → toru_vault-0.2.0}/toru_vault/lazy_dict.py +10 -15
  13. toru_vault-0.2.0/toru_vault/vault.py +252 -0
  14. {toru_vault-0.1.2 → toru_vault-0.2.0/toru_vault.egg-info}/PKG-INFO +22 -17
  15. {toru_vault-0.1.2 → toru_vault-0.2.0}/toru_vault.egg-info/SOURCES.txt +5 -3
  16. {toru_vault-0.1.2 → toru_vault-0.2.0}/toru_vault.egg-info/requires.txt +3 -0
  17. toru_vault-0.1.2/tests/test_cli.py +0 -96
  18. toru_vault-0.1.2/tests/test_lazy_dict.py +0 -125
  19. toru_vault-0.1.2/tests/test_vault.py +0 -196
  20. toru_vault-0.1.2/toru_vault/vault.py +0 -532
  21. {toru_vault-0.1.2 → toru_vault-0.2.0}/LICENSE +0 -0
  22. {toru_vault-0.1.2 → toru_vault-0.2.0}/setup.cfg +0 -0
  23. {toru_vault-0.1.2 → toru_vault-0.2.0}/toru_vault/py.typed +0 -0
  24. {toru_vault-0.1.2 → toru_vault-0.2.0}/toru_vault.egg-info/dependency_links.txt +0 -0
  25. {toru_vault-0.1.2 → toru_vault-0.2.0}/toru_vault.egg-info/entry_points.txt +0 -0
  26. {toru_vault-0.1.2 → toru_vault-0.2.0}/toru_vault.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: toru-vault
3
- Version: 0.1.2
3
+ Version: 0.2.0
4
4
  Summary: ToruVault: A simple Python package for managing Bitwarden secrets
5
5
  Author: Toru AI
6
6
  Author-email: ToruAI <mpaszynski@toruai.com>
@@ -15,20 +15,20 @@ Description-Content-Type: text/markdown
15
15
  License-File: LICENSE
16
16
  Requires-Dist: bitwarden-sdk
17
17
  Requires-Dist: cryptography>=36.0.0
18
+ Provides-Extra: keyring
19
+ Requires-Dist: keyring>=23.0.0; extra == "keyring"
18
20
  Dynamic: author
19
21
  Dynamic: license-file
20
22
  Dynamic: requires-python
21
23
 
22
- <p align="center">
23
- <img src="img/logo.svg" alt="ToruVault Logo" width="300"/>
24
- </p>
24
+ ![ToruVault Logo](https://toruai.com/toruai-logo.png)
25
25
 
26
26
  # ToruVault
27
27
 
28
28
  A simple Python package for managing Bitwarden secrets with enhanced security.
29
29
 
30
30
 
31
- ![Version](https://img.shields.io/badge/version-1.0.0-blue)
31
+ ![Version](https://img.shields.io/badge/version-0.2.0-blue)
32
32
  ![Python](https://img.shields.io/badge/python-3.10%2B-blue)
33
33
  ![License](https://img.shields.io/badge/license-MIT-green)
34
34
 
@@ -37,8 +37,8 @@ A simple Python package for managing Bitwarden secrets with enhanced security.
37
37
  - Load secrets from Bitwarden Secret Manager into environment variables
38
38
  - Get secrets as a Python dictionary
39
39
  - Filter secrets by project ID
40
- - Secure in-memory caching with encryption
41
- - Automatic cache expiration (5 minutes)
40
+ - JIT decryption of individual secrets
41
+ - No persistent caching of decrypted values
42
42
  - Secure file permissions for state storage
43
43
  - Machine-specific secret protection
44
44
  - Secure credential storage using OS keyring
@@ -51,21 +51,27 @@ A simple Python package for managing Bitwarden secrets with enhanced security.
51
51
  # Install UV if you don't have it already
52
52
  curl -LsSf https://astral.sh/uv/install.sh | sh
53
53
 
54
- # Install toru-vault package
54
+ # Install toru-vault package (basic installation)
55
55
  uv pip install toru-vault
56
56
 
57
+ # Or install with keyring support (recommended for secure storage)
58
+ uv pip install toru-vault[keyring]
59
+
57
60
  # Or install in a virtual environment (recommended)
58
61
  uv venv create -p python3.10 .venv
59
62
  source .venv/bin/activate # On Windows: .venv\Scripts\activate
60
- uv pip install toru-vault
63
+ uv pip install toru-vault[keyring]
61
64
  ```
62
65
 
63
-
64
- This will automatically install all required dependencies:
66
+ This will install all required dependencies:
65
67
  - bitwarden-sdk - For interfacing with Bitwarden API
66
- - keyring - For secure credential storage
67
68
  - cryptography - For encryption/decryption operations
68
69
 
70
+ And when installed with the keyring option:
71
+ - keyring - For secure credential storage using OS keyring
72
+
73
+ > **Note:** Keyring is now optional but recommended. Without keyring, some features like `toru-vault init` won't work, and you'll need to use the `use_keyring=False` parameter with the `get()` function to use in-memory encryption instead of the system keyring.
74
+
69
75
  ### From Source with UV
70
76
 
71
77
  ```bash
@@ -193,11 +199,10 @@ vault.env_load_all(override=True)
193
199
  The vault package includes several security enhancements:
194
200
 
195
201
  1. **OS Keyring Integration**: Securely stores BWS_TOKEN, ORGANIZATION_ID, and STATE_FILE in your OS keyring
196
- 2. **Memory Protection**: Secrets are encrypted in memory using Fernet encryption (AES-128)
197
- 3. **Lazy Decryption**: Secrets are only decrypted when explicitly accessed
198
- 4. **Cache Expiration**: Cached secrets expire after 5 minutes by default
199
- 5. **Secure File Permissions**: Sets secure permissions on state files
200
- 6. **Machine-Specific Encryption**: Uses machine-specific identifiers for encryption keys
202
+ 2. **Memory Protection**: Secrets are individually encrypted in memory using Fernet encryption (AES-128)
203
+ 3. **JIT Decryption**: Secrets are only decrypted when explicitly accessed and never stored in decrypted form
204
+ 4. **Secure File Permissions**: Sets secure permissions on state files
205
+ 5. **Machine-Specific Encryption**: Uses machine-specific identifiers for encryption keys
201
206
  7. **Cache Clearing**: Automatically clears secret cache on program exit
202
207
  8. **Environment Variable Protection**: Doesn't override existing environment variables by default
203
208
  9. **Secure Key Derivation**: Uses PBKDF2 with SHA-256 for key derivation
@@ -1,13 +1,11 @@
1
- <p align="center">
2
- <img src="img/logo.svg" alt="ToruVault Logo" width="300"/>
3
- </p>
1
+ ![ToruVault Logo](https://toruai.com/toruai-logo.png)
4
2
 
5
3
  # ToruVault
6
4
 
7
5
  A simple Python package for managing Bitwarden secrets with enhanced security.
8
6
 
9
7
 
10
- ![Version](https://img.shields.io/badge/version-1.0.0-blue)
8
+ ![Version](https://img.shields.io/badge/version-0.2.0-blue)
11
9
  ![Python](https://img.shields.io/badge/python-3.10%2B-blue)
12
10
  ![License](https://img.shields.io/badge/license-MIT-green)
13
11
 
@@ -16,8 +14,8 @@ A simple Python package for managing Bitwarden secrets with enhanced security.
16
14
  - Load secrets from Bitwarden Secret Manager into environment variables
17
15
  - Get secrets as a Python dictionary
18
16
  - Filter secrets by project ID
19
- - Secure in-memory caching with encryption
20
- - Automatic cache expiration (5 minutes)
17
+ - JIT decryption of individual secrets
18
+ - No persistent caching of decrypted values
21
19
  - Secure file permissions for state storage
22
20
  - Machine-specific secret protection
23
21
  - Secure credential storage using OS keyring
@@ -30,21 +28,27 @@ A simple Python package for managing Bitwarden secrets with enhanced security.
30
28
  # Install UV if you don't have it already
31
29
  curl -LsSf https://astral.sh/uv/install.sh | sh
32
30
 
33
- # Install toru-vault package
31
+ # Install toru-vault package (basic installation)
34
32
  uv pip install toru-vault
35
33
 
34
+ # Or install with keyring support (recommended for secure storage)
35
+ uv pip install toru-vault[keyring]
36
+
36
37
  # Or install in a virtual environment (recommended)
37
38
  uv venv create -p python3.10 .venv
38
39
  source .venv/bin/activate # On Windows: .venv\Scripts\activate
39
- uv pip install toru-vault
40
+ uv pip install toru-vault[keyring]
40
41
  ```
41
42
 
42
-
43
- This will automatically install all required dependencies:
43
+ This will install all required dependencies:
44
44
  - bitwarden-sdk - For interfacing with Bitwarden API
45
- - keyring - For secure credential storage
46
45
  - cryptography - For encryption/decryption operations
47
46
 
47
+ And when installed with the keyring option:
48
+ - keyring - For secure credential storage using OS keyring
49
+
50
+ > **Note:** Keyring is now optional but recommended. Without keyring, some features like `toru-vault init` won't work, and you'll need to use the `use_keyring=False` parameter with the `get()` function to use in-memory encryption instead of the system keyring.
51
+
48
52
  ### From Source with UV
49
53
 
50
54
  ```bash
@@ -172,11 +176,10 @@ vault.env_load_all(override=True)
172
176
  The vault package includes several security enhancements:
173
177
 
174
178
  1. **OS Keyring Integration**: Securely stores BWS_TOKEN, ORGANIZATION_ID, and STATE_FILE in your OS keyring
175
- 2. **Memory Protection**: Secrets are encrypted in memory using Fernet encryption (AES-128)
176
- 3. **Lazy Decryption**: Secrets are only decrypted when explicitly accessed
177
- 4. **Cache Expiration**: Cached secrets expire after 5 minutes by default
178
- 5. **Secure File Permissions**: Sets secure permissions on state files
179
- 6. **Machine-Specific Encryption**: Uses machine-specific identifiers for encryption keys
179
+ 2. **Memory Protection**: Secrets are individually encrypted in memory using Fernet encryption (AES-128)
180
+ 3. **JIT Decryption**: Secrets are only decrypted when explicitly accessed and never stored in decrypted form
181
+ 4. **Secure File Permissions**: Sets secure permissions on state files
182
+ 5. **Machine-Specific Encryption**: Uses machine-specific identifiers for encryption keys
180
183
  7. **Cache Clearing**: Automatically clears secret cache on program exit
181
184
  8. **Environment Variable Protection**: Doesn't override existing environment variables by default
182
185
  9. **Secure Key Derivation**: Uses PBKDF2 with SHA-256 for key derivation
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "toru-vault"
7
- version = "0.1.2"
7
+ version = "0.2.0"
8
8
  description = "ToruVault: A simple Python package for managing Bitwarden secrets"
9
9
  readme = "README.md"
10
10
  authors = [
@@ -22,6 +22,9 @@ dependencies = [
22
22
  "cryptography>=36.0.0",
23
23
  ]
24
24
 
25
+ [project.optional-dependencies]
26
+ keyring = ["keyring>=23.0.0"]
27
+
25
28
  [project.urls]
26
29
  Homepage = "https://github.com/ToruAI/ToruVault"
27
30
  Issues = "https://github.com/ToruAI/ToruVault/issues"
@@ -1,14 +1,16 @@
1
- from setuptools import setup, find_packages
1
+ from setuptools import setup
2
2
 
3
3
  setup(
4
4
  name="toru-vault",
5
- version='0.1.2',
5
+ version='0.2.0',
6
6
  packages=["toru_vault"],
7
7
  install_requires=[
8
8
  "bitwarden-sdk",
9
- "keyring>=23.0.0",
10
9
  "cryptography>=36.0.0",
11
10
  ],
11
+ extras_require={
12
+ "keyring": ["keyring>=23.0.0"],
13
+ },
12
14
  description="ToruVault: A simple Python package for managing Bitwarden secrets",
13
15
  author="Toru AI",
14
16
  author_email="dev@toruai.com",
@@ -0,0 +1,101 @@
1
+ import os
2
+ import pytest
3
+ from unittest.mock import patch, MagicMock
4
+
5
+ from toru_vault.vault import env_load, env_load_all
6
+
7
+ class TestEnvLoad:
8
+
9
+ def test_env_load_single_project(self, mock_env_vars, mock_initialize_client, mock_bitwarden_client):
10
+ """Test loading secrets from a single project into environment variables"""
11
+ # Clear any existing test environment variables
12
+ if "TEST_SECRET1" in os.environ:
13
+ del os.environ["TEST_SECRET1"]
14
+ if "TEST_SECRET2" in os.environ:
15
+ del os.environ["TEST_SECRET2"]
16
+
17
+ # Call the function with a specific project
18
+ env_load(project_id="project1")
19
+
20
+ # Verify Bitwarden client was initialized
21
+ mock_initialize_client.assert_called_once()
22
+
23
+ # Verify secrets synchronization was performed
24
+ mock_bitwarden_client.secrets().sync.assert_called_once()
25
+
26
+ # Verify environment variables were set
27
+ assert os.environ["TEST_SECRET1"] == "test_value1"
28
+ assert os.environ["TEST_SECRET2"] == "test_value2"
29
+
30
+ # Test overriding
31
+ os.environ["TEST_SECRET1"] = "existing_value"
32
+
33
+ # Call with override=False, shouldn't change existing value
34
+ env_load(project_id="project1", override=False)
35
+ assert os.environ["TEST_SECRET1"] == "existing_value"
36
+
37
+ # Call with override=True, should change existing value
38
+ env_load(project_id="project1", override=True)
39
+ assert os.environ["TEST_SECRET1"] == "test_value1"
40
+
41
+ def test_env_load_all(self, mock_env_vars, mock_initialize_client, mock_bitwarden_client):
42
+ """Test loading secrets from all projects into environment variables"""
43
+ # Clear any existing test environment variables
44
+ if "TEST_SECRET1" in os.environ:
45
+ del os.environ["TEST_SECRET1"]
46
+ if "TEST_SECRET2" in os.environ:
47
+ del os.environ["TEST_SECRET2"]
48
+
49
+ # Call the function to load all secrets
50
+ env_load_all()
51
+
52
+ # Verify Bitwarden client was initialized
53
+ mock_initialize_client.assert_called_once()
54
+
55
+ # Verify secrets synchronization was performed
56
+ mock_bitwarden_client.secrets().sync.assert_called_once()
57
+
58
+ # Verify secrets were fetched with list and get_by_ids
59
+ mock_bitwarden_client.secrets().list.assert_called_once()
60
+ mock_bitwarden_client.secrets().get_by_ids.assert_called_once()
61
+
62
+ # Verify environment variables were set
63
+ assert os.environ["TEST_SECRET1"] == "test_value1"
64
+ assert os.environ["TEST_SECRET2"] == "test_value2"
65
+
66
+ # Test overriding
67
+ os.environ["TEST_SECRET1"] = "existing_value"
68
+
69
+ # Call with override=False, shouldn't change existing value
70
+ env_load_all(override=False)
71
+ assert os.environ["TEST_SECRET1"] == "existing_value"
72
+
73
+ # Call with override=True, should change existing value
74
+ env_load_all(override=True)
75
+ assert os.environ["TEST_SECRET1"] == "test_value1"
76
+
77
+ def test_env_load_missing_organization_id(self, mock_env_vars):
78
+ """Test env_load behavior when organization_id is missing"""
79
+ # Remove organization ID from environment
80
+ if "ORGANIZATION_ID" in os.environ:
81
+ del os.environ["ORGANIZATION_ID"]
82
+
83
+ # Should not raise exception, just return without doing anything
84
+ env_load(project_id="project1")
85
+
86
+ # Verify no environment variables were set
87
+ assert "TEST_SECRET1" not in os.environ
88
+ assert "TEST_SECRET2" not in os.environ
89
+
90
+ def test_env_load_all_missing_organization_id(self, mock_env_vars):
91
+ """Test env_load_all behavior when organization_id is missing"""
92
+ # Remove organization ID from environment
93
+ if "ORGANIZATION_ID" in os.environ:
94
+ del os.environ["ORGANIZATION_ID"]
95
+
96
+ # Should not raise exception, just return without doing anything
97
+ env_load_all()
98
+
99
+ # Verify no environment variables were set
100
+ assert "TEST_SECRET1" not in os.environ
101
+ assert "TEST_SECRET2" not in os.environ
@@ -0,0 +1,124 @@
1
+ import pytest
2
+ import os
3
+ from unittest.mock import patch, MagicMock
4
+
5
+ from toru_vault.in_memory import (
6
+ _encrypt_secret,
7
+ _decrypt_secret,
8
+ _encrypt_secrets,
9
+ _decrypt_secrets,
10
+ create_secrets_dict
11
+ )
12
+
13
+ class TestEncryption:
14
+ """Tests for encryption/decryption implementation"""
15
+
16
+ def test_single_secret_encryption_decryption(self):
17
+ """Test individual secret encryption and decryption"""
18
+ secret_value = "super-sensitive-value"
19
+
20
+ # Encrypt the secret
21
+ encrypted = _encrypt_secret(secret_value)
22
+ assert encrypted is not None
23
+ assert ":" in encrypted # Should contain salt and encrypted data
24
+
25
+ # Should not be the same as original
26
+ assert encrypted != secret_value
27
+
28
+ # Decrypt the secret
29
+ decrypted = _decrypt_secret(encrypted)
30
+ assert decrypted == secret_value
31
+
32
+ def test_multiple_secrets_encryption_decryption(self):
33
+ """Test encryption and decryption of multiple secrets"""
34
+ secrets = {
35
+ "API_KEY": "secret-api-key",
36
+ "PASSWORD": "secret-password",
37
+ "TOKEN": "secret-token"
38
+ }
39
+
40
+ # Encrypt dictionary of secrets
41
+ encrypted_secrets = _encrypt_secrets(secrets)
42
+ assert encrypted_secrets is not None
43
+
44
+ # Ensure each value is encrypted
45
+ for key, value in encrypted_secrets.items():
46
+ assert ":" in value # Should contain salt and encrypted data
47
+ assert value != secrets[key] # Should not match original value
48
+
49
+ # Decrypt all secrets
50
+ decrypted_secrets = _decrypt_secrets(encrypted_secrets)
51
+ assert decrypted_secrets is not None
52
+
53
+ # Ensure decrypted values match original
54
+ for key, value in secrets.items():
55
+ assert decrypted_secrets[key] == value
56
+
57
+ def test_decryption_in_memory(self):
58
+ """Test decryption in non-keyring mode"""
59
+ # Create test secrets
60
+ test_secrets = {
61
+ "API_KEY": "test-api-key",
62
+ "DB_PASSWORD": "test-db-password"
63
+ }
64
+
65
+ # Create secret keys set
66
+ secret_keys = set(test_secrets.keys())
67
+
68
+ # Create LazySecretsDict with in-memory decryption
69
+ secrets_dict = create_secrets_dict(
70
+ secret_keys,
71
+ "test_org_id",
72
+ "test_project_id",
73
+ test_secrets, # Direct use of secrets
74
+ False # No keyring
75
+ )
76
+
77
+ # Access each secret and verify correct values
78
+ for key, expected_value in test_secrets.items():
79
+ assert secrets_dict[key] == expected_value
80
+
81
+ @patch("keyring.get_password")
82
+ @patch("keyring.set_password")
83
+ def test_decryption_with_keyring(self, mock_set_password, mock_get_password):
84
+ """Test decryption with keyring enabled"""
85
+ # Create test secrets
86
+ test_secrets = {
87
+ "API_KEY": "test-api-key",
88
+ "DB_PASSWORD": "test-db-password"
89
+ }
90
+
91
+ # Mock keyring to return encrypted values
92
+ def mock_get_side_effect(service_name, key):
93
+ # Return encrypted version of the test secret
94
+ if key in test_secrets:
95
+ return _encrypt_secret(test_secrets[key])
96
+ return None
97
+
98
+ mock_get_password.side_effect = mock_get_side_effect
99
+
100
+ # Create secret keys set
101
+ secret_keys = set(test_secrets.keys())
102
+ organization_id = "test_org_id"
103
+ project_id = "test_project_id"
104
+
105
+ # Create LazySecretsDict with keyring decryption
106
+ secrets_dict = create_secrets_dict(
107
+ secret_keys,
108
+ organization_id,
109
+ project_id,
110
+ test_secrets, # Used to initialize keyring
111
+ True # Use keyring
112
+ )
113
+
114
+ # Verify secrets were encrypted and stored in keyring
115
+ assert mock_set_password.call_count == len(test_secrets)
116
+
117
+ # Access each secret and verify decryption
118
+ for key, expected_value in test_secrets.items():
119
+ # Value should be decrypted from keyring when accessed
120
+ assert secrets_dict[key] == expected_value
121
+
122
+
123
+
124
+
@@ -0,0 +1,79 @@
1
+ import pytest
2
+ import os
3
+ from unittest.mock import patch, MagicMock
4
+
5
+ class TestVaultDecryption:
6
+ """Test encryption/decryption with vault API functions"""
7
+
8
+ def test_get_in_memory(self, mock_initialize_client, mock_env_vars):
9
+ """Test retrieval with get() using in-memory mode"""
10
+ from toru_vault import vault
11
+
12
+ # Get secrets with in-memory storage (no keyring)
13
+ secrets = vault.get("project1", use_keyring=False)
14
+
15
+ # Verify we got the expected keys
16
+ assert "TEST_SECRET1" in secrets
17
+ assert "TEST_SECRET2" in secrets
18
+
19
+ # Access secrets and verify values
20
+ assert secrets["TEST_SECRET1"] == "test_value1"
21
+ assert secrets["TEST_SECRET2"] == "test_value2"
22
+
23
+
24
+
25
+ @patch("keyring.get_password")
26
+ @patch("keyring.set_password")
27
+ def test_get_with_keyring(self, mock_set, mock_get, mock_initialize_client, mock_env_vars):
28
+ """Test decryption with get() using keyring"""
29
+ from toru_vault import vault
30
+ from toru_vault.in_memory import _encrypt_secret, _decrypt_secret
31
+
32
+ # Setup keyring mock to simulate stored encrypted values
33
+ def mock_get_side_effect(service_name, key):
34
+ if key == "TEST_SECRET1":
35
+ return _encrypt_secret("test_value1")
36
+ elif key == "TEST_SECRET2":
37
+ return _encrypt_secret("test_value2")
38
+ return None
39
+
40
+ mock_get.side_effect = mock_get_side_effect
41
+
42
+ # Get secrets using keyring
43
+ secrets = vault.get("project1", use_keyring=True)
44
+
45
+ # Access both secrets sequentially to test individual decryption
46
+ value1 = secrets["TEST_SECRET1"]
47
+ assert value1 == "test_value1"
48
+
49
+ value2 = secrets["TEST_SECRET2"]
50
+ assert value2 == "test_value2"
51
+
52
+ # Verify we called get_password at least twice, plus once for org_id lookup
53
+ # Expected number: 2 secret lookups + 1 for ORGANIZATION_ID
54
+ assert mock_get.call_count >= 3
55
+
56
+ # Tests for env_load and env_load_all removed as they already exist in TestEnvLoad
57
+
58
+ def test_get_all(self, mock_initialize_client, mock_env_vars):
59
+ """Test decryption with get_all()"""
60
+ with patch("toru_vault.vault.get") as mock_get:
61
+ from toru_vault import vault
62
+
63
+ # Setup mock get to return test secrets
64
+ mock_get.return_value = {
65
+ "TEST_SECRET1": "test_value1",
66
+ "TEST_SECRET2": "test_value2"
67
+ }
68
+
69
+ # Call get_all
70
+ all_secrets = vault.get_all(use_keyring=False)
71
+
72
+ # Verify get was called for the project
73
+ mock_get.assert_called_once_with("project1", use_keyring=False)
74
+
75
+ # Verify expected secrets are in result
76
+ assert "TEST_SECRET1" in all_secrets
77
+ assert all_secrets["TEST_SECRET1"] == "test_value1"
78
+ assert "TEST_SECRET2" in all_secrets
79
+ assert all_secrets["TEST_SECRET2"] == "test_value2"
@@ -2,4 +2,5 @@
2
2
  # Import functions from the core module
3
3
  from .vault import env_load, env_load_all, get, get_all
4
4
 
5
+ __version__ = "0.2.0"
5
6
  __all__ = ["env_load", "env_load_all", "get", "get_all"]
@@ -7,9 +7,32 @@ import os
7
7
  import sys
8
8
  import getpass
9
9
 
10
- from .vault import (_initialize_client, set_to_keyring, _get_from_keyring_or_env,
11
- _KEYRING_BWS_TOKEN_KEY, _KEYRING_ORG_ID_KEY, _KEYRING_STATE_FILE_KEY,
12
- _KEYRING_AVAILABLE)
10
+ from .vault import (_initialize_client, _get_from_keyring_or_env,
11
+ _KEYRING_SERVICE_NAME, _KEYRING_BWS_TOKEN_KEY, _KEYRING_ORG_ID_KEY,
12
+ _KEYRING_STATE_FILE_KEY, _KEYRING_AVAILABLE)
13
+
14
+
15
+ def _set_to_keyring(key, value):
16
+ """
17
+ Set a value to keyring
18
+
19
+ Args:
20
+ key (str): Key in keyring
21
+ value (str): Value to store
22
+
23
+ Returns:
24
+ bool: True if successful, False otherwise
25
+ """
26
+ if not _KEYRING_AVAILABLE:
27
+ return False
28
+
29
+ try:
30
+ import keyring
31
+ keyring.set_password(_KEYRING_SERVICE_NAME, key, value)
32
+ return True
33
+ except Exception as e:
34
+ print(f"Failed to set {key} to keyring: {e}")
35
+ return False
13
36
 
14
37
 
15
38
  def list_projects(organization_id=None):
@@ -103,21 +126,21 @@ def init_vault():
103
126
  # Store in keyring
104
127
  if _KEYRING_AVAILABLE:
105
128
  if existing_token != token or not existing_token:
106
- if set_to_keyring(_KEYRING_BWS_TOKEN_KEY, token):
129
+ if _set_to_keyring(_KEYRING_BWS_TOKEN_KEY, token):
107
130
  print("BWS_TOKEN stored in keyring")
108
131
  else:
109
132
  print("Failed to store BWS_TOKEN in keyring")
110
133
  return False
111
134
 
112
135
  if existing_org_id != org_id or not existing_org_id:
113
- if set_to_keyring(_KEYRING_ORG_ID_KEY, org_id):
136
+ if _set_to_keyring(_KEYRING_ORG_ID_KEY, org_id):
114
137
  print("ORGANIZATION_ID stored in keyring")
115
138
  else:
116
139
  print("Failed to store ORGANIZATION_ID in keyring")
117
140
  return False
118
141
 
119
142
  if existing_state_file != state_file or not existing_state_file:
120
- if set_to_keyring(_KEYRING_STATE_FILE_KEY, state_file):
143
+ if _set_to_keyring(_KEYRING_STATE_FILE_KEY, state_file):
121
144
  print("STATE_FILE stored in keyring")
122
145
  else:
123
146
  print("Failed to store STATE_FILE in keyring")