proxilion 0.0.1__py3-none-any.whl

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.
Files changed (94) hide show
  1. proxilion/__init__.py +136 -0
  2. proxilion/audit/__init__.py +133 -0
  3. proxilion/audit/base_exporters.py +527 -0
  4. proxilion/audit/compliance/__init__.py +130 -0
  5. proxilion/audit/compliance/base.py +457 -0
  6. proxilion/audit/compliance/eu_ai_act.py +603 -0
  7. proxilion/audit/compliance/iso27001.py +544 -0
  8. proxilion/audit/compliance/soc2.py +491 -0
  9. proxilion/audit/events.py +493 -0
  10. proxilion/audit/explainability.py +1173 -0
  11. proxilion/audit/exporters/__init__.py +58 -0
  12. proxilion/audit/exporters/aws_s3.py +636 -0
  13. proxilion/audit/exporters/azure_storage.py +608 -0
  14. proxilion/audit/exporters/cloud_base.py +468 -0
  15. proxilion/audit/exporters/gcp_storage.py +570 -0
  16. proxilion/audit/exporters/multi_exporter.py +498 -0
  17. proxilion/audit/hash_chain.py +652 -0
  18. proxilion/audit/logger.py +543 -0
  19. proxilion/caching/__init__.py +49 -0
  20. proxilion/caching/tool_cache.py +633 -0
  21. proxilion/context/__init__.py +73 -0
  22. proxilion/context/context_window.py +556 -0
  23. proxilion/context/message_history.py +505 -0
  24. proxilion/context/session.py +735 -0
  25. proxilion/contrib/__init__.py +51 -0
  26. proxilion/contrib/anthropic.py +609 -0
  27. proxilion/contrib/google.py +1012 -0
  28. proxilion/contrib/langchain.py +641 -0
  29. proxilion/contrib/mcp.py +893 -0
  30. proxilion/contrib/openai.py +646 -0
  31. proxilion/core.py +3058 -0
  32. proxilion/decorators.py +966 -0
  33. proxilion/engines/__init__.py +287 -0
  34. proxilion/engines/base.py +266 -0
  35. proxilion/engines/casbin_engine.py +412 -0
  36. proxilion/engines/opa_engine.py +493 -0
  37. proxilion/engines/simple.py +437 -0
  38. proxilion/exceptions.py +887 -0
  39. proxilion/guards/__init__.py +54 -0
  40. proxilion/guards/input_guard.py +522 -0
  41. proxilion/guards/output_guard.py +634 -0
  42. proxilion/observability/__init__.py +198 -0
  43. proxilion/observability/cost_tracker.py +866 -0
  44. proxilion/observability/hooks.py +683 -0
  45. proxilion/observability/metrics.py +798 -0
  46. proxilion/observability/session_cost_tracker.py +1063 -0
  47. proxilion/policies/__init__.py +67 -0
  48. proxilion/policies/base.py +304 -0
  49. proxilion/policies/builtin.py +486 -0
  50. proxilion/policies/registry.py +376 -0
  51. proxilion/providers/__init__.py +201 -0
  52. proxilion/providers/adapter.py +468 -0
  53. proxilion/providers/anthropic_adapter.py +330 -0
  54. proxilion/providers/gemini_adapter.py +391 -0
  55. proxilion/providers/openai_adapter.py +294 -0
  56. proxilion/py.typed +0 -0
  57. proxilion/resilience/__init__.py +81 -0
  58. proxilion/resilience/degradation.py +615 -0
  59. proxilion/resilience/fallback.py +555 -0
  60. proxilion/resilience/retry.py +554 -0
  61. proxilion/scheduling/__init__.py +57 -0
  62. proxilion/scheduling/priority_queue.py +419 -0
  63. proxilion/scheduling/scheduler.py +459 -0
  64. proxilion/security/__init__.py +244 -0
  65. proxilion/security/agent_trust.py +968 -0
  66. proxilion/security/behavioral_drift.py +794 -0
  67. proxilion/security/cascade_protection.py +869 -0
  68. proxilion/security/circuit_breaker.py +428 -0
  69. proxilion/security/cost_limiter.py +690 -0
  70. proxilion/security/idor_protection.py +460 -0
  71. proxilion/security/intent_capsule.py +849 -0
  72. proxilion/security/intent_validator.py +495 -0
  73. proxilion/security/memory_integrity.py +767 -0
  74. proxilion/security/rate_limiter.py +509 -0
  75. proxilion/security/scope_enforcer.py +680 -0
  76. proxilion/security/sequence_validator.py +636 -0
  77. proxilion/security/trust_boundaries.py +784 -0
  78. proxilion/streaming/__init__.py +70 -0
  79. proxilion/streaming/detector.py +761 -0
  80. proxilion/streaming/transformer.py +674 -0
  81. proxilion/timeouts/__init__.py +55 -0
  82. proxilion/timeouts/decorators.py +477 -0
  83. proxilion/timeouts/manager.py +545 -0
  84. proxilion/tools/__init__.py +69 -0
  85. proxilion/tools/decorators.py +493 -0
  86. proxilion/tools/registry.py +732 -0
  87. proxilion/types.py +339 -0
  88. proxilion/validation/__init__.py +93 -0
  89. proxilion/validation/pydantic_schema.py +351 -0
  90. proxilion/validation/schema.py +651 -0
  91. proxilion-0.0.1.dist-info/METADATA +872 -0
  92. proxilion-0.0.1.dist-info/RECORD +94 -0
  93. proxilion-0.0.1.dist-info/WHEEL +4 -0
  94. proxilion-0.0.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,287 @@
1
+ """
2
+ Policy engines for Proxilion.
3
+
4
+ This module provides the policy engine abstraction layer, enabling
5
+ pluggable authorization backends. Available engines:
6
+
7
+ - SimplePolicyEngine: Built-in engine using Policy classes (no dependencies)
8
+ - CasbinPolicyEngine: Integration with Casbin (requires casbin package)
9
+ - OPAPolicyEngine: Integration with Open Policy Agent (requires OPA server)
10
+
11
+ Quick Start:
12
+ >>> from proxilion.engines import EngineFactory
13
+ >>>
14
+ >>> # Create a simple engine
15
+ >>> engine = EngineFactory.create("simple")
16
+ >>>
17
+ >>> # Or create with configuration
18
+ >>> engine = EngineFactory.create("casbin", {
19
+ ... "model_path": "model.conf",
20
+ ... "policy_path": "policy.csv",
21
+ ... })
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import logging
27
+ from typing import Any
28
+
29
+ from proxilion.engines.base import (
30
+ BasePolicyEngine,
31
+ EngineCapabilities,
32
+ EngineNotAvailableError,
33
+ PolicyEngine,
34
+ PolicyEngineError,
35
+ PolicyEvaluationError,
36
+ PolicyLoadError,
37
+ )
38
+ from proxilion.engines.simple import SimplePolicyEngine
39
+
40
+ logger = logging.getLogger(__name__)
41
+
42
+ # Type alias for engine types
43
+ EngineType = str # "simple", "casbin", "opa"
44
+
45
+
46
+ class EngineFactory:
47
+ """
48
+ Factory for creating policy engine instances.
49
+
50
+ The EngineFactory provides a unified interface for creating
51
+ policy engines based on configuration. It handles dependency
52
+ checking and provides helpful error messages when optional
53
+ engines are not available.
54
+
55
+ Example:
56
+ >>> # Create with default configuration
57
+ >>> engine = EngineFactory.create("simple")
58
+ >>>
59
+ >>> # Create with custom configuration
60
+ >>> engine = EngineFactory.create("casbin", {
61
+ ... "model_path": "model.conf",
62
+ ... "policy_path": "policy.csv",
63
+ ... })
64
+ >>>
65
+ >>> # Create OPA engine
66
+ >>> engine = EngineFactory.create("opa", {
67
+ ... "opa_url": "http://localhost:8181",
68
+ ... "policy_path": "v1/data/myapp/authz",
69
+ ... })
70
+
71
+ Available Engine Types:
72
+ - "simple": Built-in SimplePolicyEngine (always available)
73
+ - "casbin": CasbinPolicyEngine (requires casbin package)
74
+ - "opa": OPAPolicyEngine (requires OPA server)
75
+ """
76
+
77
+ # Registry of available engine types
78
+ _engines: dict[str, type[BasePolicyEngine]] = {
79
+ "simple": SimplePolicyEngine,
80
+ }
81
+
82
+ @classmethod
83
+ def create(
84
+ cls,
85
+ engine_type: str = "simple",
86
+ config: dict[str, Any] | None = None,
87
+ ) -> BasePolicyEngine:
88
+ """
89
+ Create a policy engine instance.
90
+
91
+ Args:
92
+ engine_type: The type of engine to create.
93
+ config: Engine-specific configuration.
94
+
95
+ Returns:
96
+ An initialized policy engine instance.
97
+
98
+ Raises:
99
+ EngineNotAvailableError: If the engine type is not available.
100
+ PolicyEngineError: If engine initialization fails.
101
+
102
+ Example:
103
+ >>> engine = EngineFactory.create("simple")
104
+ >>> engine = EngineFactory.create("casbin", {
105
+ ... "model_path": "model.conf",
106
+ ... "policy_path": "policy.csv",
107
+ ... })
108
+ """
109
+ engine_type = engine_type.lower()
110
+
111
+ # Handle casbin engine (lazy import due to optional dependency)
112
+ if engine_type == "casbin":
113
+ return cls._create_casbin_engine(config)
114
+
115
+ # Handle OPA engine
116
+ if engine_type == "opa":
117
+ return cls._create_opa_engine(config)
118
+
119
+ # Check built-in engines
120
+ if engine_type not in cls._engines:
121
+ available = cls.get_available_engines()
122
+ raise EngineNotAvailableError(
123
+ f"Unknown engine type: '{engine_type}'. "
124
+ f"Available engines: {', '.join(available)}",
125
+ engine_name=engine_type,
126
+ )
127
+
128
+ engine_class = cls._engines[engine_type]
129
+ return engine_class(config)
130
+
131
+ @classmethod
132
+ def _create_casbin_engine(
133
+ cls,
134
+ config: dict[str, Any] | None,
135
+ ) -> BasePolicyEngine:
136
+ """Create a Casbin engine instance."""
137
+ try:
138
+ from proxilion.engines.casbin_engine import CasbinPolicyEngine
139
+ return CasbinPolicyEngine(config)
140
+ except ImportError:
141
+ raise EngineNotAvailableError(
142
+ "Casbin engine requires the 'casbin' package. "
143
+ "Install with: pip install proxilion[casbin]",
144
+ engine_name="casbin",
145
+ ) from None
146
+
147
+ @classmethod
148
+ def _create_opa_engine(
149
+ cls,
150
+ config: dict[str, Any] | None,
151
+ ) -> BasePolicyEngine:
152
+ """Create an OPA engine instance."""
153
+ from proxilion.engines.opa_engine import OPAPolicyEngine
154
+ return OPAPolicyEngine(config)
155
+
156
+ @classmethod
157
+ def register(
158
+ cls,
159
+ engine_type: str,
160
+ engine_class: type[BasePolicyEngine],
161
+ ) -> None:
162
+ """
163
+ Register a custom engine type.
164
+
165
+ This allows extending Proxilion with custom policy engines.
166
+
167
+ Args:
168
+ engine_type: The name for this engine type.
169
+ engine_class: The engine class to register.
170
+
171
+ Example:
172
+ >>> class MyCustomEngine(BasePolicyEngine):
173
+ ... def evaluate(self, user, action, resource, context=None):
174
+ ... # Custom logic
175
+ ... pass
176
+ >>>
177
+ >>> EngineFactory.register("custom", MyCustomEngine)
178
+ >>> engine = EngineFactory.create("custom")
179
+ """
180
+ cls._engines[engine_type.lower()] = engine_class
181
+ logger.debug(f"Registered engine type: {engine_type}")
182
+
183
+ @classmethod
184
+ def unregister(cls, engine_type: str) -> bool:
185
+ """
186
+ Unregister an engine type.
187
+
188
+ Args:
189
+ engine_type: The engine type to remove.
190
+
191
+ Returns:
192
+ True if the engine was unregistered.
193
+ """
194
+ engine_type = engine_type.lower()
195
+ if engine_type in cls._engines and engine_type != "simple":
196
+ del cls._engines[engine_type]
197
+ return True
198
+ return False
199
+
200
+ @classmethod
201
+ def get_available_engines(cls) -> list[str]:
202
+ """
203
+ Get a list of available engine types.
204
+
205
+ Returns:
206
+ List of engine type names that can be created.
207
+ """
208
+ engines = list(cls._engines.keys())
209
+
210
+ # Check if casbin is available
211
+ try:
212
+ import casbin # noqa: F401
213
+ engines.append("casbin")
214
+ except ImportError:
215
+ pass
216
+
217
+ # OPA is always "available" (it just needs a server)
218
+ engines.append("opa")
219
+
220
+ return sorted(set(engines))
221
+
222
+ @classmethod
223
+ def is_available(cls, engine_type: str) -> bool:
224
+ """
225
+ Check if an engine type is available.
226
+
227
+ Args:
228
+ engine_type: The engine type to check.
229
+
230
+ Returns:
231
+ True if the engine can be created.
232
+ """
233
+ engine_type = engine_type.lower()
234
+
235
+ if engine_type in cls._engines:
236
+ return True
237
+
238
+ if engine_type == "casbin":
239
+ try:
240
+ import casbin # noqa: F401
241
+ return True
242
+ except ImportError:
243
+ return False
244
+
245
+ return engine_type == "opa"
246
+
247
+
248
+ # Convenience function for creating engines
249
+ def create_engine(
250
+ engine_type: str = "simple",
251
+ config: dict[str, Any] | None = None,
252
+ ) -> BasePolicyEngine:
253
+ """
254
+ Create a policy engine instance.
255
+
256
+ Convenience function that delegates to EngineFactory.create().
257
+
258
+ Args:
259
+ engine_type: The type of engine to create.
260
+ config: Engine-specific configuration.
261
+
262
+ Returns:
263
+ An initialized policy engine instance.
264
+
265
+ Example:
266
+ >>> from proxilion.engines import create_engine
267
+ >>> engine = create_engine("simple")
268
+ """
269
+ return EngineFactory.create(engine_type, config)
270
+
271
+
272
+ __all__ = [
273
+ # Protocol and base classes
274
+ "PolicyEngine",
275
+ "BasePolicyEngine",
276
+ "EngineCapabilities",
277
+ # Engines
278
+ "SimplePolicyEngine",
279
+ # Factory
280
+ "EngineFactory",
281
+ "create_engine",
282
+ # Exceptions
283
+ "PolicyEngineError",
284
+ "PolicyLoadError",
285
+ "PolicyEvaluationError",
286
+ "EngineNotAvailableError",
287
+ ]
@@ -0,0 +1,266 @@
1
+ """
2
+ Policy engine base classes and protocols for Proxilion.
3
+
4
+ This module defines the PolicyEngine protocol that all policy engines
5
+ must implement, enabling pluggable authorization backends like
6
+ Casbin, OPA, or custom implementations.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import asyncio
12
+ from abc import ABC, abstractmethod
13
+ from pathlib import Path
14
+ from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
15
+
16
+ if TYPE_CHECKING:
17
+ from proxilion.types import AuthorizationResult, UserContext
18
+
19
+
20
+ @runtime_checkable
21
+ class PolicyEngine(Protocol):
22
+ """
23
+ Protocol defining the interface for policy engines.
24
+
25
+ All policy engines must implement this protocol to be compatible
26
+ with Proxilion. This includes the built-in SimplePolicyEngine,
27
+ as well as optional integrations like Casbin and OPA.
28
+
29
+ The protocol requires both synchronous and asynchronous evaluation
30
+ methods to support different application architectures.
31
+
32
+ Example:
33
+ >>> class CustomEngine:
34
+ ... def evaluate(
35
+ ... self, user: UserContext, action: str,
36
+ ... resource: str, context: dict
37
+ ... ) -> AuthorizationResult:
38
+ ... # Custom authorization logic
39
+ ... return AuthorizationResult(allowed=True, reason="Custom check passed")
40
+ ...
41
+ ... async def evaluate_async(
42
+ ... self, user: UserContext, action: str,
43
+ ... resource: str, context: dict
44
+ ... ) -> AuthorizationResult:
45
+ ... return self.evaluate(user, action, resource, context)
46
+ ...
47
+ ... def load_policies(self, source: str | Path) -> None:
48
+ ... pass
49
+ """
50
+
51
+ def evaluate(
52
+ self,
53
+ user: UserContext,
54
+ action: str,
55
+ resource: str,
56
+ context: dict[str, Any] | None = None,
57
+ ) -> AuthorizationResult:
58
+ """
59
+ Evaluate an authorization request synchronously.
60
+
61
+ Args:
62
+ user: The user context containing identity and roles.
63
+ action: The action being attempted (e.g., "read", "execute").
64
+ resource: The resource being accessed (e.g., "database_query").
65
+ context: Additional context for the authorization decision.
66
+
67
+ Returns:
68
+ AuthorizationResult indicating whether the action is allowed.
69
+ """
70
+ ...
71
+
72
+ async def evaluate_async(
73
+ self,
74
+ user: UserContext,
75
+ action: str,
76
+ resource: str,
77
+ context: dict[str, Any] | None = None,
78
+ ) -> AuthorizationResult:
79
+ """
80
+ Evaluate an authorization request asynchronously.
81
+
82
+ This method is useful for engines that need to make external
83
+ calls (e.g., OPA server) or perform I/O operations.
84
+
85
+ Args:
86
+ user: The user context containing identity and roles.
87
+ action: The action being attempted.
88
+ resource: The resource being accessed.
89
+ context: Additional context for the authorization decision.
90
+
91
+ Returns:
92
+ AuthorizationResult indicating whether the action is allowed.
93
+ """
94
+ ...
95
+
96
+ def load_policies(self, source: str | Path) -> None:
97
+ """
98
+ Load policies from a source.
99
+
100
+ The source format depends on the engine:
101
+ - SimplePolicyEngine: Python module path or directory
102
+ - CasbinPolicyEngine: Path to policy.csv file
103
+ - OPAPolicyEngine: Path to Rego files or OPA bundle
104
+
105
+ Args:
106
+ source: Path or identifier for the policy source.
107
+ """
108
+ ...
109
+
110
+
111
+ class BasePolicyEngine(ABC):
112
+ """
113
+ Abstract base class for policy engines.
114
+
115
+ Provides common functionality and default implementations
116
+ for policy engines. Engines can extend this class instead
117
+ of implementing the Protocol directly.
118
+
119
+ Attributes:
120
+ name: Human-readable name for the engine.
121
+ config: Configuration dictionary passed during initialization.
122
+ """
123
+
124
+ name: str = "base"
125
+
126
+ def __init__(self, config: dict[str, Any] | None = None) -> None:
127
+ """
128
+ Initialize the policy engine.
129
+
130
+ Args:
131
+ config: Engine-specific configuration options.
132
+ """
133
+ self.config = config or {}
134
+ self._initialized = False
135
+
136
+ @abstractmethod
137
+ def evaluate(
138
+ self,
139
+ user: UserContext,
140
+ action: str,
141
+ resource: str,
142
+ context: dict[str, Any] | None = None,
143
+ ) -> AuthorizationResult:
144
+ """
145
+ Evaluate an authorization request.
146
+
147
+ Subclasses must implement this method.
148
+
149
+ Args:
150
+ user: The user context.
151
+ action: The action being attempted.
152
+ resource: The resource being accessed.
153
+ context: Additional context.
154
+
155
+ Returns:
156
+ AuthorizationResult with the decision.
157
+ """
158
+ pass
159
+
160
+ async def evaluate_async(
161
+ self,
162
+ user: UserContext,
163
+ action: str,
164
+ resource: str,
165
+ context: dict[str, Any] | None = None,
166
+ ) -> AuthorizationResult:
167
+ """
168
+ Async version of evaluate.
169
+
170
+ Default implementation wraps the sync version.
171
+ Override for truly async implementations.
172
+
173
+ Args:
174
+ user: The user context.
175
+ action: The action being attempted.
176
+ resource: The resource being accessed.
177
+ context: Additional context.
178
+
179
+ Returns:
180
+ AuthorizationResult with the decision.
181
+ """
182
+ # Run sync version in thread pool for non-blocking behavior
183
+ loop = asyncio.get_event_loop()
184
+ return await loop.run_in_executor(
185
+ None, self.evaluate, user, action, resource, context
186
+ )
187
+
188
+ def load_policies(self, source: str | Path) -> None:
189
+ """
190
+ Load policies from a source.
191
+
192
+ Default implementation does nothing. Override in subclasses
193
+ that support external policy loading.
194
+
195
+ Args:
196
+ source: Path or identifier for policy source.
197
+ """
198
+ pass
199
+
200
+ def is_initialized(self) -> bool:
201
+ """Check if the engine has been initialized."""
202
+ return self._initialized
203
+
204
+ def get_config(self, key: str, default: Any = None) -> Any:
205
+ """Get a configuration value."""
206
+ return self.config.get(key, default)
207
+
208
+
209
+ class EngineCapabilities:
210
+ """
211
+ Describes the capabilities of a policy engine.
212
+
213
+ This class helps the Proxilion core understand what features
214
+ an engine supports, enabling graceful degradation when features
215
+ are unavailable.
216
+ """
217
+
218
+ def __init__(
219
+ self,
220
+ supports_async: bool = True,
221
+ supports_caching: bool = False,
222
+ supports_explain: bool = False,
223
+ supports_partial_eval: bool = False,
224
+ supports_hot_reload: bool = False,
225
+ max_batch_size: int = 1,
226
+ ) -> None:
227
+ """
228
+ Initialize engine capabilities.
229
+
230
+ Args:
231
+ supports_async: Whether async evaluation is truly async.
232
+ supports_caching: Whether the engine caches decisions.
233
+ supports_explain: Whether the engine can explain decisions.
234
+ supports_partial_eval: Whether partial evaluation is supported.
235
+ supports_hot_reload: Whether policies can be reloaded at runtime.
236
+ max_batch_size: Maximum number of requests in batch evaluation.
237
+ """
238
+ self.supports_async = supports_async
239
+ self.supports_caching = supports_caching
240
+ self.supports_explain = supports_explain
241
+ self.supports_partial_eval = supports_partial_eval
242
+ self.supports_hot_reload = supports_hot_reload
243
+ self.max_batch_size = max_batch_size
244
+
245
+
246
+ class PolicyEngineError(Exception):
247
+ """Base exception for policy engine errors."""
248
+
249
+ def __init__(self, message: str, engine_name: str | None = None) -> None:
250
+ self.engine_name = engine_name
251
+ super().__init__(f"[{engine_name or 'unknown'}] {message}")
252
+
253
+
254
+ class PolicyLoadError(PolicyEngineError):
255
+ """Raised when policies fail to load."""
256
+ pass
257
+
258
+
259
+ class PolicyEvaluationError(PolicyEngineError):
260
+ """Raised when policy evaluation fails."""
261
+ pass
262
+
263
+
264
+ class EngineNotAvailableError(PolicyEngineError):
265
+ """Raised when a required engine is not available."""
266
+ pass