From 8d397b3f4bf60be0b015b5591aaec4307b03b833 Mon Sep 17 00:00:00 2001 From: achmelo <37397715+achmelo@users.noreply.github.com> Date: Fri, 8 Dec 2023 16:36:31 +0100 Subject: [PATCH] feat: Cloud Gateway SAF IDT auth scheme (#3234) * chore: move babel to dev, modify webpack config, spring security Signed-off-by: achmelo * read keys from common httpsconfig Signed-off-by: achmelo * New ZAAS safIdt endpoint to generate SAF ID tokens for authenticated user. Signed-off-by: Petr Weinfurt * IT tests for safIdt endpoint. Signed-off-by: Petr Weinfurt * initial safidt filter impl Signed-off-by: achmelo * Address code review comments. Signed-off-by: Petr Weinfurt * Remove unused class Signed-off-by: Petr Weinfurt * Fix tests Signed-off-by: Petr Weinfurt * Fix IT tests Signed-off-by: Petr Weinfurt * Fix unit tests Signed-off-by: Petr Weinfurt * Add Controller Advice to handle exceptions. Signed-off-by: Petr Weinfurt * refactor, unit tests Signed-off-by: achmelo * Handle more exceptions. Signed-off-by: Petr Weinfurt * safidt service in test Signed-off-by: achmelo * Add content type and body to negative test. Signed-off-by: Petr Weinfurt * Add content type and body to negative test. Signed-off-by: Petr Weinfurt * Add content type and body to negative test. Signed-off-by: Petr Weinfurt * Add negative tests with valid Okta token and no mapping. Signed-off-by: Petr Weinfurt * Fix Rest assured RequestSpec preparation. Signed-off-by: Petr Weinfurt * Checkout the main branch before Sonar scan to resolve issue 'Could not find ref 'v2.x.x' in refs/heads, refs/remotes/upstream or refs/remotes/origin.' Signed-off-by: Petr Weinfurt * Fetch the main branch before Sonar scan to resolve issue 'Could not find ref 'v2.x.x' in refs/heads, refs/remotes/upstream or refs/remotes/origin.' Signed-off-by: Petr Weinfurt * Replace deprecated sonar.login property Signed-off-by: Petr Weinfurt * Replace deprecated sonar.login property Signed-off-by: Petr Weinfurt * remove git fetch Signed-off-by: Petr Weinfurt * Fetch depth 0 Signed-off-by: Petr Weinfurt * Fetch depth 0 Signed-off-by: Petr Weinfurt * Add SAF IDT request to negative IT tests Signed-off-by: Petr Weinfurt * Handle TokenNotValid and TokenExpired exception with 401 response. Signed-off-by: Petr Weinfurt * Handle TokenNotValid and TokenExpired exception with 401 response. Signed-off-by: Petr Weinfurt * remove duplicated code Signed-off-by: achmelo * delete unused imports Signed-off-by: achmelo --------- Signed-off-by: achmelo Signed-off-by: Petr Weinfurt Co-authored-by: Petr Weinfurt --- .../AbstractRequestBodyAuthSchemeFactory.java | 66 +++++++++ .../filters/PassticketFilterFactory.java | 39 +---- .../filters/SafIdtFilterFactory.java | 63 ++++++++ .../service/scheme/SafIdt.java | 37 +++++ .../acceptance/SafIdtSchemeTest.java | 135 ++++++++++++++++++ .../zowe/apiml/constants/ApimlConstants.java | 1 + .../security/service/schema/SafIdtScheme.java | 4 +- .../service/schema/SafIdtSchemeTest.java | 27 +++- .../schemes/CloudGatewayAuthTest.java | 6 + 9 files changed, 336 insertions(+), 42 deletions(-) create mode 100644 cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/AbstractRequestBodyAuthSchemeFactory.java create mode 100644 cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/SafIdtFilterFactory.java create mode 100644 cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/SafIdt.java create mode 100644 cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/SafIdtSchemeTest.java diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/AbstractRequestBodyAuthSchemeFactory.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/AbstractRequestBodyAuthSchemeFactory.java new file mode 100644 index 0000000000..83b791ecf1 --- /dev/null +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/AbstractRequestBodyAuthSchemeFactory.java @@ -0,0 +1,66 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + */ + +package org.zowe.apiml.cloudgatewayservice.filters; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.http.HttpHeaders; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.gateway.filter.GatewayFilter; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClient; +import org.zowe.apiml.cloudgatewayservice.service.InstanceInfoService; +import org.zowe.apiml.message.core.MessageService; +import org.zowe.apiml.ticket.TicketRequest; + +@Service +public abstract class AbstractRequestBodyAuthSchemeFactory extends AbstractAuthSchemeFactory { + private static final ObjectWriter WRITER = new ObjectMapper().writer(); + + public AbstractRequestBodyAuthSchemeFactory(WebClient webClient, InstanceInfoService instanceInfoService, MessageService messageService) { + super(Config.class, webClient, instanceInfoService, messageService); + } + + public abstract String getEndpointUrl(ServiceInstance instance); + + @Override + protected WebClient.RequestHeadersSpec createRequest(ServiceInstance instance, String requestBody) { + String url = getEndpointUrl(instance); + return webClient.post() + .uri(url).header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .bodyValue(requestBody); + } + + @Override + public GatewayFilter apply(Config config) { + try { + return createGatewayFilter(config, WRITER.writeValueAsString(new TicketRequest(config.getApplicationName()))); + } catch (JsonProcessingException e) { + return ((exchange, chain) -> { + ServerHttpRequest request = updateHeadersForError(exchange, e.getMessage()); + return chain.filter(exchange.mutate().request(request).build()); + }); + } + } + + @Data + @EqualsAndHashCode(callSuper = true) + public static class Config extends AbstractAuthSchemeFactory.AbstractConfig { + + private String applicationName; + + } +} diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/PassticketFilterFactory.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/PassticketFilterFactory.java index 580e50932c..392dde464d 100644 --- a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/PassticketFilterFactory.java +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/PassticketFilterFactory.java @@ -10,24 +10,16 @@ package org.zowe.apiml.cloudgatewayservice.filters; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectWriter; -import lombok.Data; -import lombok.EqualsAndHashCode; import org.apache.http.HttpHeaders; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; -import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Service; import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.server.ServerWebExchange; import org.zowe.apiml.cloudgatewayservice.service.InstanceInfoService; import org.zowe.apiml.message.core.MessageService; -import org.zowe.apiml.ticket.TicketRequest; import org.zowe.apiml.ticket.TicketResponse; import reactor.core.publisher.Mono; @@ -35,13 +27,12 @@ import java.util.Base64; @Service -public class PassticketFilterFactory extends AbstractAuthSchemeFactory { +public class PassticketFilterFactory extends AbstractRequestBodyAuthSchemeFactory { private static final String TICKET_URL = "%s://%s:%d/%s/zaas/ticket"; - private static final ObjectWriter WRITER = new ObjectMapper().writer(); public PassticketFilterFactory(@Qualifier("webClientClientCert") WebClient webClient, InstanceInfoService instanceInfoService, MessageService messageService) { - super(Config.class, webClient, instanceInfoService, messageService); + super(webClient, instanceInfoService, messageService); } @Override @@ -55,11 +46,8 @@ protected TicketResponse getResponseFor401() { } @Override - protected WebClient.RequestHeadersSpec createRequest(ServiceInstance instance, String requestBody) { - return webClient.post() - .uri(String.format(TICKET_URL, instance.getScheme(), instance.getHost(), instance.getPort(), instance.getServiceId().toLowerCase())) - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) - .bodyValue(requestBody); + public String getEndpointUrl(ServiceInstance instance) { + return String.format(TICKET_URL, instance.getScheme(), instance.getHost(), instance.getPort(), instance.getServiceId().toLowerCase()); } @Override @@ -78,23 +66,4 @@ protected Mono processResponse(ServerWebExchange exchange, GatewayFilterCh return chain.filter(exchange); } - @Override - public GatewayFilter apply(Config config) { - try { - return createGatewayFilter(config, WRITER.writeValueAsString(new TicketRequest(config.getApplicationName()))); - } catch (JsonProcessingException e) { - return ((exchange, chain) -> { - ServerHttpRequest request = updateHeadersForError(exchange, e.getMessage()); - return chain.filter(exchange.mutate().request(request).build()); - }); - } - } - - @Data - @EqualsAndHashCode(callSuper = true) - public static class Config extends AbstractAuthSchemeFactory.AbstractConfig { - - private String applicationName; - - } } diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/SafIdtFilterFactory.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/SafIdtFilterFactory.java new file mode 100644 index 0000000000..02f7efab3e --- /dev/null +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/filters/SafIdtFilterFactory.java @@ -0,0 +1,63 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + */ + +package org.zowe.apiml.cloudgatewayservice.filters; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.server.ServerWebExchange; +import org.zowe.apiml.cloudgatewayservice.service.InstanceInfoService; +import org.zowe.apiml.constants.ApimlConstants; +import org.zowe.apiml.message.core.MessageService; +import org.zowe.apiml.zaas.ZaasTokenResponse; +import reactor.core.publisher.Mono; + +@Service +public class SafIdtFilterFactory extends AbstractRequestBodyAuthSchemeFactory { + + public SafIdtFilterFactory(@Qualifier("webClientClientCert") WebClient webClient, InstanceInfoService instanceInfoService, MessageService messageService) { + super(webClient, instanceInfoService, messageService); + } + + @Override + public String getEndpointUrl(ServiceInstance instance) { + return String.format("%s://%s:%d/%s/zaas/safIdt", instance.getScheme(), instance.getHost(), instance.getPort(), instance.getServiceId().toLowerCase()); + } + + @Override + protected Mono processResponse(ServerWebExchange exchange, GatewayFilterChain chain, ZaasTokenResponse response) { + ServerHttpRequest request; + if (response.getToken() != null) { + request = exchange.getRequest().mutate().headers(headers -> + headers.add(ApimlConstants.SAF_TOKEN_HEADER, response.getToken()) + ).build(); + } else { + request = updateHeadersForError(exchange, "Invalid or missing authentication"); + } + + exchange = exchange.mutate().request(request).build(); + return chain.filter(exchange); + } + + @Override + protected Class getResponseClass() { + return ZaasTokenResponse.class; + } + + @Override + protected ZaasTokenResponse getResponseFor401() { + return new ZaasTokenResponse(); + } + +} diff --git a/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/SafIdt.java b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/SafIdt.java new file mode 100644 index 0000000000..4163dfbcd9 --- /dev/null +++ b/cloud-gateway-service/src/main/java/org/zowe/apiml/cloudgatewayservice/service/scheme/SafIdt.java @@ -0,0 +1,37 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + */ + +package org.zowe.apiml.cloudgatewayservice.service.scheme; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.gateway.filter.FilterDefinition; +import org.springframework.cloud.gateway.route.RouteDefinition; +import org.springframework.stereotype.Component; +import org.zowe.apiml.auth.Authentication; +import org.zowe.apiml.auth.AuthenticationScheme; + +@Component +public class SafIdt implements SchemeHandler { + + @Override + public AuthenticationScheme getAuthenticationScheme() { + return AuthenticationScheme.SAF_IDT; + } + + @Override + public void apply(ServiceInstance serviceInstance, RouteDefinition routeDefinition, Authentication auth) { + FilterDefinition filterDef = new FilterDefinition(); + filterDef.setName("SafIdtFilterFactory"); + filterDef.addArg("applicationName", auth.getApplid()); + filterDef.addArg("serviceId", StringUtils.lowerCase(serviceInstance.getServiceId())); + routeDefinition.getFilters().add(filterDef); + } +} diff --git a/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/SafIdtSchemeTest.java b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/SafIdtSchemeTest.java new file mode 100644 index 0000000000..fbcbd60ac5 --- /dev/null +++ b/cloud-gateway-service/src/test/java/org/zowe/apiml/cloudgatewayservice/acceptance/SafIdtSchemeTest.java @@ -0,0 +1,135 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + */ + +package org.zowe.apiml.cloudgatewayservice.acceptance; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.sun.net.httpserver.HttpExchange; +import org.apache.commons.io.IOUtils; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.springframework.http.HttpHeaders; +import org.zowe.apiml.auth.AuthenticationScheme; +import org.zowe.apiml.cloudgatewayservice.acceptance.common.AcceptanceTest; +import org.zowe.apiml.cloudgatewayservice.acceptance.common.AcceptanceTestWithMockServices; +import org.zowe.apiml.cloudgatewayservice.acceptance.common.MockService; +import org.zowe.apiml.constants.ApimlConstants; +import org.zowe.apiml.ticket.TicketRequest; +import org.zowe.apiml.zaas.ZaasTokenResponse; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static io.restassured.RestAssured.given; +import static org.apache.http.HttpStatus.SC_OK; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class SafIdtSchemeTest { + + private static final String SERVICE_ID = "service"; + private static final String SAF_IDT = "eyJhbGciOiJub25lIn0.eyJzdWIiOiJVU0VSIiwiZXhwIjoxNzAxMjc2NTUyfQ."; + private static final ObjectWriter WRITER = new ObjectMapper().writer(); + + + private String getHeaderValue(HttpExchange httpExchange, String headerName) { + List headerValue = Optional.ofNullable(httpExchange.getRequestHeaders().get(headerName)) + .orElse(Collections.emptyList()); + assertTrue(headerValue.size() <= 1); + return headerValue.isEmpty() ? null : headerValue.get(0); + } + + @Nested + @AcceptanceTest + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + class GivenValidAuth extends AcceptanceTestWithMockServices { + MockService zaas; + MockService service; + + @BeforeEach + void setup() throws IOException { + ZaasTokenResponse response = new ZaasTokenResponse(); + response.setToken(SAF_IDT); + + zaas = mockService("gateway").scope(MockService.Scope.TEST) + .addEndpoint("/gateway/zaas/safIdt") + .responseCode(200) + .assertion(he -> assertEquals("Bearer userJwt", he.getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION))) + .assertion(he -> { + try { + assertEquals(WRITER.writeValueAsString(new TicketRequest("IZUDFLT")), IOUtils.toString(he.getRequestBody(), StandardCharsets.UTF_8)); + } catch (IOException e) { + throw new RuntimeException(e); + } + }) + .bodyJson(response) + .and().start(); + service = mockService("service").scope(MockService.Scope.TEST) + .authenticationScheme(AuthenticationScheme.SAF_IDT).applid("IZUDFLT") + .addEndpoint("/service/test") + .assertion(he -> assertEquals(SAF_IDT, getHeaderValue(he, "x-saf-token"))) + .and().start(); + } + + @Test + void thenReturnSAFIDtoken() { + given() + .header(HttpHeaders.AUTHORIZATION, "Bearer userJwt") + .when() + .get(basePath + "/" + SERVICE_ID + "/api/v1/test") + .then() + .statusCode(Matchers.is(SC_OK)); + } + } + + @Nested + @AcceptanceTest + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + class GivenNoAuth extends AcceptanceTestWithMockServices { + MockService zaas; + MockService service; + + @BeforeEach + void setup() throws IOException { + + zaas = mockService("gateway").scope(MockService.Scope.CLASS) + .addEndpoint("/gateway/zaas/safIdt") + .responseCode(401) + .assertion(he -> assertNull(he.getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION))) + .and().start(); + service = mockService("service").scope(MockService.Scope.CLASS) + .authenticationScheme(AuthenticationScheme.SAF_IDT).applid("IZUDFLT") + .addEndpoint("/service/test") + .assertion(he -> assertNull(getHeaderValue(he, "x-saf-token"))) + .assertion(he -> assertEquals("Invalid or missing authentication", getHeaderValue(he, ApimlConstants.AUTH_FAIL_HEADER))) + .and().start(); + } + + @Test + void thenReturnError() { + + given() + .when() + .get(basePath + "/" + SERVICE_ID + "/api/v1/test") + .then() + .statusCode(Matchers.is(SC_OK)); + } + } + + +} diff --git a/common-service-core/src/main/java/org/zowe/apiml/constants/ApimlConstants.java b/common-service-core/src/main/java/org/zowe/apiml/constants/ApimlConstants.java index f51ad26eb0..2e76dbeb59 100644 --- a/common-service-core/src/main/java/org/zowe/apiml/constants/ApimlConstants.java +++ b/common-service-core/src/main/java/org/zowe/apiml/constants/ApimlConstants.java @@ -25,5 +25,6 @@ private ApimlConstants() { public static final String PAT_HEADER_NAME = "PRIVATE-TOKEN"; public static final String AUTH_FAIL_HEADER = "X-Zowe-Auth-Failure"; public static final String HTTP_CLIENT_USE_CLIENT_CERTIFICATE = "apiml.useClientCert"; + public static final String SAF_TOKEN_HEADER = "X-SAF-Token"; } diff --git a/gateway-service/src/main/java/org/zowe/apiml/gateway/security/service/schema/SafIdtScheme.java b/gateway-service/src/main/java/org/zowe/apiml/gateway/security/service/schema/SafIdtScheme.java index cc52c506cf..88e4144c75 100644 --- a/gateway-service/src/main/java/org/zowe/apiml/gateway/security/service/schema/SafIdtScheme.java +++ b/gateway-service/src/main/java/org/zowe/apiml/gateway/security/service/schema/SafIdtScheme.java @@ -20,6 +20,7 @@ import org.springframework.stereotype.Component; import org.zowe.apiml.auth.Authentication; import org.zowe.apiml.auth.AuthenticationScheme; +import org.zowe.apiml.constants.ApimlConstants; import org.zowe.apiml.gateway.security.service.saf.SafIdtAuthException; import org.zowe.apiml.gateway.security.service.saf.SafIdtException; import org.zowe.apiml.gateway.security.service.saf.SafIdtProvider; @@ -148,14 +149,13 @@ public class SafIdtCommand extends AuthenticationCommand { @Getter private final Long expireAt; - protected static final String SAF_TOKEN_HEADER = "X-SAF-Token"; @Override public void apply(InstanceInfo instanceInfo) { if (safIdentityToken != null) { final RequestContext context = RequestContext.getCurrentContext(); // add header with SafIdt token to request and remove APIML token from Cookie if exists - context.addZuulRequestHeader(SAF_TOKEN_HEADER, safIdentityToken); + context.addZuulRequestHeader(ApimlConstants.SAF_TOKEN_HEADER, safIdentityToken); String[] cookiesToBeRemoved = new String[]{authConfigurationProperties.getCookieProperties().getCookieName(), authConfigurationProperties.getCookieProperties().getCookieNamePAT()}; JwtCommand.removeCookie(context, cookiesToBeRemoved); } diff --git a/gateway-service/src/test/java/org/zowe/apiml/gateway/security/service/schema/SafIdtSchemeTest.java b/gateway-service/src/test/java/org/zowe/apiml/gateway/security/service/schema/SafIdtSchemeTest.java index 7fc2d69012..9eb112d286 100644 --- a/gateway-service/src/test/java/org/zowe/apiml/gateway/security/service/schema/SafIdtSchemeTest.java +++ b/gateway-service/src/test/java/org/zowe/apiml/gateway/security/service/schema/SafIdtSchemeTest.java @@ -18,9 +18,15 @@ import org.junit.jupiter.api.Test; import org.springframework.mock.web.MockHttpServletRequest; import org.zowe.apiml.auth.Authentication; +import org.zowe.apiml.constants.ApimlConstants; import org.zowe.apiml.gateway.security.service.saf.SafIdtException; import org.zowe.apiml.gateway.security.service.saf.SafIdtProvider; -import org.zowe.apiml.gateway.security.service.schema.source.*; +import org.zowe.apiml.gateway.security.service.schema.source.AuthSchemeException; +import org.zowe.apiml.gateway.security.service.schema.source.AuthSource; +import org.zowe.apiml.gateway.security.service.schema.source.AuthSourceService; +import org.zowe.apiml.gateway.security.service.schema.source.JwtAuthSource; +import org.zowe.apiml.gateway.security.service.schema.source.ParsedTokenAuthSource; +import org.zowe.apiml.gateway.security.service.schema.source.X509AuthSource; import org.zowe.apiml.passticket.IRRPassTicketGenerationException; import org.zowe.apiml.passticket.PassTicketService; import org.zowe.apiml.security.common.config.AuthConfigurationProperties; @@ -33,8 +39,19 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import static org.zowe.apiml.auth.AuthenticationScheme.SAF_IDT; import static org.zowe.apiml.constants.ApimlConstants.AUTH_FAIL_HEADER; @@ -120,7 +137,7 @@ void givenAuthenticatedJwtToken() { assertTrue(ac.isRequiredValidSource()); ac.apply(null); - assertThat(getValueOfZuulHeader(SafIdtScheme.SafIdtCommand.SAF_TOKEN_HEADER), is(safIdt)); + assertThat(getValueOfZuulHeader(ApimlConstants.SAF_TOKEN_HEADER), is(safIdt)); assertNull(getValueOfZuulHeader(AUTH_FAIL_HEADER)); } @@ -138,7 +155,7 @@ void givenIdtWithoutExpiration() { assertTrue(ac.isRequiredValidSource()); ac.apply(null); - assertThat(getValueOfZuulHeader(SafIdtScheme.SafIdtCommand.SAF_TOKEN_HEADER), is(safIdt)); + assertThat(getValueOfZuulHeader(ApimlConstants.SAF_TOKEN_HEADER), is(safIdt)); assertNull(getValueOfZuulHeader(AUTH_FAIL_HEADER)); } } diff --git a/integration-tests/src/test/java/org/zowe/apiml/integration/authentication/schemes/CloudGatewayAuthTest.java b/integration-tests/src/test/java/org/zowe/apiml/integration/authentication/schemes/CloudGatewayAuthTest.java index 231fee5758..121eee4973 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/integration/authentication/schemes/CloudGatewayAuthTest.java +++ b/integration-tests/src/test/java/org/zowe/apiml/integration/authentication/schemes/CloudGatewayAuthTest.java @@ -56,6 +56,11 @@ private static Stream validToBeTransformed() { assertNull(response.jsonPath().getString("headers.authorization")); assertTrue(CollectionUtils.isEmpty(response.jsonPath().getList("certs"))); }), + Arguments.of("SAF IDT auth scheme", SAF_IDT_REQUEST, (Consumer) response -> { + assertNull(response.jsonPath().getString("cookies.jwtToken")); + assertNotNull(response.jsonPath().getString("headers.x-saf-token")); + assertTrue(CollectionUtils.isEmpty(response.jsonPath().getList("certs"))); + }), Arguments.of("PassTicket auth scheme", REQUEST_INFO_ENDPOINT, (Consumer) response -> { assertNotNull(response.jsonPath().getString("headers.authorization")); assertTrue(response.jsonPath().getString("headers.authorization").startsWith("Basic ")); @@ -77,6 +82,7 @@ private static Stream noAuthTransformation() { return Stream.of( Arguments.of("Zowe auth scheme", ZOWE_JWT_REQUEST, assertions), Arguments.of("z/OSMF auth scheme", ZOSMF_REQUEST, assertions), + Arguments.of("SAF IDT auth scheme", SAF_IDT_REQUEST, assertions), Arguments.of("PassTicket auth scheme", REQUEST_INFO_ENDPOINT, assertions) ); }