Skip to content

Commit

Permalink
build and test fixes
Browse files Browse the repository at this point in the history
Signed-off-by: Pavel Jareš <[email protected]>
  • Loading branch information
pj892031 committed Jan 7, 2025
1 parent 23f0460 commit 7ff78a4
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 29 deletions.
1 change: 1 addition & 0 deletions apiml-common/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ dependencies {
implementation libs.spring.boot.starter.actuator
implementation libs.spring.boot.starter.web
implementation libs.spring.cloud.starter.eureka.client
implementation libs.eureka.core

implementation libs.apache.commons.lang3
implementation libs.janino
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration;
import org.springframework.cloud.gateway.config.GatewayReactiveOAuth2AutoConfiguration;

@SpringBootApplication(
scanBasePackages = {
Expand All @@ -24,7 +23,7 @@
"org.zowe.apiml.product.logging",
"org.zowe.apiml.security"
},
exclude = {ReactiveOAuth2ClientAutoConfiguration.class, GatewayReactiveOAuth2AutoConfiguration.class}
exclude = {ReactiveOAuth2ClientAutoConfiguration.class}
)
public class GatewayServiceApplication {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,25 +91,28 @@ protected Mono<Void> processResponse(ServerWebExchange exchange, GatewayFilterCh
);
headers.set(HttpHeaders.COOKIE, cookieHeader);
}).build();
exchange = exchange.mutate().request(request).build();
}
if (!StringUtils.isEmpty(response.get().getHeaderName())) {
request = cleanHeadersOnAuthSuccess(exchange);
request = request.mutate().headers(headers ->
headers.add(response.get().getHeaderName(), response.get().getToken())
).build();
exchange = exchange.mutate().request(request).build();
}
if (failureHeader.isPresent()) {
if (request != null) {
request = request.mutate().headers(httpHeaders -> httpHeaders.add(ApimlConstants.AUTH_FAIL_HEADER, failureHeader.get())).build();
exchange = exchange.mutate().request(request).build();
}
exchange.getResponse().getHeaders().add(ApimlConstants.AUTH_FAIL_HEADER, failureHeader.get());
}
}
if (request == null) {
request = cleanHeadersOnAuthFail(exchange, failureHeader.orElse("Invalid or missing authentication"));
exchange = exchange.mutate().request(request).build();
}

exchange = exchange.mutate().request(request).build();
return chain.filter(exchange);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,44 +10,60 @@

package org.zowe.apiml.gateway.filters;

import lombok.Builder;
import lombok.RequiredArgsConstructor;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.server.ServerWebExchange;
import org.zowe.apiml.constants.ApimlConstants;
import org.zowe.apiml.zaas.ZaasTokenResponse;

import java.util.List;
import java.util.Optional;
import java.util.OptionalLong;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

class AbstractTokenFilterFactoryTest {

@Nested
class RequestUpdate {

private MockServerHttpRequest testRequestMutation(AbstractAuthSchemeFactory.AuthorizationResponse<ZaasTokenResponse> tokenResponse) {
MockServerHttpRequest request = MockServerHttpRequest.get("/url").build();
MockServerWebExchange exchange = MockServerWebExchange.from(request);
private ServerHttpRequest testRequestMutation(AbstractAuthSchemeFactory.AuthorizationResponse<ZaasTokenResponse> tokenResponse) {
var chain = mock(GatewayFilterChain.class);
var request = MockServerHttpRequest.get("/url").build();
var exchange = MockServerWebExchange.from(request);

new AbstractTokenFilterFactory<>(AbstractTokenFilterFactory.Config.class, null, null, null) {
@Override
public String getEndpointUrl(ServiceInstance instance) {
return null;
}
}.processResponse(exchange, mock(GatewayFilterChain.class), tokenResponse);
}.processResponse(exchange, chain, tokenResponse);

var modifiedExchange = ArgumentCaptor.forClass(ServerWebExchange.class);
verify(chain).filter(modifiedExchange.capture());

return request;
return modifiedExchange.getValue().getRequest();
}

@Nested
class ValidResponse {

@Test
void givenHeaderResponse_whenHandling_thenUpdateTheRequest() {
MockServerHttpRequest request = testRequestMutation(new AbstractAuthSchemeFactory.AuthorizationResponse<>(null,ZaasTokenResponse.builder()
var request = testRequestMutation(new AbstractAuthSchemeFactory.AuthorizationResponse<>(null,ZaasTokenResponse.builder()
.headerName("headerName")
.token("headerValue")
.build()
Expand All @@ -57,7 +73,7 @@ void givenHeaderResponse_whenHandling_thenUpdateTheRequest() {

@Test
void givenCookieResponse_whenHandling_thenUpdateTheRequest() {
MockServerHttpRequest request = testRequestMutation(new AbstractAuthSchemeFactory.AuthorizationResponse<>(null,ZaasTokenResponse.builder()
var request = testRequestMutation(new AbstractAuthSchemeFactory.AuthorizationResponse<>(null,ZaasTokenResponse.builder()
.cookieName("cookieName")
.token("cookieValue")
.build()
Expand All @@ -72,17 +88,34 @@ class InvalidResponse {

@Test
void givenEmptyResponse_whenHandling_thenNoUpdate() {
MockServerHttpRequest request = testRequestMutation(new AbstractAuthSchemeFactory.AuthorizationResponse<>(null,ZaasTokenResponse.builder()
var request = testRequestMutation(new AbstractAuthSchemeFactory.AuthorizationResponse<>(null, ZaasTokenResponse.builder()
.token("jwt")
.build()
));
assertEquals(1, request.getHeaders().size());
assertTrue(request.getHeaders().containsKey(ApimlConstants.AUTH_FAIL_HEADER));
assertEquals("Invalid or missing authentication", request.getHeaders().getFirst(ApimlConstants.AUTH_FAIL_HEADER));
}

@Test
void givenEmptyResponseWithError_whenHandling_thenProvideErrorHeader() {
var request = testRequestMutation(new AbstractAuthSchemeFactory.AuthorizationResponse<>(
MockHeaders.builder()
.name(ApimlConstants.AUTH_FAIL_HEADER.toLowerCase())
.value("anError")
.build(),
ZaasTokenResponse.builder()
.token("jwt")
.build()
));
assertEquals(1, request.getHeaders().size());
assertTrue(request.getHeaders().containsKey(ApimlConstants.AUTH_FAIL_HEADER));
assertEquals("anError", request.getHeaders().getFirst(ApimlConstants.AUTH_FAIL_HEADER));
}

@Test
void givenCookieAndHeaderInResponse_whenHandling_thenSetBoth() {
MockServerHttpRequest request = testRequestMutation(new AbstractAuthSchemeFactory.AuthorizationResponse<>(null,ZaasTokenResponse.builder()
var request = testRequestMutation(new AbstractAuthSchemeFactory.AuthorizationResponse<>(null,ZaasTokenResponse.builder()
.cookieName("cookie")
.headerName("header")
.token("jwt")
Expand All @@ -96,4 +129,35 @@ void givenCookieAndHeaderInResponse_whenHandling_thenSetBoth() {

}

@RequiredArgsConstructor
@Builder
static class MockHeaders implements ClientResponse.Headers {

private final String name;
private final String value;

@Override
public OptionalLong contentLength() {
return OptionalLong.empty();
}

@Override
public Optional<MediaType> contentType() {
return Optional.empty();
}

@Override
public List<String> header(String headerName) {
return List.of(value);
}

@Override
public org.springframework.http.HttpHeaders asHttpHeaders() {
var headers = new org.springframework.http.HttpHeaders();
headers.add(name, value);
return headers;
}

}

}
1 change: 1 addition & 0 deletions gradle/versions.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ dependencyResolutionManagement {
library('commons_io', 'commons-io', 'commons-io').versionRef('commonsIo')
library('eh_cache', 'org.ehcache', 'ehcache').versionRef('ehCache')
library('eureka_jersey_client', 'com.netflix.eureka', 'eureka-client-jersey3').versionRef('eureka')
library('eureka_core', 'com.netflix.eureka', 'eureka-core').versionRef('eureka')
library('google_errorprone', 'com.google.errorprone', 'error_prone_annotations').versionRef('googleErrorprone') // to define minimum version and avoid duplicity libraries in the classpath
library('guava', 'com.google.guava', 'guava').versionRef('guava')
library('hamcrest', 'org.hamcrest', 'hamcrest').versionRef('hamcrest')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,34 +16,29 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import org.zowe.apiml.constants.ApimlConstants;
import org.zowe.apiml.passticket.IRRPassTicketGenerationException;
import org.zowe.apiml.passticket.PassTicketService;
import org.zowe.apiml.security.common.token.NoMainframeIdentityException;
import org.zowe.apiml.ticket.TicketRequest;
import org.zowe.apiml.ticket.TicketResponse;
import org.zowe.apiml.zaas.ZaasTokenResponse;
import org.zowe.apiml.zaas.security.service.TokenCreationService;
import org.zowe.apiml.zaas.security.service.schema.source.AuthSource;
import org.zowe.apiml.zaas.security.service.schema.source.AuthSourceService;
import org.zowe.apiml.zaas.security.service.zosmf.ZosmfService;
import org.zowe.apiml.zaas.security.ticket.ApplicationNameNotFoundException;
import org.zowe.apiml.passticket.IRRPassTicketGenerationException;
import org.zowe.apiml.passticket.PassTicketService;
import org.zowe.apiml.ticket.TicketRequest;
import org.zowe.apiml.ticket.TicketResponse;
import org.zowe.apiml.zaas.ZaasTokenResponse;

import javax.management.ServiceNotFoundException;

import static org.zowe.apiml.security.SecurityUtils.COOKIE_AUTH_NAME;
import static org.zowe.apiml.zaas.zaas.ExtractAuthSourceFilter.AUTH_SOURCE_ATTR;
import static org.zowe.apiml.zaas.zaas.ExtractAuthSourceFilter.AUTH_SOURCE_PARSED_ATTR;
import static org.zowe.apiml.security.SecurityUtils.COOKIE_AUTH_NAME;

@RequiredArgsConstructor
@RestController
@RequestMapping(SchemeController.CONTROLLER_PATH)
@RequestMapping(value = SchemeController.CONTROLLER_PATH)
public class SchemeController {
public static final String CONTROLLER_PATH = "/zaas/scheme"; // NOSONAR

Expand All @@ -52,7 +47,7 @@ public class SchemeController {
private final ZosmfService zosmfService;
private final TokenCreationService tokenCreationService;

@PostMapping(path = "ticket", produces = MediaType.APPLICATION_JSON_VALUE)
@PostMapping(path = "ticket", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Provides PassTicket for authenticated user.")
public ResponseEntity<TicketResponse> getPassTicket(@RequestBody TicketRequest ticketRequest, @RequestAttribute(AUTH_SOURCE_PARSED_ATTR) AuthSource.Parsed authSourceParsed)
throws IRRPassTicketGenerationException, ApplicationNameNotFoundException {
Expand Down Expand Up @@ -111,7 +106,7 @@ public ResponseEntity<ZaasTokenResponse> handleNoMainframeIdException(@RequestAt
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}

@PostMapping(path = "safIdt", produces = MediaType.APPLICATION_JSON_VALUE)
@PostMapping(path = "safIdt", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "Provides SAF Identity Token for authenticated user.")
public ResponseEntity<ZaasTokenResponse> getSafIdToken(@RequestBody TicketRequest ticketRequest, @RequestAttribute(AUTH_SOURCE_PARSED_ATTR) AuthSource.Parsed authSourceParsed)
throws IRRPassTicketGenerationException, ApplicationNameNotFoundException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ void givenIncorrectMethod_whenRequestPassticket_thenBadRequest() throws Exceptio

@Test
void givenIncorrectMediaType_whenRequestPassticket_thenUnsupportedMedia() throws Exception {
ticketBody.put("applicationName", "");
ticketBody.put("applicationName", "DUMMY");

mockMvc.perform(post(PASSTICKET_URL)
.contentType(MediaType.TEXT_XML)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ void givenUnknownEndpoint_whenCallZaas_thenReturns404WithMessage() {
.get("/unknown/endpoint")
.then()
.statusCode(404)
.body("messages[0].messageKey", is("org.zowe.apiml.common.endPointNotFound"));
.body("messages[0].messageKey", is("org.zowe.apiml.common.notFound"));
}

@ParameterizedTest
Expand Down

0 comments on commit 7ff78a4

Please sign in to comment.