opa-python-client 2.0.1__tar.gz → 2.0.3__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.
- {opa_python_client-2.0.1 → opa_python_client-2.0.3}/PKG-INFO +38 -28
- {opa_python_client-2.0.1 → opa_python_client-2.0.3}/README.md +34 -25
- {opa_python_client-2.0.1 → opa_python_client-2.0.3}/opa_client/__init__.py +1 -1
- {opa_python_client-2.0.1 → opa_python_client-2.0.3}/opa_client/base.py +2 -2
- {opa_python_client-2.0.1 → opa_python_client-2.0.3}/opa_client/opa.py +20 -6
- {opa_python_client-2.0.1 → opa_python_client-2.0.3}/opa_client/opa_async.py +8 -7
- {opa_python_client-2.0.1 → opa_python_client-2.0.3}/pyproject.toml +2 -1
- {opa_python_client-2.0.1 → opa_python_client-2.0.3}/LICENCE.md +0 -0
- {opa_python_client-2.0.1 → opa_python_client-2.0.3}/opa_client/errors.py +0 -0
- {opa_python_client-2.0.1 → opa_python_client-2.0.3}/opa_client/test/__init__.py +0 -0
- {opa_python_client-2.0.1 → opa_python_client-2.0.3}/opa_client/test/test_async_client.py +0 -0
- {opa_python_client-2.0.1 → opa_python_client-2.0.3}/opa_client/test/test_integaration_opa.py +0 -0
- {opa_python_client-2.0.1 → opa_python_client-2.0.3}/opa_client/test/test_opa.py +0 -0
- {opa_python_client-2.0.1 → opa_python_client-2.0.3}/opa_client/test/test_opa_client.py +0 -0
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: opa-python-client
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.3
|
|
4
4
|
Summary: Client for connection to the OPA service
|
|
5
|
-
Home-page: https://github.com/Turall/OPA-python-client
|
|
6
5
|
License: MIT
|
|
7
6
|
Author: Tural Muradov
|
|
8
7
|
Author-email: tural.muradoov@gmail.com
|
|
@@ -21,6 +20,8 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
21
20
|
Requires-Dist: aiofiles (>=24.1.0,<25.0.0)
|
|
22
21
|
Requires-Dist: aiohttp[speedups] (>=3.10.9,<4.0.0)
|
|
23
22
|
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
|
23
|
+
Requires-Dist: urllib3 (>=2.5.0,<3.0.0)
|
|
24
|
+
Project-URL: Homepage, https://github.com/Turall/OPA-python-client
|
|
24
25
|
Project-URL: Repository, https://github.com/Turall/OPA-python-client
|
|
25
26
|
Description-Content-Type: text/markdown
|
|
26
27
|
|
|
@@ -380,31 +381,7 @@ await client.delete_data('users')
|
|
|
380
381
|
|
|
381
382
|
#### Check Permission (Policy Evaluation)
|
|
382
383
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
- **Synchronous**:
|
|
386
|
-
|
|
387
|
-
```python
|
|
388
|
-
input_data = {"user": "admin"}
|
|
389
|
-
policy_name = 'example_policy'
|
|
390
|
-
rule_name = 'allow'
|
|
391
|
-
|
|
392
|
-
result = client.check_permission(input_data, policy_name, rule_name)
|
|
393
|
-
print(result)
|
|
394
|
-
```
|
|
395
|
-
|
|
396
|
-
- **Asynchronous**:
|
|
397
|
-
|
|
398
|
-
```python
|
|
399
|
-
input_data = {"user": "admin"}
|
|
400
|
-
policy_name = 'example_policy'
|
|
401
|
-
rule_name = 'allow'
|
|
402
|
-
|
|
403
|
-
result = await client.check_permission(input_data, policy_name, rule_name)
|
|
404
|
-
print(result)
|
|
405
|
-
```
|
|
406
|
-
|
|
407
|
-
Queries a package rule with the given input data
|
|
384
|
+
Evaluate a rule from a known package path. This is the **recommended method** for evaluating OPA decisions.
|
|
408
385
|
|
|
409
386
|
```python
|
|
410
387
|
|
|
@@ -448,6 +425,39 @@ print(await client.query_rule(input_data=check_data, package_path="play", rule_n
|
|
|
448
425
|
|
|
449
426
|
```
|
|
450
427
|
|
|
428
|
+
You can evaluate policies with input data using `check_permission`.
|
|
429
|
+
### ⚠️ Deprecated: `check_permission()`
|
|
430
|
+
|
|
431
|
+
This method introspects the policy AST to construct a query path dynamically. It introduces unnecessary overhead and is **not recommended** for production use.
|
|
432
|
+
|
|
433
|
+
- **Synchronous**:
|
|
434
|
+
|
|
435
|
+
```python
|
|
436
|
+
input_data = {"user": "admin"}
|
|
437
|
+
policy_name = 'example_policy'
|
|
438
|
+
rule_name = 'allow'
|
|
439
|
+
|
|
440
|
+
result = client.check_permission(input_data, policy_name, rule_name)
|
|
441
|
+
print(result)
|
|
442
|
+
```
|
|
443
|
+
> 🔥 Prefer `query_rule()` instead for better performance and maintainability.
|
|
444
|
+
|
|
445
|
+
### ⚠️ Deprecated: `check_permission()`
|
|
446
|
+
|
|
447
|
+
- **Asynchronous**:
|
|
448
|
+
|
|
449
|
+
```python
|
|
450
|
+
input_data = {"user": "admin"}
|
|
451
|
+
policy_name = 'example_policy'
|
|
452
|
+
rule_name = 'allow'
|
|
453
|
+
|
|
454
|
+
result = await client.check_permission(input_data, policy_name, rule_name)
|
|
455
|
+
print(result)
|
|
456
|
+
```
|
|
457
|
+
> 🔥 Prefer `query_rule()` instead for better performance and maintainability.
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
|
|
451
461
|
### Ad-hoc Queries
|
|
452
462
|
|
|
453
463
|
Execute ad-hoc queries directly:
|
|
@@ -354,31 +354,7 @@ await client.delete_data('users')
|
|
|
354
354
|
|
|
355
355
|
#### Check Permission (Policy Evaluation)
|
|
356
356
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
- **Synchronous**:
|
|
360
|
-
|
|
361
|
-
```python
|
|
362
|
-
input_data = {"user": "admin"}
|
|
363
|
-
policy_name = 'example_policy'
|
|
364
|
-
rule_name = 'allow'
|
|
365
|
-
|
|
366
|
-
result = client.check_permission(input_data, policy_name, rule_name)
|
|
367
|
-
print(result)
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
- **Asynchronous**:
|
|
371
|
-
|
|
372
|
-
```python
|
|
373
|
-
input_data = {"user": "admin"}
|
|
374
|
-
policy_name = 'example_policy'
|
|
375
|
-
rule_name = 'allow'
|
|
376
|
-
|
|
377
|
-
result = await client.check_permission(input_data, policy_name, rule_name)
|
|
378
|
-
print(result)
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
Queries a package rule with the given input data
|
|
357
|
+
Evaluate a rule from a known package path. This is the **recommended method** for evaluating OPA decisions.
|
|
382
358
|
|
|
383
359
|
```python
|
|
384
360
|
|
|
@@ -422,6 +398,39 @@ print(await client.query_rule(input_data=check_data, package_path="play", rule_n
|
|
|
422
398
|
|
|
423
399
|
```
|
|
424
400
|
|
|
401
|
+
You can evaluate policies with input data using `check_permission`.
|
|
402
|
+
### ⚠️ Deprecated: `check_permission()`
|
|
403
|
+
|
|
404
|
+
This method introspects the policy AST to construct a query path dynamically. It introduces unnecessary overhead and is **not recommended** for production use.
|
|
405
|
+
|
|
406
|
+
- **Synchronous**:
|
|
407
|
+
|
|
408
|
+
```python
|
|
409
|
+
input_data = {"user": "admin"}
|
|
410
|
+
policy_name = 'example_policy'
|
|
411
|
+
rule_name = 'allow'
|
|
412
|
+
|
|
413
|
+
result = client.check_permission(input_data, policy_name, rule_name)
|
|
414
|
+
print(result)
|
|
415
|
+
```
|
|
416
|
+
> 🔥 Prefer `query_rule()` instead for better performance and maintainability.
|
|
417
|
+
|
|
418
|
+
### ⚠️ Deprecated: `check_permission()`
|
|
419
|
+
|
|
420
|
+
- **Asynchronous**:
|
|
421
|
+
|
|
422
|
+
```python
|
|
423
|
+
input_data = {"user": "admin"}
|
|
424
|
+
policy_name = 'example_policy'
|
|
425
|
+
rule_name = 'allow'
|
|
426
|
+
|
|
427
|
+
result = await client.check_permission(input_data, policy_name, rule_name)
|
|
428
|
+
print(result)
|
|
429
|
+
```
|
|
430
|
+
> 🔥 Prefer `query_rule()` instead for better performance and maintainability.
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
|
|
425
434
|
### Ad-hoc Queries
|
|
426
435
|
|
|
427
436
|
Execute ad-hoc queries directly:
|
|
@@ -23,6 +23,7 @@ class BaseClient:
|
|
|
23
23
|
ssl: bool = False,
|
|
24
24
|
cert: Optional[Union[str, tuple]] = None,
|
|
25
25
|
headers: Optional[dict] = None,
|
|
26
|
+
retries: int = 2,
|
|
26
27
|
timeout: float = 1.5,
|
|
27
28
|
):
|
|
28
29
|
if not isinstance(port, int):
|
|
@@ -34,6 +35,7 @@ class BaseClient:
|
|
|
34
35
|
self.ssl = ssl
|
|
35
36
|
self.cert = cert
|
|
36
37
|
self.timeout = timeout
|
|
38
|
+
self.retries = retries
|
|
37
39
|
|
|
38
40
|
self.schema = "https://" if ssl else "http://"
|
|
39
41
|
self.root_url = f"{self.schema}{self.host}:{self.port}/{self.version}"
|
|
@@ -41,8 +43,6 @@ class BaseClient:
|
|
|
41
43
|
self.headers = headers
|
|
42
44
|
|
|
43
45
|
self._session = None # Will be initialized in the subclass
|
|
44
|
-
self.retries = 2
|
|
45
|
-
self.timeout = 1.5
|
|
46
46
|
|
|
47
47
|
def _build_url(
|
|
48
48
|
self, path: str, query_params: Dict[str, str] = None
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import json
|
|
2
1
|
import os
|
|
3
2
|
import threading
|
|
3
|
+
import warnings
|
|
4
4
|
from typing import Dict, Optional
|
|
5
5
|
from urllib.parse import urlencode
|
|
6
6
|
|
|
@@ -62,7 +62,9 @@ class OpaClient(BaseClient):
|
|
|
62
62
|
session.mount("https://", adapter)
|
|
63
63
|
|
|
64
64
|
if self.ssl:
|
|
65
|
-
session.verify = self.
|
|
65
|
+
session.verify = self.ssl
|
|
66
|
+
if self.cert:
|
|
67
|
+
session.cert = self.cert
|
|
66
68
|
|
|
67
69
|
return session
|
|
68
70
|
|
|
@@ -202,7 +204,9 @@ class OpaClient(BaseClient):
|
|
|
202
204
|
bool: True if the policy was successfully updated.
|
|
203
205
|
"""
|
|
204
206
|
if not os.path.isfile(filepath):
|
|
205
|
-
raise FileError(
|
|
207
|
+
raise FileError(
|
|
208
|
+
"file_not_found", f"'{filepath}' is not a valid file"
|
|
209
|
+
)
|
|
206
210
|
|
|
207
211
|
with open(filepath, "r", encoding="utf-8") as file:
|
|
208
212
|
policy_str = file.read()
|
|
@@ -300,7 +304,9 @@ class OpaClient(BaseClient):
|
|
|
300
304
|
policy_raw = policy.get("result", {}).get("raw", "")
|
|
301
305
|
|
|
302
306
|
if not policy_raw:
|
|
303
|
-
raise PolicyNotFoundError(
|
|
307
|
+
raise PolicyNotFoundError(
|
|
308
|
+
"resource_not_found", "Policy content is empty"
|
|
309
|
+
)
|
|
304
310
|
|
|
305
311
|
full_path = os.path.join(path or "", filename)
|
|
306
312
|
|
|
@@ -309,7 +315,9 @@ class OpaClient(BaseClient):
|
|
|
309
315
|
file.write(policy_raw)
|
|
310
316
|
return True
|
|
311
317
|
except OSError as e:
|
|
312
|
-
raise PathNotFoundError(
|
|
318
|
+
raise PathNotFoundError(
|
|
319
|
+
"path_not_found", f"Failed to write to '{full_path}'"
|
|
320
|
+
) from e
|
|
313
321
|
|
|
314
322
|
def get_policy(self, policy_name: str) -> dict:
|
|
315
323
|
"""
|
|
@@ -384,6 +392,11 @@ class OpaClient(BaseClient):
|
|
|
384
392
|
Returns:
|
|
385
393
|
dict: The result of the permission check.
|
|
386
394
|
"""
|
|
395
|
+
warnings.warn(
|
|
396
|
+
"check_permission is deprecated and will be removed in a future release. Use `query_rule` instead.",
|
|
397
|
+
DeprecationWarning,
|
|
398
|
+
stacklevel=2
|
|
399
|
+
)
|
|
387
400
|
policy = self.get_policy(policy_name)
|
|
388
401
|
ast = policy.get("result", {}).get("ast", {})
|
|
389
402
|
package_path = "/".join(
|
|
@@ -395,7 +408,8 @@ class OpaClient(BaseClient):
|
|
|
395
408
|
|
|
396
409
|
if rule_name not in rules:
|
|
397
410
|
raise CheckPermissionError(
|
|
398
|
-
|
|
411
|
+
"resource_not_found",
|
|
412
|
+
f"Rule '{rule_name}' not found in policy '{policy_name}'",
|
|
399
413
|
)
|
|
400
414
|
|
|
401
415
|
url = f"{self.root_url}/{package_path}/{rule_name}"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import
|
|
2
|
+
import warnings
|
|
3
3
|
import os
|
|
4
4
|
import ssl
|
|
5
5
|
from typing import Dict, Optional, Union
|
|
@@ -50,9 +50,6 @@ class AsyncOpaClient:
|
|
|
50
50
|
headers: Optional[dict] = None,
|
|
51
51
|
timeout: float = 1.5,
|
|
52
52
|
):
|
|
53
|
-
if not isinstance(port, int):
|
|
54
|
-
raise TypeError("The port must be an integer")
|
|
55
|
-
|
|
56
53
|
self.host = host.strip()
|
|
57
54
|
self.port = port
|
|
58
55
|
self.version = version
|
|
@@ -221,9 +218,8 @@ class AsyncOpaClient:
|
|
|
221
218
|
) as response:
|
|
222
219
|
if response.status == 200:
|
|
223
220
|
return True
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
raise RegoParseError(error.get("code"), error.get("message"))
|
|
221
|
+
error = await response.json()
|
|
222
|
+
raise RegoParseError(error.get("code"), error.get("message"))
|
|
227
223
|
|
|
228
224
|
async def update_policy_from_file(
|
|
229
225
|
self, filepath: str, endpoint: str
|
|
@@ -426,6 +422,11 @@ class AsyncOpaClient:
|
|
|
426
422
|
Returns:
|
|
427
423
|
dict: The result of the permission check.
|
|
428
424
|
"""
|
|
425
|
+
warnings.warn(
|
|
426
|
+
"check_permission is deprecated and will be removed in a future release. Use `query_rule` instead.",
|
|
427
|
+
DeprecationWarning,
|
|
428
|
+
stacklevel=2
|
|
429
|
+
)
|
|
429
430
|
policy = await self.get_policy(policy_name)
|
|
430
431
|
ast = policy.get("result", {}).get("ast", {})
|
|
431
432
|
package_path = "/".join(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "opa-python-client"
|
|
3
|
-
version = "2.0.
|
|
3
|
+
version = "2.0.3"
|
|
4
4
|
description = "Client for connection to the OPA service"
|
|
5
5
|
authors = ["Tural Muradov <tural.muradoov@gmail.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -25,6 +25,7 @@ python = "^3.9"
|
|
|
25
25
|
requests = "^2.32.3"
|
|
26
26
|
aiohttp = {extras = ["speedups"], version = "^3.10.9"}
|
|
27
27
|
aiofiles = "^24.1.0"
|
|
28
|
+
urllib3 = "^2.5.0"
|
|
28
29
|
|
|
29
30
|
[tool.poetry.group.dev.dependencies]
|
|
30
31
|
pytest = "^8.3.3"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{opa_python_client-2.0.1 → opa_python_client-2.0.3}/opa_client/test/test_integaration_opa.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|