diff --git a/apiml/src/main/java/org/zowe/apiml/filter/BasicLoginFilter.java b/apiml/src/main/java/org/zowe/apiml/filter/BasicLoginFilter.java
index 82ba534c12..ad9470a1fc 100644
--- a/apiml/src/main/java/org/zowe/apiml/filter/BasicLoginFilter.java
+++ b/apiml/src/main/java/org/zowe/apiml/filter/BasicLoginFilter.java
@@ -25,6 +25,7 @@
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import org.zowe.apiml.handler.FailedAuthenticationWebHandler;
+import org.zowe.apiml.product.opentelemetry.OtelRequestContext;
import org.zowe.apiml.security.common.login.LoginFilter;
import org.zowe.apiml.security.common.login.LoginRequest;
import org.zowe.apiml.zaas.security.config.CompoundAuthProvider;
@@ -50,7 +51,7 @@
*
*
*
This filter is intended to be used on /login endpoints.
- *
+ *
* Caution: Filter will read the body and make it available as a request attribute
*
* @see LoginRequest
@@ -72,6 +73,8 @@ public BasicLoginFilter(CompoundAuthProvider compoundAuthProvider, FailedAuthent
@Override
public Mono filter(ServerWebExchange exchange, WebFilterChain chain) {
var hasBody = Optional.ofNullable(exchange.getAttribute(CachedBodyFilter.CACHED_BODY_ATTR)).isPresent();
+ var otelContext = OtelRequestContext.of(exchange);
+ otelContext.authMethod(OtelRequestContext.BASIC_AUTH_TYPE);
exchange.getAttributes().put(X509AuthFilter.SKIP_X509_AUTH_ATTR, hasBody);
return extractBasicAuth(exchange)
.map(this::useCredentials)
diff --git a/apiml/src/main/java/org/zowe/apiml/handler/FailedAuthenticationWebHandler.java b/apiml/src/main/java/org/zowe/apiml/handler/FailedAuthenticationWebHandler.java
index 2e20cd68b3..28ab96a943 100644
--- a/apiml/src/main/java/org/zowe/apiml/handler/FailedAuthenticationWebHandler.java
+++ b/apiml/src/main/java/org/zowe/apiml/handler/FailedAuthenticationWebHandler.java
@@ -26,6 +26,7 @@
import org.zowe.apiml.message.api.ApiMessageView;
import org.zowe.apiml.message.log.ApimlLogger;
import org.zowe.apiml.product.logging.annotations.InjectApimlLogger;
+import org.zowe.apiml.product.opentelemetry.OtelRequestContext;
import org.zowe.apiml.security.common.error.AuthExceptionHandler;
import reactor.core.publisher.Mono;
@@ -48,11 +49,15 @@ public class FailedAuthenticationWebHandler implements ServerAuthenticationFailu
public Mono onAuthenticationFailure(WebFilterExchange webFilterExchange, AuthenticationException exception) {
var exchange = webFilterExchange.getExchange();
var requestUri = exchange.getRequest().getURI().getPath();
+ var otelContext = OtelRequestContext.of(exchange);
log.debug("Unauthorized access to '{}' endpoint", requestUri);
+ otelContext.authenticationFailed();
+ otelContext.authErrorMessage(exception.getMessage());
var bufferFactory = new DefaultDataBufferFactory();
AtomicReference buffer = new AtomicReference<>();
BiConsumer consumer = (message, status) -> {
exchange.getResponse().setStatusCode(status);
+ otelContext.authErrorType(status.getReasonPhrase());
if (message != null) {
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
try {
@@ -64,7 +69,7 @@ public Mono onAuthenticationFailure(WebFilterExchange webFilterExchange, A
buffer.set(bufferFactory.wrap(new byte[0]));
}
};
- var addHeader = (BiConsumer)(name, value) -> exchange.getResponse().getHeaders().add(name, value);
+ var addHeader = (BiConsumer) (name, value) -> exchange.getResponse().getHeaders().add(name, value);
try {
handler.handleException(requestUri, consumer, addHeader, exception);
} catch (ServletException e) {
diff --git a/apiml/src/test/java/org/zowe/apiml/acceptance/OpenTelemetryResourceAttributesZosTest.java b/apiml/src/test/java/org/zowe/apiml/acceptance/OpenTelemetryResourceAttributesZosTest.java
index f2f67eff38..8881f4ff62 100644
--- a/apiml/src/test/java/org/zowe/apiml/acceptance/OpenTelemetryResourceAttributesZosTest.java
+++ b/apiml/src/test/java/org/zowe/apiml/acceptance/OpenTelemetryResourceAttributesZosTest.java
@@ -69,7 +69,7 @@ private boolean assertAttributesBase(Attributes attributes, int port) {
@Nested
@AcceptanceTest
- @ActiveProfiles({ "OpenTelemetryTest", "zos" })
+ @ActiveProfiles({"OpenTelemetryTest", "zos"})
@TestPropertySource(
properties = {
"otel.sdk.disabled=false",
@@ -118,7 +118,7 @@ void thenLogCustomAttributes() {
"apiml.security.filterChainConfiguration=new"
}
)
- @ActiveProfiles({ "OpenTelemetryTest", "zos" })
+ @ActiveProfiles({"OpenTelemetryTest", "zos"})
class WhenOnboardedService extends AcceptanceTestWithMockServices {
private static final String VALID_OIDC_TOKEN = "ewogICJ0eXAiOiAiSldUIiwKICAibm9uY2UiOiAiYVZhbHVlVG9CZVZlcmlmaWVkIiwKICAiYWxnIjogIlJTMjU2IiwKICAia2lkIjogIlNlQ1JldEtleSIKfQ.ewogICJhdWQiOiAiMDAwMDAwMDMtMDAwMC0wMDAwLWMwMDAtMDAwMDAwMDAwMDAwIiwKICAiaXNzIjogImh0dHBzOi8vb2lkYy5wcm92aWRlci5vcmcvYXBwIiwKICAiaWF0IjogMTcyMjUxNDEyOSwKICAibmJmIjogMTcyMjUxNDEyOSwKICAiZXhwIjogODcyMjUxODEyNSwKICAic3ViIjogIm9pZGMudXNlcm5hbWUiCn0.c29tZVNpZ25lZEhhc2hDb2Rl";
@@ -217,7 +217,7 @@ void givenRouted_whenAuthFail_thenLog() {
given()
.get(basePath + "/testservice/api/v1/200")
.then()
- .statusCode(200);
+ .statusCode(200);
var logRecord = assertOneLogRecordExported();
@@ -226,7 +226,7 @@ void givenRouted_whenAuthFail_thenLog() {
var logBody = logRecord.getBodyValue().asString();
assertEquals("testservice", getAttribute(logBody, "service.id"));
assertEquals("GET", getAttribute(logBody, "http.request.method"));
- assertEquals("FAILED", getAttribute(logBody, "auth.status"));
+ assertEquals("ERROR", getAttribute(logBody, "auth.status"));
assertEquals("localhost:testservice:" + mockServiceZoweJwt.getPort(), getAttribute(logBody, "service.instance.id"));
assertEquals("200", getAttribute(logBody, "service.response_code"));
assertEquals("/testservice/api/v1/200", getAttribute(logBody, "url.path"));
@@ -246,27 +246,28 @@ private Object getAttribute(String logBody, String attributeName) {
}
@Test
- @Disabled("This test is for invalid authentication (server error). To be reviewed in follow up story")
void givenLoginEndpoint_thenLog() {
given()
.auth().preemptive()
.basic("wronguser", "wrongpass")
.post(basePath + "/gateway/api/v1/auth/login")
.then()
- .statusCode(500);
+ .statusCode(401);
var logRecord = assertOneLogRecordExported();
assertAttributesBase(logRecord.getResource().getAttributes(), port);
@SuppressWarnings("null")
var logBody = logRecord.getBodyValue().asString();
- assertEquals("apicatalog", getAttribute(logBody, "service.id"));
- assertEquals("GET", getAttribute(logBody, "http.request.method"));
- assertEquals("FAILED", getAttribute(logBody, "auth.status"));
- assertEquals("localhost:testservice:" + mockServiceZoweJwt.getPort(), getAttribute(logBody, "service.instance.id"));
- assertEquals("200", getAttribute(logBody, "service.response_code"));
- assertEquals("/testservice/api/v1/200", getAttribute(logBody, "url.path"));
+ assertEquals("gateway", getAttribute(logBody, "service.id"));
+ assertEquals("POST", getAttribute(logBody, "http.request.method"));
+ assertEquals("ERROR", getAttribute(logBody, "auth.status"));
+ assertEquals("EACCES: Permission is denied; the specified password is incorrect", getAttribute(logBody, "auth.error.message"));
+ assertEquals("Unauthorized", getAttribute(logBody, "auth.error.type"));
+ assertEquals("localhost:gateway:" + port, getAttribute(logBody, "service.instance.id"));
+ assertEquals("401", getAttribute(logBody, "service.response_code"));
+ assertEquals("/gateway/api/v1/auth/login", getAttribute(logBody, "url.path"));
assertEquals("https", getAttribute(logBody, "url.scheme"));
- assertEquals("zoweJwt", getAttribute(logBody, "auth.method"));
+ assertEquals("basicAuth", getAttribute(logBody, "auth.service.auth.method"));
}
@Test
@@ -320,7 +321,7 @@ void givenNoRoute_thenLog() {
given()
.get(basePath + "/nonexistant/api/v1/200")
.then()
- .statusCode(404);
+ .statusCode(404);
var logRecord = assertOneLogRecordExported();
assertAttributesBase(logRecord.getResource().getAttributes(), port);
@@ -400,7 +401,7 @@ void givenRouted_withMisconfiguredAuthPassTicket_thenLog() {
assertNull(getAttribute(logBody, "user.id"));
assertEquals("testservicepterror", getAttribute(logBody, "service.id"));
assertEquals("GET", getAttribute(logBody, "http.request.method"));
- assertEquals("FAILED", getAttribute(logBody, "auth.status"));
+ assertEquals("ERROR", getAttribute(logBody, "auth.status"));
assertEquals(mockServicePassTicketMisconfigured.getInstanceId(), getAttribute(logBody, "service.instance.id"));
assertEquals("200", getAttribute(logBody, "service.response_code"));
assertEquals("/testservicepterror/api/v1/200", getAttribute(logBody, "url.path"));
@@ -465,11 +466,11 @@ private String login() {
var token = given()
.contentType(ContentType.JSON)
.body("""
- {
- "username": "USER",
- "password": "validPassword"
- }
- """)
+ {
+ "username": "USER",
+ "password": "validPassword"
+ }
+ """)
.log().all()
.when()
.post(URI.create(basePath + LOGIN_ENDPOINT))
diff --git a/gateway-service/src/main/java/org/zowe/apiml/gateway/filters/AbstractAuthSchemeFactory.java b/gateway-service/src/main/java/org/zowe/apiml/gateway/filters/AbstractAuthSchemeFactory.java
index 7cc474a8b8..6e561624ce 100644
--- a/gateway-service/src/main/java/org/zowe/apiml/gateway/filters/AbstractAuthSchemeFactory.java
+++ b/gateway-service/src/main/java/org/zowe/apiml/gateway/filters/AbstractAuthSchemeFactory.java
@@ -20,6 +20,7 @@
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.server.ServerWebExchange;
@@ -96,7 +97,6 @@
* public static class Config extends AbstractAuthSchemeFactory.AbstractConfig {
* }
* }
- *
* @Data class MyResponse {
* private String token;
* }
@@ -112,27 +112,27 @@ public abstract class AbstractAuthSchemeFactory CERTIFICATE_HEADERS_TEST = headerName ->
StringUtils.equalsIgnoreCase(headerName, CERTIFICATE_HEADERS[0]) ||
- StringUtils.equalsIgnoreCase(headerName, CERTIFICATE_HEADERS[1]) ||
- StringUtils.equalsIgnoreCase(headerName, CERTIFICATE_HEADERS[2]);
+ StringUtils.equalsIgnoreCase(headerName, CERTIFICATE_HEADERS[1]) ||
+ StringUtils.equalsIgnoreCase(headerName, CERTIFICATE_HEADERS[2]);
private static final Predicate CREDENTIALS_COOKIE_INPUT = cookie ->
StringUtils.equalsIgnoreCase(cookie.getName(), PAT_COOKIE_AUTH_NAME) ||
- StringUtils.equalsIgnoreCase(cookie.getName(), COOKIE_AUTH_NAME) ||
- StringUtils.startsWithIgnoreCase(cookie.getName(), COOKIE_AUTH_NAME + ".");
+ StringUtils.equalsIgnoreCase(cookie.getName(), COOKIE_AUTH_NAME) ||
+ StringUtils.startsWithIgnoreCase(cookie.getName(), COOKIE_AUTH_NAME + ".");
private static final Predicate CREDENTIALS_COOKIE = cookie ->
CREDENTIALS_COOKIE_INPUT.test(cookie) ||
- StringUtils.equalsIgnoreCase(cookie.getName(), "jwtToken") ||
- StringUtils.equalsIgnoreCase(cookie.getName(), "LtpaToken2");
+ StringUtils.equalsIgnoreCase(cookie.getName(), "jwtToken") ||
+ StringUtils.equalsIgnoreCase(cookie.getName(), "LtpaToken2");
private static final Predicate CREDENTIALS_HEADER_INPUT = headerName ->
StringUtils.equalsIgnoreCase(headerName, HttpHeaders.AUTHORIZATION) ||
- StringUtils.equalsIgnoreCase(headerName, PAT_HEADER_NAME);
+ StringUtils.equalsIgnoreCase(headerName, PAT_HEADER_NAME);
private static final Predicate CREDENTIALS_HEADER = headerName ->
CREDENTIALS_HEADER_INPUT.test(headerName) ||
- CERTIFICATE_HEADERS_TEST.test(headerName) ||
- StringUtils.equalsIgnoreCase(headerName, "X-SAF-Token") ||
- StringUtils.equalsIgnoreCase(headerName, CLIENT_CERT_HEADER) ||
- StringUtils.equalsIgnoreCase(headerName, HttpHeaders.COOKIE);
+ CERTIFICATE_HEADERS_TEST.test(headerName) ||
+ StringUtils.equalsIgnoreCase(headerName, "X-SAF-Token") ||
+ StringUtils.equalsIgnoreCase(headerName, CLIENT_CERT_HEADER) ||
+ StringUtils.equalsIgnoreCase(headerName, HttpHeaders.COOKIE);
protected final InstanceInfoService instanceInfoService;
protected final MessageService messageService;
@@ -209,6 +209,10 @@ protected RequestCredentials.RequestCredentialsBuilder createRequestCredentials(
protected ServerHttpRequest cleanHeadersOnAuthFail(ServerWebExchange exchange, String errorMessage) {
var otelContext = OtelRequestContext.of(exchange);
otelContext.authenticationFailed();
+ otelContext.authErrorMessage(errorMessage);
+ Optional.ofNullable(exchange.getResponse().getStatusCode())
+ .flatMap(httpStatusCode -> Optional.ofNullable(HttpStatus.resolve(httpStatusCode.value())))
+ .ifPresent(httpStatus -> otelContext.authErrorType(httpStatus.getReasonPhrase()));
Optional.ofNullable(getAuthenticationScheme()).ifPresent(otelContext::authMethod);
return exchange.getRequest().mutate().headers(headers -> {
diff --git a/gateway-service/src/main/java/org/zowe/apiml/product/opentelemetry/OtelRequestContext.java b/gateway-service/src/main/java/org/zowe/apiml/product/opentelemetry/OtelRequestContext.java
index dc4e5e433b..1d70c42771 100644
--- a/gateway-service/src/main/java/org/zowe/apiml/product/opentelemetry/OtelRequestContext.java
+++ b/gateway-service/src/main/java/org/zowe/apiml/product/opentelemetry/OtelRequestContext.java
@@ -32,7 +32,8 @@ public final class OtelRequestContext {
public static final String OTEL_CONTEXT = "otel-context";
private static final String OK = "OK";
- private static final String FAILED = "FAILED";
+ private static final String ERROR = "ERROR";
+ public static final String BASIC_AUTH_TYPE = "basicAuth";
private static final String OTEL_ATTRIBUTE_METHOD = "http.request.method";
private static final String OTEL_ATTRIBUTE_SCHEME = "url.scheme";
@@ -43,6 +44,8 @@ public final class OtelRequestContext {
private static final String OTEL_ATTRIBUTE_AUTH_METHOD = "auth.service.auth.method";
private static final String OTEL_ATTRIBUTE_AUTH_SOURCE_TYPE = "auth.method";
private static final String OTEL_ATTRIBUTE_AUTH_STATUS = "auth.status";
+ private static final String OTEL_ATTRIBUTE_AUTH_ERROR_TYPE = "auth.error.type";
+ private static final String OTEL_ATTRIBUTE_AUTH_ERROR_MESSAGE = "auth.error.message";
private static final String OTEL_ATTRIBUTE_USER_ID = "user.id";
private static final String OTEL_ATTRIBUTE_DISTRIBUTED_USER_ID = "user.distributed.id";
@@ -97,8 +100,20 @@ public OtelRequestContext authMethod(AuthenticationScheme authenticationScheme)
return put(OTEL_ATTRIBUTE_AUTH_METHOD, String.valueOf(authenticationScheme));
}
+ public OtelRequestContext authMethod(String authenticationScheme) {
+ return put(OTEL_ATTRIBUTE_AUTH_METHOD, authenticationScheme);
+ }
+
public OtelRequestContext authenticationFailed() {
- return put(OTEL_ATTRIBUTE_AUTH_STATUS, FAILED);
+ return put(OTEL_ATTRIBUTE_AUTH_STATUS, ERROR);
+ }
+
+ public OtelRequestContext authErrorType(String authErrorType) {
+ return put(OTEL_ATTRIBUTE_AUTH_ERROR_TYPE, authErrorType);
+ }
+
+ public OtelRequestContext authErrorMessage(String authErrorMessage) {
+ return put(OTEL_ATTRIBUTE_AUTH_ERROR_MESSAGE, authErrorMessage);
}
public OtelRequestContext authenticationSuccess() {
diff --git a/gateway-service/src/test/java/org/zowe/apiml/gateway/filters/AbstractTokenFilterFactoryTest.java b/gateway-service/src/test/java/org/zowe/apiml/gateway/filters/AbstractTokenFilterFactoryTest.java
index 0631263bd9..fb152e9cda 100644
--- a/gateway-service/src/test/java/org/zowe/apiml/gateway/filters/AbstractTokenFilterFactoryTest.java
+++ b/gateway-service/src/test/java/org/zowe/apiml/gateway/filters/AbstractTokenFilterFactoryTest.java
@@ -18,6 +18,7 @@
import org.mockito.ArgumentCaptor;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
@@ -73,7 +74,7 @@ class ValidResponse {
@Test
void givenHeaderResponse_whenHandling_thenUpdateTheRequest() {
- var request = testRequestMutation(new AbstractAuthSchemeFactory.AuthorizationResponse<>(null,ZaasTokenResponse.builder()
+ var request = testRequestMutation(new AbstractAuthSchemeFactory.AuthorizationResponse<>(null, ZaasTokenResponse.builder()
.headerName("headerName")
.token("headerValue")
.build()
@@ -83,13 +84,13 @@ void givenHeaderResponse_whenHandling_thenUpdateTheRequest() {
@Test
void givenCookieResponse_whenHandling_thenUpdateTheRequest() {
- var request = testRequestMutation(new AbstractAuthSchemeFactory.AuthorizationResponse<>(null,ZaasTokenResponse.builder()
+ var request = testRequestMutation(new AbstractAuthSchemeFactory.AuthorizationResponse<>(null, ZaasTokenResponse.builder()
.cookieName("cookieName")
.token("cookieValue")
.build()
));
assertEquals("cookieName=cookieValue", request.getHeaders().getFirst("cookie"));
- assertEquals("Bearer cookieValue" , request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION));
+ assertEquals("Bearer cookieValue", request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION));
}
}
@@ -126,7 +127,7 @@ void givenEmptyResponseWithError_whenHandling_thenProvideErrorHeader() {
@Test
void givenCookieAndHeaderInResponse_whenHandling_thenSetBoth() {
- var request = testRequestMutation(new AbstractAuthSchemeFactory.AuthorizationResponse<>(null,ZaasTokenResponse.builder()
+ var request = testRequestMutation(new AbstractAuthSchemeFactory.AuthorizationResponse<>(null, ZaasTokenResponse.builder()
.cookieName("cookie")
.headerName("header")
.token("jwt")
@@ -144,20 +145,24 @@ void givenCookieAndHeaderInResponse_whenHandling_thenSetBoth() {
class Otel {
MockServerHttpRequest request = MockServerHttpRequest.get("/aPath").build();
- MockServerWebExchange exchange = MockServerWebExchange.from(request);
+ MockServerWebExchange exchange;
OtelRequestContext otelRequestContext;
@BeforeEach
void mockOtelContext() {
+ exchange = MockServerWebExchange.from(request);
otelRequestContext = spy(OtelRequestContext.of(exchange));
exchange.getAttributes().put("otel-context", otelRequestContext);
}
@Test
void givenOtelRequestContext_whenFail_thenCallAuthenticationFailed() {
+ exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
spy(AbstractAuthSchemeFactory.class).cleanHeadersOnAuthFail(exchange, "test");
verify(otelRequestContext, times(1)).authenticationFailed();
+ verify(otelRequestContext, times(1)).authErrorMessage("test");
+ verify(otelRequestContext, times(1)).authErrorType(HttpStatus.FORBIDDEN.getReasonPhrase());
}
@Test
diff --git a/gateway-service/src/test/java/org/zowe/apiml/gateway/filters/RoutingConfigurationErrorFilterFactoryTest.java b/gateway-service/src/test/java/org/zowe/apiml/gateway/filters/RoutingConfigurationErrorFilterFactoryTest.java
index db29456a3a..0f0a1f7cfc 100644
--- a/gateway-service/src/test/java/org/zowe/apiml/gateway/filters/RoutingConfigurationErrorFilterFactoryTest.java
+++ b/gateway-service/src/test/java/org/zowe/apiml/gateway/filters/RoutingConfigurationErrorFilterFactoryTest.java
@@ -10,10 +10,11 @@
package org.zowe.apiml.gateway.filters;
-import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.springframework.cloud.gateway.filter.GatewayFilter;
+import org.springframework.http.HttpStatus;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.zowe.apiml.auth.AuthenticationScheme;
@@ -33,10 +34,11 @@ class RoutingConfigurationErrorFilterFactoryTest {
private GatewayFilter filter;
private MockServerHttpRequest request = MockServerHttpRequest.get("https://localhost/some/url").build();
- private MockServerWebExchange exchange = MockServerWebExchange.from(request);
+ private MockServerWebExchange exchange;
- @BeforeAll
+ @BeforeEach
void init() {
+ exchange = MockServerWebExchange.from(request);
var config = new RoutingConfigurationErrorFilterFactory.Config();
config.setMessage(MESSAGE);
config.setAuthenticationScheme("safIdt");
@@ -47,13 +49,31 @@ void init() {
}
@Test
- void givenConfig_whenApply_thenSetAuthInformation() {
+ void givenConfig_whenApply_thenSetAuthInformationWithoutErrorType() {
var otelContext = spy(OtelRequestContext.of(exchange));
exchange.getAttributes().put(OtelRequestContext.OTEL_CONTEXT, otelContext);
StepVerifier.create(filter.filter(exchange, e -> Mono.empty())).verifyComplete();
verify(otelContext).authenticationFailed();
+ verify(otelContext).authErrorMessage(MESSAGE);
+
+ verify(otelContext).authMethod(AuthenticationScheme.SAF_IDT);
+ verify(underTest).cleanHeadersOnAuthFail(exchange, MESSAGE);
+ }
+
+ @Test
+ void givenConfig_whenApply_thenSetFailedAuthInformationWithErrorType() {
+ var otelContext = spy(OtelRequestContext.of(exchange));
+ exchange.getAttributes().put(OtelRequestContext.OTEL_CONTEXT, otelContext);
+ exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
+
+ StepVerifier.create(filter.filter(exchange, e -> Mono.empty())).verifyComplete();
+
+ verify(otelContext).authenticationFailed();
+ verify(otelContext).authErrorMessage(MESSAGE);
+ verify(otelContext).authErrorType(HttpStatus.UNAUTHORIZED.getReasonPhrase());
+
verify(otelContext).authMethod(AuthenticationScheme.SAF_IDT);
verify(underTest).cleanHeadersOnAuthFail(exchange, MESSAGE);
}
diff --git a/gateway-service/src/test/java/org/zowe/apiml/product/opentelemetry/OtelRequestContextTest.java b/gateway-service/src/test/java/org/zowe/apiml/product/opentelemetry/OtelRequestContextTest.java
index 922b156005..e16f595652 100644
--- a/gateway-service/src/test/java/org/zowe/apiml/product/opentelemetry/OtelRequestContextTest.java
+++ b/gateway-service/src/test/java/org/zowe/apiml/product/opentelemetry/OtelRequestContextTest.java
@@ -113,7 +113,19 @@ void givenOtelContext_whenSetZoweJwtAuthMethod_thenTransformToString() {
@Test
void givenOtelContext_whenAuthenticationFailed_thenStoreFailedStringAsStatus() {
OtelRequestContext.of(exchange).authenticationFailed();
- assertEquals("FAILED", getValue("auth.status"));
+ assertEquals("ERROR", getValue("auth.status"));
+ }
+
+ @Test
+ void givenOtelContext_whenAuthErrorMessage_thenStoreMessageAsAuthErrorMessage() {
+ OtelRequestContext.of(exchange).authErrorMessage("Invalid credentials");
+ assertEquals("Invalid credentials", getValue("auth.error.message"));
+ }
+
+ @Test
+ void givenOtelContext_whenAuthErrorType_thenStoreErrorTypeAsAuthErrorType() {
+ OtelRequestContext.of(exchange).authErrorType("Forbidden");
+ assertEquals("Forbidden", getValue("auth.error.type"));
}
@Test
@@ -154,7 +166,8 @@ void givenInvalidData_whenObjectMapperFails_thenThrowIllegalStateException() thr
exchange = MockServerWebExchange.from(request);
var objectMapper = mock(ObjectMapper.class);
var otelRequestContext = spy(OtelRequestContext.of(exchange));
- var jsonProcessingException = new JsonProcessingException("test") {};
+ var jsonProcessingException = new JsonProcessingException("test") {
+ };
doReturn(objectMapper).when(otelRequestContext).getObjectMapper();
doThrow(jsonProcessingException).when(objectMapper).writeValueAsString(any());