toru-vault 0.1.4__tar.gz → 0.3.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.4 → toru_vault-0.3.0}/PKG-INFO +19 -9
  2. {toru_vault-0.1.4 → toru_vault-0.3.0}/README.md +18 -8
  3. {toru_vault-0.1.4 → toru_vault-0.3.0}/pyproject.toml +1 -1
  4. {toru_vault-0.1.4 → toru_vault-0.3.0}/setup.py +2 -2
  5. toru_vault-0.3.0/tests/test_env_load.py +101 -0
  6. toru_vault-0.3.0/tests/test_vault_encryption.py +124 -0
  7. toru_vault-0.3.0/tests/test_vault_jit.py +79 -0
  8. {toru_vault-0.1.4 → toru_vault-0.3.0}/toru_vault/__init__.py +1 -0
  9. {toru_vault-0.1.4 → toru_vault-0.3.0}/toru_vault/__main__.py +29 -6
  10. toru_vault-0.3.0/toru_vault/in_env.py +173 -0
  11. toru_vault-0.3.0/toru_vault/in_memory.py +379 -0
  12. {toru_vault-0.1.4 → toru_vault-0.3.0}/toru_vault/lazy_dict.py +10 -15
  13. toru_vault-0.3.0/toru_vault/vault.py +261 -0
  14. {toru_vault-0.1.4 → toru_vault-0.3.0}/toru_vault.egg-info/PKG-INFO +19 -9
  15. {toru_vault-0.1.4 → toru_vault-0.3.0}/toru_vault.egg-info/SOURCES.txt +5 -3
  16. toru_vault-0.1.4/tests/test_cli.py +0 -96
  17. toru_vault-0.1.4/tests/test_lazy_dict.py +0 -125
  18. toru_vault-0.1.4/tests/test_vault.py +0 -196
  19. toru_vault-0.1.4/toru_vault/vault.py +0 -575
  20. {toru_vault-0.1.4 → toru_vault-0.3.0}/LICENSE +0 -0
  21. {toru_vault-0.1.4 → toru_vault-0.3.0}/setup.cfg +0 -0
  22. {toru_vault-0.1.4 → toru_vault-0.3.0}/toru_vault/py.typed +0 -0
  23. {toru_vault-0.1.4 → toru_vault-0.3.0}/toru_vault.egg-info/dependency_links.txt +0 -0
  24. {toru_vault-0.1.4 → toru_vault-0.3.0}/toru_vault.egg-info/entry_points.txt +0 -0
  25. {toru_vault-0.1.4 → toru_vault-0.3.0}/toru_vault.egg-info/requires.txt +0 -0
  26. {toru_vault-0.1.4 → toru_vault-0.3.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.4
3
+ Version: 0.3.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>
@@ -28,7 +28,7 @@ Dynamic: requires-python
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.3.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
@@ -118,6 +118,7 @@ Alternatively, you can set the following environment variables:
118
118
  - `BWS_TOKEN`: Your Bitwarden access token
119
119
  - `ORGANIZATION_ID`: Your Bitwarden organization ID
120
120
  - `STATE_FILE`: Path to the state file (must be in an existing directory)
121
+ - `PROJECT_ID` (optional): Your Bitwarden project ID to filter secrets
121
122
  - `API_URL` (optional): Defaults to "https://api.bitwarden.com"
122
123
  - `IDENTITY_URL` (optional): Defaults to "https://identity.bitwarden.com"
123
124
 
@@ -159,6 +160,11 @@ print(os.environ.get("SECRET_NAME"))
159
160
  # Load secrets for a specific project
160
161
  vault.env_load(project_id="your-project-id")
161
162
 
163
+ # Alternatively, set PROJECT_ID environment variable and call without parameter
164
+ # export PROJECT_ID="your-project-id" # Linux/macOS
165
+ # set PROJECT_ID=your-project-id # Windows
166
+ vault.env_load() # Will use PROJECT_ID from environment
167
+
162
168
  # Override existing environment variables (default: False)
163
169
  vault.env_load(override=True)
164
170
  ```
@@ -178,6 +184,11 @@ secrets = vault.get(refresh=True)
178
184
  # Get secrets for a specific project
179
185
  secrets = vault.get(project_id="your-project-id")
180
186
 
187
+ # Alternatively, set PROJECT_ID environment variable and call without parameter
188
+ # export PROJECT_ID="your-project-id" # Linux/macOS
189
+ # set PROJECT_ID=your-project-id # Windows
190
+ secrets = vault.get() # Will use PROJECT_ID from environment
191
+
181
192
  # Use in-memory encryption instead of system keyring
182
193
  secrets = vault.get(use_keyring=False)
183
194
  ```
@@ -199,11 +210,10 @@ vault.env_load_all(override=True)
199
210
  The vault package includes several security enhancements:
200
211
 
201
212
  1. **OS Keyring Integration**: Securely stores BWS_TOKEN, ORGANIZATION_ID, and STATE_FILE in your OS keyring
202
- 2. **Memory Protection**: Secrets are encrypted in memory using Fernet encryption (AES-128)
203
- 3. **Lazy Decryption**: Secrets are only decrypted when explicitly accessed
204
- 4. **Cache Expiration**: Cached secrets expire after 5 minutes by default
205
- 5. **Secure File Permissions**: Sets secure permissions on state files
206
- 6. **Machine-Specific Encryption**: Uses machine-specific identifiers for encryption keys
213
+ 2. **Memory Protection**: Secrets are individually encrypted in memory using Fernet encryption (AES-128)
214
+ 3. **JIT Decryption**: Secrets are only decrypted when explicitly accessed and never stored in decrypted form
215
+ 4. **Secure File Permissions**: Sets secure permissions on state files
216
+ 5. **Machine-Specific Encryption**: Uses machine-specific identifiers for encryption keys
207
217
  7. **Cache Clearing**: Automatically clears secret cache on program exit
208
218
  8. **Environment Variable Protection**: Doesn't override existing environment variables by default
209
219
  9. **Secure Key Derivation**: Uses PBKDF2 with SHA-256 for key derivation
@@ -5,7 +5,7 @@
5
5
  A simple Python package for managing Bitwarden secrets with enhanced security.
6
6
 
7
7
 
8
- ![Version](https://img.shields.io/badge/version-1.0.0-blue)
8
+ ![Version](https://img.shields.io/badge/version-0.3.0-blue)
9
9
  ![Python](https://img.shields.io/badge/python-3.10%2B-blue)
10
10
  ![License](https://img.shields.io/badge/license-MIT-green)
11
11
 
@@ -14,8 +14,8 @@ A simple Python package for managing Bitwarden secrets with enhanced security.
14
14
  - Load secrets from Bitwarden Secret Manager into environment variables
15
15
  - Get secrets as a Python dictionary
16
16
  - Filter secrets by project ID
17
- - Secure in-memory caching with encryption
18
- - Automatic cache expiration (5 minutes)
17
+ - JIT decryption of individual secrets
18
+ - No persistent caching of decrypted values
19
19
  - Secure file permissions for state storage
20
20
  - Machine-specific secret protection
21
21
  - Secure credential storage using OS keyring
@@ -95,6 +95,7 @@ Alternatively, you can set the following environment variables:
95
95
  - `BWS_TOKEN`: Your Bitwarden access token
96
96
  - `ORGANIZATION_ID`: Your Bitwarden organization ID
97
97
  - `STATE_FILE`: Path to the state file (must be in an existing directory)
98
+ - `PROJECT_ID` (optional): Your Bitwarden project ID to filter secrets
98
99
  - `API_URL` (optional): Defaults to "https://api.bitwarden.com"
99
100
  - `IDENTITY_URL` (optional): Defaults to "https://identity.bitwarden.com"
100
101
 
@@ -136,6 +137,11 @@ print(os.environ.get("SECRET_NAME"))
136
137
  # Load secrets for a specific project
137
138
  vault.env_load(project_id="your-project-id")
138
139
 
140
+ # Alternatively, set PROJECT_ID environment variable and call without parameter
141
+ # export PROJECT_ID="your-project-id" # Linux/macOS
142
+ # set PROJECT_ID=your-project-id # Windows
143
+ vault.env_load() # Will use PROJECT_ID from environment
144
+
139
145
  # Override existing environment variables (default: False)
140
146
  vault.env_load(override=True)
141
147
  ```
@@ -155,6 +161,11 @@ secrets = vault.get(refresh=True)
155
161
  # Get secrets for a specific project
156
162
  secrets = vault.get(project_id="your-project-id")
157
163
 
164
+ # Alternatively, set PROJECT_ID environment variable and call without parameter
165
+ # export PROJECT_ID="your-project-id" # Linux/macOS
166
+ # set PROJECT_ID=your-project-id # Windows
167
+ secrets = vault.get() # Will use PROJECT_ID from environment
168
+
158
169
  # Use in-memory encryption instead of system keyring
159
170
  secrets = vault.get(use_keyring=False)
160
171
  ```
@@ -176,11 +187,10 @@ vault.env_load_all(override=True)
176
187
  The vault package includes several security enhancements:
177
188
 
178
189
  1. **OS Keyring Integration**: Securely stores BWS_TOKEN, ORGANIZATION_ID, and STATE_FILE in your OS keyring
179
- 2. **Memory Protection**: Secrets are encrypted in memory using Fernet encryption (AES-128)
180
- 3. **Lazy Decryption**: Secrets are only decrypted when explicitly accessed
181
- 4. **Cache Expiration**: Cached secrets expire after 5 minutes by default
182
- 5. **Secure File Permissions**: Sets secure permissions on state files
183
- 6. **Machine-Specific Encryption**: Uses machine-specific identifiers for encryption keys
190
+ 2. **Memory Protection**: Secrets are individually encrypted in memory using Fernet encryption (AES-128)
191
+ 3. **JIT Decryption**: Secrets are only decrypted when explicitly accessed and never stored in decrypted form
192
+ 4. **Secure File Permissions**: Sets secure permissions on state files
193
+ 5. **Machine-Specific Encryption**: Uses machine-specific identifiers for encryption keys
184
194
  7. **Cache Clearing**: Automatically clears secret cache on program exit
185
195
  8. **Environment Variable Protection**: Doesn't override existing environment variables by default
186
196
  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.4"
7
+ version = "0.3.0"
8
8
  description = "ToruVault: A simple Python package for managing Bitwarden secrets"
9
9
  readme = "README.md"
10
10
  authors = [
@@ -1,8 +1,8 @@
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.4',
5
+ version='0.3.0',
6
6
  packages=["toru_vault"],
7
7
  install_requires=[
8
8
  "bitwarden-sdk",
@@ -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")