opensecureconf-client 1.0.2__tar.gz → 2.0.2__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.
- opensecureconf_client-2.0.2/ADVANCED_EXAMPLES.md +754 -0
- opensecureconf_client-2.0.2/MANIFEST.in +6 -0
- opensecureconf_client-2.0.2/PKG-INFO +1289 -0
- opensecureconf_client-2.0.2/README.md +1247 -0
- opensecureconf_client-2.0.2/example_enhanced_usage.py +278 -0
- opensecureconf_client-2.0.2/example_usage.py +166 -0
- opensecureconf_client-2.0.2/opensecureconf_client.egg-info/PKG-INFO +1289 -0
- {opensecureconf_client-1.0.2 → opensecureconf_client-2.0.2}/opensecureconf_client.egg-info/SOURCES.txt +3 -0
- {opensecureconf_client-1.0.2 → opensecureconf_client-2.0.2}/opensecureconf_client.egg-info/requires.txt +6 -0
- opensecureconf_client-2.0.2/opensecureconf_client.py +769 -0
- opensecureconf_client-2.0.2/pyproject.toml +119 -0
- opensecureconf_client-1.0.2/MANIFEST.in +0 -3
- opensecureconf_client-1.0.2/PKG-INFO +0 -229
- opensecureconf_client-1.0.2/README.md +0 -194
- opensecureconf_client-1.0.2/opensecureconf_client.egg-info/PKG-INFO +0 -229
- opensecureconf_client-1.0.2/opensecureconf_client.py +0 -314
- opensecureconf_client-1.0.2/pyproject.toml +0 -52
- {opensecureconf_client-1.0.2 → opensecureconf_client-2.0.2}/LICENSE +0 -0
- {opensecureconf_client-1.0.2 → opensecureconf_client-2.0.2}/opensecureconf_client.egg-info/dependency_links.txt +0 -0
- {opensecureconf_client-1.0.2 → opensecureconf_client-2.0.2}/opensecureconf_client.egg-info/top_level.txt +0 -0
- {opensecureconf_client-1.0.2 → opensecureconf_client-2.0.2}/setup.cfg +0 -0
- {opensecureconf_client-1.0.2 → opensecureconf_client-2.0.2}/setup.py +0 -0
|
@@ -0,0 +1,754 @@
|
|
|
1
|
+
# Advanced Usage Examples
|
|
2
|
+
|
|
3
|
+
This document contains advanced usage patterns and real-world examples for the OpenSecureConf Python Client.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Production Deployment Patterns](#production-deployment-patterns)
|
|
8
|
+
- [Microservices Configuration](#microservices-configuration)
|
|
9
|
+
- [High Availability Patterns](#high-availability-patterns)
|
|
10
|
+
- [Performance Optimization](#performance-optimization)
|
|
11
|
+
- [Integration Examples](#integration-examples)
|
|
12
|
+
- [Security Best Practices](#security-best-practices)
|
|
13
|
+
|
|
14
|
+
## Production Deployment Patterns
|
|
15
|
+
|
|
16
|
+
### Environment-Based Configuration
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
import os
|
|
20
|
+
from opensecureconf_client_enhanced import OpenSecureConfClient
|
|
21
|
+
|
|
22
|
+
def get_client():
|
|
23
|
+
"""Factory function to create client based on environment."""
|
|
24
|
+
env = os.getenv("ENVIRONMENT", "development")
|
|
25
|
+
|
|
26
|
+
if env == "production":
|
|
27
|
+
return OpenSecureConfClient(
|
|
28
|
+
base_url=os.getenv("OSC_URL"),
|
|
29
|
+
user_key=os.getenv("OSC_USER_KEY"),
|
|
30
|
+
api_key=os.getenv("OSC_API_KEY"),
|
|
31
|
+
timeout=60,
|
|
32
|
+
verify_ssl=True,
|
|
33
|
+
enable_retry=True,
|
|
34
|
+
max_retries=5,
|
|
35
|
+
backoff_factor=2.0,
|
|
36
|
+
pool_connections=50,
|
|
37
|
+
pool_maxsize=100,
|
|
38
|
+
log_level="INFO"
|
|
39
|
+
)
|
|
40
|
+
elif env == "staging":
|
|
41
|
+
return OpenSecureConfClient(
|
|
42
|
+
base_url=os.getenv("OSC_URL", "http://staging-osc.internal:9000"),
|
|
43
|
+
user_key=os.getenv("OSC_USER_KEY"),
|
|
44
|
+
api_key=os.getenv("OSC_API_KEY"),
|
|
45
|
+
timeout=30,
|
|
46
|
+
verify_ssl=True,
|
|
47
|
+
enable_retry=True,
|
|
48
|
+
max_retries=3,
|
|
49
|
+
log_level="DEBUG"
|
|
50
|
+
)
|
|
51
|
+
else: # development
|
|
52
|
+
return OpenSecureConfClient(
|
|
53
|
+
base_url="http://localhost:9000",
|
|
54
|
+
user_key="dev-key-12345678",
|
|
55
|
+
api_key="dev-api-key",
|
|
56
|
+
timeout=10,
|
|
57
|
+
verify_ssl=False,
|
|
58
|
+
enable_retry=False,
|
|
59
|
+
log_level="DEBUG"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Usage
|
|
63
|
+
client = get_client()
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Configuration Singleton Pattern
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from opensecureconf_client_enhanced import OpenSecureConfClient
|
|
70
|
+
from threading import Lock
|
|
71
|
+
import os
|
|
72
|
+
|
|
73
|
+
class ConfigurationManager:
|
|
74
|
+
"""Thread-safe singleton for managing OpenSecureConf client."""
|
|
75
|
+
|
|
76
|
+
_instance = None
|
|
77
|
+
_lock = Lock()
|
|
78
|
+
|
|
79
|
+
def __new__(cls):
|
|
80
|
+
if cls._instance is None:
|
|
81
|
+
with cls._lock:
|
|
82
|
+
if cls._instance is None:
|
|
83
|
+
cls._instance = super().__new__(cls)
|
|
84
|
+
cls._instance._initialize()
|
|
85
|
+
return cls._instance
|
|
86
|
+
|
|
87
|
+
def _initialize(self):
|
|
88
|
+
"""Initialize the client."""
|
|
89
|
+
self.client = OpenSecureConfClient(
|
|
90
|
+
base_url=os.getenv("OSC_URL"),
|
|
91
|
+
user_key=os.getenv("OSC_USER_KEY"),
|
|
92
|
+
api_key=os.getenv("OSC_API_KEY"),
|
|
93
|
+
enable_retry=True,
|
|
94
|
+
log_level="INFO"
|
|
95
|
+
)
|
|
96
|
+
self._cache = {}
|
|
97
|
+
self._cache_ttl = 300 # 5 minutes
|
|
98
|
+
|
|
99
|
+
def get_config(self, key: str, use_cache: bool = True):
|
|
100
|
+
"""Get configuration with optional caching."""
|
|
101
|
+
if use_cache and key in self._cache:
|
|
102
|
+
return self._cache[key]
|
|
103
|
+
|
|
104
|
+
config = self.client.read(key)
|
|
105
|
+
if use_cache:
|
|
106
|
+
self._cache[key] = config
|
|
107
|
+
return config
|
|
108
|
+
|
|
109
|
+
def invalidate_cache(self, key: str = None):
|
|
110
|
+
"""Invalidate cache for specific key or all keys."""
|
|
111
|
+
if key:
|
|
112
|
+
self._cache.pop(key, None)
|
|
113
|
+
else:
|
|
114
|
+
self._cache.clear()
|
|
115
|
+
|
|
116
|
+
# Usage
|
|
117
|
+
config_manager = ConfigurationManager()
|
|
118
|
+
db_config = config_manager.get_config("database")
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Microservices Configuration
|
|
122
|
+
|
|
123
|
+
### Service Discovery Pattern
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
from opensecureconf_client_enhanced import OpenSecureConfClient
|
|
127
|
+
from typing import Dict, List
|
|
128
|
+
|
|
129
|
+
class ServiceRegistry:
|
|
130
|
+
"""Manage microservice configurations."""
|
|
131
|
+
|
|
132
|
+
def __init__(self, client: OpenSecureConfClient):
|
|
133
|
+
self.client = client
|
|
134
|
+
self.service_category = "microservices"
|
|
135
|
+
|
|
136
|
+
def register_service(self, name: str, config: Dict):
|
|
137
|
+
"""Register a new microservice."""
|
|
138
|
+
return self.client.create(
|
|
139
|
+
key=f"service.{name}",
|
|
140
|
+
value={
|
|
141
|
+
"name": name,
|
|
142
|
+
"url": config["url"],
|
|
143
|
+
"port": config.get("port", 8080),
|
|
144
|
+
"health_endpoint": config.get("health_endpoint", "/health"),
|
|
145
|
+
"timeout": config.get("timeout", 30),
|
|
146
|
+
"retries": config.get("retries", 3)
|
|
147
|
+
},
|
|
148
|
+
category=self.service_category
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
def discover_service(self, name: str) -> Dict:
|
|
152
|
+
"""Discover service configuration."""
|
|
153
|
+
return self.client.read(f"service.{name}")
|
|
154
|
+
|
|
155
|
+
def list_services(self) -> List[Dict]:
|
|
156
|
+
"""List all registered services."""
|
|
157
|
+
return self.client.list_all(category=self.service_category)
|
|
158
|
+
|
|
159
|
+
def update_service(self, name: str, config: Dict):
|
|
160
|
+
"""Update service configuration."""
|
|
161
|
+
current = self.discover_service(name)
|
|
162
|
+
updated_config = {**current['value'], **config}
|
|
163
|
+
return self.client.update(f"service.{name}", updated_config)
|
|
164
|
+
|
|
165
|
+
def deregister_service(self, name: str):
|
|
166
|
+
"""Remove service from registry."""
|
|
167
|
+
return self.client.delete(f"service.{name}")
|
|
168
|
+
|
|
169
|
+
# Usage
|
|
170
|
+
with OpenSecureConfClient(base_url="...", user_key="...") as client:
|
|
171
|
+
registry = ServiceRegistry(client)
|
|
172
|
+
|
|
173
|
+
# Register services
|
|
174
|
+
registry.register_service("api-gateway", {
|
|
175
|
+
"url": "http://api-gateway.internal",
|
|
176
|
+
"port": 8080
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
registry.register_service("auth-service", {
|
|
180
|
+
"url": "http://auth.internal",
|
|
181
|
+
"port": 8081
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
# Discover service
|
|
185
|
+
api_config = registry.discover_service("api-gateway")
|
|
186
|
+
print(f"API Gateway URL: {api_config['value']['url']}")
|
|
187
|
+
|
|
188
|
+
# List all services
|
|
189
|
+
services = registry.list_services()
|
|
190
|
+
print(f"Total services: {len(services)}")
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Feature Flags System
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
from opensecureconf_client_enhanced import OpenSecureConfClient
|
|
197
|
+
from typing import Any, Dict
|
|
198
|
+
from datetime import datetime
|
|
199
|
+
|
|
200
|
+
class FeatureFlags:
|
|
201
|
+
"""Manage feature flags across environments."""
|
|
202
|
+
|
|
203
|
+
def __init__(self, client: OpenSecureConfClient, environment: str):
|
|
204
|
+
self.client = client
|
|
205
|
+
self.environment = environment
|
|
206
|
+
self.category = f"features.{environment}"
|
|
207
|
+
|
|
208
|
+
def create_flag(self, name: str, enabled: bool, metadata: Dict = None):
|
|
209
|
+
"""Create a new feature flag."""
|
|
210
|
+
return self.client.create(
|
|
211
|
+
key=f"feature.{name}",
|
|
212
|
+
value={
|
|
213
|
+
"enabled": enabled,
|
|
214
|
+
"environment": self.environment,
|
|
215
|
+
"created_at": datetime.utcnow().isoformat(),
|
|
216
|
+
"metadata": metadata or {}
|
|
217
|
+
},
|
|
218
|
+
category=self.category
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
def is_enabled(self, name: str, default: bool = False) -> bool:
|
|
222
|
+
"""Check if feature is enabled."""
|
|
223
|
+
try:
|
|
224
|
+
flag = self.client.read(f"feature.{name}")
|
|
225
|
+
return flag['value']['enabled']
|
|
226
|
+
except:
|
|
227
|
+
return default
|
|
228
|
+
|
|
229
|
+
def enable_flag(self, name: str):
|
|
230
|
+
"""Enable a feature flag."""
|
|
231
|
+
flag = self.client.read(f"feature.{name}")
|
|
232
|
+
flag['value']['enabled'] = True
|
|
233
|
+
return self.client.update(f"feature.{name}", flag['value'])
|
|
234
|
+
|
|
235
|
+
def disable_flag(self, name: str):
|
|
236
|
+
"""Disable a feature flag."""
|
|
237
|
+
flag = self.client.read(f"feature.{name}")
|
|
238
|
+
flag['value']['enabled'] = False
|
|
239
|
+
return self.client.update(f"feature.{name}", flag['value'])
|
|
240
|
+
|
|
241
|
+
def list_flags(self) -> Dict[str, bool]:
|
|
242
|
+
"""List all feature flags."""
|
|
243
|
+
flags = self.client.list_all(category=self.category)
|
|
244
|
+
return {
|
|
245
|
+
flag['key'].replace('feature.', ''): flag['value']['enabled']
|
|
246
|
+
for flag in flags
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
# Usage
|
|
250
|
+
with OpenSecureConfClient(base_url="...", user_key="...") as client:
|
|
251
|
+
features = FeatureFlags(client, environment="production")
|
|
252
|
+
|
|
253
|
+
# Create flags
|
|
254
|
+
features.create_flag("new_ui", enabled=False, metadata={
|
|
255
|
+
"description": "New user interface",
|
|
256
|
+
"owner": "frontend-team"
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
features.create_flag("beta_features", enabled=True, metadata={
|
|
260
|
+
"description": "Beta features access",
|
|
261
|
+
"rollout_percentage": 10
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
# Check flags
|
|
265
|
+
if features.is_enabled("new_ui"):
|
|
266
|
+
# Show new UI
|
|
267
|
+
pass
|
|
268
|
+
|
|
269
|
+
# List all flags
|
|
270
|
+
all_flags = features.list_flags()
|
|
271
|
+
print("Active flags:", [k for k, v in all_flags.items() if v])
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## High Availability Patterns
|
|
275
|
+
|
|
276
|
+
### Circuit Breaker Pattern
|
|
277
|
+
|
|
278
|
+
```python
|
|
279
|
+
from opensecureconf_client_enhanced import OpenSecureConfClient
|
|
280
|
+
from datetime import datetime, timedelta
|
|
281
|
+
import time
|
|
282
|
+
|
|
283
|
+
class CircuitBreaker:
|
|
284
|
+
"""Circuit breaker for OpenSecureConf client."""
|
|
285
|
+
|
|
286
|
+
CLOSED = "CLOSED"
|
|
287
|
+
OPEN = "OPEN"
|
|
288
|
+
HALF_OPEN = "HALF_OPEN"
|
|
289
|
+
|
|
290
|
+
def __init__(self, client: OpenSecureConfClient,
|
|
291
|
+
failure_threshold: int = 5,
|
|
292
|
+
timeout: int = 60):
|
|
293
|
+
self.client = client
|
|
294
|
+
self.failure_threshold = failure_threshold
|
|
295
|
+
self.timeout = timeout
|
|
296
|
+
self.failures = 0
|
|
297
|
+
self.last_failure_time = None
|
|
298
|
+
self.state = self.CLOSED
|
|
299
|
+
|
|
300
|
+
def call(self, method: str, *args, **kwargs):
|
|
301
|
+
"""Execute method with circuit breaker protection."""
|
|
302
|
+
if self.state == self.OPEN:
|
|
303
|
+
if self._should_attempt_reset():
|
|
304
|
+
self.state = self.HALF_OPEN
|
|
305
|
+
else:
|
|
306
|
+
raise Exception("Circuit breaker is OPEN")
|
|
307
|
+
|
|
308
|
+
try:
|
|
309
|
+
result = getattr(self.client, method)(*args, **kwargs)
|
|
310
|
+
self._on_success()
|
|
311
|
+
return result
|
|
312
|
+
except Exception as e:
|
|
313
|
+
self._on_failure()
|
|
314
|
+
raise e
|
|
315
|
+
|
|
316
|
+
def _should_attempt_reset(self) -> bool:
|
|
317
|
+
"""Check if circuit breaker should attempt to reset."""
|
|
318
|
+
return (self.last_failure_time and
|
|
319
|
+
datetime.now() - self.last_failure_time > timedelta(seconds=self.timeout))
|
|
320
|
+
|
|
321
|
+
def _on_success(self):
|
|
322
|
+
"""Handle successful request."""
|
|
323
|
+
self.failures = 0
|
|
324
|
+
self.state = self.CLOSED
|
|
325
|
+
|
|
326
|
+
def _on_failure(self):
|
|
327
|
+
"""Handle failed request."""
|
|
328
|
+
self.failures += 1
|
|
329
|
+
self.last_failure_time = datetime.now()
|
|
330
|
+
|
|
331
|
+
if self.failures >= self.failure_threshold:
|
|
332
|
+
self.state = self.OPEN
|
|
333
|
+
|
|
334
|
+
# Usage
|
|
335
|
+
client = OpenSecureConfClient(base_url="...", user_key="...")
|
|
336
|
+
breaker = CircuitBreaker(client, failure_threshold=3, timeout=30)
|
|
337
|
+
|
|
338
|
+
try:
|
|
339
|
+
config = breaker.call("read", "database")
|
|
340
|
+
except Exception as e:
|
|
341
|
+
print(f"Circuit breaker: {e}")
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### Failover Strategy
|
|
345
|
+
|
|
346
|
+
```python
|
|
347
|
+
from opensecureconf_client_enhanced import OpenSecureConfClient
|
|
348
|
+
from typing import List
|
|
349
|
+
|
|
350
|
+
class FailoverClient:
|
|
351
|
+
"""Client with automatic failover to backup nodes."""
|
|
352
|
+
|
|
353
|
+
def __init__(self, nodes: List[str], user_key: str, api_key: str = None):
|
|
354
|
+
self.nodes = nodes
|
|
355
|
+
self.user_key = user_key
|
|
356
|
+
self.api_key = api_key
|
|
357
|
+
self.current_node = 0
|
|
358
|
+
self.clients = self._create_clients()
|
|
359
|
+
|
|
360
|
+
def _create_clients(self) -> List[OpenSecureConfClient]:
|
|
361
|
+
"""Create client for each node."""
|
|
362
|
+
return [
|
|
363
|
+
OpenSecureConfClient(
|
|
364
|
+
base_url=node,
|
|
365
|
+
user_key=self.user_key,
|
|
366
|
+
api_key=self.api_key,
|
|
367
|
+
enable_retry=True,
|
|
368
|
+
timeout=10
|
|
369
|
+
)
|
|
370
|
+
for node in self.nodes
|
|
371
|
+
]
|
|
372
|
+
|
|
373
|
+
def _execute_with_failover(self, method: str, *args, **kwargs):
|
|
374
|
+
"""Execute method with automatic failover."""
|
|
375
|
+
attempts = len(self.clients)
|
|
376
|
+
last_exception = None
|
|
377
|
+
|
|
378
|
+
for i in range(attempts):
|
|
379
|
+
client = self.clients[(self.current_node + i) % len(self.clients)]
|
|
380
|
+
try:
|
|
381
|
+
result = getattr(client, method)(*args, **kwargs)
|
|
382
|
+
self.current_node = (self.current_node + i) % len(self.clients)
|
|
383
|
+
return result
|
|
384
|
+
except Exception as e:
|
|
385
|
+
last_exception = e
|
|
386
|
+
continue
|
|
387
|
+
|
|
388
|
+
raise Exception(f"All nodes failed: {last_exception}")
|
|
389
|
+
|
|
390
|
+
def create(self, *args, **kwargs):
|
|
391
|
+
return self._execute_with_failover("create", *args, **kwargs)
|
|
392
|
+
|
|
393
|
+
def read(self, *args, **kwargs):
|
|
394
|
+
return self._execute_with_failover("read", *args, **kwargs)
|
|
395
|
+
|
|
396
|
+
def update(self, *args, **kwargs):
|
|
397
|
+
return self._execute_with_failover("update", *args, **kwargs)
|
|
398
|
+
|
|
399
|
+
def delete(self, *args, **kwargs):
|
|
400
|
+
return self._execute_with_failover("delete", *args, **kwargs)
|
|
401
|
+
|
|
402
|
+
# Usage
|
|
403
|
+
failover_client = FailoverClient(
|
|
404
|
+
nodes=[
|
|
405
|
+
"http://node1.example.com:9000",
|
|
406
|
+
"http://node2.example.com:9000",
|
|
407
|
+
"http://node3.example.com:9000"
|
|
408
|
+
],
|
|
409
|
+
user_key="my-key",
|
|
410
|
+
api_key="api-key"
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
# Automatically fails over to healthy nodes
|
|
414
|
+
config = failover_client.read("database")
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
## Performance Optimization
|
|
418
|
+
|
|
419
|
+
### Batch Processing with Progress
|
|
420
|
+
|
|
421
|
+
```python
|
|
422
|
+
from opensecureconf_client_enhanced import OpenSecureConfClient
|
|
423
|
+
from typing import List, Dict
|
|
424
|
+
from tqdm import tqdm # pip install tqdm
|
|
425
|
+
|
|
426
|
+
def bulk_migrate_configs(
|
|
427
|
+
client: OpenSecureConfClient,
|
|
428
|
+
configs: List[Dict],
|
|
429
|
+
batch_size: int = 100
|
|
430
|
+
):
|
|
431
|
+
"""Migrate large number of configurations with progress bar."""
|
|
432
|
+
total = len(configs)
|
|
433
|
+
|
|
434
|
+
with tqdm(total=total, desc="Migrating configs") as pbar:
|
|
435
|
+
for i in range(0, total, batch_size):
|
|
436
|
+
batch = configs[i:i + batch_size]
|
|
437
|
+
try:
|
|
438
|
+
client.bulk_create(batch, ignore_errors=True)
|
|
439
|
+
pbar.update(len(batch))
|
|
440
|
+
except Exception as e:
|
|
441
|
+
print(f"Batch {i//batch_size} failed: {e}")
|
|
442
|
+
|
|
443
|
+
# Usage
|
|
444
|
+
configs = [
|
|
445
|
+
{"key": f"config_{i}", "value": {"index": i}, "category": "bulk"}
|
|
446
|
+
for i in range(1000)
|
|
447
|
+
]
|
|
448
|
+
|
|
449
|
+
with OpenSecureConfClient(base_url="...", user_key="...") as client:
|
|
450
|
+
bulk_migrate_configs(client, configs, batch_size=50)
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### Concurrent Operations
|
|
454
|
+
|
|
455
|
+
```python
|
|
456
|
+
from opensecureconf_client_enhanced import OpenSecureConfClient
|
|
457
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
458
|
+
from typing import List
|
|
459
|
+
|
|
460
|
+
def parallel_read_configs(
|
|
461
|
+
client: OpenSecureConfClient,
|
|
462
|
+
keys: List[str],
|
|
463
|
+
max_workers: int = 10
|
|
464
|
+
) -> Dict[str, Dict]:
|
|
465
|
+
"""Read multiple configurations in parallel."""
|
|
466
|
+
results = {}
|
|
467
|
+
|
|
468
|
+
def read_config(key: str):
|
|
469
|
+
try:
|
|
470
|
+
return key, client.read(key)
|
|
471
|
+
except Exception as e:
|
|
472
|
+
return key, {"error": str(e)}
|
|
473
|
+
|
|
474
|
+
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
|
475
|
+
futures = {executor.submit(read_config, key): key for key in keys}
|
|
476
|
+
|
|
477
|
+
for future in as_completed(futures):
|
|
478
|
+
key, result = future.result()
|
|
479
|
+
results[key] = result
|
|
480
|
+
|
|
481
|
+
return results
|
|
482
|
+
|
|
483
|
+
# Usage
|
|
484
|
+
keys = [f"service_{i}" for i in range(100)]
|
|
485
|
+
|
|
486
|
+
with OpenSecureConfClient(
|
|
487
|
+
base_url="...",
|
|
488
|
+
user_key="...",
|
|
489
|
+
pool_connections=20,
|
|
490
|
+
pool_maxsize=50
|
|
491
|
+
) as client:
|
|
492
|
+
configs = parallel_read_configs(client, keys, max_workers=20)
|
|
493
|
+
print(f"Retrieved {len(configs)} configurations")
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
## Integration Examples
|
|
497
|
+
|
|
498
|
+
### Flask Integration
|
|
499
|
+
|
|
500
|
+
```python
|
|
501
|
+
from flask import Flask, g
|
|
502
|
+
from opensecureconf_client_enhanced import OpenSecureConfClient
|
|
503
|
+
import os
|
|
504
|
+
|
|
505
|
+
app = Flask(__name__)
|
|
506
|
+
|
|
507
|
+
def get_config_client():
|
|
508
|
+
"""Get or create OpenSecureConf client for current request."""
|
|
509
|
+
if 'osc_client' not in g:
|
|
510
|
+
g.osc_client = OpenSecureConfClient(
|
|
511
|
+
base_url=os.getenv("OSC_URL"),
|
|
512
|
+
user_key=os.getenv("OSC_USER_KEY"),
|
|
513
|
+
api_key=os.getenv("OSC_API_KEY"),
|
|
514
|
+
enable_retry=True
|
|
515
|
+
)
|
|
516
|
+
return g.osc_client
|
|
517
|
+
|
|
518
|
+
@app.teardown_appcontext
|
|
519
|
+
def close_config_client(error):
|
|
520
|
+
"""Close client at end of request."""
|
|
521
|
+
client = g.pop('osc_client', None)
|
|
522
|
+
if client is not None:
|
|
523
|
+
client.close()
|
|
524
|
+
|
|
525
|
+
@app.route('/api/config/<key>')
|
|
526
|
+
def get_config(key):
|
|
527
|
+
"""Get configuration from OpenSecureConf."""
|
|
528
|
+
client = get_config_client()
|
|
529
|
+
try:
|
|
530
|
+
config = client.read(key)
|
|
531
|
+
return config['value']
|
|
532
|
+
except Exception as e:
|
|
533
|
+
return {"error": str(e)}, 404
|
|
534
|
+
|
|
535
|
+
if __name__ == '__main__':
|
|
536
|
+
app.run()
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
### Django Integration
|
|
540
|
+
|
|
541
|
+
```python
|
|
542
|
+
# settings.py
|
|
543
|
+
from opensecureconf_client_enhanced import OpenSecureConfClient
|
|
544
|
+
import os
|
|
545
|
+
|
|
546
|
+
# Initialize client
|
|
547
|
+
OSC_CLIENT = OpenSecureConfClient(
|
|
548
|
+
base_url=os.getenv("OSC_URL"),
|
|
549
|
+
user_key=os.getenv("OSC_USER_KEY"),
|
|
550
|
+
api_key=os.getenv("OSC_API_KEY"),
|
|
551
|
+
enable_retry=True,
|
|
552
|
+
log_level="INFO"
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
# Load database config from OpenSecureConf
|
|
556
|
+
db_config = OSC_CLIENT.read("database")
|
|
557
|
+
|
|
558
|
+
DATABASES = {
|
|
559
|
+
'default': {
|
|
560
|
+
'ENGINE': 'django.db.backends.postgresql',
|
|
561
|
+
'NAME': db_config['value']['name'],
|
|
562
|
+
'USER': db_config['value']['user'],
|
|
563
|
+
'PASSWORD': db_config['value']['password'],
|
|
564
|
+
'HOST': db_config['value']['host'],
|
|
565
|
+
'PORT': db_config['value']['port'],
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
# Load cache config
|
|
570
|
+
cache_config = OSC_CLIENT.read("cache")
|
|
571
|
+
|
|
572
|
+
CACHES = {
|
|
573
|
+
'default': {
|
|
574
|
+
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
|
|
575
|
+
'LOCATION': f"redis://{cache_config['value']['host']}:{cache_config['value']['port']}",
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
### FastAPI Integration
|
|
581
|
+
|
|
582
|
+
```python
|
|
583
|
+
from fastapi import FastAPI, Depends, HTTPException
|
|
584
|
+
from opensecureconf_client_enhanced import OpenSecureConfClient
|
|
585
|
+
from typing import Dict
|
|
586
|
+
import os
|
|
587
|
+
|
|
588
|
+
app = FastAPI()
|
|
589
|
+
|
|
590
|
+
# Global client
|
|
591
|
+
osc_client = OpenSecureConfClient(
|
|
592
|
+
base_url=os.getenv("OSC_URL"),
|
|
593
|
+
user_key=os.getenv("OSC_USER_KEY"),
|
|
594
|
+
api_key=os.getenv("OSC_API_KEY"),
|
|
595
|
+
enable_retry=True
|
|
596
|
+
)
|
|
597
|
+
|
|
598
|
+
def get_client() -> OpenSecureConfClient:
|
|
599
|
+
"""Dependency to inject OpenSecureConf client."""
|
|
600
|
+
return osc_client
|
|
601
|
+
|
|
602
|
+
@app.get("/config/{key}")
|
|
603
|
+
async def get_config(
|
|
604
|
+
key: str,
|
|
605
|
+
client: OpenSecureConfClient = Depends(get_client)
|
|
606
|
+
) -> Dict:
|
|
607
|
+
"""Get configuration from OpenSecureConf."""
|
|
608
|
+
try:
|
|
609
|
+
config = client.read(key)
|
|
610
|
+
return config['value']
|
|
611
|
+
except Exception as e:
|
|
612
|
+
raise HTTPException(status_code=404, detail=str(e))
|
|
613
|
+
|
|
614
|
+
@app.on_event("shutdown")
|
|
615
|
+
async def shutdown_event():
|
|
616
|
+
"""Close client on shutdown."""
|
|
617
|
+
osc_client.close()
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
## Security Best Practices
|
|
621
|
+
|
|
622
|
+
### Credentials from AWS Secrets Manager
|
|
623
|
+
|
|
624
|
+
```python
|
|
625
|
+
import boto3
|
|
626
|
+
from opensecureconf_client_enhanced import OpenSecureConfClient
|
|
627
|
+
import json
|
|
628
|
+
|
|
629
|
+
def get_credentials_from_aws():
|
|
630
|
+
"""Get credentials from AWS Secrets Manager."""
|
|
631
|
+
client = boto3.client('secretsmanager', region_name='us-east-1')
|
|
632
|
+
secret = client.get_secret_value(SecretId='opensecureconf/credentials')
|
|
633
|
+
return json.loads(secret['SecretString'])
|
|
634
|
+
|
|
635
|
+
# Initialize client with AWS credentials
|
|
636
|
+
creds = get_credentials_from_aws()
|
|
637
|
+
osc_client = OpenSecureConfClient(
|
|
638
|
+
base_url=creds['url'],
|
|
639
|
+
user_key=creds['user_key'],
|
|
640
|
+
api_key=creds['api_key'],
|
|
641
|
+
enable_retry=True
|
|
642
|
+
)
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
### Credentials from HashiCorp Vault
|
|
646
|
+
|
|
647
|
+
```python
|
|
648
|
+
import hvac
|
|
649
|
+
from opensecureconf_client_enhanced import OpenSecureConfClient
|
|
650
|
+
|
|
651
|
+
def get_credentials_from_vault():
|
|
652
|
+
"""Get credentials from HashiCorp Vault."""
|
|
653
|
+
client = hvac.Client(url='https://vault.example.com')
|
|
654
|
+
client.auth.approle.login(
|
|
655
|
+
role_id='your-role-id',
|
|
656
|
+
secret_id='your-secret-id'
|
|
657
|
+
)
|
|
658
|
+
|
|
659
|
+
secret = client.secrets.kv.v2.read_secret_version(
|
|
660
|
+
path='opensecureconf/credentials'
|
|
661
|
+
)
|
|
662
|
+
return secret['data']['data']
|
|
663
|
+
|
|
664
|
+
# Initialize client with Vault credentials
|
|
665
|
+
creds = get_credentials_from_vault()
|
|
666
|
+
osc_client = OpenSecureConfClient(
|
|
667
|
+
base_url=creds['url'],
|
|
668
|
+
user_key=creds['user_key'],
|
|
669
|
+
api_key=creds['api_key'],
|
|
670
|
+
enable_retry=True
|
|
671
|
+
)
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
### Audit Logging Wrapper
|
|
675
|
+
|
|
676
|
+
```python
|
|
677
|
+
from opensecureconf_client_enhanced import OpenSecureConfClient
|
|
678
|
+
import logging
|
|
679
|
+
from functools import wraps
|
|
680
|
+
from datetime import datetime
|
|
681
|
+
|
|
682
|
+
class AuditedClient:
|
|
683
|
+
"""Wrapper that logs all operations for auditing."""
|
|
684
|
+
|
|
685
|
+
def __init__(self, client: OpenSecureConfClient, audit_logger: logging.Logger):
|
|
686
|
+
self.client = client
|
|
687
|
+
self.audit_logger = audit_logger
|
|
688
|
+
|
|
689
|
+
def _audit_log(self, operation: str, key: str, success: bool, error: str = None):
|
|
690
|
+
"""Log operation for audit trail."""
|
|
691
|
+
self.audit_logger.info({
|
|
692
|
+
"timestamp": datetime.utcnow().isoformat(),
|
|
693
|
+
"operation": operation,
|
|
694
|
+
"key": key,
|
|
695
|
+
"success": success,
|
|
696
|
+
"error": error
|
|
697
|
+
})
|
|
698
|
+
|
|
699
|
+
def create(self, key: str, value: dict, category: str = None):
|
|
700
|
+
"""Create with audit logging."""
|
|
701
|
+
try:
|
|
702
|
+
result = self.client.create(key, value, category)
|
|
703
|
+
self._audit_log("CREATE", key, True)
|
|
704
|
+
return result
|
|
705
|
+
except Exception as e:
|
|
706
|
+
self._audit_log("CREATE", key, False, str(e))
|
|
707
|
+
raise
|
|
708
|
+
|
|
709
|
+
def read(self, key: str):
|
|
710
|
+
"""Read with audit logging."""
|
|
711
|
+
try:
|
|
712
|
+
result = self.client.read(key)
|
|
713
|
+
self._audit_log("READ", key, True)
|
|
714
|
+
return result
|
|
715
|
+
except Exception as e:
|
|
716
|
+
self._audit_log("READ", key, False, str(e))
|
|
717
|
+
raise
|
|
718
|
+
|
|
719
|
+
def update(self, key: str, value: dict, category: str = None):
|
|
720
|
+
"""Update with audit logging."""
|
|
721
|
+
try:
|
|
722
|
+
result = self.client.update(key, value, category)
|
|
723
|
+
self._audit_log("UPDATE", key, True)
|
|
724
|
+
return result
|
|
725
|
+
except Exception as e:
|
|
726
|
+
self._audit_log("UPDATE", key, False, str(e))
|
|
727
|
+
raise
|
|
728
|
+
|
|
729
|
+
def delete(self, key: str):
|
|
730
|
+
"""Delete with audit logging."""
|
|
731
|
+
try:
|
|
732
|
+
result = self.client.delete(key)
|
|
733
|
+
self._audit_log("DELETE", key, True)
|
|
734
|
+
return result
|
|
735
|
+
except Exception as e:
|
|
736
|
+
self._audit_log("DELETE", key, False, str(e))
|
|
737
|
+
raise
|
|
738
|
+
|
|
739
|
+
# Usage
|
|
740
|
+
audit_logger = logging.getLogger('opensecureconf.audit')
|
|
741
|
+
audit_logger.setLevel(logging.INFO)
|
|
742
|
+
handler = logging.FileHandler('/var/log/osc-audit.log')
|
|
743
|
+
audit_logger.addHandler(handler)
|
|
744
|
+
|
|
745
|
+
client = OpenSecureConfClient(base_url="...", user_key="...")
|
|
746
|
+
audited_client = AuditedClient(client, audit_logger)
|
|
747
|
+
|
|
748
|
+
# All operations are now audited
|
|
749
|
+
audited_client.create("secret", {"password": "..."})
|
|
750
|
+
```
|
|
751
|
+
|
|
752
|
+
---
|
|
753
|
+
|
|
754
|
+
These advanced examples demonstrate production-ready patterns for using the OpenSecureConf Python Client in real-world scenarios. Adapt them to your specific needs!
|