Copilot commented on code in PR #11622:
URL: https://github.com/apache/gravitino/pull/11622#discussion_r3418925161


##########
mcp-server/tests/integration/gravitino_setup.py:
##########
@@ -0,0 +1,140 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+"""Provision Gravitino metadata and authorization fixtures for the integration 
test.
+
+All requests are issued as the service admin using Gravitino simple
+authentication (``Authorization: Basic base64(user:dummy)``). The fixture
+creates a metalake with two model catalogs, a non-admin user ``bob``, and a 
role
+that grants ``bob`` access to only one of the two catalogs. This produces a
+visibly different authorization slice between the admin and ``bob`` principals.
+"""
+
+import base64
+
+import httpx
+
+
+def basic_auth_header(user: str) -> str:
+    """Build a Gravitino simple-auth header value for ``user``."""
+    credential = base64.b64encode(f"{user}:dummy".encode("utf-8")).decode(
+        "ascii"
+    )
+    return f"Basic {credential}"
+
+
+class GravitinoFixture:  # pylint: disable=too-many-instance-attributes
+    """Sets up metalake/catalogs/user/role/grant via the Gravitino REST API."""
+
+    def __init__(  # pylint: 
disable=too-many-positional-arguments,too-many-arguments
+        self,
+        gravitino_uri: str,
+        metalake: str,
+        admin_user: str = "admin",
+        granted_user: str = "bob",
+        catalog_allowed: str = "cat_allowed",
+        catalog_denied: str = "cat_denied",
+        role_name: str = "reader_role",
+    ):
+        self.gravitino_uri = gravitino_uri.rstrip("/")
+        self.metalake = metalake
+        self.admin_user = admin_user
+        self.granted_user = granted_user
+        self.catalog_allowed = catalog_allowed
+        self.catalog_denied = catalog_denied
+        self.role_name = role_name
+        self._client = httpx.Client(
+            base_url=self.gravitino_uri,
+            headers={"Authorization": basic_auth_header(admin_user)},
+            timeout=30.0,
+        )
+
+    def _post(self, path: str, body: dict) -> httpx.Response:
+        response = self._client.post(path, json=body)
+        response.raise_for_status()
+        return response
+
+    def _put(self, path: str, body: dict) -> httpx.Response:
+        response = self._client.put(path, json=body)
+        response.raise_for_status()
+        return response
+
+    def provision(self) -> None:
+        """Create all metadata and authorization fixtures (idempotent-ish)."""
+        self._create_metalake()

Review Comment:
   The provision() docstring claims the fixture is "idempotent-ish", but 
_post/_put always raise for non-2xx responses, so rerunning against an 
already-provisioned metalake will fail (e.g., HTTP 409 conflicts). Either make 
provisioning tolerate existing resources or update the docstring to avoid 
implying idempotency.



##########
mcp-server/mcp_server/server.py:
##########
@@ -21,18 +21,62 @@
 from typing import AsyncIterator
 from urllib.parse import urlparse
 
+import mcp.types as mt
 from fastmcp import FastMCP
 from fastmcp.server.middleware.error_handling import ErrorHandlingMiddleware
 from fastmcp.server.middleware.logging import (
     LoggingMiddleware,
 )
+from fastmcp.server.middleware.middleware import (
+    CallNext,
+    Middleware,
+    MiddlewareContext,
+)
 from fastmcp.server.middleware.timing import TimingMiddleware
+from fastmcp.tools.base import ToolResult
 
-from mcp_server.core.context import GravitinoContext
+from mcp_server.core import audit
+from mcp_server.core.context import (
+    GravitinoContext,
+    _get_request_authorization,
+)
 from mcp_server.core.setting import Setting
 from mcp_server.tools import load_tools
 
 
+def _get_principal_from_request() -> str:
+    """Derive a display principal from the current HTTP request's 
Authorization header.
+
+    Returns "anonymous" in stdio mode or when no token is present.
+    """
+    # pylint: disable=protected-access
+    return audit._extract_principal(_get_request_authorization())
+
+
+class AuditMiddleware(Middleware):
+    """Emit a structured audit record for every tool invocation."""
+
+    async def on_call_tool(
+        self,
+        context: MiddlewareContext[mt.CallToolRequestParams],
+        call_next: CallNext[mt.CallToolRequestParams, ToolResult],
+    ) -> ToolResult:
+        tool_name = context.message.name if context.message else "unknown"
+        principal = _get_principal_from_request()

Review Comment:
   AuditMiddleware derives the principal only from the incoming HTTP request 
header. When running in stdio mode, or in HTTP mode when the request has no 
Authorization header, calls may still be authenticated via the startup --token 
fallback, but the audit record will be logged as "anonymous", which is 
misleading and breaks attribution.



##########
mcp-server/.gitignore:
##########
@@ -34,6 +34,9 @@ logs/
 *.err
 gravitino_mcp_server.egg-info
 
+# Local run artifacts from dev/start_inspector_demo.sh
+.inspector-demo-*.out
+

Review Comment:
   The inspector demo scripts create .inspector-demo-*.pid files, but 
.gitignore only ignores the .out files. This leaves untracked PID files behind 
after running the demo.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to