Skip to content

Commit d4a91b0

Browse files
authored
feat: Certificate authentication for static refresh endpoint (#1782)
* Add integration tests for static refresh cert auth Signed-off-by: Carson Cook <carson.cook@ibm.com> * Add security config for static refresh endpoint Signed-off-by: Carson Cook <carson.cook@ibm.com> * Fix merge not adding import Signed-off-by: Carson Cook <carson.cook@ibm.com> * Fix unnecessary security config Signed-off-by: Carson Cook <carson.cook@ibm.com> * Fix integration testing of refresh endpoint Signed-off-by: Carson Cook <carson.cook@ibm.com>
1 parent 73bdfe0 commit d4a91b0

File tree

2 files changed

+113
-67
lines changed

2 files changed

+113
-67
lines changed

api-catalog-services/src/main/java/org/zowe/apiml/apicatalog/security/SecurityConfiguration.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@
3030
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
3131
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
3232
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
33-
import org.zowe.apiml.filter.SecureConnectionFilter;
3433
import org.zowe.apiml.filter.AttlsFilter;
34+
import org.zowe.apiml.filter.SecureConnectionFilter;
3535
import org.zowe.apiml.security.client.EnableApimlAuth;
3636
import org.zowe.apiml.security.client.login.GatewayLoginProvider;
3737
import org.zowe.apiml.security.client.token.GatewayTokenProvider;
@@ -60,6 +60,7 @@
6060
@EnableApimlAuth
6161
public class SecurityConfiguration {
6262
private static final String APIDOC_ROUTES = "/apidoc/**";
63+
private static final String STATIC_REFRESH_ROUTE = "/static-api/refresh";
6364

6465
private final ObjectMapper securityObjectMapper;
6566
private final AuthConfigurationProperties authConfigurationProperties;
@@ -84,7 +85,6 @@ public class FilterChainBasicAuthOrTokenOrCertForApiDoc extends WebSecurityConfi
8485
@Value("${apiml.security.ssl.nonStrictVerifySslCertificatesOfServices:false}")
8586
private boolean nonStrictVerifySslCertificatesOfServices;
8687

87-
8888
@Override
8989
protected void configure(AuthenticationManagerBuilder auth) {
9090
auth.authenticationProvider(gatewayLoginProvider);
@@ -95,11 +95,11 @@ protected void configure(AuthenticationManagerBuilder auth) {
9595
@Override
9696
protected void configure(HttpSecurity http) throws Exception {
9797
mainframeCredentialsConfiguration(
98-
baseConfiguration(http.antMatcher(APIDOC_ROUTES)),
98+
baseConfiguration(http.requestMatchers().antMatchers(APIDOC_ROUTES, STATIC_REFRESH_ROUTE).and()),
9999
authenticationManager()
100100
)
101101
.authorizeRequests()
102-
.antMatchers(APIDOC_ROUTES).authenticated();
102+
.antMatchers(APIDOC_ROUTES, STATIC_REFRESH_ROUTE).authenticated();
103103

104104
if (verifySslCertificatesOfServices || nonStrictVerifySslCertificatesOfServices) {
105105
if (isAttlsEnabled) {
@@ -188,6 +188,9 @@ private HttpSecurity baseConfiguration(HttpSecurity http) throws Exception {
188188
.defaultAuthenticationEntryPointFor(
189189
handlerInitializer.getBasicAuthUnauthorizedHandler(), new AntPathRequestMatcher(APIDOC_ROUTES)
190190
)
191+
.defaultAuthenticationEntryPointFor(
192+
handlerInitializer.getBasicAuthUnauthorizedHandler(), new AntPathRequestMatcher(STATIC_REFRESH_ROUTE)
193+
)
191194
.defaultAuthenticationEntryPointFor(
192195
handlerInitializer.getUnAuthorizedHandler(), new AntPathRequestMatcher("/**")
193196
)

integration-tests/src/test/java/org/zowe/apiml/functional/apicatalog/ApiCatalogAuthenticationTest.java

Lines changed: 106 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,22 @@
1111

1212
import io.restassured.RestAssured;
1313
import io.restassured.config.SSLConfig;
14+
import io.restassured.response.Validatable;
15+
import io.restassured.specification.RequestSpecification;
1416
import org.apache.commons.lang3.StringUtils;
15-
import org.junit.jupiter.api.*;
17+
import org.junit.jupiter.api.BeforeAll;
18+
import org.junit.jupiter.api.BeforeEach;
19+
import org.junit.jupiter.api.Nested;
20+
import org.junit.jupiter.api.Test;
1621
import org.junit.jupiter.params.ParameterizedTest;
1722
import org.junit.jupiter.params.provider.Arguments;
1823
import org.junit.jupiter.params.provider.MethodSource;
1924
import org.springframework.http.HttpHeaders;
2025
import org.springframework.http.HttpStatus;
2126
import org.zowe.apiml.util.categories.GeneralAuthenticationTest;
22-
import org.zowe.apiml.util.config.*;
27+
import org.zowe.apiml.util.config.ConfigReader;
28+
import org.zowe.apiml.util.config.ItSslConfigFactory;
29+
import org.zowe.apiml.util.config.SslContext;
2330
import org.zowe.apiml.util.service.DiscoveryUtils;
2431

2532
import java.util.List;
@@ -37,11 +44,12 @@ class ApiCatalogAuthenticationTest {
3744
private final static String PASSWORD = ConfigReader.environmentConfiguration().getCredentials().getPassword();
3845
private final static String USERNAME = ConfigReader.environmentConfiguration().getCredentials().getUser();
3946

40-
private static final String CATALOG_PREFIX = "/api/v1";
4147
private static final String CATALOG_SERVICE_ID = "apicatalog";
4248
private static final String CATALOG_SERVICE_ID_PATH = "/" + CATALOG_SERVICE_ID;
49+
private static final String CATALOG_PREFIX = "/api/v1";
4350

4451
private static final String CATALOG_APIDOC_ENDPOINT = "/apidoc/discoverableclient/v1";
52+
private static final String CATALOG_STATIC_REFRESH_ENDPOINT = "/static-api/refresh";
4553
private static final String CATALOG_ACTUATOR_ENDPOINT = "/application";
4654

4755
private final static String COOKIE = "apimlAuthenticationToken";
@@ -51,10 +59,23 @@ class ApiCatalogAuthenticationTest {
5159

5260
private static String apiCatalogServiceUrl = ConfigReader.environmentConfiguration().getApiCatalogServiceConfiguration().getUrl();
5361

54-
static Stream<Arguments> urlsToTest() {
62+
@FunctionalInterface
63+
private interface Request {
64+
Validatable<?, ?> execute(RequestSpecification when, String endpoint);
65+
}
66+
67+
static Stream<Arguments> requestsToTest() {
5568
return Stream.of(
56-
Arguments.of(CATALOG_APIDOC_ENDPOINT),
57-
Arguments.of(CATALOG_ACTUATOR_ENDPOINT)
69+
Arguments.of(CATALOG_APIDOC_ENDPOINT, (Request) (when, endpoint) -> when.get(getUriFromGateway(CATALOG_SERVICE_ID_PATH + CATALOG_PREFIX + endpoint))),
70+
Arguments.of(CATALOG_STATIC_REFRESH_ENDPOINT, (Request) (when, endpoint) -> when.post(getUriFromGateway(CATALOG_SERVICE_ID_PATH + CATALOG_PREFIX + endpoint))),
71+
Arguments.of(CATALOG_ACTUATOR_ENDPOINT, (Request) (when, endpoint) -> when.get(getUriFromGateway(CATALOG_SERVICE_ID_PATH + CATALOG_PREFIX + endpoint)))
72+
);
73+
}
74+
75+
static Stream<Arguments> requestsToTestWithCertificate() {
76+
return Stream.of(
77+
Arguments.of(CATALOG_SERVICE_ID_PATH + CATALOG_APIDOC_ENDPOINT, (Request) (when, endpoint) -> when.get(apiCatalogServiceUrl + endpoint)),
78+
Arguments.of(CATALOG_SERVICE_ID_PATH + CATALOG_STATIC_REFRESH_ENDPOINT, (Request) (when, endpoint) -> when.post(apiCatalogServiceUrl + endpoint))
5879
);
5980
}
6081

@@ -82,24 +103,28 @@ class WhenAccessingCatalog {
82103
@Nested
83104
class ReturnOk {
84105
@ParameterizedTest(name = "givenValidBasicAuthentication {index} {0} ")
85-
@MethodSource("org.zowe.apiml.functional.apicatalog.ApiCatalogAuthenticationTest#urlsToTest")
86-
void givenValidBasicAuthentication(String endpoint) {
87-
given()
88-
.auth().preemptive().basic(USERNAME, PASSWORD) // Isn't this kind of strange behavior?
89-
.when()
90-
.get(getUriFromGateway(CATALOG_PREFIX + CATALOG_SERVICE_ID_PATH + endpoint))
106+
@MethodSource("org.zowe.apiml.functional.apicatalog.ApiCatalogAuthenticationTest#requestsToTest")
107+
void givenValidBasicAuthentication(String endpoint, Request request) {
108+
request.execute(
109+
given()
110+
.auth().preemptive().basic(USERNAME, PASSWORD) // Isn't this kind of strange behavior?
111+
.when(),
112+
endpoint
113+
)
91114
.then()
92115
.statusCode(is(SC_OK));
93116
}
94117

95-
@ParameterizedTest(name = "givenValidBasicAuthentication {index} {0} ")
96-
@MethodSource("org.zowe.apiml.functional.apicatalog.ApiCatalogAuthenticationTest#urlsToTest")
97-
void givenValidBasicAuthenticationAndCertificate(String endpoint) {
98-
given()
99-
.config(SslContext.clientCertApiml)
100-
.auth().preemptive().basic(USERNAME, PASSWORD) // Isn't this kind of strange behavior?
101-
.when()
102-
.get(getUriFromGateway(CATALOG_PREFIX + CATALOG_SERVICE_ID_PATH + endpoint))
118+
@ParameterizedTest(name = "givenValidBasicAuthenticationAndCertificate {index} {0} ")
119+
@MethodSource("org.zowe.apiml.functional.apicatalog.ApiCatalogAuthenticationTest#requestsToTest")
120+
void givenValidBasicAuthenticationAndCertificate(String endpoint, Request request) {
121+
request.execute(
122+
given()
123+
.config(SslContext.clientCertApiml)
124+
.auth().preemptive().basic(USERNAME, PASSWORD) // Isn't this kind of strange behavior?
125+
.when(),
126+
endpoint
127+
)
103128
.then()
104129
.statusCode(is(SC_OK));
105130
}
@@ -108,14 +133,16 @@ void givenValidBasicAuthenticationAndCertificate(String endpoint) {
108133
@Nested
109134
class ReturnUnauthorized {
110135
@ParameterizedTest(name = "givenNoAuthentication {index} {0}")
111-
@MethodSource("org.zowe.apiml.functional.apicatalog.ApiCatalogAuthenticationTest#urlsToTest")
112-
void givenNoAuthentication(String endpoint) {
136+
@MethodSource("org.zowe.apiml.functional.apicatalog.ApiCatalogAuthenticationTest#requestsToTest")
137+
void givenNoAuthentication(String endpoint, Request request) {
113138
String expectedMessage = "Authentication is required for URL '" + CATALOG_SERVICE_ID_PATH + endpoint + "'";
114139

115-
given()
116-
.config(SslContext.tlsWithoutCert)
117-
.when()
118-
.get(getUriFromGateway(CATALOG_PREFIX + CATALOG_SERVICE_ID_PATH + endpoint))
140+
request.execute(
141+
given()
142+
.config(SslContext.tlsWithoutCert)
143+
.when(),
144+
endpoint
145+
)
119146
.then()
120147
.statusCode(is(SC_UNAUTHORIZED))
121148
.header(HttpHeaders.WWW_AUTHENTICATE, BASIC_AUTHENTICATION_PREFIX)
@@ -125,30 +152,34 @@ void givenNoAuthentication(String endpoint) {
125152
}
126153

127154
@ParameterizedTest(name = "givenInvalidBasicAuthentication {index} {0}")
128-
@MethodSource("org.zowe.apiml.functional.apicatalog.ApiCatalogAuthenticationTest#urlsToTest")
129-
void givenInvalidBasicAuthentication(String endpoint) {
155+
@MethodSource("org.zowe.apiml.functional.apicatalog.ApiCatalogAuthenticationTest#requestsToTest")
156+
void givenInvalidBasicAuthentication(String endpoint, Request request) {
130157
String expectedMessage = "Invalid username or password for URL '" + CATALOG_SERVICE_ID_PATH + endpoint + "'";
131158

132-
given()
133-
.auth().preemptive().basic(INVALID_USERNAME, INVALID_PASSWORD)
134-
.when()
135-
.get(getUriFromGateway(CATALOG_PREFIX + CATALOG_SERVICE_ID_PATH + endpoint))
159+
request.execute(
160+
given()
161+
.auth().preemptive().basic(INVALID_USERNAME, INVALID_PASSWORD)
162+
.when(),
163+
endpoint
164+
)
136165
.then()
137166
.body(
138167
"messages.find { it.messageNumber == 'ZWEAS120E' }.messageContent", equalTo(expectedMessage)
139168
).statusCode(is(SC_UNAUTHORIZED));
140169
}
141170

142171
@ParameterizedTest(name = "givenInvalidTokenInCookie {index} {0}")
143-
@MethodSource("org.zowe.apiml.functional.apicatalog.ApiCatalogAuthenticationTest#urlsToTest")
144-
void givenInvalidTokenInCookie(String endpoint) {
172+
@MethodSource("org.zowe.apiml.functional.apicatalog.ApiCatalogAuthenticationTest#requestsToTest")
173+
void givenInvalidTokenInCookie(String endpoint, Request request) {
145174
String expectedMessage = "Token is not valid for URL '" + CATALOG_SERVICE_ID_PATH + endpoint + "'";
146175
String invalidToken = "nonsense";
147176

148-
given()
149-
.cookie(COOKIE, invalidToken)
150-
.when()
151-
.get(getUriFromGateway(CATALOG_PREFIX + CATALOG_SERVICE_ID_PATH + endpoint))
177+
request.execute(
178+
given()
179+
.cookie(COOKIE, invalidToken)
180+
.when(),
181+
endpoint
182+
)
152183
.then()
153184
.statusCode(is(SC_UNAUTHORIZED))
154185
.body(
@@ -166,53 +197,65 @@ class WhenAccessApiDocRoute {
166197

167198
@Nested
168199
class ThenReturnOk {
169-
@Test
170-
void givenValidCertificate() {
171-
given()
172-
.config(SslContext.clientCertUser)
173-
.when()
174-
.get(apiCatalogServiceUrl + CATALOG_SERVICE_ID_PATH + CATALOG_APIDOC_ENDPOINT)
200+
@ParameterizedTest(name = "givenValidCertificate {index} {0} ")
201+
@MethodSource("org.zowe.apiml.functional.apicatalog.ApiCatalogAuthenticationTest#requestsToTestWithCertificate")
202+
void givenValidCertificate(String endpoint, Request request) {
203+
request.execute(
204+
given()
205+
.config(SslContext.clientCertUser)
206+
.when(),
207+
endpoint
208+
)
175209
.then()
176210
.statusCode(HttpStatus.OK.value());
177211
}
178212

179-
@Test
180-
void givenValidCertificateAndBasicAuth() {
181-
given()
182-
.config(SslContext.clientCertApiml)
183-
.auth().preemptive().basic(USERNAME, PASSWORD) // Isn't this kind of strange behavior?
184-
.when()
185-
.get(apiCatalogServiceUrl + CATALOG_SERVICE_ID_PATH + CATALOG_APIDOC_ENDPOINT)
213+
@ParameterizedTest(name = "givenValidCertificateAndBasicAuth {index} {0} ")
214+
@MethodSource("org.zowe.apiml.functional.apicatalog.ApiCatalogAuthenticationTest#requestsToTestWithCertificate")
215+
void givenValidCertificateAndBasicAuth(String endpoint, Request request) {
216+
request.execute(
217+
given()
218+
.config(SslContext.clientCertApiml)
219+
.auth().preemptive().basic(USERNAME, PASSWORD) // Isn't this kind of strange behavior?
220+
.when(),
221+
endpoint
222+
)
186223
.then()
187224
.statusCode(is(SC_OK));
188225
}
189226
}
190227

191228
@Nested
192229
class ThenReturnUnauthorized {
193-
@Test
194-
void givenUnTrustedCertificateAndNoBasicAuth_thenReturnUnauthorized() {
195-
given()
196-
.config(SslContext.selfSignedUntrusted)
197-
.when()
198-
.get(apiCatalogServiceUrl + CATALOG_SERVICE_ID_PATH + CATALOG_APIDOC_ENDPOINT)
230+
@ParameterizedTest(name = "givenUnTrustedCertificateAndNoBasicAuth_thenReturnUnauthorized {index} {0} ")
231+
@MethodSource("org.zowe.apiml.functional.apicatalog.ApiCatalogAuthenticationTest#requestsToTestWithCertificate")
232+
void givenUnTrustedCertificateAndNoBasicAuth_thenReturnUnauthorized(String endpoint, Request request) {
233+
request.execute(
234+
given()
235+
.config(SslContext.selfSignedUntrusted)
236+
.when(),
237+
endpoint
238+
)
199239
.then()
200240
.statusCode(HttpStatus.UNAUTHORIZED.value());
201241
}
202242

203-
@Test
204-
void givenNoCertificateAndNoBasicAuth_thenReturnUnauthorized() {
205-
given()
206-
.when()
207-
.get(apiCatalogServiceUrl + CATALOG_SERVICE_ID_PATH + CATALOG_APIDOC_ENDPOINT)
243+
@ParameterizedTest(name = "givenNoCertificateAndNoBasicAuth_thenReturnUnauthorized {index} {0} ")
244+
@MethodSource("org.zowe.apiml.functional.apicatalog.ApiCatalogAuthenticationTest#requestsToTestWithCertificate")
245+
void givenNoCertificateAndNoBasicAuth_thenReturnUnauthorized(String endpoint, Request request) {
246+
request.execute(
247+
given()
248+
.when(),
249+
endpoint
250+
)
208251
.then()
209252
.statusCode(HttpStatus.UNAUTHORIZED.value());
210253
}
211254
}
212255
}
213256

214257
@Test
215-
void givenOnlyValidCertificate_whenAccessNotApiDocRoute_thenReturnUnauthorized() {
258+
void givenOnlyValidCertificate_whenAccessNotCertificateAuthedRoute_thenReturnUnauthorized() {
216259
given()
217260
.config(SslContext.clientCertApiml)
218261
.when()

0 commit comments

Comments
 (0)