summaryrefslogtreecommitdiffstats
path: root/jdisc-security-filters
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@oath.com>2018-10-04 15:41:15 +0200
committerBjørn Christian Seime <bjorncs@oath.com>2018-10-04 15:41:15 +0200
commitdb93461fbcb56af55d08a81d9b93db07846646a0 (patch)
tree3d00b6c7cf7c94ba445a76fdc03d315910c11ead /jdisc-security-filters
parent963d16440f7534e39cbe36be695c272a95b7e3fb (diff)
Add passthrough mode to AthenzPrincipalFilter
- No http response when passthrough mode is enable - Introduce attributes for error code and message - Introduce attribute for AthenzPrincipal instance
Diffstat (limited to 'jdisc-security-filters')
-rw-r--r--jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzPrincipalFilter.java30
-rw-r--r--jdisc-security-filters/src/main/resources/configdefinitions/athenz-principal-filter.def3
-rw-r--r--jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzPrincipalFilterTest.java50
3 files changed, 66 insertions, 17 deletions
diff --git a/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzPrincipalFilter.java b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzPrincipalFilter.java
index ad6c82138e1..5b79b806190 100644
--- a/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzPrincipalFilter.java
+++ b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzPrincipalFilter.java
@@ -31,22 +31,31 @@ import java.util.Set;
*/
public class AthenzPrincipalFilter extends CorsRequestFilterBase {
+ private static final String RESULT_ATTRIBUTE_PREFIX = "jdisc-security-filters.athenz-principal-filter.result";
+ public static final String RESULT_ERROR_CODE_ATTRIBUTE = RESULT_ATTRIBUTE_PREFIX + ".error.code";
+ public static final String RESULT_ERROR_MESSAGE_ATTRIBUTE = RESULT_ATTRIBUTE_PREFIX + ".error.message";
+ public static final String RESULT_PRINCIPAL = RESULT_ATTRIBUTE_PREFIX + ".principal";
+
private final NTokenValidator validator;
private final String principalTokenHeader;
+ private final boolean passthroughMode;
@Inject
public AthenzPrincipalFilter(AthenzPrincipalFilterConfig athenzPrincipalFilterConfig, CorsFilterConfig corsConfig) {
this(new NTokenValidator(Paths.get(athenzPrincipalFilterConfig.athenzConfFile())),
athenzPrincipalFilterConfig.principalHeaderName(),
- new HashSet<>(corsConfig.allowedUrls()));
+ new HashSet<>(corsConfig.allowedUrls()),
+ athenzPrincipalFilterConfig.passthroughMode());
}
AthenzPrincipalFilter(NTokenValidator validator,
String principalTokenHeader,
- Set<String> corsAllowedUrls) {
+ Set<String> corsAllowedUrls,
+ boolean passthroughMode) {
super(corsAllowedUrls);
this.validator = validator;
this.principalTokenHeader = principalTokenHeader;
+ this.passthroughMode = passthroughMode;
}
@Override
@@ -61,7 +70,7 @@ public class AthenzPrincipalFilter extends CorsRequestFilterBase {
if (!certificatePrincipal.isPresent() && !nTokenPrincipal.isPresent()) {
String errorMessage = "Unable to authenticate Athenz identity. " +
"Either client certificate or principal token is required.";
- return Optional.of(new ErrorResponse(Response.Status.UNAUTHORIZED, errorMessage));
+ return createResponse(request, Response.Status.UNAUTHORIZED, errorMessage);
}
if (certificatePrincipal.isPresent() && nTokenPrincipal.isPresent()
&& !certificatePrincipal.get().getIdentity().equals(nTokenPrincipal.get().getIdentity())) {
@@ -69,14 +78,15 @@ public class AthenzPrincipalFilter extends CorsRequestFilterBase {
"Identity in principal token does not match x509 CN: token-identity=%s, cert-identity=%s",
nTokenPrincipal.get().getIdentity().getFullName(),
certificatePrincipal.get().getIdentity().getFullName());
- return Optional.of(new ErrorResponse(Response.Status.UNAUTHORIZED, errorMessage));
+ return createResponse(request, Response.Status.UNAUTHORIZED, errorMessage);
}
AthenzPrincipal principal = nTokenPrincipal.orElseGet(certificatePrincipal::get);
request.setUserPrincipal(principal);
request.setRemoteUser(principal.getName());
+ request.setAttribute(RESULT_PRINCIPAL, principal);
return Optional.empty();
} catch (Exception e) {
- return Optional.of(new ErrorResponse(Response.Status.UNAUTHORIZED, e.getMessage()));
+ return createResponse(request, Response.Status.UNAUTHORIZED, e.getMessage());
}
}
@@ -92,4 +102,14 @@ public class AthenzPrincipalFilter extends CorsRequestFilterBase {
.map(NToken::new);
}
+ private Optional<ErrorResponse> createResponse(DiscFilterRequest request, int statusCode, String message) {
+ request.setAttribute(RESULT_ERROR_CODE_ATTRIBUTE, statusCode);
+ request.setAttribute(RESULT_ERROR_MESSAGE_ATTRIBUTE, message);
+ if (passthroughMode) {
+ return Optional.empty();
+ } else {
+ return Optional.of(new ErrorResponse(statusCode, message));
+ }
+ }
+
}
diff --git a/jdisc-security-filters/src/main/resources/configdefinitions/athenz-principal-filter.def b/jdisc-security-filters/src/main/resources/configdefinitions/athenz-principal-filter.def
index 59e481b0d80..96f79f6d664 100644
--- a/jdisc-security-filters/src/main/resources/configdefinitions/athenz-principal-filter.def
+++ b/jdisc-security-filters/src/main/resources/configdefinitions/athenz-principal-filter.def
@@ -6,3 +6,6 @@ principalHeaderName string default="Athenz-Principal-Auth"
# Path to athenz.conf file
athenzConfFile string
+
+# Pass-through mode
+passthroughMode bool default=false
diff --git a/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzPrincipalFilterTest.java b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzPrincipalFilterTest.java
index fdab450b435..3bbc606cf2b 100644
--- a/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzPrincipalFilterTest.java
+++ b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/athenz/AthenzPrincipalFilterTest.java
@@ -14,6 +14,7 @@ import com.yahoo.security.KeyAlgorithm;
import com.yahoo.security.KeyUtils;
import com.yahoo.security.X509CertificateBuilder;
import com.yahoo.vespa.athenz.utils.ntoken.NTokenValidator;
+import org.jetbrains.annotations.NotNull;
import org.junit.Before;
import org.junit.Test;
@@ -40,6 +41,7 @@ import static java.util.stream.Collectors.joining;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -72,10 +74,10 @@ public class AthenzPrincipalFilterTest {
when(request.getClientCertificateChain()).thenReturn(emptyList());
when(validator.validate(NTOKEN)).thenReturn(principal);
- AthenzPrincipalFilter filter = new AthenzPrincipalFilter(validator, ATHENZ_PRINCIPAL_HEADER, CORS_ALLOWED_URLS);
+ AthenzPrincipalFilter filter = createFilter(false);
filter.filter(request, new ResponseHandlerMock());
- verify(request).setUserPrincipal(principal);
+ assertAuthenticated(request, principal);
}
private DiscFilterRequest createRequestMock() {
@@ -92,10 +94,10 @@ public class AthenzPrincipalFilterTest {
ResponseHandlerMock responseHandler = new ResponseHandlerMock();
- AthenzPrincipalFilter filter = new AthenzPrincipalFilter(validator, ATHENZ_PRINCIPAL_HEADER, CORS_ALLOWED_URLS);
+ AthenzPrincipalFilter filter = createFilter(false);
filter.filter(request, responseHandler);
- assertUnauthorized(responseHandler, "Unable to authenticate Athenz identity");
+ assertUnauthorized(request, responseHandler, "Unable to authenticate Athenz identity");
}
@Test
@@ -108,10 +110,10 @@ public class AthenzPrincipalFilterTest {
ResponseHandlerMock responseHandler = new ResponseHandlerMock();
- AthenzPrincipalFilter filter = new AthenzPrincipalFilter(validator, ATHENZ_PRINCIPAL_HEADER, CORS_ALLOWED_URLS);
+ AthenzPrincipalFilter filter = createFilter(false);
filter.filter(request, responseHandler);
- assertUnauthorized(responseHandler, errorMessage);
+ assertUnauthorized(request, responseHandler, errorMessage);
}
@Test
@@ -122,11 +124,16 @@ public class AthenzPrincipalFilterTest {
ResponseHandlerMock responseHandler = new ResponseHandlerMock();
- AthenzPrincipalFilter filter = new AthenzPrincipalFilter(validator, ATHENZ_PRINCIPAL_HEADER, CORS_ALLOWED_URLS);
+ AthenzPrincipalFilter filter = createFilter(false);
filter.filter(request, responseHandler);
AthenzPrincipal expectedPrincipal = new AthenzPrincipal(IDENTITY);
+ assertAuthenticated(request, expectedPrincipal);
+ }
+
+ private void assertAuthenticated(DiscFilterRequest request, AthenzPrincipal expectedPrincipal) {
verify(request).setUserPrincipal(expectedPrincipal);
+ verify(request).setAttribute(AthenzPrincipalFilter.RESULT_PRINCIPAL, expectedPrincipal);
}
@Test
@@ -139,10 +146,10 @@ public class AthenzPrincipalFilterTest {
ResponseHandlerMock responseHandler = new ResponseHandlerMock();
- AthenzPrincipalFilter filter = new AthenzPrincipalFilter(validator, ATHENZ_PRINCIPAL_HEADER, CORS_ALLOWED_URLS);
+ AthenzPrincipalFilter filter = createFilter(false);
filter.filter(request, responseHandler);
- verify(request).setUserPrincipal(principalWithToken);
+ assertAuthenticated(request, principalWithToken);
}
@Test
@@ -156,16 +163,35 @@ public class AthenzPrincipalFilterTest {
ResponseHandlerMock responseHandler = new ResponseHandlerMock();
- AthenzPrincipalFilter filter = new AthenzPrincipalFilter(validator, ATHENZ_PRINCIPAL_HEADER, CORS_ALLOWED_URLS);
+ AthenzPrincipalFilter filter = createFilter(false);
filter.filter(request, responseHandler);
- assertUnauthorized(responseHandler, "Identity in principal token does not match x509 CN");
+ assertUnauthorized(request, responseHandler, "Identity in principal token does not match x509 CN");
+ }
+
+ @Test
+ public void no_response_produced_when_passthrough_mode_is_enabled() {
+ DiscFilterRequest request = createRequestMock();
+ when(request.getHeader(ATHENZ_PRINCIPAL_HEADER)).thenReturn(null);
+ when(request.getClientCertificateChain()).thenReturn(emptyList());
+
+ ResponseHandlerMock responseHandler = new ResponseHandlerMock();
+
+ AthenzPrincipalFilter filter = createFilter(true);
+ filter.filter(request, responseHandler);
+
+ assertThat(responseHandler.response, nullValue());
+ }
+
+ private AthenzPrincipalFilter createFilter(boolean passthroughModeEnabled) {
+ return new AthenzPrincipalFilter(validator, ATHENZ_PRINCIPAL_HEADER, CORS_ALLOWED_URLS, passthroughModeEnabled);
}
- private static void assertUnauthorized(ResponseHandlerMock responseHandler, String expectedMessageSubstring) {
+ private static void assertUnauthorized(DiscFilterRequest request, ResponseHandlerMock responseHandler, String expectedMessageSubstring) {
assertThat(responseHandler.response, notNullValue());
assertThat(responseHandler.response.getStatus(), equalTo(UNAUTHORIZED));
assertThat(responseHandler.getResponseContent(), containsString(expectedMessageSubstring));
+ verify(request).setAttribute(AthenzPrincipalFilter.RESULT_ERROR_CODE_ATTRIBUTE, UNAUTHORIZED);
}
private static class ResponseHandlerMock implements ResponseHandler {