summaryrefslogtreecommitdiffstats
path: root/jdisc-security-filters
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@oath.com>2018-04-24 16:52:49 +0200
committerBjørn Christian Seime <bjorncs@oath.com>2018-04-24 17:24:11 +0200
commitfd6b82e60eaf6b85800e7615d2382263a655478f (patch)
tree997d39f413441eca842b170e1b7fbff12543581f /jdisc-security-filters
parent04002b8760c2061e03285be71b353de3f502fa93 (diff)
Add base class for security filters rendering errors as json
Diffstat (limited to 'jdisc-security-filters')
-rw-r--r--jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/base/JsonSecurityRequestFilterBase.java86
-rw-r--r--jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/base/package-info.java8
-rw-r--r--jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/base/JsonSecurityRequestFilterBaseTest.java57
3 files changed, 151 insertions, 0 deletions
diff --git a/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/base/JsonSecurityRequestFilterBase.java b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/base/JsonSecurityRequestFilterBase.java
new file mode 100644
index 00000000000..e2440bc4c5f
--- /dev/null
+++ b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/base/JsonSecurityRequestFilterBase.java
@@ -0,0 +1,86 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.jdisc.http.filter.security.base;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.yahoo.jdisc.Response;
+import com.yahoo.jdisc.handler.FastContentWriter;
+import com.yahoo.jdisc.handler.ResponseDispatch;
+import com.yahoo.jdisc.handler.ResponseHandler;
+import com.yahoo.jdisc.http.filter.DiscFilterRequest;
+import com.yahoo.jdisc.http.filter.SecurityRequestFilter;
+
+import java.io.UncheckedIOException;
+import java.util.Optional;
+
+/**
+ * A base class for {@link SecurityRequestFilter} implementations that renders an error response as JSON.
+ *
+ * @author bjorncs
+ */
+public abstract class JsonSecurityRequestFilterBase implements SecurityRequestFilter {
+
+ private static final ObjectMapper mapper = new ObjectMapper();
+
+ @Override
+ public final void filter(DiscFilterRequest request, ResponseHandler handler) {
+ filter(request)
+ .ifPresent(errorResponse -> writeResponse(errorResponse, handler));
+ }
+
+ protected abstract Optional<ErrorResponse> filter(DiscFilterRequest request);
+
+ private void writeResponse(ErrorResponse error, ResponseHandler responseHandler) {
+ ObjectNode errorMessage = mapper.createObjectNode();
+ errorMessage.put("code", error.errorCode);
+ errorMessage.put("message", error.message);
+ error.response.headers().put("Content-Type", "application/json"); // Note: Overwrites header if already exists
+ try (FastContentWriter writer = ResponseDispatch.newInstance(error.response).connectFastWriter(responseHandler)) {
+ writer.write(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(errorMessage));
+ } catch (JsonProcessingException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ /**
+ * An error response that contains a {@link Response}, error code and message.
+ * The error code and message will be rendered as JSON fields with name 'code' and 'message' respectively.
+ */
+ protected static class ErrorResponse {
+ private final Response response;
+ private final int errorCode;
+ private final String message;
+
+ public ErrorResponse(Response response, int errorCode, String message) {
+ this.response = response;
+ this.errorCode = errorCode;
+ this.message = message;
+ }
+
+ public ErrorResponse(Response response, String message) {
+ this(response, response.getStatus(), message);
+ }
+
+ public ErrorResponse(int httpStatusCode, int errorCode, String message) {
+ this(new Response(httpStatusCode), errorCode, message);
+ }
+
+ public ErrorResponse(int httpStatusCode, String message) {
+ this(new Response(httpStatusCode), message);
+ }
+
+ public Response getResponse() {
+ return response;
+ }
+
+ public int getErrorCode() {
+ return errorCode;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ }
+}
diff --git a/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/base/package-info.java b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/base/package-info.java
new file mode 100644
index 00000000000..38f7b126443
--- /dev/null
+++ b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/base/package-info.java
@@ -0,0 +1,8 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+/**
+ * @author bjorncs
+ */
+@ExportPackage
+package com.yahoo.jdisc.http.filter.security.base;
+
+import com.yahoo.osgi.annotation.ExportPackage; \ No newline at end of file
diff --git a/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/base/JsonSecurityRequestFilterBaseTest.java b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/base/JsonSecurityRequestFilterBaseTest.java
new file mode 100644
index 00000000000..9b0a875a73d
--- /dev/null
+++ b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/base/JsonSecurityRequestFilterBaseTest.java
@@ -0,0 +1,57 @@
+package com.yahoo.jdisc.http.filter.security.base;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.yahoo.container.jdisc.RequestHandlerTestDriver;
+import com.yahoo.jdisc.Response;
+import com.yahoo.jdisc.http.filter.DiscFilterRequest;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.Optional;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+
+/**
+ * @author bjorncs
+ */
+public class JsonSecurityRequestFilterBaseTest {
+
+ private final ObjectMapper mapper = new ObjectMapper();
+
+ @Test
+ public void filter_renders_errors_as_json() throws IOException {
+ int statusCode = 403;
+ String message = "Forbidden";
+ DiscFilterRequest request = mock(DiscFilterRequest.class);
+ SimpleSecurityRequestFilter filter =
+ new SimpleSecurityRequestFilter(new JsonSecurityRequestFilterBase.ErrorResponse(statusCode, message));
+ RequestHandlerTestDriver.MockResponseHandler responseHandler = new RequestHandlerTestDriver.MockResponseHandler();
+ filter.filter(request, responseHandler);
+
+ Response response = responseHandler.getResponse();
+ assertThat(response, notNullValue());
+ assertThat(response.getStatus(), equalTo(statusCode));
+
+ JsonNode jsonNode = mapper.readTree(responseHandler.readAll());
+ assertThat(jsonNode.get("message").asText(), equalTo(message));
+ assertThat(jsonNode.get("code").asInt(), equalTo(statusCode));
+ }
+
+ private static class SimpleSecurityRequestFilter extends JsonSecurityRequestFilterBase {
+ private final ErrorResponse errorResponse;
+
+ SimpleSecurityRequestFilter(ErrorResponse errorResponse) {
+ this.errorResponse = errorResponse;
+ }
+
+ @Override
+ protected Optional<ErrorResponse> filter(DiscFilterRequest request) {
+ return Optional.ofNullable(this.errorResponse);
+ }
+ }
+
+} \ No newline at end of file