Skip to content

Commit 89d113e

Browse files
committed
resolve user initiator ID from JWT token claim in audit events
1 parent dd2b7af commit 89d113e

4 files changed

Lines changed: 236 additions & 133 deletions

File tree

src/sap_cloud_sdk/agentgateway/agw_client.py

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,6 @@ async def call_mcp_tool(
499499
tool: MCPTool,
500500
user_token: str | Callable[[], str] | None = None,
501501
app_tid: str | None = None,
502-
user_id: str | None = None,
503502
**kwargs,
504503
) -> str:
505504
"""Invoke an MCP tool.
@@ -524,8 +523,6 @@ async def call_mcp_tool(
524523
for tenant-scoped token exchange.
525524
TODO: This parameter's requirement is still being clarified with
526525
the IBD team and may be removed if unnecessary.
527-
user_id: User identifier recorded in the audit event when an
528-
audit_client is configured on the client.
529526
**kwargs: Tool input parameters (passed directly to the tool).
530527
531528
Returns:
@@ -568,7 +565,11 @@ async def call_mcp_tool(
568565
auth = await self.get_system_auth(app_tid)
569566

570567
_send_audit_event(
571-
self._audit_client, McpToolEvent.INVOKED, {"tool": tool.name}, user_id, self._config.audit_log_mode
568+
self._audit_client,
569+
McpToolEvent.INVOKED,
570+
{"tool": tool.name},
571+
user_token,
572+
self._config.audit_log_mode,
572573
)
573574
try:
574575
result = await call_mcp_tool_customer(
@@ -579,12 +580,16 @@ async def call_mcp_tool(
579580
self._audit_client,
580581
McpToolEvent.FAILED,
581582
{"tool": tool.name, "error_type": type(e).__name__},
582-
user_id,
583+
user_token,
583584
self._config.audit_log_mode,
584585
)
585586
raise
586587
_send_audit_event(
587-
self._audit_client, McpToolEvent.COMPLETED, {"tool": tool.name}, user_id, self._config.audit_log_mode
588+
self._audit_client,
589+
McpToolEvent.COMPLETED,
590+
{"tool": tool.name},
591+
user_token,
592+
self._config.audit_log_mode,
588593
)
589594
return result
590595

@@ -593,17 +598,30 @@ async def call_mcp_tool(
593598
logger.warning("app_tid parameter ignored for LoB agent flow")
594599

595600
auth = await self.get_user_auth(user_token, app_tid)
596-
_send_audit_event(self._audit_client, McpToolEvent.INVOKED, {"tool": tool.name}, user_id)
601+
_send_audit_event(
602+
self._audit_client,
603+
McpToolEvent.INVOKED,
604+
{"tool": tool.name},
605+
user_token,
606+
)
597607
try:
598608
result = await call_mcp_tool_lob(
599609
tool, auth.access_token, self._config.timeout, **kwargs
600610
)
601611
except Exception as e:
602612
_send_audit_event(
603-
self._audit_client, McpToolEvent.FAILED, {"tool": tool.name, "error_type": type(e).__name__}, user_id
613+
self._audit_client,
614+
McpToolEvent.FAILED,
615+
{"tool": tool.name, "error_type": type(e).__name__},
616+
user_token,
604617
)
605618
raise
606-
_send_audit_event(self._audit_client, McpToolEvent.COMPLETED, {"tool": tool.name}, user_id)
619+
_send_audit_event(
620+
self._audit_client,
621+
McpToolEvent.COMPLETED,
622+
{"tool": tool.name},
623+
user_token,
624+
)
607625
return result
608626

609627
except AgentGatewaySDKError:

src/sap_cloud_sdk/core/auditlog_ng/cross_module_helper.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
auditevent_pb2 as pb,
2121
)
2222
from sap_cloud_sdk.core.telemetry import Module, get_tenant_id
23+
from sap_cloud_sdk.ias import parse_token
2324

2425
logger = logging.getLogger(__name__)
2526

@@ -44,7 +45,7 @@ def _emit_custom_event(
4445
tenant_id: str,
4546
event_name: str,
4647
payload: dict,
47-
user_id: str | None = None,
48+
user_token: str | Callable[[], str] | None = None,
4849
) -> None:
4950
"""Build and send a ZzzCustomEvent to the audit log service.
5051
@@ -54,12 +55,14 @@ def _emit_custom_event(
5455
event_name: Event identifier (e.g. ``"MCP_TOOL_INVOKED"``).
5556
payload: Arbitrary key/value pairs serialized into the custom struct.
5657
``event_name`` is always included automatically.
57-
user_id: Optional user initiator ID stamped on the event.
58+
user_token: Optional user JWT. The user initiator ID is resolved from
59+
``scim_id`` or ``sub`` claims via ``_resolve_user_id``.
5860
"""
5961
common = pb.Common()
6062
common.timestamp.FromDatetime(datetime.now(timezone.utc))
6163
common.tenant_id = tenant_id
6264
common.app_context["event_name"] = event_name
65+
user_id = _resolve_user_id(user_token)
6366
if user_id:
6467
common.user_initiator_id = user_id
6568

@@ -77,6 +80,17 @@ def _resolve_tenant(tenant_subdomain: str | Callable[[], str] | None) -> str | N
7780
return None
7881

7982

83+
def _resolve_user_id(user_token: str | Callable[[], str] | None) -> str | None:
84+
token = user_token() if callable(user_token) else user_token
85+
if not token:
86+
return None
87+
try:
88+
claims = parse_token(token)
89+
return claims.scim_id or claims.sub or None
90+
except Exception:
91+
return None
92+
93+
8094
def create_audit_client(
8195
tenant_subdomain: str | Callable[[], str] | None,
8296
module: Module,
@@ -118,7 +132,7 @@ def send_event(
118132
audit_client: AuditClient | None,
119133
event_name: str,
120134
payload: dict,
121-
user_id: str | None = None,
135+
user_token: str | Callable[[], str] | None = None,
122136
mode: AuditLogMode = AuditLogMode.BEST_EFFORT,
123137
) -> None:
124138
"""Send a ZzzCustomEvent to the audit log service.
@@ -131,7 +145,8 @@ def send_event(
131145
audit_client: Initialized AuditClient, or None to skip.
132146
event_name: Event identifier stamped on the event (e.g. ``"MCP_TOOL_INVOKED"``).
133147
payload: Arbitrary key/value pairs included in the custom struct.
134-
user_id: Optional user initiator ID.
148+
user_token: Optional user JWT. The user initiator ID is resolved from
149+
``scim_id`` or ``sub`` claims.
135150
mode: Controls failure handling.
136151
"""
137152
if mode is AuditLogMode.DISABLED:
@@ -140,7 +155,7 @@ def send_event(
140155
if audit_client is None or not tenant_id:
141156
return
142157
try:
143-
_emit_custom_event(audit_client, tenant_id, event_name, payload, user_id)
158+
_emit_custom_event(audit_client, tenant_id, event_name, payload, user_token)
144159
except Exception:
145160
if mode is AuditLogMode.STRICT:
146161
raise

0 commit comments

Comments
 (0)