vaultkit 0.3.0__tar.gz → 0.3.1__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.
- {vaultkit-0.3.0 → vaultkit-0.3.1}/PKG-INFO +1 -1
- {vaultkit-0.3.0 → vaultkit-0.3.1}/pyproject.toml +1 -1
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/__init__.py +1 -1
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/client.py +2 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/core/http.py +1 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/errors/exceptions.py +6 -1
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/models/fetch_result.py +12 -1
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/tools/executor.py +11 -6
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit.egg-info/PKG-INFO +1 -1
- {vaultkit-0.3.0 → vaultkit-0.3.1}/README.md +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/setup.cfg +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/tests/test_fetch.py +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/tests/test_query.py +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/core/__init__.py +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/core/polling.py +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/errors/__init__.py +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/errors/base.py +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/mcp/__init__.py +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/mcp/__main__.py +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/mcp/identity.py +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/mcp/server.py +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/models/__init__.py +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/models/dataset_info.py +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/models/dataset_schema.py +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/models/query_result.py +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/tools/__init__.py +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/tools/adapters/__init__.py +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/tools/adapters/anthropic.py +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/tools/adapters/mcp.py +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/tools/adapters/openai.py +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/tools/builder.py +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/tools/definitions.py +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/tools/schemas.py +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/utils/__init__.py +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/utils/retry.py +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit/utils/validation.py +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit.egg-info/SOURCES.txt +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit.egg-info/dependency_links.txt +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit.egg-info/entry_points.txt +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit.egg-info/requires.txt +0 -0
- {vaultkit-0.3.0 → vaultkit-0.3.1}/vaultkit.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "vaultkit"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.1"
|
|
8
8
|
description = "VaultKit Python SDK for policy-driven, runtime governed data access"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "VaultKit", email = "founders@vaultkit.io" }
|
|
@@ -190,6 +190,7 @@ class VaultKitClient:
|
|
|
190
190
|
raise DeniedError(
|
|
191
191
|
result.reason or "Request denied by policy",
|
|
192
192
|
policy_id=result.policy_id,
|
|
193
|
+
reason=result.reason,
|
|
193
194
|
)
|
|
194
195
|
|
|
195
196
|
# Case 1: data already returned (reuse path)
|
|
@@ -315,6 +316,7 @@ class VaultKitClient:
|
|
|
315
316
|
raise DeniedError(
|
|
316
317
|
result.reason or "Request denied",
|
|
317
318
|
policy_id=result.policy_id,
|
|
319
|
+
reason=result.reason,
|
|
318
320
|
)
|
|
319
321
|
if result.is_pending and not poll:
|
|
320
322
|
raise QueuedError(
|
|
@@ -62,10 +62,12 @@ class DeniedError(VaultKitError):
|
|
|
62
62
|
message: str,
|
|
63
63
|
*,
|
|
64
64
|
policy_id: Optional[str] = None,
|
|
65
|
+
reason: Optional[str] = None,
|
|
65
66
|
context: Optional[ErrorContext] = None,
|
|
66
67
|
) -> None:
|
|
67
68
|
super().__init__(message, context=context)
|
|
68
69
|
self.policy_id = policy_id
|
|
70
|
+
self.reason = reason or message
|
|
69
71
|
|
|
70
72
|
|
|
71
73
|
class QueuedError(VaultKitError):
|
|
@@ -185,10 +187,13 @@ def map_http_error(status_code: int, message: str, *, context: ErrorContext) ->
|
|
|
185
187
|
return AuthError(message, context=context)
|
|
186
188
|
|
|
187
189
|
if status_code == 403:
|
|
188
|
-
|
|
190
|
+
# The intent API answers a denial with {"status": "denied", "policy_id", "reason"};
|
|
191
|
+
# older backends sent {"type": "policy_denied"}.
|
|
192
|
+
if error_type == "policy_denied" or raw.get("status") == "denied":
|
|
189
193
|
return DeniedError(
|
|
190
194
|
message,
|
|
191
195
|
policy_id=raw.get("policy_id"),
|
|
196
|
+
reason=raw.get("reason"),
|
|
192
197
|
context=context,
|
|
193
198
|
)
|
|
194
199
|
return ForbiddenError(message, context=context)
|
|
@@ -42,12 +42,23 @@ class FetchResult:
|
|
|
42
42
|
@property
|
|
43
43
|
def masked_fields(self) -> List[str]:
|
|
44
44
|
"""
|
|
45
|
-
Masked field names from meta
|
|
45
|
+
Masked field names from meta.
|
|
46
|
+
|
|
47
|
+
funl sends meta.masked_fields as a map of field -> mask method
|
|
48
|
+
({"email": "partial"}); a plain list of names is also accepted.
|
|
46
49
|
Returns [] safely if meta is absent or has no masked_fields key.
|
|
47
50
|
"""
|
|
48
51
|
value = (self.meta or {}).get("masked_fields")
|
|
52
|
+
if isinstance(value, dict):
|
|
53
|
+
return list(value.keys())
|
|
49
54
|
return value if isinstance(value, list) else []
|
|
50
55
|
|
|
56
|
+
@property
|
|
57
|
+
def masking_methods(self) -> Dict[str, str]:
|
|
58
|
+
"""Mask method per masked field, e.g. {"email": "partial"}. Empty if the API sent a list."""
|
|
59
|
+
value = (self.meta or {}).get("masked_fields")
|
|
60
|
+
return dict(value) if isinstance(value, dict) else {}
|
|
61
|
+
|
|
51
62
|
@property
|
|
52
63
|
def is_empty(self) -> bool:
|
|
53
64
|
return not self.rows
|
|
@@ -86,7 +86,6 @@ class ToolExecutor:
|
|
|
86
86
|
"name": d.dataset,
|
|
87
87
|
"datasource": d.datasource,
|
|
88
88
|
"visibility": visibility_map.get(getattr(d, "visibility", None), getattr(d, "visibility", None)),
|
|
89
|
-
"field_count": len(getattr(d, "fields", []) or []),
|
|
90
89
|
}
|
|
91
90
|
for d in datasets
|
|
92
91
|
],
|
|
@@ -121,6 +120,7 @@ class ToolExecutor:
|
|
|
121
120
|
}
|
|
122
121
|
|
|
123
122
|
if result.masked_fields:
|
|
123
|
+
response["masked_fields"] = result.masked_fields
|
|
124
124
|
response["note"] = f"Fields {result.masked_fields} were masked per data policy."
|
|
125
125
|
|
|
126
126
|
return response
|
|
@@ -158,12 +158,17 @@ class ToolExecutor:
|
|
|
158
158
|
|
|
159
159
|
if isinstance(exc, DeniedError):
|
|
160
160
|
policy_id = getattr(exc, "policy_id", None)
|
|
161
|
+
reason = getattr(exc, "reason", None) or exc.message
|
|
161
162
|
extra = f" (policy_id={policy_id})" if policy_id else ""
|
|
162
|
-
return
|
|
163
|
-
|
|
164
|
-
"
|
|
165
|
-
|
|
166
|
-
|
|
163
|
+
return {
|
|
164
|
+
"status": "denied",
|
|
165
|
+
"reason": reason,
|
|
166
|
+
"policy_id": policy_id,
|
|
167
|
+
"error": (
|
|
168
|
+
f"Access denied by VaultKit policy{extra}. Reason: {reason}. "
|
|
169
|
+
"Do not retry — this denial is deterministic."
|
|
170
|
+
),
|
|
171
|
+
}
|
|
167
172
|
|
|
168
173
|
if isinstance(exc, ApprovalRequiredError):
|
|
169
174
|
return {
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|