Repository navigation
Conversation
| 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']), |
There was a problem hiding this comment.
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.
| 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 |
There was a problem hiding this comment.
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.
| surfnet_stepup_middleware_api.monolog.request_context_processor: | ||
| class: Surfnet\StepupMiddleware\ApiBundle\Monolog\Processor\RequestContextProcessor | ||
| arguments: | ||
| - "@request_stack" | ||
| tags: | ||
| - { name: monolog.processor } |
There was a problem hiding this comment.
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.
| foreach ($context as $key => $value) { | ||
| $record->extra[$key] = $value; | ||
| } | ||
|
|
||
| return $record; |
There was a problem hiding this comment.
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'], |
There was a problem hiding this comment.
recoveryTokenIdId / recovery_token_id_id key variants aren't explained or tested. Typo, or covering a real payload quirk?
Description
Adds
RequestContextProcessorMonolog 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 underextra.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 /commandwith JSON payload containingid,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" } }