raqeb 1.0.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.
- raqeb-1.0.0/PKG-INFO +274 -0
- raqeb-1.0.0/README.md +238 -0
- raqeb-1.0.0/raqeb/__init__.py +25 -0
- raqeb-1.0.0/raqeb/client.py +193 -0
- raqeb-1.0.0/raqeb/exceptions.py +21 -0
- raqeb-1.0.0/raqeb.egg-info/PKG-INFO +274 -0
- raqeb-1.0.0/raqeb.egg-info/SOURCES.txt +10 -0
- raqeb-1.0.0/raqeb.egg-info/dependency_links.txt +1 -0
- raqeb-1.0.0/raqeb.egg-info/requires.txt +7 -0
- raqeb-1.0.0/raqeb.egg-info/top_level.txt +1 -0
- raqeb-1.0.0/setup.cfg +4 -0
- raqeb-1.0.0/setup.py +42 -0
raqeb-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: raqeb
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Python SDK for Raqeb Database PAM and Secrets Management
|
|
5
|
+
Home-page: https://github.com/raqeb/python-sdk
|
|
6
|
+
Author: Raqeb
|
|
7
|
+
Author-email: support@raqeb.cloud
|
|
8
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Requires-Python: >=3.7
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
Requires-Dist: requests>=2.25.0
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
23
|
+
Requires-Dist: pytest-cov>=2.0; extra == "dev"
|
|
24
|
+
Requires-Dist: black>=21.0; extra == "dev"
|
|
25
|
+
Requires-Dist: flake8>=3.9; extra == "dev"
|
|
26
|
+
Dynamic: author
|
|
27
|
+
Dynamic: author-email
|
|
28
|
+
Dynamic: classifier
|
|
29
|
+
Dynamic: description
|
|
30
|
+
Dynamic: description-content-type
|
|
31
|
+
Dynamic: home-page
|
|
32
|
+
Dynamic: provides-extra
|
|
33
|
+
Dynamic: requires-dist
|
|
34
|
+
Dynamic: requires-python
|
|
35
|
+
Dynamic: summary
|
|
36
|
+
|
|
37
|
+
# Raqeb Python SDK
|
|
38
|
+
|
|
39
|
+
Official Python SDK for Raqeb Database PAM and Secrets Management.
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install raqeb
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Quick Start
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from raqeb import RaqebClient
|
|
51
|
+
|
|
52
|
+
# Initialize client with service account API key
|
|
53
|
+
client = RaqebClient(api_key="sa_your_api_key_here")
|
|
54
|
+
|
|
55
|
+
# Get a secret
|
|
56
|
+
secret = client.get_secret("secret-id")
|
|
57
|
+
print(f"Secret value: {secret['value']}")
|
|
58
|
+
|
|
59
|
+
# Get temporary database credentials
|
|
60
|
+
creds = client.get_database_credentials(
|
|
61
|
+
database_id="db-id",
|
|
62
|
+
ttl_hours=4,
|
|
63
|
+
access_level="read-only"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
print(f"Username: {creds['username']}")
|
|
67
|
+
print(f"Password: {creds['password']}")
|
|
68
|
+
print(f"Expires: {creds['expires_at']}")
|
|
69
|
+
|
|
70
|
+
# Revoke credentials when done
|
|
71
|
+
client.revoke_lease(creds['lease_id'])
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Usage Examples
|
|
75
|
+
|
|
76
|
+
### Secrets Management
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from raqeb import RaqebClient
|
|
80
|
+
|
|
81
|
+
client = RaqebClient(api_key="sa_your_key")
|
|
82
|
+
|
|
83
|
+
# Retrieve a secret
|
|
84
|
+
secret = client.get_secret("api-key-prod")
|
|
85
|
+
api_key = secret['value']
|
|
86
|
+
|
|
87
|
+
# Use the secret in your application
|
|
88
|
+
import requests
|
|
89
|
+
response = requests.get(
|
|
90
|
+
"https://api.example.com/data",
|
|
91
|
+
headers={"Authorization": f"Bearer {api_key}"}
|
|
92
|
+
)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Database Access
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from raqeb import RaqebClient
|
|
99
|
+
import psycopg2
|
|
100
|
+
|
|
101
|
+
client = RaqebClient(api_key="sa_your_key")
|
|
102
|
+
|
|
103
|
+
# Get temporary database credentials
|
|
104
|
+
creds = client.get_database_credentials(
|
|
105
|
+
database_id="prod-postgres",
|
|
106
|
+
ttl_hours=2,
|
|
107
|
+
access_level="read-only"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Connect to database
|
|
111
|
+
conn = psycopg2.connect(
|
|
112
|
+
host="db.example.com",
|
|
113
|
+
port=5432,
|
|
114
|
+
database="myapp",
|
|
115
|
+
user=creds['username'],
|
|
116
|
+
password=creds['password']
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
# Use the connection
|
|
121
|
+
cursor = conn.cursor()
|
|
122
|
+
cursor.execute("SELECT * FROM users LIMIT 10")
|
|
123
|
+
results = cursor.fetchall()
|
|
124
|
+
|
|
125
|
+
finally:
|
|
126
|
+
conn.close()
|
|
127
|
+
# Revoke credentials
|
|
128
|
+
client.revoke_lease(creds['lease_id'])
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Context Manager
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from raqeb import RaqebClient
|
|
135
|
+
|
|
136
|
+
# Use context manager for automatic cleanup
|
|
137
|
+
with RaqebClient(api_key="sa_your_key") as client:
|
|
138
|
+
secret = client.get_secret("secret-id")
|
|
139
|
+
print(secret['value'])
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### API Key Management
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from raqeb import RaqebClient
|
|
146
|
+
|
|
147
|
+
client = RaqebClient(api_key="sa_your_key")
|
|
148
|
+
|
|
149
|
+
# List API keys
|
|
150
|
+
keys = client.list_api_keys()
|
|
151
|
+
for key in keys:
|
|
152
|
+
print(f"{key['name']}: {key['key_prefix']}...")
|
|
153
|
+
|
|
154
|
+
# Create new API key
|
|
155
|
+
new_key = client.create_api_key(
|
|
156
|
+
name="CI/CD Pipeline",
|
|
157
|
+
scopes=["secrets:read", "databases:read"],
|
|
158
|
+
description="Key for automated deployments"
|
|
159
|
+
)
|
|
160
|
+
print(f"New API Key: {new_key['api_key']}") # Save this!
|
|
161
|
+
|
|
162
|
+
# Delete API key
|
|
163
|
+
client.delete_api_key("key-id")
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Error Handling
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
from raqeb import RaqebClient, AuthenticationError, PermissionError, NotFoundError
|
|
170
|
+
|
|
171
|
+
client = RaqebClient(api_key="sa_your_key")
|
|
172
|
+
|
|
173
|
+
try:
|
|
174
|
+
secret = client.get_secret("secret-id")
|
|
175
|
+
|
|
176
|
+
except AuthenticationError:
|
|
177
|
+
print("Invalid or expired API key")
|
|
178
|
+
|
|
179
|
+
except PermissionError:
|
|
180
|
+
print("Insufficient permissions - check API key scopes")
|
|
181
|
+
|
|
182
|
+
except NotFoundError:
|
|
183
|
+
print("Secret not found")
|
|
184
|
+
|
|
185
|
+
except RaqebError as e:
|
|
186
|
+
print(f"API error: {e}")
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## API Reference
|
|
190
|
+
|
|
191
|
+
### RaqebClient
|
|
192
|
+
|
|
193
|
+
#### `__init__(api_key, base_url="https://app.raqeb.cloud/api/v1")`
|
|
194
|
+
Initialize the client.
|
|
195
|
+
|
|
196
|
+
#### `get_secret(secret_id) -> dict`
|
|
197
|
+
Retrieve a secret value.
|
|
198
|
+
|
|
199
|
+
**Returns:**
|
|
200
|
+
```python
|
|
201
|
+
{
|
|
202
|
+
'secret_id': 'secret-123',
|
|
203
|
+
'name': 'API Key',
|
|
204
|
+
'value': 'secret-value',
|
|
205
|
+
'retrieved_at': '2026-02-14T16:09:00Z'
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
#### `get_database_credentials(database_id, ttl_hours=4, access_level='read-only') -> dict`
|
|
210
|
+
Generate temporary database credentials.
|
|
211
|
+
|
|
212
|
+
**Parameters:**
|
|
213
|
+
- `database_id` (str): Database ID
|
|
214
|
+
- `ttl_hours` (int): Time to live in hours (default: 4)
|
|
215
|
+
- `access_level` (str): 'read-only', 'read-write', or 'admin'
|
|
216
|
+
|
|
217
|
+
**Returns:**
|
|
218
|
+
```python
|
|
219
|
+
{
|
|
220
|
+
'lease_id': 'lease-123',
|
|
221
|
+
'username': 'temp_user_abc',
|
|
222
|
+
'password': 'temp_pass_xyz',
|
|
223
|
+
'database_id': 'db-123',
|
|
224
|
+
'access_level': 'read-only',
|
|
225
|
+
'issued_at': '2026-02-14T16:09:00Z',
|
|
226
|
+
'expires_at': '2026-02-14T20:09:00Z',
|
|
227
|
+
'ttl_seconds': 14400
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
#### `revoke_lease(lease_id) -> None`
|
|
232
|
+
Revoke a dynamic secret lease.
|
|
233
|
+
|
|
234
|
+
#### `list_api_keys() -> list`
|
|
235
|
+
List user's API keys.
|
|
236
|
+
|
|
237
|
+
#### `create_api_key(name, description=None, scopes=None, expires_at=None) -> dict`
|
|
238
|
+
Create a new API key.
|
|
239
|
+
|
|
240
|
+
#### `delete_api_key(key_id) -> None`
|
|
241
|
+
Delete an API key.
|
|
242
|
+
|
|
243
|
+
## Environment Variables
|
|
244
|
+
|
|
245
|
+
You can use environment variables for configuration:
|
|
246
|
+
|
|
247
|
+
```python
|
|
248
|
+
import os
|
|
249
|
+
from raqeb import RaqebClient
|
|
250
|
+
|
|
251
|
+
client = RaqebClient(
|
|
252
|
+
api_key=os.getenv('RAQEB_API_KEY'),
|
|
253
|
+
base_url=os.getenv('RAQEB_BASE_URL', 'https://app.raqeb.cloud/api/v1')
|
|
254
|
+
)
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## Best Practices
|
|
258
|
+
|
|
259
|
+
1. **Never hardcode API keys** - Use environment variables
|
|
260
|
+
2. **Use minimal scopes** - Only grant necessary permissions
|
|
261
|
+
3. **Set appropriate TTLs** - Use shortest time needed
|
|
262
|
+
4. **Always revoke leases** - Clean up credentials when done
|
|
263
|
+
5. **Handle errors gracefully** - Catch and handle SDK exceptions
|
|
264
|
+
6. **Use context managers** - Automatic resource cleanup
|
|
265
|
+
|
|
266
|
+
## License
|
|
267
|
+
|
|
268
|
+
MIT License - see LICENSE file for details
|
|
269
|
+
|
|
270
|
+
## Support
|
|
271
|
+
|
|
272
|
+
- Documentation: https://docs.raqeb.cloud
|
|
273
|
+
- Email: support@raqeb.cloud
|
|
274
|
+
- GitHub: https://github.com/raqeb/python-sdk
|
raqeb-1.0.0/README.md
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# Raqeb Python SDK
|
|
2
|
+
|
|
3
|
+
Official Python SDK for Raqeb Database PAM and Secrets Management.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install raqeb
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from raqeb import RaqebClient
|
|
15
|
+
|
|
16
|
+
# Initialize client with service account API key
|
|
17
|
+
client = RaqebClient(api_key="sa_your_api_key_here")
|
|
18
|
+
|
|
19
|
+
# Get a secret
|
|
20
|
+
secret = client.get_secret("secret-id")
|
|
21
|
+
print(f"Secret value: {secret['value']}")
|
|
22
|
+
|
|
23
|
+
# Get temporary database credentials
|
|
24
|
+
creds = client.get_database_credentials(
|
|
25
|
+
database_id="db-id",
|
|
26
|
+
ttl_hours=4,
|
|
27
|
+
access_level="read-only"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
print(f"Username: {creds['username']}")
|
|
31
|
+
print(f"Password: {creds['password']}")
|
|
32
|
+
print(f"Expires: {creds['expires_at']}")
|
|
33
|
+
|
|
34
|
+
# Revoke credentials when done
|
|
35
|
+
client.revoke_lease(creds['lease_id'])
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Usage Examples
|
|
39
|
+
|
|
40
|
+
### Secrets Management
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from raqeb import RaqebClient
|
|
44
|
+
|
|
45
|
+
client = RaqebClient(api_key="sa_your_key")
|
|
46
|
+
|
|
47
|
+
# Retrieve a secret
|
|
48
|
+
secret = client.get_secret("api-key-prod")
|
|
49
|
+
api_key = secret['value']
|
|
50
|
+
|
|
51
|
+
# Use the secret in your application
|
|
52
|
+
import requests
|
|
53
|
+
response = requests.get(
|
|
54
|
+
"https://api.example.com/data",
|
|
55
|
+
headers={"Authorization": f"Bearer {api_key}"}
|
|
56
|
+
)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Database Access
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from raqeb import RaqebClient
|
|
63
|
+
import psycopg2
|
|
64
|
+
|
|
65
|
+
client = RaqebClient(api_key="sa_your_key")
|
|
66
|
+
|
|
67
|
+
# Get temporary database credentials
|
|
68
|
+
creds = client.get_database_credentials(
|
|
69
|
+
database_id="prod-postgres",
|
|
70
|
+
ttl_hours=2,
|
|
71
|
+
access_level="read-only"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Connect to database
|
|
75
|
+
conn = psycopg2.connect(
|
|
76
|
+
host="db.example.com",
|
|
77
|
+
port=5432,
|
|
78
|
+
database="myapp",
|
|
79
|
+
user=creds['username'],
|
|
80
|
+
password=creds['password']
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
# Use the connection
|
|
85
|
+
cursor = conn.cursor()
|
|
86
|
+
cursor.execute("SELECT * FROM users LIMIT 10")
|
|
87
|
+
results = cursor.fetchall()
|
|
88
|
+
|
|
89
|
+
finally:
|
|
90
|
+
conn.close()
|
|
91
|
+
# Revoke credentials
|
|
92
|
+
client.revoke_lease(creds['lease_id'])
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Context Manager
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from raqeb import RaqebClient
|
|
99
|
+
|
|
100
|
+
# Use context manager for automatic cleanup
|
|
101
|
+
with RaqebClient(api_key="sa_your_key") as client:
|
|
102
|
+
secret = client.get_secret("secret-id")
|
|
103
|
+
print(secret['value'])
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### API Key Management
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from raqeb import RaqebClient
|
|
110
|
+
|
|
111
|
+
client = RaqebClient(api_key="sa_your_key")
|
|
112
|
+
|
|
113
|
+
# List API keys
|
|
114
|
+
keys = client.list_api_keys()
|
|
115
|
+
for key in keys:
|
|
116
|
+
print(f"{key['name']}: {key['key_prefix']}...")
|
|
117
|
+
|
|
118
|
+
# Create new API key
|
|
119
|
+
new_key = client.create_api_key(
|
|
120
|
+
name="CI/CD Pipeline",
|
|
121
|
+
scopes=["secrets:read", "databases:read"],
|
|
122
|
+
description="Key for automated deployments"
|
|
123
|
+
)
|
|
124
|
+
print(f"New API Key: {new_key['api_key']}") # Save this!
|
|
125
|
+
|
|
126
|
+
# Delete API key
|
|
127
|
+
client.delete_api_key("key-id")
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Error Handling
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from raqeb import RaqebClient, AuthenticationError, PermissionError, NotFoundError
|
|
134
|
+
|
|
135
|
+
client = RaqebClient(api_key="sa_your_key")
|
|
136
|
+
|
|
137
|
+
try:
|
|
138
|
+
secret = client.get_secret("secret-id")
|
|
139
|
+
|
|
140
|
+
except AuthenticationError:
|
|
141
|
+
print("Invalid or expired API key")
|
|
142
|
+
|
|
143
|
+
except PermissionError:
|
|
144
|
+
print("Insufficient permissions - check API key scopes")
|
|
145
|
+
|
|
146
|
+
except NotFoundError:
|
|
147
|
+
print("Secret not found")
|
|
148
|
+
|
|
149
|
+
except RaqebError as e:
|
|
150
|
+
print(f"API error: {e}")
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## API Reference
|
|
154
|
+
|
|
155
|
+
### RaqebClient
|
|
156
|
+
|
|
157
|
+
#### `__init__(api_key, base_url="https://app.raqeb.cloud/api/v1")`
|
|
158
|
+
Initialize the client.
|
|
159
|
+
|
|
160
|
+
#### `get_secret(secret_id) -> dict`
|
|
161
|
+
Retrieve a secret value.
|
|
162
|
+
|
|
163
|
+
**Returns:**
|
|
164
|
+
```python
|
|
165
|
+
{
|
|
166
|
+
'secret_id': 'secret-123',
|
|
167
|
+
'name': 'API Key',
|
|
168
|
+
'value': 'secret-value',
|
|
169
|
+
'retrieved_at': '2026-02-14T16:09:00Z'
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
#### `get_database_credentials(database_id, ttl_hours=4, access_level='read-only') -> dict`
|
|
174
|
+
Generate temporary database credentials.
|
|
175
|
+
|
|
176
|
+
**Parameters:**
|
|
177
|
+
- `database_id` (str): Database ID
|
|
178
|
+
- `ttl_hours` (int): Time to live in hours (default: 4)
|
|
179
|
+
- `access_level` (str): 'read-only', 'read-write', or 'admin'
|
|
180
|
+
|
|
181
|
+
**Returns:**
|
|
182
|
+
```python
|
|
183
|
+
{
|
|
184
|
+
'lease_id': 'lease-123',
|
|
185
|
+
'username': 'temp_user_abc',
|
|
186
|
+
'password': 'temp_pass_xyz',
|
|
187
|
+
'database_id': 'db-123',
|
|
188
|
+
'access_level': 'read-only',
|
|
189
|
+
'issued_at': '2026-02-14T16:09:00Z',
|
|
190
|
+
'expires_at': '2026-02-14T20:09:00Z',
|
|
191
|
+
'ttl_seconds': 14400
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
#### `revoke_lease(lease_id) -> None`
|
|
196
|
+
Revoke a dynamic secret lease.
|
|
197
|
+
|
|
198
|
+
#### `list_api_keys() -> list`
|
|
199
|
+
List user's API keys.
|
|
200
|
+
|
|
201
|
+
#### `create_api_key(name, description=None, scopes=None, expires_at=None) -> dict`
|
|
202
|
+
Create a new API key.
|
|
203
|
+
|
|
204
|
+
#### `delete_api_key(key_id) -> None`
|
|
205
|
+
Delete an API key.
|
|
206
|
+
|
|
207
|
+
## Environment Variables
|
|
208
|
+
|
|
209
|
+
You can use environment variables for configuration:
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
import os
|
|
213
|
+
from raqeb import RaqebClient
|
|
214
|
+
|
|
215
|
+
client = RaqebClient(
|
|
216
|
+
api_key=os.getenv('RAQEB_API_KEY'),
|
|
217
|
+
base_url=os.getenv('RAQEB_BASE_URL', 'https://app.raqeb.cloud/api/v1')
|
|
218
|
+
)
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Best Practices
|
|
222
|
+
|
|
223
|
+
1. **Never hardcode API keys** - Use environment variables
|
|
224
|
+
2. **Use minimal scopes** - Only grant necessary permissions
|
|
225
|
+
3. **Set appropriate TTLs** - Use shortest time needed
|
|
226
|
+
4. **Always revoke leases** - Clean up credentials when done
|
|
227
|
+
5. **Handle errors gracefully** - Catch and handle SDK exceptions
|
|
228
|
+
6. **Use context managers** - Automatic resource cleanup
|
|
229
|
+
|
|
230
|
+
## License
|
|
231
|
+
|
|
232
|
+
MIT License - see LICENSE file for details
|
|
233
|
+
|
|
234
|
+
## Support
|
|
235
|
+
|
|
236
|
+
- Documentation: https://docs.raqeb.cloud
|
|
237
|
+
- Email: support@raqeb.cloud
|
|
238
|
+
- GitHub: https://github.com/raqeb/python-sdk
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Raqeb Python SDK - Database PAM and Secrets Management
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
from raqeb import RaqebClient
|
|
6
|
+
|
|
7
|
+
client = RaqebClient(api_key="sa_your_api_key")
|
|
8
|
+
|
|
9
|
+
# Get secret
|
|
10
|
+
secret = client.get_secret("secret-id")
|
|
11
|
+
|
|
12
|
+
# Get database credentials
|
|
13
|
+
creds = client.get_database_credentials("db-id", ttl_hours=4)
|
|
14
|
+
print(f"Username: {creds['username']}")
|
|
15
|
+
print(f"Password: {creds['password']}")
|
|
16
|
+
|
|
17
|
+
# Revoke when done
|
|
18
|
+
client.revoke_lease(creds['lease_id'])
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from .client import RaqebClient
|
|
22
|
+
from .exceptions import RaqebError, AuthenticationError, NotFoundError, PermissionError
|
|
23
|
+
|
|
24
|
+
__version__ = "1.0.0"
|
|
25
|
+
__all__ = ["RaqebClient", "RaqebError", "AuthenticationError", "NotFoundError", "PermissionError"]
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"""Raqeb Python SDK Client."""
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
from typing import Dict, List, Optional, Any
|
|
5
|
+
from .exceptions import RaqebError, AuthenticationError, NotFoundError, PermissionError
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class RaqebClient:
|
|
9
|
+
"""Client for interacting with Raqeb API."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, api_key: str, base_url: str = "https://app.raqeb.cloud/api/v1"):
|
|
12
|
+
"""
|
|
13
|
+
Initialize Raqeb client.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
api_key: Service account API key (starts with 'sa_')
|
|
17
|
+
base_url: Base URL for Raqeb API
|
|
18
|
+
"""
|
|
19
|
+
self.api_key = api_key
|
|
20
|
+
self.base_url = base_url.rstrip('/')
|
|
21
|
+
self.session = requests.Session()
|
|
22
|
+
self.session.headers.update({
|
|
23
|
+
'Authorization': f'Bearer {api_key}',
|
|
24
|
+
'Content-Type': 'application/json'
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
def _request(self, method: str, endpoint: str, **kwargs) -> Any:
|
|
28
|
+
"""Make HTTP request to API."""
|
|
29
|
+
url = f"{self.base_url}{endpoint}"
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
response = self.session.request(method, url, **kwargs)
|
|
33
|
+
response.raise_for_status()
|
|
34
|
+
return response.json() if response.content else None
|
|
35
|
+
|
|
36
|
+
except requests.exceptions.HTTPError as e:
|
|
37
|
+
status_code = e.response.status_code
|
|
38
|
+
detail = e.response.json().get('detail', str(e))
|
|
39
|
+
|
|
40
|
+
if status_code == 401:
|
|
41
|
+
raise AuthenticationError(detail)
|
|
42
|
+
elif status_code == 403:
|
|
43
|
+
raise PermissionError(detail)
|
|
44
|
+
elif status_code == 404:
|
|
45
|
+
raise NotFoundError(detail)
|
|
46
|
+
else:
|
|
47
|
+
raise RaqebError(f"HTTP {status_code}: {detail}")
|
|
48
|
+
|
|
49
|
+
except requests.exceptions.RequestException as e:
|
|
50
|
+
raise RaqebError(f"Request failed: {str(e)}")
|
|
51
|
+
|
|
52
|
+
# ============================================================================
|
|
53
|
+
# Secrets Management
|
|
54
|
+
# ============================================================================
|
|
55
|
+
|
|
56
|
+
def get_secret(self, secret_id: str) -> Dict[str, Any]:
|
|
57
|
+
"""
|
|
58
|
+
Retrieve a secret value.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
secret_id: ID of the secret to retrieve
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
Dict with keys: secret_id, name, value, retrieved_at
|
|
65
|
+
|
|
66
|
+
Example:
|
|
67
|
+
>>> secret = client.get_secret("secret-123")
|
|
68
|
+
>>> print(secret['value'])
|
|
69
|
+
"""
|
|
70
|
+
return self._request('POST', '/service-accounts/secrets/retrieve', params={'secret_id': secret_id})
|
|
71
|
+
|
|
72
|
+
# ============================================================================
|
|
73
|
+
# Database Credentials
|
|
74
|
+
# ============================================================================
|
|
75
|
+
|
|
76
|
+
def get_database_credentials(
|
|
77
|
+
self,
|
|
78
|
+
database_id: str,
|
|
79
|
+
ttl_hours: int = 4,
|
|
80
|
+
access_level: str = 'read-only'
|
|
81
|
+
) -> Dict[str, Any]:
|
|
82
|
+
"""
|
|
83
|
+
Generate temporary database credentials.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
database_id: ID of the database
|
|
87
|
+
ttl_hours: Time to live in hours (default: 4)
|
|
88
|
+
access_level: Access level - 'read-only', 'read-write', or 'admin'
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Dict with keys: lease_id, username, password, database_id, access_level,
|
|
92
|
+
issued_at, expires_at, ttl_seconds
|
|
93
|
+
|
|
94
|
+
Example:
|
|
95
|
+
>>> creds = client.get_database_credentials("db-123", ttl_hours=2)
|
|
96
|
+
>>> print(f"Username: {creds['username']}")
|
|
97
|
+
>>> print(f"Password: {creds['password']}")
|
|
98
|
+
>>> print(f"Expires: {creds['expires_at']}")
|
|
99
|
+
"""
|
|
100
|
+
payload = {
|
|
101
|
+
'database_id': database_id,
|
|
102
|
+
'ttl_hours': ttl_hours,
|
|
103
|
+
'access_level': access_level
|
|
104
|
+
}
|
|
105
|
+
return self._request('POST', '/service-accounts/databases/dynamic-credentials', json=payload)
|
|
106
|
+
|
|
107
|
+
def revoke_lease(self, lease_id: str) -> None:
|
|
108
|
+
"""
|
|
109
|
+
Revoke a dynamic secret lease.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
lease_id: ID of the lease to revoke
|
|
113
|
+
|
|
114
|
+
Example:
|
|
115
|
+
>>> client.revoke_lease("lease-123")
|
|
116
|
+
"""
|
|
117
|
+
self._request('POST', f'/service-accounts/leases/{lease_id}/revoke')
|
|
118
|
+
|
|
119
|
+
# ============================================================================
|
|
120
|
+
# API Keys Management
|
|
121
|
+
# ============================================================================
|
|
122
|
+
|
|
123
|
+
def list_api_keys(self) -> List[Dict[str, Any]]:
|
|
124
|
+
"""
|
|
125
|
+
List user's API keys.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
List of API key objects
|
|
129
|
+
|
|
130
|
+
Example:
|
|
131
|
+
>>> keys = client.list_api_keys()
|
|
132
|
+
>>> for key in keys:
|
|
133
|
+
... print(f"{key['name']}: {key['key_prefix']}...")
|
|
134
|
+
"""
|
|
135
|
+
return self._request('GET', '/service-accounts/api-keys')
|
|
136
|
+
|
|
137
|
+
def create_api_key(
|
|
138
|
+
self,
|
|
139
|
+
name: str,
|
|
140
|
+
description: Optional[str] = None,
|
|
141
|
+
scopes: Optional[List[str]] = None,
|
|
142
|
+
expires_at: Optional[str] = None
|
|
143
|
+
) -> Dict[str, Any]:
|
|
144
|
+
"""
|
|
145
|
+
Create a new API key.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
name: Name for the API key
|
|
149
|
+
description: Optional description
|
|
150
|
+
scopes: List of scopes (e.g., ['secrets:read', 'databases:read'])
|
|
151
|
+
expires_at: Optional expiration date (ISO format)
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
Dict with keys: id, name, api_key, key_prefix, scopes, created_at
|
|
155
|
+
|
|
156
|
+
Note:
|
|
157
|
+
The api_key is only returned once. Save it securely!
|
|
158
|
+
|
|
159
|
+
Example:
|
|
160
|
+
>>> key = client.create_api_key("My App", scopes=['secrets:read'])
|
|
161
|
+
>>> print(f"API Key: {key['api_key']}") # Save this!
|
|
162
|
+
"""
|
|
163
|
+
payload = {
|
|
164
|
+
'name': name,
|
|
165
|
+
'description': description,
|
|
166
|
+
'scopes': scopes or ['secrets:read', 'databases:read'],
|
|
167
|
+
'expires_at': expires_at
|
|
168
|
+
}
|
|
169
|
+
return self._request('POST', '/service-accounts/api-keys', json=payload)
|
|
170
|
+
|
|
171
|
+
def delete_api_key(self, key_id: str) -> None:
|
|
172
|
+
"""
|
|
173
|
+
Delete an API key.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
key_id: ID of the key to delete
|
|
177
|
+
|
|
178
|
+
Example:
|
|
179
|
+
>>> client.delete_api_key("key-123")
|
|
180
|
+
"""
|
|
181
|
+
self._request('DELETE', f'/service-accounts/api-keys/{key_id}')
|
|
182
|
+
|
|
183
|
+
# ============================================================================
|
|
184
|
+
# Context Manager Support
|
|
185
|
+
# ============================================================================
|
|
186
|
+
|
|
187
|
+
def __enter__(self):
|
|
188
|
+
"""Context manager entry."""
|
|
189
|
+
return self
|
|
190
|
+
|
|
191
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
192
|
+
"""Context manager exit - close session."""
|
|
193
|
+
self.session.close()
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Raqeb SDK Exceptions."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class RaqebError(Exception):
|
|
5
|
+
"""Base exception for Raqeb SDK."""
|
|
6
|
+
pass
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AuthenticationError(RaqebError):
|
|
10
|
+
"""Authentication failed - invalid or expired API key."""
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PermissionError(RaqebError):
|
|
15
|
+
"""Permission denied - insufficient scopes."""
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class NotFoundError(RaqebError):
|
|
20
|
+
"""Resource not found."""
|
|
21
|
+
pass
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: raqeb
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Python SDK for Raqeb Database PAM and Secrets Management
|
|
5
|
+
Home-page: https://github.com/raqeb/python-sdk
|
|
6
|
+
Author: Raqeb
|
|
7
|
+
Author-email: support@raqeb.cloud
|
|
8
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Requires-Python: >=3.7
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
Requires-Dist: requests>=2.25.0
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
23
|
+
Requires-Dist: pytest-cov>=2.0; extra == "dev"
|
|
24
|
+
Requires-Dist: black>=21.0; extra == "dev"
|
|
25
|
+
Requires-Dist: flake8>=3.9; extra == "dev"
|
|
26
|
+
Dynamic: author
|
|
27
|
+
Dynamic: author-email
|
|
28
|
+
Dynamic: classifier
|
|
29
|
+
Dynamic: description
|
|
30
|
+
Dynamic: description-content-type
|
|
31
|
+
Dynamic: home-page
|
|
32
|
+
Dynamic: provides-extra
|
|
33
|
+
Dynamic: requires-dist
|
|
34
|
+
Dynamic: requires-python
|
|
35
|
+
Dynamic: summary
|
|
36
|
+
|
|
37
|
+
# Raqeb Python SDK
|
|
38
|
+
|
|
39
|
+
Official Python SDK for Raqeb Database PAM and Secrets Management.
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install raqeb
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Quick Start
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from raqeb import RaqebClient
|
|
51
|
+
|
|
52
|
+
# Initialize client with service account API key
|
|
53
|
+
client = RaqebClient(api_key="sa_your_api_key_here")
|
|
54
|
+
|
|
55
|
+
# Get a secret
|
|
56
|
+
secret = client.get_secret("secret-id")
|
|
57
|
+
print(f"Secret value: {secret['value']}")
|
|
58
|
+
|
|
59
|
+
# Get temporary database credentials
|
|
60
|
+
creds = client.get_database_credentials(
|
|
61
|
+
database_id="db-id",
|
|
62
|
+
ttl_hours=4,
|
|
63
|
+
access_level="read-only"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
print(f"Username: {creds['username']}")
|
|
67
|
+
print(f"Password: {creds['password']}")
|
|
68
|
+
print(f"Expires: {creds['expires_at']}")
|
|
69
|
+
|
|
70
|
+
# Revoke credentials when done
|
|
71
|
+
client.revoke_lease(creds['lease_id'])
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Usage Examples
|
|
75
|
+
|
|
76
|
+
### Secrets Management
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from raqeb import RaqebClient
|
|
80
|
+
|
|
81
|
+
client = RaqebClient(api_key="sa_your_key")
|
|
82
|
+
|
|
83
|
+
# Retrieve a secret
|
|
84
|
+
secret = client.get_secret("api-key-prod")
|
|
85
|
+
api_key = secret['value']
|
|
86
|
+
|
|
87
|
+
# Use the secret in your application
|
|
88
|
+
import requests
|
|
89
|
+
response = requests.get(
|
|
90
|
+
"https://api.example.com/data",
|
|
91
|
+
headers={"Authorization": f"Bearer {api_key}"}
|
|
92
|
+
)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Database Access
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from raqeb import RaqebClient
|
|
99
|
+
import psycopg2
|
|
100
|
+
|
|
101
|
+
client = RaqebClient(api_key="sa_your_key")
|
|
102
|
+
|
|
103
|
+
# Get temporary database credentials
|
|
104
|
+
creds = client.get_database_credentials(
|
|
105
|
+
database_id="prod-postgres",
|
|
106
|
+
ttl_hours=2,
|
|
107
|
+
access_level="read-only"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Connect to database
|
|
111
|
+
conn = psycopg2.connect(
|
|
112
|
+
host="db.example.com",
|
|
113
|
+
port=5432,
|
|
114
|
+
database="myapp",
|
|
115
|
+
user=creds['username'],
|
|
116
|
+
password=creds['password']
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
# Use the connection
|
|
121
|
+
cursor = conn.cursor()
|
|
122
|
+
cursor.execute("SELECT * FROM users LIMIT 10")
|
|
123
|
+
results = cursor.fetchall()
|
|
124
|
+
|
|
125
|
+
finally:
|
|
126
|
+
conn.close()
|
|
127
|
+
# Revoke credentials
|
|
128
|
+
client.revoke_lease(creds['lease_id'])
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Context Manager
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from raqeb import RaqebClient
|
|
135
|
+
|
|
136
|
+
# Use context manager for automatic cleanup
|
|
137
|
+
with RaqebClient(api_key="sa_your_key") as client:
|
|
138
|
+
secret = client.get_secret("secret-id")
|
|
139
|
+
print(secret['value'])
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### API Key Management
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from raqeb import RaqebClient
|
|
146
|
+
|
|
147
|
+
client = RaqebClient(api_key="sa_your_key")
|
|
148
|
+
|
|
149
|
+
# List API keys
|
|
150
|
+
keys = client.list_api_keys()
|
|
151
|
+
for key in keys:
|
|
152
|
+
print(f"{key['name']}: {key['key_prefix']}...")
|
|
153
|
+
|
|
154
|
+
# Create new API key
|
|
155
|
+
new_key = client.create_api_key(
|
|
156
|
+
name="CI/CD Pipeline",
|
|
157
|
+
scopes=["secrets:read", "databases:read"],
|
|
158
|
+
description="Key for automated deployments"
|
|
159
|
+
)
|
|
160
|
+
print(f"New API Key: {new_key['api_key']}") # Save this!
|
|
161
|
+
|
|
162
|
+
# Delete API key
|
|
163
|
+
client.delete_api_key("key-id")
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Error Handling
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
from raqeb import RaqebClient, AuthenticationError, PermissionError, NotFoundError
|
|
170
|
+
|
|
171
|
+
client = RaqebClient(api_key="sa_your_key")
|
|
172
|
+
|
|
173
|
+
try:
|
|
174
|
+
secret = client.get_secret("secret-id")
|
|
175
|
+
|
|
176
|
+
except AuthenticationError:
|
|
177
|
+
print("Invalid or expired API key")
|
|
178
|
+
|
|
179
|
+
except PermissionError:
|
|
180
|
+
print("Insufficient permissions - check API key scopes")
|
|
181
|
+
|
|
182
|
+
except NotFoundError:
|
|
183
|
+
print("Secret not found")
|
|
184
|
+
|
|
185
|
+
except RaqebError as e:
|
|
186
|
+
print(f"API error: {e}")
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## API Reference
|
|
190
|
+
|
|
191
|
+
### RaqebClient
|
|
192
|
+
|
|
193
|
+
#### `__init__(api_key, base_url="https://app.raqeb.cloud/api/v1")`
|
|
194
|
+
Initialize the client.
|
|
195
|
+
|
|
196
|
+
#### `get_secret(secret_id) -> dict`
|
|
197
|
+
Retrieve a secret value.
|
|
198
|
+
|
|
199
|
+
**Returns:**
|
|
200
|
+
```python
|
|
201
|
+
{
|
|
202
|
+
'secret_id': 'secret-123',
|
|
203
|
+
'name': 'API Key',
|
|
204
|
+
'value': 'secret-value',
|
|
205
|
+
'retrieved_at': '2026-02-14T16:09:00Z'
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
#### `get_database_credentials(database_id, ttl_hours=4, access_level='read-only') -> dict`
|
|
210
|
+
Generate temporary database credentials.
|
|
211
|
+
|
|
212
|
+
**Parameters:**
|
|
213
|
+
- `database_id` (str): Database ID
|
|
214
|
+
- `ttl_hours` (int): Time to live in hours (default: 4)
|
|
215
|
+
- `access_level` (str): 'read-only', 'read-write', or 'admin'
|
|
216
|
+
|
|
217
|
+
**Returns:**
|
|
218
|
+
```python
|
|
219
|
+
{
|
|
220
|
+
'lease_id': 'lease-123',
|
|
221
|
+
'username': 'temp_user_abc',
|
|
222
|
+
'password': 'temp_pass_xyz',
|
|
223
|
+
'database_id': 'db-123',
|
|
224
|
+
'access_level': 'read-only',
|
|
225
|
+
'issued_at': '2026-02-14T16:09:00Z',
|
|
226
|
+
'expires_at': '2026-02-14T20:09:00Z',
|
|
227
|
+
'ttl_seconds': 14400
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
#### `revoke_lease(lease_id) -> None`
|
|
232
|
+
Revoke a dynamic secret lease.
|
|
233
|
+
|
|
234
|
+
#### `list_api_keys() -> list`
|
|
235
|
+
List user's API keys.
|
|
236
|
+
|
|
237
|
+
#### `create_api_key(name, description=None, scopes=None, expires_at=None) -> dict`
|
|
238
|
+
Create a new API key.
|
|
239
|
+
|
|
240
|
+
#### `delete_api_key(key_id) -> None`
|
|
241
|
+
Delete an API key.
|
|
242
|
+
|
|
243
|
+
## Environment Variables
|
|
244
|
+
|
|
245
|
+
You can use environment variables for configuration:
|
|
246
|
+
|
|
247
|
+
```python
|
|
248
|
+
import os
|
|
249
|
+
from raqeb import RaqebClient
|
|
250
|
+
|
|
251
|
+
client = RaqebClient(
|
|
252
|
+
api_key=os.getenv('RAQEB_API_KEY'),
|
|
253
|
+
base_url=os.getenv('RAQEB_BASE_URL', 'https://app.raqeb.cloud/api/v1')
|
|
254
|
+
)
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## Best Practices
|
|
258
|
+
|
|
259
|
+
1. **Never hardcode API keys** - Use environment variables
|
|
260
|
+
2. **Use minimal scopes** - Only grant necessary permissions
|
|
261
|
+
3. **Set appropriate TTLs** - Use shortest time needed
|
|
262
|
+
4. **Always revoke leases** - Clean up credentials when done
|
|
263
|
+
5. **Handle errors gracefully** - Catch and handle SDK exceptions
|
|
264
|
+
6. **Use context managers** - Automatic resource cleanup
|
|
265
|
+
|
|
266
|
+
## License
|
|
267
|
+
|
|
268
|
+
MIT License - see LICENSE file for details
|
|
269
|
+
|
|
270
|
+
## Support
|
|
271
|
+
|
|
272
|
+
- Documentation: https://docs.raqeb.cloud
|
|
273
|
+
- Email: support@raqeb.cloud
|
|
274
|
+
- GitHub: https://github.com/raqeb/python-sdk
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
raqeb
|
raqeb-1.0.0/setup.cfg
ADDED
raqeb-1.0.0/setup.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Setup script for Raqeb Python SDK."""
|
|
2
|
+
|
|
3
|
+
from setuptools import setup, find_packages
|
|
4
|
+
|
|
5
|
+
with open("README.md", "r", encoding="utf-8") as fh:
|
|
6
|
+
long_description = fh.read()
|
|
7
|
+
|
|
8
|
+
setup(
|
|
9
|
+
name="raqeb",
|
|
10
|
+
version="1.0.0",
|
|
11
|
+
author="Raqeb",
|
|
12
|
+
author_email="support@raqeb.cloud",
|
|
13
|
+
description="Python SDK for Raqeb Database PAM and Secrets Management",
|
|
14
|
+
long_description=long_description,
|
|
15
|
+
long_description_content_type="text/markdown",
|
|
16
|
+
url="https://github.com/raqeb/python-sdk",
|
|
17
|
+
packages=find_packages(),
|
|
18
|
+
classifiers=[
|
|
19
|
+
"Development Status :: 5 - Production/Stable",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
22
|
+
"License :: OSI Approved :: MIT License",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.7",
|
|
25
|
+
"Programming Language :: Python :: 3.8",
|
|
26
|
+
"Programming Language :: Python :: 3.9",
|
|
27
|
+
"Programming Language :: Python :: 3.10",
|
|
28
|
+
"Programming Language :: Python :: 3.11",
|
|
29
|
+
],
|
|
30
|
+
python_requires=">=3.7",
|
|
31
|
+
install_requires=[
|
|
32
|
+
"requests>=2.25.0",
|
|
33
|
+
],
|
|
34
|
+
extras_require={
|
|
35
|
+
"dev": [
|
|
36
|
+
"pytest>=6.0",
|
|
37
|
+
"pytest-cov>=2.0",
|
|
38
|
+
"black>=21.0",
|
|
39
|
+
"flake8>=3.9",
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
)
|