Spaces:
Running
Running
File size: 7,446 Bytes
d817b84 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 | """
FastMCP Policy Engine Example
This example demonstrates the pluggable policy engine with:
- Minimum Necessary Access Policy
- RBAC Policy
- Policy evaluation via HTTP endpoint
- Hot reloading of policies
"""
import asyncio
import json
from pathlib import Path
from fastmcp import FastMCP
from fastmcp.policy import PolicyEngine
from fastmcp.policy.policies import MinimumNecessaryAccessPolicy, RBACPolicy
def create_policy_example():
"""Create a FastMCP server with policy engine enabled."""
# Create server
server = FastMCP("Policy Example Server")
# Enable policy engine
policy_engine = server.enable_policy_engine()
# Register built-in policies
policy_engine.register_policy(MinimumNecessaryAccessPolicy())
policy_engine.register_policy(RBACPolicy())
# Add a simple tool that demonstrates policy evaluation
@server.tool
async def access_resource(user_id: str, action: str, resource_type: str) -> dict:
"""Access a resource with policy evaluation."""
# Create context for policy evaluation
context = {
"user": {
"id": user_id,
"roles": ["user"] if user_id != "admin" else ["admin"],
"permissions": ["read", "write"] if user_id != "admin" else ["*"]
},
"action": action,
"resource": {
"type": resource_type,
"id": f"{resource_type}_123",
"owner": user_id,
"visibility": "private"
}
}
# Evaluate policies
decision = await policy_engine.evaluate(context)
return {
"access_granted": decision.allow,
"reason": decision.reason,
"obligations": decision.obligations,
"proof": decision.proof
}
return server
async def demonstrate_policy_evaluation():
"""Demonstrate policy evaluation with different scenarios."""
server = create_policy_example()
policy_engine = server.get_policy_engine()
assert policy_engine is not None # Ensure policy engine is enabled
print("π FastMCP Policy Engine Example")
print("=" * 50)
# Test scenarios
scenarios = [
{
"name": "Regular user reading document",
"user_id": "user123",
"action": "read",
"resource_type": "document"
},
{
"name": "Regular user deleting sensitive data",
"user_id": "user123",
"action": "delete",
"resource_type": "user_data"
},
{
"name": "Admin accessing sensitive data",
"user_id": "admin",
"action": "delete",
"resource_type": "user_data"
},
{
"name": "User without roles",
"user_id": "guest",
"action": "read",
"resource_type": "document"
}
]
for scenario in scenarios:
print(f"\nπ Scenario: {scenario['name']}")
print("-" * 40)
# Create context
context = {
"user": {
"id": scenario["user_id"],
"roles": ["user"] if scenario["user_id"] != "admin" else ["admin"],
"permissions": ["read", "write"] if scenario["user_id"] != "admin" else ["*"]
},
"action": scenario["action"],
"resource": {
"type": scenario["resource_type"],
"id": f"{scenario['resource_type']}_123",
"owner": scenario["user_id"],
"visibility": "private"
}
}
# Evaluate policies
decision = await policy_engine.evaluate(context)
print(f"User: {scenario['user_id']}")
print(f"Action: {scenario['action']}")
print(f"Resource: {scenario['resource_type']}")
print(f"Decision: {'β
ALLOW' if decision.allow else 'β DENY'}")
print(f"Reason: {decision.reason}")
if decision.obligations:
print("Obligations:")
for obligation in decision.obligations:
print(f" - {obligation.get('type', 'unknown')}: {obligation.get('description', 'No description')}")
if decision.proof:
print(f"Proof: {json.dumps(decision.proof, indent=2)}")
def create_yaml_config():
"""Create a YAML configuration file for policies."""
config_content = """
policies:
- name: custom_minimum_necessary
type: minimum_necessary
parameters:
required_justification: true
sensitive_actions:
- "delete"
- "admin"
- "privileged"
sensitive_resources:
- "user_data"
- "financial"
- "medical"
- name: custom_rbac
type: rbac
parameters:
version: "1.0.0"
roles:
admin:
description: "Administrator with full access"
permissions: ["*"]
user:
description: "Regular user with basic access"
permissions: ["read", "write"]
guest:
description: "Guest user with read-only access"
permissions: ["read"]
"""
config_path = Path("policies.yaml")
config_path.write_text(config_content)
print(f"π Created YAML config: {config_path}")
return config_path
async def demonstrate_hot_reload():
"""Demonstrate hot reloading of policies."""
print("\nπ Hot Reload Demonstration")
print("=" * 50)
# Create server with policy engine
server = create_policy_example()
policy_engine = server.get_policy_engine()
assert policy_engine is not None # Ensure policy engine is enabled
# Register policy classes for YAML loading
policy_engine.registry.register_policy_class("minimum_necessary", MinimumNecessaryAccessPolicy)
policy_engine.registry.register_policy_class("rbac", RBACPolicy)
# Create YAML config
config_path = create_yaml_config()
# Load policies from YAML
print("Loading policies from YAML...")
policy_engine.registry.load_policies_from_yaml(config_path)
# List loaded policies
policies = policy_engine.get_policy_metadata()
print(f"Loaded {len(policies)} policies:")
for policy in policies:
print(f" - {policy['name']} ({policy['type']}) v{policy['version']}")
# Demonstrate hot reload
print("\nHot reloading policies...")
policy_engine.registry.hot_reload_policies(config_path)
# Clean up
config_path.unlink()
print("β
Hot reload demonstration complete")
def main():
"""Run the policy engine example."""
async def run_example():
# Demonstrate policy evaluation
await demonstrate_policy_evaluation()
# Demonstrate hot reload
await demonstrate_hot_reload()
print("\nπ Policy Engine Example Complete!")
print("\nTo test the HTTP endpoint:")
print("1. Run: fastmcp run examples/policy_example.py --transport http")
print("2. POST to /policy/evaluate with JSON body:")
print("""
{
"context": {
"user": {"id": "user123", "roles": ["user"]},
"action": "read",
"resource": {"type": "document", "id": "doc123"}
}
}
""")
asyncio.run(run_example())
if __name__ == "__main__":
main()
|