Class TenantPolicyRedactor
- All Implemented Interfaces:
Redactor
Redactor that dispatches each call to a per-tenant
Redactor based on the active RedactionContext (issue #80).
Resolution order on every call:
RedactionContext.tenantPolicyId()— explicit override.EventContext.tenantId()fromRedactionContext.eventContext()— implicit tenant scope carried by the framework's correlation context.- The configured default redactor — used when neither id is set, or when no per-tenant override is registered for the resolved id.
Why dispatch per call (not per-thread cache): an agent may
service multiple tenants in the same JVM, and a single SLF4J emit can
cross tenant boundaries inside a multi-agent group. Looking up by
RedactionContext on every call keeps the policy boundary
exactly where the framework already enforces it.
Composition: each per-tenant value is itself a Redactor,
so operators can stack CompositeRedactor (e.g. fast pattern +
slow LLM-classifier) per tenant. The dispatcher is one layer above the
composition, not a replacement for it.
Thread-safety: the registry is immutable after construction;
dispatch is a single Map.get on each call.
- Since:
- 0.9.0 (issue #80)
-
Nested Class Summary
Nested Classes -
Constructor Summary
ConstructorsConstructorDescriptionTenantPolicyRedactor(Map<String, Redactor> byTenant, Redactor defaultRedactor) -
Method Summary
Modifier and TypeMethodDescriptionstatic TenantPolicyRedactor.Builderbuilder()booleanmaybeContainsSensitive(String input) Cheap pre-check: returnstruewheninputmight contain something worth scanning.resolve(RedactionContext ctx) Returns theRedactorchosen forctx.scrubString(String input, RedactionContext ctx) Scrub sensitive patterns out ofinput, returning the redacted text + every match.scrubValue(Object input, RedactionContext ctx) Recursive scrub of a structured value — Map, List, or scalar.Snapshot of the registry for inspection / testing.
-
Constructor Details
-
TenantPolicyRedactor
- Parameters:
byTenant- immutable mapping of tenant id → redactor; empty map makes every call fall through todefaultRedactordefaultRedactor- fallback used when no tenant id is resolvable, or no override is registered- Throws:
NullPointerException- if either arg is null
-
-
Method Details
-
builder
-
scrubString
Description copied from interface:RedactorScrub sensitive patterns out ofinput, returning the redacted text + every match.- Specified by:
scrubStringin interfaceRedactor- Parameters:
input- plaintext to scan; never null.""is acceptable and returns an unchanged result.ctx- per-call context (sink, tenant policy)- Returns:
- redacted text + findings; never null
-
scrubValue
Description copied from interface:RedactorRecursive scrub of a structured value — Map, List, or scalar. WalksMap<String, Object>andList<Object>(the framework's two carry-over container types from JSON deserialization), applyingRedactor.scrubString(java.lang.String, com.tnsai.quality.redaction.RedactionContext)to every leaf String. Other scalars pass through unchanged.Default implementation handles Maps + Lists; redactors that need to peek into custom types override.
- Specified by:
scrubValuein interfaceRedactor
-
maybeContainsSensitive
Description copied from interface:RedactorCheap pre-check: returnstruewheninputmight contain something worth scanning. Lets the framework short-circuit the regex loop on guaranteed-clean strings (UUIDs, numeric IDs, fixed-vocabulary enums). Defaulttruebecause the safe fallback is "always scan"; override when you can do better.- Specified by:
maybeContainsSensitivein interfaceRedactor
-
resolve
Returns theRedactorchosen forctx. Public for test inspection; the dispatch path uses it on every scrub call. -
tenantOverrides
Snapshot of the registry for inspection / testing. -
defaultRedactor
-