Skip to main content

Admin API License Watermarking Filter

Documents the servlet filter that adds the X-S2R-License header and a customer_id MDC key to all Admin API requests for license tracking.

Overview

The LicenseWatermarkFilter is a servlet filter that intercepts every request to the Admin API. Its primary function is to "watermark" each request with information derived from the currently active product license. This serves two main purposes:

  1. Response Identification: It adds a unique X-S2R-License HTTP header to responses, allowing API consumers and support teams to identify the specific license associated with a given API interaction.
  2. Centralized Logging: It injects the customer_id into the Mapped Diagnostic Context (MDC) for logging. This ensures that all log entries generated while processing a request are automatically tagged with the customer identifier, simplifying log analysis and troubleshooting.

The filter is designed to run on every request after authentication and authorization have been established but before the core business logic is executed. This ensures that the license context is available throughout the request's lifecycle.

How It Works

The LicenseWatermarkFilter is implemented as a OncePerRequestFilter, guaranteeing it executes exactly once for each request that enters the application.

sequenceDiagram
    participant C as API Consumer
    participant F as LicenseWatermarkFilter
    participant V as LicenseValidator
    participant MDC
    participant Chain as Filter Chain

    C->>+F: Sends HTTP Request
    F->>V: currentState()
    V-->>F: Returns LicenseState
    F->>MDC: put("customer_id", ...)
    F->>Chain: doFilter(request, response)
    Note right of Chain: Request is processed by<br/>downstream filters and controllers.
    Chain-->>-F: Returns
    F->>MDC: remove("customer_id")
    F-->>-C: Sends HTTP Response<br/>(with X-S2R-License header)

The processing sequence for an incoming request is as follows:

  1. Path Evaluation: The filter first checks if the request path starts with /actuator/. If it does, the filter does nothing and immediately passes the request to the next filter in the chain. This exclusion prevents liveness and readiness probes from depending on the license validation logic, which may involve database access.
  2. State Retrieval: The filter calls the LicenseValidator to get the current LicenseState. If the license state cannot be determined due to an error, the process continues as if the system is unlicensed. Such failures are logged at a DEBUG level.
  3. MDC Population: Based on the LicenseState, a customer identifier is resolved and placed into the MDC with the key customer_id. This value is used for tagging all subsequent log messages for the duration of the request.
  4. Header Injection: The filter resolves a value for the X-S2R-License response header. If a value is determined, it is set on the HttpServletResponse. If the license state is unlicensed or unavailable, the header is omitted entirely.
  5. Chain Progression: The request is passed down the filter chain for processing by other components and the target API controller.
  6. MDC Cleanup: In a finally block, the customer_id is removed from the MDC. This is a critical step to ensure that the identifier does not leak into the logging context of another request that might be processed by the same thread.

Request Path Matching

IMPORTANT

The filter's decision to skip processing for /actuator/ paths is based on the request path as resolved by the application's routing mechanism. This ensures that path-obfuscation techniques (such as URL encoding or matrix parameters) cannot be used to bypass the watermarking logic for non-actuator endpoints.

Response Header: X-S2R-License

The filter adds the X-S2R-License header to all responses for watermarked requests. The value of this header depends on the current license status.

License StateX-S2R-License Header Value
Full LicenseThe first 16 hexadecimal characters of the SHA-256 hash of the license's installation ID (licenseId).
Bootstrap (Activation Grace)The literal string bootstrap.
Unlicensed or UnavailableThe header is omitted from the response.

Hash Generation for Full Licenses

When a full license is active, the header value is a truncated SHA-256 hash. The hash is calculated on the UTF-8 bytes of the license's installation ID UUID string. Only the first 8 bytes of the resulting digest are used, which are then formatted as 16 lowercase hexadecimal characters.

For example, if a license's installation ID is a1b2c3d4-e5f6-a7b8-c9d0-e1f2a3b4c5d6, the header value would be the first 16 characters of its SHA-256 hash.

Logging Context (MDC)

For each processed request, the filter adds the customer_id key to the MDC. This allows structured logging systems to correlate log entries with a specific customer.

License Statecustomer_id MDC Value
Full LicenseThe customer ID string (customerId) from the license. If the ID is null or blank, it defaults to unlicensed.
Bootstrap (Activation Grace)The literal string bootstrap.
Unlicensed or UnavailableThe literal string unlicensed.

Troubleshooting

License State Unavailable

If the LicenseValidator component throws an exception while retrieving the current license state, the LicenseWatermarkFilter will catch it and log a debug message. In this scenario, the filter operates as if the system is unlicensed:

  • The X-S2R-License header will be omitted from the response.
  • The customer_id key in the MDC will be set to unlicensed.

This ensures that a failure in the licensing subsystem does not cause a total failure of the Admin API.

See Also

  • Authentication and Authorization Filters: The LicenseWatermarkFilter is ordered to run after the IdpAuthFilter and AdminAuthorizationFilter, ensuring that its logic operates on authenticated and authorized requests.
  • Licensing Architecture: This component is part of the platform's overall licensing and entitlement system.
Tagsguides

All SOAP-to-REST docs