Skip to content

Enrich log records with request context parameters - #641

Open
phavekes wants to merge 3 commits into
mainfrom
feature/log-request-context
Open

phavekes wants to merge 3 commits into
mainfrom
feature/log-request-context

Conversation

@phavekes

@phavekes phavekes commented Oct 7, 2026

Copy link
Copy Markdown
Member

Description

Adds RequestContextProcessor Monolog processor to extract request context parameters (identity_id, collab_person_id, second_factor_id, recovery_token_id, name_id, institution) from route attributes, query parameters, POST request bodies, and JSON command payloads, and append them to log records under extra.

Example Log Entries

1. Identity authorization check (GET path parameter)

Request: GET /authorization/may-self-vet-using-self-asserted-token/f9913e4b-8f50-4729-97b9-87ca9b33f0b8

{
  "message": "Checking self-asserted token authorization",
  "context": {},
  "level": 200,
  "level_name": "INFO",
  "channel": "app",
  "datetime": "2026-10-07T09:19:55+00:00",
  "extra": {
    "request_id": "29e025623a3a1029ddbd5db71d1c064a",
    "application": "middleware",
    "server": "mw-dev.stepup.coin.surf.net",
    "identity_id": "f9913e4b-8f50-4729-97b9-87ca9b33f0b8"
  }
}

2. Deprovisioning (DELETE path parameter)

Request: DELETE /deprovision/urn:collab:person:surfnet.nl:jdoe

{
  "message": "Deprovision request processed",
  "context": {},
  "level": 200,
  "level_name": "INFO",
  "channel": "app",
  "datetime": "2026-10-07T09:19:55+00:00",
  "extra": {
    "request_id": "29e025623a3a1029ddbd5db71d1c064a",
    "application": "middleware",
    "server": "mw-dev.stepup.coin.surf.net",
    "collab_person_id": "urn:collab:person:surfnet.nl:jdoe"
  }
}

3. RA listing (Path parameters for Identity and Institution)

Request: GET /ra-listing/f9913e4b-8f50-4729-97b9-87ca9b33f0b8/surfnet

{
  "message": "Retrieving RA listing",
  "context": {},
  "level": 200,
  "level_name": "INFO",
  "channel": "app",
  "datetime": "2026-10-07T09:19:55+00:00",
  "extra": {
    "request_id": "29e025623a3a1029ddbd5db71d1c064a",
    "application": "middleware",
    "server": "mw-dev.stepup.coin.surf.net",
    "identity_id": "f9913e4b-8f50-4729-97b9-87ca9b33f0b8",
    "institution": "surfnet"
  }
}

4. Command execution (POST JSON payload)

Request: POST /command with JSON payload containing id, name_id, institution

{
  "message": "Command received and handled",
  "context": {},
  "level": 200,
  "level_name": "INFO",
  "channel": "app",
  "datetime": "2026-10-07T09:19:55+00:00",
  "extra": {
    "request_id": "29e025623a3a1029ddbd5db71d1c064a",
    "application": "middleware",
    "server": "mw-dev.stepup.coin.surf.net",
    "identity_id": "abb1b9f8-20c9-44a9-9694-176f00aaa618",
    "name_id": "29c41b84214b4fd5fb4d508e680fc921",
    "institution": "Example Org"
  }
}

@phavekes
phavekes requested a review from kayjoosten October 9, 2026 19:13
Comment on lines +86 to +96
private function extractRequestContext(Request $request): array
{
$jsonData = $this->parseJsonContent($request->getContent());
$sources = $this->collectSources($request, $jsonData);

$fields = [
'identity_id' => $this->extractIdentityId($request, $sources, $jsonData),
'collab_person_id' => $this->extractFromSources($sources, ['collabPersonId', 'collab_person_id']),
'second_factor_id' => $this->extractSecondFactorId($request, $sources),
'recovery_token_id' => $this->extractRecoveryTokenId($request, $sources),
'name_id' => $this->extractFromSources($sources, ['nameId', 'name_id', 'NameID']),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

identity_id and name_id are documented personal data (docs/personal-data.md:39-48), but nothing here ties into the repo's Forgettable/SensitiveData mechanism. These will sit unredacted in logs indefinitely. Please confirm this is acceptable with whoever owns privacy/compliance, or hash/pseudonymize before logging.

Comment on lines +121 to +150
private function collectSources(Request $request, ?array $jsonData): array
{
$sources = [
$request->attributes->all(),
$request->query->all(),
$request->request->all(),
];

if ($jsonData === null) {
return $sources;
}

$sources[] = $jsonData;

$command = $jsonData['command'] ?? null;
if (is_array($command) && isset($command['payload']) && is_array($command['payload'])) {
$sources[] = $command['payload'];
}

$payload = $jsonData['payload'] ?? null;
if (is_array($payload)) {
$sources[] = $payload;
}

return $sources;
}

/**
* @param list<array<array-key, mixed>> $sources
* @param array<array-key, mixed>|null $jsonData

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Query/body/JSON values are logged as is when there's no route match, with no validation. A caller can inject arbitrary values into identity_id, institution, etc. Restrict trusted fields to server controlled sources, or document these as best effort hints only.

Comment on lines +140 to +145
surfnet_stepup_middleware_api.monolog.request_context_processor:
class: Surfnet\StepupMiddleware\ApiBundle\Monolog\Processor\RequestContextProcessor
arguments:
- "@request_stack"
tags:
- { name: monolog.processor }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SecurityContextProcessor is scoped to the request channel; this processor isn't, so it runs on every Monolog channel. Intentional? If not, add the same channel restriction.

Comment on lines +60 to +64
foreach ($context as $key => $value) {
$record->extra[$key] = $value;
}

return $record;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mutates $record->extra directly instead of $record->with(extra: ...) like SecurityContextProcessor. Works today, but inconsistent. Consider aligning.

return $this->extractRouteId($request, ['recovery_token'])
?? $this->extractFromSources(
$sources,
['recoveryTokenIdId', 'recoveryTokenId', 'recovery_token_id', 'recovery_token_id_id'],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

recoveryTokenIdId / recovery_token_id_id key variants aren't explained or tested. Typo, or covering a real payload quirk?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants