Coverage for mcpgateway / plugins / framework / hooks / http.py: 98%
57 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-02-11 07:10 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-02-11 07:10 +0000
1# -*- coding: utf-8 -*-
2"""Location: ./mcpgateway/plugins/framework/models/http.py
3Copyright 2025
4SPDX-License-Identifier: Apache-2.0
5Authors: Teryl Taylor
7Pydantic models for http hooks and payloads.
8"""
10# Standard
11from enum import Enum
13# Third-Party
14from pydantic import RootModel
16# First-Party
17from mcpgateway.plugins.framework.models import PluginPayload, PluginResult
20class HttpHeaderPayload(RootModel[dict[str, str]], PluginPayload):
21 """An HTTP dictionary of headers used in the pre/post HTTP forwarding hooks."""
23 def __iter__(self): # type: ignore[no-untyped-def]
24 """Custom iterator function to override root attribute.
26 Returns:
27 A custom iterator for header dictionary.
28 """
29 return iter(self.root)
31 def __getitem__(self, item: str) -> str:
32 """Custom getitem function to override root attribute.
34 Args:
35 item: The http header key.
37 Returns:
38 A custom accesser for the header dictionary.
39 """
40 return self.root[item]
42 def __setitem__(self, key: str, value: str) -> None:
43 """Custom setitem function to override root attribute.
45 Args:
46 key: The http header key.
47 value: The http header value to be set.
48 """
49 self.root[key] = value
51 def __len__(self) -> int:
52 """Custom len function to override root attribute.
54 Returns:
55 The len of the header dictionary.
56 """
57 return len(self.root)
60HttpHeaderPayloadResult = PluginResult[HttpHeaderPayload]
63class HttpHookType(str, Enum):
64 """Hook types for HTTP request processing and authentication.
66 These hooks allow plugins to:
67 1. Transform request headers before processing (middleware layer)
68 2. Implement custom user authentication systems (auth layer)
69 3. Check and grant permissions (RBAC layer)
70 4. Process responses after request completion (middleware layer)
71 """
73 HTTP_PRE_REQUEST = "http_pre_request"
74 HTTP_POST_REQUEST = "http_post_request"
75 HTTP_AUTH_RESOLVE_USER = "http_auth_resolve_user"
76 HTTP_AUTH_CHECK_PERMISSION = "http_auth_check_permission"
79class HttpPreRequestPayload(PluginPayload):
80 """Payload for HTTP pre-request hook (middleware layer).
82 This payload contains immutable request metadata and a copy of headers
83 that plugins can inspect. Invoked before any authentication processing.
84 Plugins return only modified headers via PluginResult[HttpHeaderPayload].
86 Attributes:
87 path: HTTP path being requested.
88 method: HTTP method (GET, POST, etc.).
89 client_host: Client IP address (if available).
90 client_port: Client port (if available).
91 headers: Copy of HTTP headers that plugins can inspect and modify.
92 """
94 path: str
95 method: str
96 client_host: str | None = None
97 client_port: int | None = None
98 headers: HttpHeaderPayload
101class HttpPostRequestPayload(HttpPreRequestPayload):
102 """Payload for HTTP post-request hook (middleware layer).
104 Extends HttpPreRequestPayload with response information.
105 Invoked after request processing is complete.
106 Plugins can inspect response headers and status codes.
108 Attributes:
109 response_headers: Response headers from the request (if available).
110 status_code: HTTP status code from the response (if available).
111 """
113 response_headers: HttpHeaderPayload | None = None
114 status_code: int | None = None
117class HttpAuthResolveUserPayload(PluginPayload):
118 """Payload for custom user authentication hook (auth layer).
120 Invoked inside get_current_user() to allow plugins to provide
121 custom authentication mechanisms (LDAP, mTLS, external auth, etc.).
122 Plugins return an authenticated user via PluginResult[dict].
124 Attributes:
125 credentials: The HTTP authorization credentials from bearer_scheme (if present).
126 headers: Full request headers for custom auth extraction.
127 client_host: Client IP address (if available).
128 client_port: Client port (if available).
129 """
131 credentials: dict | None = None # HTTPAuthorizationCredentials serialized
132 headers: HttpHeaderPayload
133 client_host: str | None = None
134 client_port: int | None = None
137class HttpAuthCheckPermissionPayload(PluginPayload):
138 """Payload for permission checking hook (RBAC layer).
140 Invoked before RBAC permission checks to allow plugins to:
141 - Grant/deny permissions based on custom logic (e.g., token-based auth)
142 - Bypass RBAC for certain authentication methods
143 - Add additional permission checks (e.g., time-based, IP-based)
144 - Implement custom authorization logic
146 Attributes:
147 user_email: Email of the authenticated user
148 permission: Required permission being checked (e.g., "tools.read", "servers.write")
149 resource_type: Type of resource being accessed (e.g., "tool", "server", "prompt")
150 team_id: Team context for the permission check (if applicable)
151 is_admin: Whether the user has admin privileges
152 auth_method: Authentication method used (e.g., "simple_token", "jwt", "oauth")
153 client_host: Client IP address for IP-based permission checks
154 user_agent: User agent string for device-based permission checks
155 """
157 user_email: str
158 permission: str
159 resource_type: str | None = None
160 team_id: str | None = None
161 is_admin: bool = False
162 auth_method: str | None = None
163 client_host: str | None = None
164 user_agent: str | None = None
167class HttpAuthCheckPermissionResultPayload(PluginPayload):
168 """Result payload for permission checking hook.
170 Plugins return this to indicate whether permission should be granted.
172 Attributes:
173 granted: Whether permission is granted (True) or denied (False)
174 reason: Optional reason for the decision (for logging/auditing)
175 """
177 granted: bool
178 reason: str | None = None
181# Type aliases for hook results
182HttpPreRequestResult = PluginResult[HttpHeaderPayload]
183HttpPostRequestResult = PluginResult[HttpHeaderPayload]
184HttpAuthResolveUserResult = PluginResult[dict] # Returns user dict (EmailUser serialized)
185HttpAuthCheckPermissionResult = PluginResult[HttpAuthCheckPermissionResultPayload]
188def _register_http_auth_hooks() -> None:
189 """Register HTTP authentication and request hooks in the global registry.
191 This is called lazily to avoid circular import issues.
192 Registers four hook types:
193 - HTTP_PRE_REQUEST: Transform headers before authentication (middleware)
194 - HTTP_POST_REQUEST: Inspect response after request completion (middleware)
195 - HTTP_AUTH_RESOLVE_USER: Custom user authentication (auth layer)
196 - HTTP_AUTH_CHECK_PERMISSION: Custom permission checking (RBAC layer)
197 """
198 # Import here to avoid circular dependency at module load time
199 # First-Party
200 from mcpgateway.plugins.framework.hooks.registry import get_hook_registry # pylint: disable=import-outside-toplevel
202 registry = get_hook_registry()
204 # Only register if not already registered (idempotent)
205 if not registry.is_registered(HttpHookType.HTTP_PRE_REQUEST): 205 ↛ exitline 205 didn't return from function '_register_http_auth_hooks' because the condition on line 205 was always true
206 registry.register_hook(HttpHookType.HTTP_PRE_REQUEST, HttpPreRequestPayload, HttpPreRequestResult)
207 registry.register_hook(HttpHookType.HTTP_POST_REQUEST, HttpPostRequestPayload, HttpPostRequestResult)
208 registry.register_hook(HttpHookType.HTTP_AUTH_RESOLVE_USER, HttpAuthResolveUserPayload, HttpAuthResolveUserResult)
209 registry.register_hook(HttpHookType.HTTP_AUTH_CHECK_PERMISSION, HttpAuthCheckPermissionPayload, HttpAuthCheckPermissionResult)
212_register_http_auth_hooks()