Skip to content

Commit 13ac9a5

Browse files
CarsonCookachmelo
andauthored
Enable /api/v1/gateway path format (#1126)
* Add integration tests for new paths and old path tests for ticket and query Signed-off-by: Carson Cook <carson.cook@ibm.com> * Configure new path formats for gw routes Signed-off-by: Carson Cook <carson.cook@ibm.com> * Add PoC for controller for old logout path proxy Signed-off-by: Carson Cook <carson.cook@ibm.com> * regex path matcher for logout Signed-off-by: achmelo <a.chmelo@gmail.com> * Remove unneeded controller Signed-off-by: Carson Cook <carson.cook@ibm.com> * Clean up regex for logout paths Signed-off-by: Carson Cook <carson.cook@ibm.com> * Add integration tests for old logout path Signed-off-by: Carson Cook <carson.cook@ibm.com> * Add old path login tests Signed-off-by: Carson Cook <carson.cook@ibm.com> * Parametrize gw path format tests Signed-off-by: Carson Cook <carson.cook@ibm.com> * Parametrize gw query tests Signed-off-by: Carson Cook <carson.cook@ibm.com> * Fix styling errors Signed-off-by: Carson Cook <carson.cook@ibm.com> * Fix parametrization of logout tests Signed-off-by: Carson Cook <carson.cook@ibm.com> * Parametrize LoginTest Signed-off-by: Carson Cook <carson.cook@ibm.com> * Fix styling Signed-off-by: Carson Cook <carson.cook@ibm.com> Co-authored-by: achmelo <a.chmelo@gmail.com>
1 parent 8c1298c commit 13ac9a5

File tree

13 files changed

+300
-209
lines changed

13 files changed

+300
-209
lines changed

apiml-security-common/src/main/java/org/zowe/apiml/security/common/config/AuthConfigurationProperties.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,15 @@ public class AuthConfigurationProperties {
3131
private ApimlLogger apimlLog = ApimlLogger.empty();
3232

3333
// General properties
34-
private String gatewayLoginEndpoint = "/api/v1/gateway/auth/login";
35-
private String gatewayLogoutEndpoint = "/api/v1/gateway/auth/logout";
36-
private String gatewayQueryEndpoint = "/api/v1/gateway/auth/query";
37-
private String gatewayTicketEndpoint = "/api/v1/gateway/auth/ticket";
34+
private String gatewayLoginEndpoint = "/gateway/api/v1/auth/login";
35+
private String gatewayLogoutEndpoint = "/gateway/api/v1/auth/logout";
36+
private String gatewayQueryEndpoint = "/gateway/api/v1/auth/query";
37+
private String gatewayTicketEndpoint = "/gateway/api/v1/auth/ticket";
38+
39+
private String gatewayLoginEndpointOldFormat = "/api/v1/gateway/auth/login";
40+
private String gatewayLogoutEndpointOldFormat = "/api/v1/gateway/auth/logout";
41+
private String gatewayQueryEndpointOldFormat = "/api/v1/gateway/auth/query";
42+
private String gatewayTicketEndpointOldFormat = "/api/v1/gateway/auth/ticket";
3843

3944
private String serviceLoginEndpoint = "/auth/login";
4045
private String serviceLogoutEndpoint = "/auth/logout";

gateway-service/src/main/java/org/zowe/apiml/gateway/security/config/SecurityConfiguration.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;
3232
import org.springframework.security.web.authentication.logout.LogoutHandler;
3333
import org.springframework.security.web.firewall.StrictHttpFirewall;
34-
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
34+
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
3535
import org.springframework.web.cors.CorsConfiguration;
3636
import org.springframework.web.cors.CorsConfigurationSource;
3737
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@@ -64,6 +64,7 @@
6464
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
6565
// List of endpoints protected by content filters
6666
private static final String[] PROTECTED_ENDPOINTS = {
67+
"/gateway/api/v1",
6768
"/api/v1/gateway",
6869
"/application",
6970
"/gateway/services"
@@ -115,18 +116,24 @@ protected void configure(HttpSecurity http) throws Exception {
115116
.and()
116117
.authorizeRequests()
117118
.antMatchers(HttpMethod.POST, authConfigurationProperties.getGatewayLoginEndpoint()).permitAll()
119+
.antMatchers(HttpMethod.POST, authConfigurationProperties.getGatewayLoginEndpointOldFormat()).permitAll()
118120

119121
// ticket endpoint
120122
.and()
121123
.authorizeRequests()
122124
.antMatchers(HttpMethod.POST, authConfigurationProperties.getGatewayTicketEndpoint()).authenticated()
125+
.antMatchers(HttpMethod.POST, authConfigurationProperties.getGatewayTicketEndpointOldFormat()).authenticated()
123126
.and().x509()
124127
.userDetailsService(x509UserDetailsService())
125128

126129
// logout endpoint
127130
.and()
128131
.logout()
129-
.logoutRequestMatcher(new AntPathRequestMatcher(authConfigurationProperties.getGatewayLogoutEndpoint(), HttpMethod.POST.name()))
132+
.logoutRequestMatcher(new RegexRequestMatcher(
133+
String.format("(%s|%s)",
134+
authConfigurationProperties.getGatewayLogoutEndpoint(),
135+
authConfigurationProperties.getGatewayLogoutEndpointOldFormat())
136+
, HttpMethod.POST.name()))
130137
.addLogoutHandler(logoutHandler())
131138
.logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler(HttpStatus.NO_CONTENT))
132139
.permitAll()
@@ -166,8 +173,12 @@ protected void configure(HttpSecurity http) throws Exception {
166173
.and()
167174
.addFilterBefore(loginFilter(authConfigurationProperties.getGatewayLoginEndpoint()), UsernamePasswordAuthenticationFilter.class)
168175
.addFilterBefore(x509Filter(authConfigurationProperties.getGatewayLoginEndpoint()), LoginFilter.class)
176+
.addFilterBefore(loginFilter(authConfigurationProperties.getGatewayLoginEndpointOldFormat()), UsernamePasswordAuthenticationFilter.class)
177+
.addFilterBefore(x509Filter(authConfigurationProperties.getGatewayLoginEndpointOldFormat()), LoginFilter.class)
169178
.addFilterBefore(queryFilter(authConfigurationProperties.getGatewayQueryEndpoint()), UsernamePasswordAuthenticationFilter.class)
179+
.addFilterBefore(queryFilter(authConfigurationProperties.getGatewayQueryEndpointOldFormat()), UsernamePasswordAuthenticationFilter.class)
170180
.addFilterBefore(ticketFilter(authConfigurationProperties.getGatewayTicketEndpoint()), UsernamePasswordAuthenticationFilter.class)
181+
.addFilterBefore(ticketFilter(authConfigurationProperties.getGatewayTicketEndpointOldFormat()), UsernamePasswordAuthenticationFilter.class)
171182
.addFilterBefore(basicFilter(), UsernamePasswordAuthenticationFilter.class)
172183
.addFilterBefore(cookieFilter(), UsernamePasswordAuthenticationFilter.class);
173184
}

integration-tests/src/test/java/org/zowe/apiml/gatewayservice/PassTicketTest.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ class PassTicketTest {
5050
private final static String DISCOVERABLECLIENT_PASSTICKET_BASE_PATH = "/api/v1/dcpassticket";
5151
private final static String DISCOVERABLECLIENT_BASE_PATH = "/api/v1/discoverableclient";
5252
private final static String PASSTICKET_TEST_ENDPOINT = "/passticketTest";
53-
private final static String TICKET_ENDPOINT = "/api/v1/gateway/auth/ticket";
53+
private final static String TICKET_ENDPOINT = "/gateway/api/v1/auth/ticket";
54+
private final static String TICKET_ENDPOINT_OLD_PATH_FORMAT = "/api/v1/gateway/auth/ticket";
5455
private final static String COOKIE = "apimlAuthenticationToken";
5556
private final static String REQUEST_INFO_ENDPOINT = "/request";
5657

@@ -144,6 +145,30 @@ void doTicketWithValidCookieAndCertificate() {
144145

145146
}
146147

148+
@Test
149+
@TestsNotMeantForZowe
150+
void generateTicketWithOldPathFormat() {
151+
RestAssured.config = RestAssured.config().sslConfig(getConfiguredSslConfig());
152+
String jwt = gatewayToken();
153+
log.info(APPLICATION_NAME);
154+
TicketRequest ticketRequest = new TicketRequest(APPLICATION_NAME);
155+
156+
// Generate ticket
157+
TicketResponse ticketResponse = given()
158+
.contentType(JSON)
159+
.body(ticketRequest)
160+
.cookie(COOKIE, jwt)
161+
.when()
162+
.post(String.format("%s://%s:%d%s", SCHEME, HOST, PORT, TICKET_ENDPOINT_OLD_PATH_FORMAT))
163+
.then()
164+
.statusCode(is(SC_OK))
165+
.extract().body().as(TicketResponse.class);
166+
167+
assertEquals(jwt, ticketResponse.getToken());
168+
assertEquals(USERNAME, ticketResponse.getUserId());
169+
assertEquals(APPLICATION_NAME, ticketResponse.getApplicationName());
170+
}
171+
147172
@Test
148173
@TestsNotMeantForZowe
149174
void doTicketWithValidHeaderAndCertificate() {

integration-tests/src/test/java/org/zowe/apiml/gatewayservice/QueryIntegrationTest.java

Lines changed: 66 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010
package org.zowe.apiml.gatewayservice;
1111

1212
import io.restassured.RestAssured;
13+
import org.apache.commons.lang3.StringUtils;
1314
import org.junit.jupiter.api.BeforeEach;
14-
import org.junit.jupiter.api.Test;
15+
import org.junit.jupiter.params.ParameterizedTest;
16+
import org.junit.jupiter.params.provider.MethodSource;
1517
import org.zowe.apiml.util.config.ConfigReader;
1618

1719
import static io.restassured.RestAssured.given;
@@ -25,14 +27,22 @@ public class QueryIntegrationTest {
2527
private final static String SCHEME = ConfigReader.environmentConfiguration().getGatewayServiceConfiguration().getScheme();
2628
private final static String HOST = ConfigReader.environmentConfiguration().getGatewayServiceConfiguration().getHost();
2729
private final static int PORT = ConfigReader.environmentConfiguration().getGatewayServiceConfiguration().getPort();
28-
private final static String BASE_PATH = "/api/v1/gateway";
30+
private final static String BASE_PATH = "/gateway/api/v1";
31+
private final static String BASE_PATH_OLD_FORMAT = "/api/v1/gateway";
2932
private final static String QUERY_ENDPOINT = "/auth/query";
3033
private final static String PASSWORD = ConfigReader.environmentConfiguration().getCredentials().getPassword();
3134
private final static String USERNAME = ConfigReader.environmentConfiguration().getCredentials().getUser();
3235
private final static String COOKIE = "apimlAuthenticationToken";
3336

3437
private String token;
3538

39+
private static String[] queryUrlsSource() {
40+
return new String[]{
41+
String.format("%s://%s:%d%s%s", SCHEME, HOST, PORT, BASE_PATH, QUERY_ENDPOINT),
42+
String.format("%s://%s:%d%s%s", SCHEME, HOST, PORT, BASE_PATH_OLD_FORMAT, QUERY_ENDPOINT)
43+
};
44+
}
45+
3646
@BeforeEach
3747
public void setUp() {
3848
RestAssured.port = PORT;
@@ -43,134 +53,149 @@ public void setUp() {
4353
}
4454

4555
//@formatter:off
46-
@Test
47-
void doQueryWithValidTokenFromHeader() {
56+
@ParameterizedTest
57+
@MethodSource("queryUrlsSource")
58+
void doQueryWithValidTokenFromHeader(String queryUrl) {
4859
given()
49-
.header("Authorization", "Bearer " + token)
60+
.header("Authorization", "Bearer " + token)
5061
.when()
51-
.get(String.format("%s://%s:%d%s%s", SCHEME, HOST, PORT, BASE_PATH, QUERY_ENDPOINT))
62+
.get(queryUrl)
5263
.then()
5364
.statusCode(is(SC_OK))
5465
.body("userId", equalTo(USERNAME));
5566
}
5667

57-
@Test
58-
void doQueryWithValidTokenFromCookie() {
68+
@ParameterizedTest
69+
@MethodSource("queryUrlsSource")
70+
void doQueryWithValidTokenFromCookie(String queryUrl) {
5971
given()
6072
.cookie(COOKIE, token)
6173
.when()
62-
.get(String.format("%s://%s:%d%s%s", SCHEME, HOST, PORT, BASE_PATH, QUERY_ENDPOINT))
74+
.get(queryUrl)
6375
.then()
6476
.statusCode(is(SC_OK))
6577
.body("userId", equalTo(USERNAME));
6678
}
6779

68-
@Test
69-
void doQueryWithInvalidTokenFromHeader() {
80+
@ParameterizedTest
81+
@MethodSource("queryUrlsSource")
82+
void doQueryWithInvalidTokenFromHeader(String queryUrl) {
7083
String invalidToken = "1234";
71-
String expectedMessage = "Token is not valid for URL '" + BASE_PATH + QUERY_ENDPOINT + "'";
84+
String queryPath = queryUrl.substring(StringUtils.ordinalIndexOf(queryUrl,"/",3));
85+
String expectedMessage = "Token is not valid for URL '" + queryPath + "'";
7286

7387
given()
7488
.header("Authorization", "Bearer " + invalidToken)
7589
.contentType(JSON)
7690
.when()
77-
.get(String.format("%s://%s:%d%s%s", SCHEME, HOST, PORT, BASE_PATH, QUERY_ENDPOINT))
91+
.get(queryUrl)
7892
.then()
7993
.statusCode(is(SC_UNAUTHORIZED))
8094
.body(
81-
"messages.find { it.messageNumber == 'ZWEAG130E' }.messageContent", equalTo(expectedMessage)
82-
);
95+
"messages.find { it.messageNumber == 'ZWEAG130E' }.messageContent", equalTo(expectedMessage)
96+
);
8397
}
8498

85-
@Test
86-
void doQueryWithInvalidTokenFromCookie() {
99+
@ParameterizedTest
100+
@MethodSource("queryUrlsSource")
101+
void doQueryWithInvalidTokenFromCookie(String queryUrl) {
87102
String invalidToken = "1234";
88-
String expectedMessage = "Token is not valid for URL '" + BASE_PATH + QUERY_ENDPOINT + "'";
103+
String queryPath = queryUrl.substring(StringUtils.ordinalIndexOf(queryUrl,"/",3));
104+
String expectedMessage = "Token is not valid for URL '" + queryPath + "'";
89105

90106
given()
91107
.cookie(COOKIE, invalidToken)
92108
.when()
93-
.get(String.format("%s://%s:%d%s%s", SCHEME, HOST, PORT, BASE_PATH, QUERY_ENDPOINT))
109+
.get(queryUrl)
94110
.then()
95111
.statusCode(is(SC_UNAUTHORIZED))
96112
.body(
97113
"messages.find { it.messageNumber == 'ZWEAG130E' }.messageContent", equalTo(expectedMessage)
98114
);
99115
}
100116

101-
@Test
102-
void doQueryWithoutHeaderOrCookie() {
103-
String expectedMessage = "No authorization token provided for URL '" + BASE_PATH + QUERY_ENDPOINT + "'";
117+
@ParameterizedTest
118+
@MethodSource("queryUrlsSource")
119+
void doQueryWithoutHeaderOrCookie(String queryUrl) {
120+
String queryPath = queryUrl.substring(StringUtils.ordinalIndexOf(queryUrl,"/",3));
121+
String expectedMessage = "No authorization token provided for URL '" + queryPath + "'";
104122

105123
given()
106124
.when()
107-
.get(String.format("%s://%s:%d%s%s", SCHEME, HOST, PORT, BASE_PATH, QUERY_ENDPOINT))
125+
.get(queryUrl)
108126
.then()
109127
.statusCode(is(SC_UNAUTHORIZED))
110128
.body(
111129
"messages.find { it.messageNumber == 'ZWEAG131E' }.messageContent", equalTo(expectedMessage)
112130
);
113131
}
114132

115-
@Test
116-
void doQueryWithWrongAuthType() {
117-
String expectedMessage = "No authorization token provided for URL '" + BASE_PATH + QUERY_ENDPOINT + "'";
133+
@ParameterizedTest
134+
@MethodSource("queryUrlsSource")
135+
void doQueryWithWrongAuthType(String queryUrl) {
136+
String queryPath = queryUrl.substring(StringUtils.ordinalIndexOf(queryUrl,"/",3));
137+
String expectedMessage = "No authorization token provided for URL '" + queryPath + "'";
118138

119139
given()
120140
.header("Authorization", "Basic " + token)
121141
.when()
122-
.get(String.format("%s://%s:%d%s%s", SCHEME, HOST, PORT, BASE_PATH, QUERY_ENDPOINT))
142+
.get(queryUrl)
123143
.then()
124144
.statusCode(is(SC_UNAUTHORIZED))
125145
.body(
126146
"messages.find { it.messageNumber == 'ZWEAG131E' }.messageContent", equalTo(expectedMessage)
127147
);
128148
}
129149

130-
@Test
131-
void doQueryWithWrongCookieName() {
150+
@ParameterizedTest
151+
@MethodSource("queryUrlsSource")
152+
void doQueryWithWrongCookieName(String queryUrl) {
132153
String invalidCookie = "badCookie";
133-
String expectedMessage = "No authorization token provided for URL '" + BASE_PATH + QUERY_ENDPOINT + "'";
154+
String queryPath = queryUrl.substring(StringUtils.ordinalIndexOf(queryUrl,"/",3));
155+
String expectedMessage = "No authorization token provided for URL '" + queryPath + "'";
134156

135157
given()
136158
.cookie(invalidCookie, token)
137159
.when()
138-
.get(String.format("%s://%s:%d%s%s", SCHEME, HOST, PORT, BASE_PATH, QUERY_ENDPOINT))
160+
.get(queryUrl)
139161
.then()
140162
.statusCode(is(SC_UNAUTHORIZED))
141163
.body(
142164
"messages.find { it.messageNumber == 'ZWEAG131E' }.messageContent", equalTo(expectedMessage)
143165
);
144166
}
145167

146-
@Test
147-
void doQueryWithEmptyHeader() {
168+
@ParameterizedTest
169+
@MethodSource("queryUrlsSource")
170+
void doQueryWithEmptyHeader(String queryUrl) {
148171
String emptyToken = " ";
149-
String expectedMessage = "No authorization token provided for URL '" + BASE_PATH + QUERY_ENDPOINT + "'";
172+
String queryPath = queryUrl.substring(StringUtils.ordinalIndexOf(queryUrl,"/",3));
173+
String expectedMessage = "No authorization token provided for URL '" + queryPath + "'";
150174

151175
given()
152176
.header("Authorization", "Bearer " + emptyToken)
153177
.when()
154-
.get(String.format("%s://%s:%d%s%s", SCHEME, HOST, PORT, BASE_PATH, QUERY_ENDPOINT))
178+
.get(queryUrl)
155179
.then()
156180
.statusCode(is(SC_UNAUTHORIZED))
157181
.body(
158182
"messages.find { it.messageNumber == 'ZWEAG131E' }.messageContent", equalTo(expectedMessage)
159183
);
160184
}
161185

162-
@Test
163-
void doQueryWithWrongHttpMethod() {
164-
String expectedMessage = "Authentication method 'POST' is not supported for URL '" +
165-
BASE_PATH + QUERY_ENDPOINT + "'";
186+
@ParameterizedTest
187+
@MethodSource("queryUrlsSource")
188+
void doQueryWithWrongHttpMethod(String queryUrl) {
189+
String queryPath = queryUrl.substring(StringUtils.ordinalIndexOf(queryUrl,"/",3));
190+
String expectedMessage = "Authentication method 'POST' is not supported for URL '" + queryPath + "'";
166191

167192
given()
168193
.header("Authorization", "Bearer " + token)
169194
.when()
170-
.post(String.format("%s://%s:%d%s%s", SCHEME, HOST, PORT, BASE_PATH, QUERY_ENDPOINT))
195+
.post(queryUrl)
171196
.then()
172197
.body(
173-
"messages.find { it.messageNumber == 'ZWEAG101E' }.messageContent", equalTo(expectedMessage)
198+
"messages.find { it.messageNumber == 'ZWEAG101E' }.messageContent", equalTo(expectedMessage)
174199
);
175200
}
176201
//@formatter:on

0 commit comments

Comments
 (0)