Skip to content

Commit

Permalink
#16 - Obfuscate Authorization header and suppress logging payloads fo…
Browse files Browse the repository at this point in the history
…r AuthToken request and via request.suppressLogging()
  • Loading branch information
rbygrave committed Jun 16, 2021
1 parent 17a7ae3 commit 36593fe
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@

class DHttpClientContext implements HttpClientContext {

/**
* HTTP Authorization header.
*/
static final String AUTHORIZATION = "Authorization";
private static final String BEARER = "Bearer ";

private final HttpClient httpClient;
private final String baseUrl;
private final Duration requestTimeout;
Expand Down Expand Up @@ -172,7 +178,7 @@ void afterResponse(DHttpClientRequest request) {

void beforeRequest(DHttpClientRequest request) {
if (withAuthToken && !request.isSkipAuthToken()) {
request.header("Authorization", "Bearer " + authToken());
request.header(AUTHORIZATION, BEARER + authToken());
}
if (requestIntercept != null) {
requestIntercept.beforeRequest(request);
Expand Down
14 changes: 14 additions & 0 deletions client/src/main/java/io/avaje/http/client/DHttpClientRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class DHttpClientRequest implements HttpClientRequest, HttpClientResponse {
private BodyContent encodedResponseBody;
private boolean loggableResponseBody;
private boolean skipAuthToken;
private boolean suppressLogging;

DHttpClientRequest(DHttpClientContext context, Duration requestTimeout) {
this.context = context;
Expand All @@ -58,6 +59,13 @@ class DHttpClientRequest implements HttpClientRequest, HttpClientResponse {
@Override
public HttpClientRequest skipAuthToken() {
this.skipAuthToken = true;
this.suppressLogging = true;
return this;
}

@Override
public HttpClientRequest suppressLogging() {
this.suppressLogging = true;
return this;
}

Expand Down Expand Up @@ -471,6 +479,9 @@ public HttpRequest request() {

@Override
public String requestBody() {
if (suppressLogging) {
return "<suppressed request body>";
}
if (encodedRequestBody != null) {
return new String(encodedRequestBody.content(), StandardCharsets.UTF_8);
} else if (bodyFormEncoded) {
Expand All @@ -485,6 +496,9 @@ public String requestBody() {

@Override
public String responseBody() {
if (suppressLogging) {
return "<suppressed response body>";
}
if (encodedResponseBody != null) {
return new String(encodedResponseBody.content(), StandardCharsets.UTF_8);
} else if (httpResponse != null && loggableResponseBody) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ public interface HttpClientRequest {
*/
HttpClientRequest skipAuthToken();

/**
* For this request suppress payload logging.
* <p>
* The payload contains sensitive content and the request and response content
* should be suppressed and not included in request logging.
*/
HttpClientRequest suppressLogging();

/**
* Set the request timeout to use for this request. When not set the default
* request timeout will be used.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ interface Event {
* This will return null if the response is not String or byte array
* encoded string content. For example, when requests use response
* handlers for InputStream, Path, Stream etc this will return null.
* <p>
*/
String responseBody();

Expand Down
11 changes: 10 additions & 1 deletion client/src/main/java/io/avaje/http/client/RequestLogger.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,17 @@ private void headers(StringBuilder sb, String label, HttpHeaders headers) {
if (!entries.isEmpty()) {
sb.append(delimiter).append(label);
for (Map.Entry<String, List<String>> entry : entries) {
sb.append(entry.getKey()).append("=").append(entry.getValue()).append(", ");
final String key = entry.getKey();
if (obfuscate(key)) {
sb.append(key).append("=<obfuscated>, ");
} else {
sb.append(key).append("=").append(entry.getValue()).append(", ");
}
}
}
}

boolean obfuscate(String key) {
return DHttpClientContext.AUTHORIZATION.equals(key);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.avaje.http.client;

import org.junit.jupiter.api.Test;

import java.time.Duration;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;

class DHttpClientRequestTest {

@Test
void suppressLogging_listenerEvent_expect_suppressedPayloadContent() {

final DHttpClientRequest request = new DHttpClientRequest(mock(DHttpClientContext.class), Duration.ZERO);

request.suppressLogging();
final RequestListener.Event event = request.listenerEvent();

assertThat(event.requestBody()).isEqualTo("<suppressed request body>");
assertThat(event.responseBody()).isEqualTo("<suppressed response body>");
}

@Test
void skipAuthToken_listenerEvent_expect_suppressedPayloadContent() {

final DHttpClientRequest request = new DHttpClientRequest(mock(DHttpClientContext.class), Duration.ZERO);

request.skipAuthToken();
final RequestListener.Event event = request.listenerEvent();

assertThat(event.requestBody()).isEqualTo("<suppressed request body>");
assertThat(event.responseBody()).isEqualTo("<suppressed response body>");
}
}
16 changes: 16 additions & 0 deletions client/src/test/java/io/avaje/http/client/RequestLoggerTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.avaje.http.client;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class RequestLoggerTest {

private final RequestLogger requestLogger = new RequestLogger();

@Test
void obfuscate() {
assertTrue(requestLogger.obfuscate("Authorization"));
assertFalse(requestLogger.obfuscate("Foo"));
}
}

0 comments on commit 36593fe

Please sign in to comment.