opa-python-client 2.0.2__tar.gz → 2.0.4__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.2 → opa_python_client-2.0.4}/PKG-INFO +45 -33
- {opa_python_client-2.0.2 → opa_python_client-2.0.4}/README.md +34 -25
- {opa_python_client-2.0.2 → opa_python_client-2.0.4}/opa_client/__init__.py +1 -1
- {opa_python_client-2.0.2 → opa_python_client-2.0.4}/opa_client/base.py +2 -2
- {opa_python_client-2.0.2 → opa_python_client-2.0.4}/opa_client/opa.py +29 -14
- {opa_python_client-2.0.2 → opa_python_client-2.0.4}/opa_client/opa_async.py +8 -7
- {opa_python_client-2.0.2 → opa_python_client-2.0.4}/opa_client/test/test_opa_client.py +0 -2
- {opa_python_client-2.0.2 → opa_python_client-2.0.4}/pyproject.toml +6 -5
- {opa_python_client-2.0.2 → opa_python_client-2.0.4}/LICENCE.md +0 -0
- {opa_python_client-2.0.2 → opa_python_client-2.0.4}/opa_client/errors.py +0 -0
- {opa_python_client-2.0.2 → opa_python_client-2.0.4}/opa_client/test/__init__.py +0 -0
- {opa_python_client-2.0.2 → opa_python_client-2.0.4}/opa_client/test/test_async_client.py +0 -0
- {opa_python_client-2.0.2 → opa_python_client-2.0.4}/opa_client/test/test_integaration_opa.py +0 -0
- {opa_python_client-2.0.2 → opa_python_client-2.0.4}/opa_client/test/test_opa.py +0 -0
|
@@ -1,26 +1,29 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: opa-python-client
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.4
|
|
4
4
|
Summary: Client for connection to the OPA service
|
|
5
|
-
Home-page: https://github.com/Turall/OPA-python-client
|
|
6
5
|
License: MIT
|
|
6
|
+
License-File: LICENCE.md
|
|
7
7
|
Author: Tural Muradov
|
|
8
8
|
Author-email: tural.muradoov@gmail.com
|
|
9
|
-
Requires-Python: >=3.
|
|
9
|
+
Requires-Python: >=3.10,<4.0
|
|
10
10
|
Classifier: Intended Audience :: Developers
|
|
11
11
|
Classifier: License :: OSI Approved :: MIT License
|
|
12
12
|
Classifier: Operating System :: OS Independent
|
|
13
13
|
Classifier: Programming Language :: Python
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.10
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
20
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
-
|
|
22
|
-
Requires-Dist:
|
|
23
|
-
Requires-Dist:
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
22
|
+
Requires-Dist: aiofiles (>=25.1.0,<26.0.0)
|
|
23
|
+
Requires-Dist: aiohttp[speedups] (>=3.13.3,<4.0.0)
|
|
24
|
+
Requires-Dist: requests (>=2.32.5,<3.0.0)
|
|
25
|
+
Requires-Dist: urllib3 (>=2.6.3,<3.0.0)
|
|
26
|
+
Project-URL: Homepage, https://github.com/Turall/OPA-python-client
|
|
24
27
|
Project-URL: Repository, https://github.com/Turall/OPA-python-client
|
|
25
28
|
Description-Content-Type: text/markdown
|
|
26
29
|
|
|
@@ -380,31 +383,7 @@ await client.delete_data('users')
|
|
|
380
383
|
|
|
381
384
|
#### Check Permission (Policy Evaluation)
|
|
382
385
|
|
|
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
|
|
386
|
+
Evaluate a rule from a known package path. This is the **recommended method** for evaluating OPA decisions.
|
|
408
387
|
|
|
409
388
|
```python
|
|
410
389
|
|
|
@@ -448,6 +427,39 @@ print(await client.query_rule(input_data=check_data, package_path="play", rule_n
|
|
|
448
427
|
|
|
449
428
|
```
|
|
450
429
|
|
|
430
|
+
You can evaluate policies with input data using `check_permission`.
|
|
431
|
+
### ⚠️ Deprecated: `check_permission()`
|
|
432
|
+
|
|
433
|
+
This method introspects the policy AST to construct a query path dynamically. It introduces unnecessary overhead and is **not recommended** for production use.
|
|
434
|
+
|
|
435
|
+
- **Synchronous**:
|
|
436
|
+
|
|
437
|
+
```python
|
|
438
|
+
input_data = {"user": "admin"}
|
|
439
|
+
policy_name = 'example_policy'
|
|
440
|
+
rule_name = 'allow'
|
|
441
|
+
|
|
442
|
+
result = client.check_permission(input_data, policy_name, rule_name)
|
|
443
|
+
print(result)
|
|
444
|
+
```
|
|
445
|
+
> 🔥 Prefer `query_rule()` instead for better performance and maintainability.
|
|
446
|
+
|
|
447
|
+
### ⚠️ Deprecated: `check_permission()`
|
|
448
|
+
|
|
449
|
+
- **Asynchronous**:
|
|
450
|
+
|
|
451
|
+
```python
|
|
452
|
+
input_data = {"user": "admin"}
|
|
453
|
+
policy_name = 'example_policy'
|
|
454
|
+
rule_name = 'allow'
|
|
455
|
+
|
|
456
|
+
result = await client.check_permission(input_data, policy_name, rule_name)
|
|
457
|
+
print(result)
|
|
458
|
+
```
|
|
459
|
+
> 🔥 Prefer `query_rule()` instead for better performance and maintainability.
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
|
|
451
463
|
### Ad-hoc Queries
|
|
452
464
|
|
|
453
465
|
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,5 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import threading
|
|
3
|
+
import warnings
|
|
3
4
|
from typing import Dict, Optional
|
|
4
5
|
from urllib.parse import urlencode
|
|
5
6
|
|
|
@@ -9,15 +10,15 @@ from urllib3.util.retry import Retry
|
|
|
9
10
|
|
|
10
11
|
from .base import BaseClient
|
|
11
12
|
from .errors import (
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
CheckPermissionError,
|
|
14
|
+
ConnectionsError,
|
|
15
|
+
DeleteDataError,
|
|
16
|
+
DeletePolicyError,
|
|
17
|
+
FileError,
|
|
18
|
+
PathNotFoundError,
|
|
19
|
+
PolicyNotFoundError,
|
|
20
|
+
RegoParseError,
|
|
21
|
+
TypeException,
|
|
21
22
|
)
|
|
22
23
|
|
|
23
24
|
|
|
@@ -61,7 +62,9 @@ class OpaClient(BaseClient):
|
|
|
61
62
|
session.mount("https://", adapter)
|
|
62
63
|
|
|
63
64
|
if self.ssl:
|
|
64
|
-
session.verify = self.
|
|
65
|
+
session.verify = self.ssl
|
|
66
|
+
if self.cert:
|
|
67
|
+
session.cert = self.cert
|
|
65
68
|
|
|
66
69
|
return session
|
|
67
70
|
|
|
@@ -201,7 +204,9 @@ class OpaClient(BaseClient):
|
|
|
201
204
|
bool: True if the policy was successfully updated.
|
|
202
205
|
"""
|
|
203
206
|
if not os.path.isfile(filepath):
|
|
204
|
-
raise FileError(
|
|
207
|
+
raise FileError(
|
|
208
|
+
"file_not_found", f"'{filepath}' is not a valid file"
|
|
209
|
+
)
|
|
205
210
|
|
|
206
211
|
with open(filepath, "r", encoding="utf-8") as file:
|
|
207
212
|
policy_str = file.read()
|
|
@@ -299,7 +304,9 @@ class OpaClient(BaseClient):
|
|
|
299
304
|
policy_raw = policy.get("result", {}).get("raw", "")
|
|
300
305
|
|
|
301
306
|
if not policy_raw:
|
|
302
|
-
raise PolicyNotFoundError(
|
|
307
|
+
raise PolicyNotFoundError(
|
|
308
|
+
"resource_not_found", "Policy content is empty"
|
|
309
|
+
)
|
|
303
310
|
|
|
304
311
|
full_path = os.path.join(path or "", filename)
|
|
305
312
|
|
|
@@ -308,7 +315,9 @@ class OpaClient(BaseClient):
|
|
|
308
315
|
file.write(policy_raw)
|
|
309
316
|
return True
|
|
310
317
|
except OSError as e:
|
|
311
|
-
raise PathNotFoundError(
|
|
318
|
+
raise PathNotFoundError(
|
|
319
|
+
"path_not_found", f"Failed to write to '{full_path}'"
|
|
320
|
+
) from e
|
|
312
321
|
|
|
313
322
|
def get_policy(self, policy_name: str) -> dict:
|
|
314
323
|
"""
|
|
@@ -383,6 +392,11 @@ class OpaClient(BaseClient):
|
|
|
383
392
|
Returns:
|
|
384
393
|
dict: The result of the permission check.
|
|
385
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
|
+
)
|
|
386
400
|
policy = self.get_policy(policy_name)
|
|
387
401
|
ast = policy.get("result", {}).get("ast", {})
|
|
388
402
|
package_path = "/".join(
|
|
@@ -394,7 +408,8 @@ class OpaClient(BaseClient):
|
|
|
394
408
|
|
|
395
409
|
if rule_name not in rules:
|
|
396
410
|
raise CheckPermissionError(
|
|
397
|
-
"resource_not_found",
|
|
411
|
+
"resource_not_found",
|
|
412
|
+
f"Rule '{rule_name}' not found in policy '{policy_name}'",
|
|
398
413
|
)
|
|
399
414
|
|
|
400
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.4"
|
|
4
4
|
description = "Client for connection to the OPA service"
|
|
5
5
|
authors = ["Tural Muradov <tural.muradoov@gmail.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -21,10 +21,11 @@ packages = [
|
|
|
21
21
|
]
|
|
22
22
|
|
|
23
23
|
[tool.poetry.dependencies]
|
|
24
|
-
python = "^3.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
python = "^3.10"
|
|
25
|
+
aiohttp = {extras = ["speedups"], version = "^3.13.3"}
|
|
26
|
+
requests = "^2.32.5"
|
|
27
|
+
urllib3 = "^2.6.3"
|
|
28
|
+
aiofiles = "^25.1.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.2 → opa_python_client-2.0.4}/opa_client/test/test_integaration_opa.py
RENAMED
|
File without changes
|
|
File without changes
|