Skip to content

Commit 7c393a6

Browse files
author
Petr Weinfurt
authored
feat: Accept all tokens in the Bearer header or in the APIML cookie (#2908)
* feat: Accept PAT and OIDC tokens in Authorization Bearer header and in apimlAuthenticationToken cookie. Signed-off-by: Petr Weinfurt <petr.weinfurt@broadcom.com> * Add integration tests with token in cookie. Signed-off-by: Petr Weinfurt <petr.weinfurt@broadcom.com> * Test PAT also in bearer header and apiml cookie. Signed-off-by: Petr Weinfurt <petr.weinfurt@broadcom.com> * Fix unit test. Signed-off-by: Petr Weinfurt <petr.weinfurt@broadcom.com> * Refactor retrieving token by services. Add more tests. Signed-off-by: Petr Weinfurt <petr.weinfurt@broadcom.com> * Rollback the usage of x509.enabled. Add more tests for getTokenOrigin Signed-off-by: Petr Weinfurt <petr.weinfurt@broadcom.com> --------- Signed-off-by: Petr Weinfurt <petr.weinfurt@broadcom.com>
1 parent 1127bc2 commit 7c393a6

File tree

18 files changed

+466
-110
lines changed

18 files changed

+466
-110
lines changed

common-service-core/src/main/java/org/zowe/apiml/constants/ApimlConstants.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ private ApimlConstants() {
2222
public static final String BASIC_AUTHENTICATION_PREFIX = "Basic";
2323
public static final String BEARER_AUTHENTICATION_PREFIX = "Bearer";
2424
public static final String PAT_COOKIE_AUTH_NAME = "personalAccessToken";
25-
26-
public static final String OIDC_HEADER_NAME = "OIDC-TOKEN";
2725
public static final String PAT_HEADER_NAME = "PRIVATE-TOKEN";
2826
public static final String AUTH_FAIL_HEADER = "X-Zowe-Auth-Failure";
2927
}

gateway-service/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ dependencies {
177177
annotationProcessor libraries.spring_boot_configuration_processor
178178

179179
testImplementation libraries.mockito_core
180+
testImplementation libraries.mockito_inline
180181
testImplementation libraries.spring_mock_mvc
181182
testImplementation libraries.spring_boot_starter_test
182183
testImplementation libraries.json_smart

gateway-service/src/main/java/org/zowe/apiml/gateway/filters/pre/ServiceAuthenticationFilter.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ public Object run() {
8383
throw new AuthSchemeException("org.zowe.apiml.gateway.security.invalidAuthentication");
8484
}
8585
} catch (TokenExpireException tee) {
86-
cmd = null;
86+
String error = this.messageService.createMessage("org.zowe.apiml.gateway.security.expiredToken").mapToLogMessage();
87+
sendErrorMessage(error, context);
88+
return null;
8789
} catch (TokenNotValidException notValidException) {
8890
String error = this.messageService.createMessage("org.zowe.apiml.gateway.security.invalidToken").mapToLogMessage();
8991
sendErrorMessage(error, context);

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

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.springframework.web.client.RestTemplate;
3939
import org.zowe.apiml.constants.ApimlConstants;
4040
import org.zowe.apiml.gateway.controllers.AuthController;
41+
import org.zowe.apiml.gateway.security.service.schema.source.AuthSource;
4142
import org.zowe.apiml.gateway.security.service.zosmf.ZosmfService;
4243
import org.zowe.apiml.product.constants.CoreService;
4344
import org.zowe.apiml.security.common.config.AuthConfigurationProperties;
@@ -354,6 +355,17 @@ public QueryResponse parseQueryResponse(Claims claims) {
354355
);
355356
}
356357

358+
/**
359+
* This method resolves the token origin directly by decoding token claims.
360+
* @param jwtToken the JWT token
361+
* @return AuthSource.Origin value based on the iss token claim.
362+
*/
363+
public AuthSource.Origin getTokenOrigin(String jwtToken) {
364+
Claims claims = getJwtClaims(jwtToken);
365+
QueryResponse.Source source = QueryResponse.Source.valueByIssuer(claims.getIssuer());
366+
return AuthSource.Origin.valueByTokenSource(source);
367+
}
368+
357369
/**
358370
* This method validates if JWT token is valid and if yes, then get claim from LTPA token.
359371
* For purpose, when is not needed validation, you can use method {@link #getLtpaToken(String)}
@@ -404,16 +416,6 @@ private Optional<String> getAccessTokenFromHeader(String header) {
404416
return header != null ? Optional.of(header) : Optional.empty();
405417
}
406418

407-
/**
408-
* Extract the OIDC token from the request.
409-
*
410-
* @param request http request
411-
* @return the OIDC token from different supported sources: header.
412-
*/
413-
public Optional<String> getOIDCTokenFromRequest(@NonNull HttpServletRequest request) {
414-
return getAccessTokenFromHeader(request.getHeader(ApimlConstants.OIDC_HEADER_NAME));
415-
}
416-
417419
private Optional<String> getTokenFromCookie(HttpServletRequest request, String cookieName) {
418420
Cookie[] cookies = request.getCookies();
419421
if (cookies == null) return Optional.empty();

gateway-service/src/main/java/org/zowe/apiml/gateway/security/service/schema/source/JwtAuthSourceService.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,14 @@ public Function<String, AuthSource> getMapper() {
5151

5252
@Override
5353
public Optional<String> getToken(RequestContext context) {
54-
return authenticationService.getJwtTokenFromRequest(context.getRequest());
54+
Optional<String> tokenOptional = authenticationService.getJwtTokenFromRequest(context.getRequest());
55+
if (tokenOptional.isPresent()) {
56+
AuthSource.Origin origin = authenticationService.getTokenOrigin(tokenOptional.get());
57+
if (Origin.ZOSMF == origin || Origin.ZOWE == origin) {
58+
return tokenOptional;
59+
}
60+
}
61+
return Optional.empty();
5562
}
5663

5764
/**

gateway-service/src/main/java/org/zowe/apiml/gateway/security/service/schema/source/OIDCAuthSourceService.java

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@
1010

1111
package org.zowe.apiml.gateway.security.service.schema.source;
1212

13-
import java.util.Optional;
14-
import java.util.function.Function;
15-
13+
import com.netflix.zuul.context.RequestContext;
14+
import lombok.RequiredArgsConstructor;
1615
import org.apache.commons.lang3.StringUtils;
1716
import org.springframework.beans.factory.annotation.Qualifier;
1817
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -28,9 +27,8 @@
2827
import org.zowe.apiml.security.common.token.QueryResponse;
2928
import org.zowe.apiml.security.common.token.TokenNotValidException;
3029

31-
import com.netflix.zuul.context.RequestContext;
32-
33-
import lombok.RequiredArgsConstructor;
30+
import java.util.Optional;
31+
import java.util.function.Function;
3432

3533
@Service
3634
@RequiredArgsConstructor
@@ -57,7 +55,14 @@ public Function<String, AuthSource> getMapper() {
5755

5856
@Override
5957
public Optional<String> getToken(RequestContext context) {
60-
return authenticationService.getOIDCTokenFromRequest(context.getRequest());
58+
Optional<String> tokenOptional = authenticationService.getJwtTokenFromRequest(context.getRequest());
59+
if (tokenOptional.isPresent()) {
60+
AuthSource.Origin origin = authenticationService.getTokenOrigin(tokenOptional.get());
61+
if (AuthSource.Origin.OIDC == origin) {
62+
return tokenOptional;
63+
}
64+
}
65+
return Optional.empty();
6166
}
6267

6368
@Override
@@ -117,7 +122,7 @@ private AuthSource.Parsed parseOIDCToken(OIDCAuthSource oidcAuthSource, Authenti
117122
@Override
118123
public String getLtpaToken(AuthSource authSource) {
119124
String zosmfToken = getJWT(authSource);
120-
AuthSource.Origin origin = getTokenOrigin(zosmfToken);
125+
AuthSource.Origin origin = authenticationService.getTokenOrigin(zosmfToken);
121126
if (AuthSource.Origin.ZOWE.equals(origin)) {
122127
zosmfToken = authenticationService.getLtpaToken(zosmfToken);
123128
}
@@ -130,9 +135,4 @@ public String getJWT(AuthSource authSource) {
130135
return tokenService.createJwtTokenWithoutCredentials(parsed.getUserId());
131136
}
132137

133-
public AuthSource.Origin getTokenOrigin(String zosmfToken) {
134-
QueryResponse response = authenticationService.parseJwtToken(zosmfToken);
135-
return AuthSource.Origin.valueByTokenSource(response.getSource());
136-
}
137-
138138
}

gateway-service/src/main/java/org/zowe/apiml/gateway/security/service/schema/source/PATAuthSourceService.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,18 @@ public Function<String, AuthSource> getMapper() {
5151

5252
@Override
5353
public Optional<String> getToken(RequestContext context) {
54-
return authenticationService.getPATFromRequest(context.getRequest());
54+
Optional<String> tokenOptional = authenticationService.getJwtTokenFromRequest(context.getRequest());
55+
if (!tokenOptional.isPresent()) {
56+
// try to get token also from PAT specific cookie or header
57+
tokenOptional = authenticationService.getPATFromRequest(context.getRequest());
58+
}
59+
if (tokenOptional.isPresent()) {
60+
AuthSource.Origin origin = authenticationService.getTokenOrigin(tokenOptional.get());
61+
if (AuthSource.Origin.ZOWE_PAT == origin) {
62+
return tokenOptional;
63+
}
64+
}
65+
return Optional.empty();
5566
}
5667

5768
@Override
@@ -86,7 +97,7 @@ public AuthSource.Parsed parse(AuthSource authSource) {
8697
@Override
8798
public String getLtpaToken(AuthSource authSource) {
8899
String zosmfToken = getJWT(authSource);
89-
AuthSource.Origin origin = getTokenOrigin(zosmfToken);
100+
AuthSource.Origin origin = authenticationService.getTokenOrigin(zosmfToken);
90101
if (AuthSource.Origin.ZOWE.equals(origin)) {
91102
zosmfToken = authenticationService.getLtpaToken(zosmfToken);
92103
}
@@ -99,8 +110,4 @@ public String getJWT(AuthSource authSource) {
99110
return tokenService.createJwtTokenWithoutCredentials(parsed.getUserId());
100111
}
101112

102-
public AuthSource.Origin getTokenOrigin(String zosmfToken) {
103-
QueryResponse response = authenticationService.parseJwtToken(zosmfToken);
104-
return AuthSource.Origin.valueByTokenSource(response.getSource());
105-
}
106113
}

gateway-service/src/test/java/org/zowe/apiml/acceptance/SafIdtSchemeTest.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,11 @@
5252
* The output to be tested is the saf idt token.
5353
*/
5454
@AcceptanceTest
55-
@TestPropertySource(properties = {"spring.profiles.active=debug", "apiml.security.x509.externalMapperUrl="})
55+
@TestPropertySource(properties = {
56+
"spring.profiles.active=debug",
57+
"apiml.security.x509.enabled=true",
58+
"apiml.security.x509.externalMapperUrl="
59+
})
5660
class SafIdtSchemeTest extends AcceptanceTestWithTwoServices {
5761
@Value("${server.ssl.keyStorePassword:password}")
5862
private char[] keystorePassword;

gateway-service/src/test/java/org/zowe/apiml/acceptance/ZosmfSchemeTest.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,12 @@
4444
* The output to be tested is the Zosmf token.
4545
*/
4646
@AcceptanceTest
47-
@TestPropertySource(properties = {"apiml.security.auth.provider=zosmf", "spring.profiles.active=debug", "apiml.security.x509.externalMapperUrl="})
47+
@TestPropertySource(properties = {
48+
"apiml.security.auth.provider=zosmf",
49+
"spring.profiles.active=debug",
50+
"apiml.security.x509.enabled=true",
51+
"apiml.security.x509.externalMapperUrl="
52+
})
4853
class ZosmfSchemeTest extends AcceptanceTestWithTwoServices {
4954
@Value("${server.ssl.keyStorePassword:password}")
5055
private char[] keystorePassword;

gateway-service/src/test/java/org/zowe/apiml/gateway/filters/pre/ServiceAuthenticationFilterTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,10 @@ public void increment(String name) {
196196
@ParameterizedTest
197197
@MethodSource("provideAuthSources")
198198
void givenExpiredJwt_thenCallThrought(AuthSource authSource) {
199+
MessageTemplate messageTemplate = new MessageTemplate("key", "number", MessageType.ERROR, "text");
200+
Message message = Message.of("requestedKey", messageTemplate, new Object[0]);
201+
doReturn(message).when(messageService).createMessage(anyString(), (Object) any());
202+
199203
AuthenticationCommand cmd = createValidationCommand(authSource);
200204
doThrow(new TokenExpireException("Token is expired.")).when(authSourceService).isValid(any());
201205

0 commit comments

Comments
 (0)