Class ScimProvisioningTrace
- Namespace
- Kuestenlogik.Bowire.Scim
- Assembly
- Kuestenlogik.Bowire.Scim.dll
Records what an identity provider's connector actually did, one line of JSON per SCIM request (#639).
public sealed class ScimProvisioningTrace
- Inheritance
-
ScimProvisioningTrace
- Inherited Members
Remarks
scim/events.jsonl already records every mutation, but it records
the outcome — action, id, name, active. That answers "what is
the state now", and #639 asks something else: what does a real connector
do on the wire. Those questions have no overlap. The event log is written
only from mutations, so a provider's reads — the paging walk, the
existence filter, the query it uses to decide whether a deactivated user
still exists — leave no trace at all, and they are most of what a live
round-trip is for.
So this records the request, not the result: method, path and query
(paging and filters become visible), the status returned, how long it
took, and for PATCH the dialect the connector actually used. Okta sends
lower-case op with a path; Entra ID capitalises Op
and for deactivation omits the path. Bowire accepts both, and until
something writes down which one arrived, "we handle both" stays a claim
about a fixture.
It also names the attributes a payload carried that Bowire does not
model. Those are kept verbatim and handed back on the next GET,
which is correct and completely silent — an unmodelled attribute that
matters would otherwise be discovered by a customer, not by us.
Off by default. This writes what the connector sent: user names, e-mail addresses, the filters a directory walk used. That is personal data about people who never agreed to be in a debug file, and it is wanted for a bounded exercise — a provisioning round-trip — not for normal operation. Turning it on is a decision, not a default.
Constructors
ScimProvisioningTrace(string, TimeProvider?)
Create a trace writing to path.
public ScimProvisioningTrace(string path, TimeProvider? clock = null)
Parameters
pathstringclockTimeProvider
Remarks
The path is resolved here rather than on first write, so a configuration mistake surfaces at startup where an operator can act on it — instead of silently dropping every line of the exercise it was turned on for.
Properties
FilePath
Where the lines are written.
public string FilePath { get; }
Property Value
Remarks
Named FilePath rather than Path so the JSON member name
"Path" below — which is Entra's capitalisation of a PATCH
operation's path, and has nothing to do with this property — is not
read as a stale reference to it.
Methods
DetectPatchDialect(string)
Which connector's PATCH shape this document is, or null when it
is not a PATCH or cannot be read.
public static string? DetectPatchDialect(string body)
Parameters
bodystring
Returns
Remarks
Reported as observed, never guessed: the two shapes are told apart by
what is actually on the wire — the casing of the op member and
whether a path is present — not by a User-Agent, which a proxy
may rewrite and a test harness will not send at all.
A document that matches neither is reported as other rather
than forced into one of them. That is the interesting case: it means
a third connector, or a version that changed its mind, and calling it
Okta would bury exactly the finding this exists to surface.
Record(string, string, string?, int, double, string?, IReadOnlyCollection<string>?)
Record one request.
public void Record(string method, string path, string? query, int status, double elapsedMs, string? patchDialect = null, IReadOnlyCollection<string>? unmodelledAttributes = null)
Parameters
methodstringpathstringquerystringstatusintelapsedMsdoublepatchDialectstringunmodelledAttributesIReadOnlyCollection<string>
Remarks
Best-effort, like the event log it sits beside: losing a trace line must never fail the call the connector is waiting on, because a provider that sees a 500 retries the whole sync.
UnmodelledAttributes(string)
Members of a payload that Bowire does not model, in the order they appear.
public static IReadOnlyCollection<string> UnmodelledAttributes(string body)
Parameters
bodystring
Returns
Remarks
Anything outside the core User and Group attributes — the Enterprise User extension, whatever a directory maps on top. Bowire keeps them verbatim and returns them, which is right and gives no signal; this is the signal.