Skip to content

Capture auth type in ExecutedRequestInfo and show masked credentials in HTML report #60

Description

@snytkine

Description

Test suites already support an auth property (currently type: basic + username/
password) that can be declared either on a test case's own request block, or suite-wide on
a rest-client / rest-clients entry (RestClientConfig.auth()). PureJavaTestEngine
already resolves and applies this precedence when sending the HTTP request (request-level
overrides suite-level), but the resulting Authorization header is never recorded anywhere
that reaches the HTML report — ExecutedRequestInfo has no auth field at all today.

The report should show that authentication was used (its type), without ever leaking the real
username/password. This is purely a reporting/visibility feature — no new YAML property, no
new auth type.

Design decision: the real username/password values ARE captured into
ExecutedRequestInfo (reusing the existing RequestAuth record as-is), and masking to the
literal string "*****" happens only at report-render time, inside HtmlReportGenerator. Raw
credentials must never be written into the map that feeds the Thymeleaf template.

Implementation Plan

1. Model — reuse RequestAuth, no new class

Add a field to ExecutedRequestInfo (model/ExecutedRequestInfo.java):

public record ExecutedRequestInfo(
        HttpMethod method,
        String url,
        @Nullable Map<String, String> headers,
        @Nullable String body,
        @Nullable RequestAuth auth) {}

No new record is needed — RequestAuth(AuthType type, String username, String password)
already has exactly the right shape. Update the class javadoc to describe the new field and
note it holds the real (unmasked) credentials, so masking is a report-rendering concern, not a
model concern.

2. Engine — resolve effective auth in PureJavaTestEngine

In executeSingleTest (service/PureJavaTestEngine.java), before constructing
ExecutedRequestInfo (~line 432), resolve which auth actually applies to this request and
pass it as the new constructor argument. Add a private helper that mirrors the precedence
already implemented across buildRestClient (suite-level default header) and
buildRequestSpec (request-level override):

private @Nullable RequestAuth resolveEffectiveAuth(TestSuite testSuite, TestCase config) {
    RequestAuth requestAuth = config.request().auth();
    if (requestAuth != null && !hasAuthorizationHeader(config.request().headers())) {
        return requestAuth;
    }
    String id = config.request().restClient();
    RestClientConfig restClientConfig = testSuite.restClientsById()
            .getOrDefault(id, testSuite.restClientsById().get(TestSuite.DEFAULT_REST_CLIENT_ID));
    return restClientConfig != null ? restClientConfig.auth() : null;
}

Notes:

  • Reuses the existing hasAuthorizationHeader helper so the captured auth matches reality: if
    the test case declares an explicit Authorization header, request-level auth is never
    actually applied (per buildRequestSpec), so it must not be reported either.
  • Falls back to the suite's default rest-client the same way selectRestClient does, when the
    request's restClient() id is null or unknown.
  • Give this method full JavaDoc per project convention.

3. Report generator — mask at render time

In HtmlReportGenerator.toTestMap() (service/HtmlReportGenerator.java), add a masked
representation without ever putting the raw values in the map:

private static final String MASKED_VALUE = "*****";
...
RequestAuth auth = tc.requestInfo() != null ? tc.requestInfo().auth() : null;
map.put("hasAuth", auth != null);
map.put("authType", auth != null ? auth.type().name() : null);
map.put("authUsername", auth != null ? MASKED_VALUE : null);
map.put("authPassword", auth != null ? MASKED_VALUE : null);

4. Template — new Authentication block

In src/main/resources/templates/suite-report.html, inside the existing Request
<details> block (near the current headers-grid), add an auth block guarded by
th:if="${t.hasAuth}", following the same headers-grid/header-row markup pattern already
used for headers:

<div th:if="${t.hasAuth}">
  <p class="section-label">Authentication</p>
  <div class="headers-grid">
    <div class="header-row">
      <span class="header-name">Type</span>
      <span class="header-value" th:text="${t.authType}">BASIC</span>
    </div>
    <div class="header-row">
      <span class="header-name">Username</span>
      <span class="header-value" th:text="${t.authUsername}">*****</span>
    </div>
    <div class="header-row">
      <span class="header-name">Password</span>
      <span class="header-value" th:text="${t.authPassword}">*****</span>
    </div>
  </div>
</div>

5. Fix existing call sites (compile breakage)

Adding a field to the ExecutedRequestInfo record breaks every existing constructor call.
Only two call sites exist today (grep -rn "new ExecutedRequestInfo(" src):

  • service/PureJavaTestEngine.java — updated as part of step 2.
  • src/test/java/.../service/HtmlReportGeneratorTest.java — add null (or a RequestAuth
    fixture, for the new masking tests) as the 5th argument at each call site.

6. Tests to add

PureJavaTestEngineTest / PureJavaTestEngineAdditionalTest:

  • Request declares authresult.requestInfo().auth() equals the declared RequestAuth.
  • Suite/rest-client declares auth, request declares none → suite-level auth is captured.
  • Both declared → request-level auth wins (matches existing header-precedence behavior).
  • Request declares auth AND an explicit Authorization header → captured auth is null
    (header wins, per hasAuthorizationHeader check — matches what's actually sent).
  • Neither declares authauth is null.

HtmlReportGeneratorTest:

  • Build a TestCaseResult whose requestInfo().auth() has a real username/password fixture
    (e.g. "realuser" / "s3cr3t") and assert the generated HTML contains "*****" for both
    fields and does not contain the raw "realuser"/"s3cr3t" strings anywhere.
  • Assert no Authentication block renders when auth is null (hasAuth false).

7. Docs

Update docs/html-report.md, in the #### Request bullet list, to add a line describing the
new Authentication block (type shown in full, username/password always shown as *****).

Files to Change

  • src/main/java/io/github/snytkine/apitester/api_tester_cli/model/ExecutedRequestInfo.java
  • src/main/java/io/github/snytkine/apitester/api_tester_cli/service/PureJavaTestEngine.java
  • src/main/java/io/github/snytkine/apitester/api_tester_cli/service/HtmlReportGenerator.java
  • src/main/resources/templates/suite-report.html
  • src/test/java/io/github/snytkine/apitester/api_tester_cli/service/HtmlReportGeneratorTest.java
  • src/test/java/io/github/snytkine/apitester/api_tester_cli/service/PureJavaTestEngineTest.java (and/or PureJavaTestEngineAdditionalTest.java)
  • docs/html-report.md

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions