tollgate 1.0.4__tar.gz → 1.0.5__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.
- {tollgate-1.0.4 → tollgate-1.0.5}/PKG-INFO +1 -1
- {tollgate-1.0.4 → tollgate-1.0.5}/pyproject.toml +1 -1
- {tollgate-1.0.4 → tollgate-1.0.5}/src/tollgate/__init__.py +3 -2
- {tollgate-1.0.4 → tollgate-1.0.5}/src/tollgate/grants.py +46 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/src/tollgate/tower.py +2 -1
- {tollgate-1.0.4 → tollgate-1.0.5}/tests/test_grants.py +19 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/.claude/settings.local.json +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/.gitignore +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/CHANGELOG.md +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/COMPARISON.md +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/CONTRIBUTING.md +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/LICENSE +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/Makefile +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/QUICKSTART.md +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/README.md +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/SECURITY.md +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/examples/mcp_minimal/audit.jsonl +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/examples/mcp_minimal/demo.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/examples/mcp_minimal/manifest.yaml +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/examples/mcp_minimal/policy.yaml +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/examples/mock_tickets/README.md +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/examples/mock_tickets/agent.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/examples/mock_tickets/demo.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/examples/mock_tickets/manifest.yaml +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/examples/mock_tickets/tickets.json +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/examples/mock_tickets/tools.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/examples/strands_minimal/audit.jsonl +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/examples/strands_minimal/demo.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/examples/strands_minimal/manifest.yaml +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/examples/strands_minimal/policy.yaml +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/policies/default.yaml +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/specs/audit_event.schema.json +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/specs/decision.schema.json +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/specs/identity.schema.json +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/specs/intent.schema.json +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/specs/tool_request.schema.json +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/src/tollgate/approvals.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/src/tollgate/audit.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/src/tollgate/exceptions.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/src/tollgate/helpers.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/src/tollgate/integrations/__init__.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/src/tollgate/integrations/mcp.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/src/tollgate/integrations/strands.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/src/tollgate/interceptors/__init__.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/src/tollgate/interceptors/base.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/src/tollgate/interceptors/langchain.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/src/tollgate/interceptors/openai.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/src/tollgate/policy.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/src/tollgate/registry.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/src/tollgate/types.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/tests/test_adapters_v1.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/tests/test_audit_integrity_v1.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/tests/test_deferred_v1.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/tests/test_helpers_v1.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/tests/test_integrations_v1.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/tests/test_policy_v1.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/tests/test_registry_v1.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/tests/test_security_v1.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/tests/test_tower_v1.py +0 -0
- {tollgate-1.0.4 → tollgate-1.0.5}/tests/test_v1_integrations.py +0 -0
|
@@ -14,7 +14,7 @@ from .exceptions import (
|
|
|
14
14
|
TollgateDenied,
|
|
15
15
|
TollgateError,
|
|
16
16
|
)
|
|
17
|
-
from .grants import InMemoryGrantStore
|
|
17
|
+
from .grants import GrantStore, InMemoryGrantStore
|
|
18
18
|
from .helpers import guard, wrap_tool
|
|
19
19
|
from .policy import PolicyEvaluator, YamlPolicyEvaluator
|
|
20
20
|
from .registry import ToolRegistry
|
|
@@ -33,7 +33,7 @@ from .types import (
|
|
|
33
33
|
ToolRequest,
|
|
34
34
|
)
|
|
35
35
|
|
|
36
|
-
__version__ = "1.0.
|
|
36
|
+
__version__ = "1.0.5"
|
|
37
37
|
|
|
38
38
|
__all__ = [
|
|
39
39
|
"ControlTower",
|
|
@@ -45,6 +45,7 @@ __all__ = [
|
|
|
45
45
|
"DecisionType",
|
|
46
46
|
"Effect",
|
|
47
47
|
"Grant",
|
|
48
|
+
"GrantStore",
|
|
48
49
|
"AuditEvent",
|
|
49
50
|
"Outcome",
|
|
50
51
|
"ApprovalOutcome",
|
|
@@ -1,9 +1,55 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import time
|
|
3
|
+
from typing import Protocol, runtime_checkable
|
|
3
4
|
|
|
4
5
|
from .types import AgentContext, Grant, ToolRequest
|
|
5
6
|
|
|
6
7
|
|
|
8
|
+
@runtime_checkable
|
|
9
|
+
class GrantStore(Protocol):
|
|
10
|
+
"""Protocol for grant storage backends.
|
|
11
|
+
|
|
12
|
+
Implement this protocol to use a custom storage backend (Redis, SQLite, etc.).
|
|
13
|
+
|
|
14
|
+
Example Redis implementation:
|
|
15
|
+
|
|
16
|
+
class RedisGrantStore:
|
|
17
|
+
def __init__(self, redis_client):
|
|
18
|
+
self.redis = redis_client
|
|
19
|
+
|
|
20
|
+
async def create_grant(self, grant: Grant) -> str:
|
|
21
|
+
await self.redis.hset(f"grant:{grant.id}", mapping=grant.to_dict())
|
|
22
|
+
await self.redis.expireat(f"grant:{grant.id}", int(grant.expires_at))
|
|
23
|
+
return grant.id
|
|
24
|
+
|
|
25
|
+
async def find_matching_grant(self, agent_ctx, tool_request) -> Grant | None:
|
|
26
|
+
# Implement matching logic with Redis SCAN or secondary indexes
|
|
27
|
+
...
|
|
28
|
+
|
|
29
|
+
All methods must be async. The InMemoryGrantStore serves as the reference implementation.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
async def create_grant(self, grant: Grant) -> str:
|
|
33
|
+
...
|
|
34
|
+
|
|
35
|
+
async def find_matching_grant(
|
|
36
|
+
self, agent_ctx: AgentContext, tool_request: ToolRequest
|
|
37
|
+
) -> Grant | None:
|
|
38
|
+
...
|
|
39
|
+
|
|
40
|
+
async def revoke_grant(self, grant_id: str) -> bool:
|
|
41
|
+
...
|
|
42
|
+
|
|
43
|
+
async def list_active_grants(self, agent_id: str | None = None) -> list[Grant]:
|
|
44
|
+
...
|
|
45
|
+
|
|
46
|
+
async def cleanup_expired(self) -> int:
|
|
47
|
+
...
|
|
48
|
+
|
|
49
|
+
async def get_usage_count(self, grant_id: str) -> int:
|
|
50
|
+
...
|
|
51
|
+
|
|
52
|
+
|
|
7
53
|
class InMemoryGrantStore:
|
|
8
54
|
"""In-memory store for action grants with thread-safe matching logic."""
|
|
9
55
|
|
|
@@ -11,6 +11,7 @@ from .exceptions import (
|
|
|
11
11
|
TollgateDeferred,
|
|
12
12
|
TollgateDenied,
|
|
13
13
|
)
|
|
14
|
+
from .grants import GrantStore
|
|
14
15
|
from .policy import PolicyEvaluator
|
|
15
16
|
from .types import (
|
|
16
17
|
AgentContext,
|
|
@@ -32,7 +33,7 @@ class ControlTower:
|
|
|
32
33
|
policy: PolicyEvaluator,
|
|
33
34
|
approver: Approver,
|
|
34
35
|
audit: AuditSink,
|
|
35
|
-
grant_store:
|
|
36
|
+
grant_store: GrantStore | None = None,
|
|
36
37
|
redact_fn: Callable[[dict[str, Any]], dict[str, Any]] | None = None,
|
|
37
38
|
):
|
|
38
39
|
self.policy = policy
|
|
@@ -284,3 +284,22 @@ async def test_tower_uses_grant(agent_ctx, tool_req):
|
|
|
284
284
|
event = audit.events[-1]
|
|
285
285
|
assert event.grant_id == grant.id
|
|
286
286
|
assert event.outcome == Outcome.EXECUTED
|
|
287
|
+
|
|
288
|
+
@pytest.mark.asyncio
|
|
289
|
+
async def test_grant_store_protocol_compliance():
|
|
290
|
+
"""Verify InMemoryGrantStore implements GrantStore protocol."""
|
|
291
|
+
from tollgate import GrantStore, InMemoryGrantStore
|
|
292
|
+
|
|
293
|
+
store = InMemoryGrantStore()
|
|
294
|
+
|
|
295
|
+
# Protocol requires these methods exist and are callable
|
|
296
|
+
assert hasattr(store, "create_grant")
|
|
297
|
+
assert hasattr(store, "find_matching_grant")
|
|
298
|
+
assert hasattr(store, "revoke_grant")
|
|
299
|
+
assert hasattr(store, "list_active_grants")
|
|
300
|
+
assert hasattr(store, "cleanup_expired")
|
|
301
|
+
assert hasattr(store, "get_usage_count")
|
|
302
|
+
|
|
303
|
+
# Verify it's recognized as implementing the protocol
|
|
304
|
+
# Note: requires runtime_checkable on GrantStore
|
|
305
|
+
assert isinstance(store, GrantStore)
|
|
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
|
|
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
|