Skip to content

Commit 80cc790

Browse files
authored
Introduce token validation providers (#1142)
1 parent 4b61377 commit 80cc790

File tree

11 files changed

+297
-177
lines changed

11 files changed

+297
-177
lines changed

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

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,15 @@
1212
import com.netflix.appinfo.InstanceInfo;
1313
import com.netflix.discovery.EurekaClient;
1414
import com.netflix.discovery.shared.Application;
15-
import io.jsonwebtoken.Claims;
16-
import io.jsonwebtoken.ExpiredJwtException;
17-
import io.jsonwebtoken.JwtException;
18-
import io.jsonwebtoken.Jwts;
15+
import io.jsonwebtoken.*;
1916
import lombok.NonNull;
2017
import lombok.RequiredArgsConstructor;
2118
import lombok.extern.slf4j.Slf4j;
2219
import org.apache.commons.lang.StringUtils;
2320
import org.springframework.cache.CacheManager;
24-
import org.springframework.cache.annotation.CacheEvict;
25-
import org.springframework.cache.annotation.CachePut;
26-
import org.springframework.cache.annotation.Cacheable;
21+
import org.springframework.cache.annotation.*;
2722
import org.springframework.context.ApplicationContext;
28-
import org.springframework.context.annotation.EnableAspectJAutoProxy;
29-
import org.springframework.context.annotation.Scope;
30-
import org.springframework.context.annotation.ScopedProxyMode;
23+
import org.springframework.context.annotation.*;
3124
import org.springframework.http.HttpHeaders;
3225
import org.springframework.security.authentication.BadCredentialsException;
3326
import org.springframework.stereotype.Service;
@@ -248,7 +241,7 @@ public TokenAuthentication validateJwtToken(String jwtToken) {
248241
isValid = true;
249242
break;
250243
case ZOSMF:
251-
isValid = zosmfService.validate(JWT, jwtToken);
244+
isValid = zosmfService.validate(jwtToken);
252245
break;
253246
default:
254247
throw new TokenNotValidException("Unknown token type.");
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* This program and the accompanying materials are made available under the terms of the
3+
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
4+
* https://www.eclipse.org/legal/epl-v20.html
5+
*
6+
* SPDX-License-Identifier: EPL-2.0
7+
*
8+
* Copyright Contributors to the Zowe Project.
9+
*/
10+
11+
package org.zowe.apiml.gateway.security.service.zosmf;
12+
13+
import lombok.RequiredArgsConstructor;
14+
import org.springframework.http.*;
15+
import org.springframework.web.client.RestTemplate;
16+
import org.zowe.apiml.message.log.ApimlLogger;
17+
import org.zowe.apiml.product.logging.annotations.InjectApimlLogger;
18+
import org.zowe.apiml.security.common.error.ServiceNotAccessibleException;
19+
import org.zowe.apiml.security.common.token.TokenNotValidException;
20+
21+
import static org.zowe.apiml.gateway.security.service.zosmf.AbstractZosmfService.ZOSMF_CSRF_HEADER;
22+
23+
/**
24+
* Strategy to validate token through Authentication endpoint of zOSMF
25+
*/
26+
@RequiredArgsConstructor
27+
public class AuthenticateEndpointStrategy implements TokenValidationStrategy {
28+
29+
private final RestTemplate restTemplateWithoutKeystore;
30+
31+
@InjectApimlLogger
32+
protected ApimlLogger apimlLog = ApimlLogger.empty();
33+
34+
protected static final String ZOSMF_AUTHENTICATE_END_POINT = "/zosmf/services/authenticate";
35+
36+
@Override
37+
public boolean validate(ZosmfService zosmfService, String token) {
38+
if (zosmfService.loginEndpointExists()) {
39+
final String url = zosmfService.getURI(zosmfService.getZosmfServiceId()) + ZOSMF_AUTHENTICATE_END_POINT;
40+
41+
final HttpHeaders headers = new HttpHeaders();
42+
headers.add(ZOSMF_CSRF_HEADER, "");
43+
headers.add(HttpHeaders.COOKIE, ZosmfService.TokenType.JWT.getCookieName() + "=" + token);
44+
45+
46+
ResponseEntity<String> re = restTemplateWithoutKeystore.exchange(url, HttpMethod.POST,
47+
new HttpEntity<>(null, headers), String.class);
48+
49+
if (re.getStatusCode().is2xxSuccessful())
50+
return true;
51+
if (HttpStatus.UNAUTHORIZED.equals(re.getStatusCode())) {
52+
throw new TokenNotValidException("Token is not valid.");
53+
}
54+
apimlLog.log("org.zowe.apiml.security.serviceUnavailable", url, re.getStatusCodeValue());
55+
throw new ServiceNotAccessibleException("Could not get an access to z/OSMF service.");
56+
57+
} else {
58+
return false;
59+
}
60+
}
61+
62+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* This program and the accompanying materials are made available under the terms of the
3+
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
4+
* https://www.eclipse.org/legal/epl-v20.html
5+
*
6+
* SPDX-License-Identifier: EPL-2.0
7+
*
8+
* Copyright Contributors to the Zowe Project.
9+
*/
10+
11+
package org.zowe.apiml.gateway.security.service.zosmf;
12+
13+
/**
14+
* General strategy for token validation
15+
*
16+
*/
17+
public interface TokenValidationStrategy {
18+
boolean validate(ZosmfService zosmfService, String token);
19+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* This program and the accompanying materials are made available under the terms of the
3+
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
4+
* https://www.eclipse.org/legal/epl-v20.html
5+
*
6+
* SPDX-License-Identifier: EPL-2.0
7+
*
8+
* Copyright Contributors to the Zowe Project.
9+
*/
10+
11+
package org.zowe.apiml.gateway.security.service.zosmf;
12+
13+
import org.springframework.beans.factory.annotation.Qualifier;
14+
import org.springframework.context.annotation.Bean;
15+
import org.springframework.context.annotation.Configuration;
16+
import org.springframework.web.client.RestTemplate;
17+
18+
/**
19+
* Config for zOSMF beans
20+
*/
21+
@Configuration
22+
public class ZosmfBeans {
23+
24+
@Bean
25+
TokenValidationStrategy tokenValidationStrategy(@Qualifier("restTemplateWithoutKeystore") RestTemplate restTemplateWithoutKeystore) {
26+
return new AuthenticateEndpointStrategy(restTemplateWithoutKeystore);
27+
}
28+
}

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

Lines changed: 17 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -14,35 +14,24 @@
1414
import com.fasterxml.jackson.databind.ObjectMapper;
1515
import com.netflix.discovery.DiscoveryClient;
1616
import com.nimbusds.jose.jwk.JWKSet;
17-
import lombok.AllArgsConstructor;
18-
import lombok.Data;
19-
import lombok.Getter;
20-
import lombok.RequiredArgsConstructor;
17+
import lombok.*;
2118
import lombok.extern.slf4j.Slf4j;
2219
import org.apache.commons.lang.StringUtils;
2320
import org.springframework.beans.factory.annotation.Qualifier;
2421
import org.springframework.cache.annotation.Cacheable;
2522
import org.springframework.context.ApplicationContext;
26-
import org.springframework.context.annotation.EnableAspectJAutoProxy;
27-
import org.springframework.context.annotation.Primary;
28-
import org.springframework.context.annotation.Scope;
29-
import org.springframework.context.annotation.ScopedProxyMode;
23+
import org.springframework.context.annotation.*;
3024
import org.springframework.http.*;
3125
import org.springframework.security.authentication.AuthenticationServiceException;
3226
import org.springframework.security.core.Authentication;
3327
import org.springframework.stereotype.Service;
34-
import org.springframework.web.client.HttpClientErrorException;
35-
import org.springframework.web.client.HttpServerErrorException;
36-
import org.springframework.web.client.RestTemplate;
28+
import org.springframework.web.client.*;
3729
import org.zowe.apiml.security.common.config.AuthConfigurationProperties;
3830
import org.zowe.apiml.security.common.error.ServiceNotAccessibleException;
39-
import org.zowe.apiml.security.common.token.TokenNotValidException;
4031

4132
import javax.annotation.PostConstruct;
4233
import java.text.ParseException;
43-
import java.util.EnumMap;
44-
import java.util.List;
45-
import java.util.Map;
34+
import java.util.*;
4635

4736
@Primary
4837
@Service
@@ -64,25 +53,25 @@ public enum TokenType {
6453
private final String cookieName;
6554

6655
}
67-
6856
/**
6957
* Response of authentication, contains all data to next processing
7058
*/
7159
@Data
7260
@AllArgsConstructor
7361
@RequiredArgsConstructor
7462
public static class AuthenticationResponse {
63+
7564
private String domain;
7665
private final Map<TokenType, String> tokens;
7766
}
78-
7967
/**
8068
* DTO with base information about z/OSMF (version and realm/domain)
8169
*/
8270
@Data
8371
@JsonIgnoreProperties(ignoreUnknown = true)
8472
public static class ZosmfInfo {
8573

74+
8675
@JsonProperty("zosmf_version")
8776
private int version;
8877

@@ -96,13 +85,15 @@ public static class ZosmfInfo {
9685

9786
private static final String PUBLIC_JWK_ENDPOINT = "/jwt/ibm/api/zOSMFBuilder/jwk";
9887
private final ApplicationContext applicationContext;
88+
private final TokenValidationStrategy tokenValidationStrategy;
9989

10090
public ZosmfService(
10191
final AuthConfigurationProperties authConfigurationProperties,
10292
final DiscoveryClient discovery,
10393
final @Qualifier("restTemplateWithoutKeystore") RestTemplate restTemplateWithoutKeystore,
10494
final ObjectMapper securityObjectMapper,
105-
final ApplicationContext applicationContext
95+
final ApplicationContext applicationContext,
96+
TokenValidationStrategy tokenValidationStrategy
10697
) {
10798
super(
10899
authConfigurationProperties,
@@ -111,10 +102,11 @@ public ZosmfService(
111102
securityObjectMapper
112103
);
113104
this.applicationContext = applicationContext;
105+
this.tokenValidationStrategy = tokenValidationStrategy;
114106
}
115107

116-
private ZosmfService meAsProxy;
117108

109+
private ZosmfService meAsProxy;
118110
@PostConstruct
119111
public void afterPropertiesSet() {
120112
meAsProxy = applicationContext.getBean(ZosmfService.class);
@@ -244,30 +236,12 @@ public boolean logoutEndpointExists() {
244236
return meAsProxy.authenticationEndpointExists(HttpMethod.DELETE, null);
245237
}
246238

247-
public boolean validate(TokenType type, String token) {
248-
if (loginEndpointExists()) {
249-
final String url = getURI(getZosmfServiceId()) + ZOSMF_AUTHENTICATE_END_POINT;
250-
251-
final HttpHeaders headers = new HttpHeaders();
252-
headers.add(ZOSMF_CSRF_HEADER, "");
253-
headers.add(HttpHeaders.COOKIE, type.getCookieName() + "=" + token);
254-
255-
try {
256-
ResponseEntity<String> re = restTemplateWithoutKeystore.exchange(url, HttpMethod.POST,
257-
new HttpEntity<>(null, headers), String.class);
258-
259-
if (re.getStatusCode().is2xxSuccessful())
260-
return true;
261-
if (re.getStatusCodeValue() == 401) {
262-
throw new TokenNotValidException("Token is not valid.");
263-
}
264-
apimlLog.log("org.zowe.apiml.security.serviceUnavailable", url, re.getStatusCodeValue());
265-
throw new ServiceNotAccessibleException("Could not get an access to z/OSMF service.");
266-
} catch (RuntimeException re) {
267-
throw handleExceptionOnCall(url, re);
268-
}
269-
} else {
270-
return false;
239+
public boolean validate(String token) {
240+
try {
241+
return tokenValidationStrategy.validate(this, token);
242+
} catch (RuntimeException re) {
243+
//TODO handle returns
244+
throw handleExceptionOnCall(null ,re);
271245
}
272246
}
273247

gateway-service/src/test/java/org/zowe/apiml/config/service/security/MockedAuthenticationServiceContext.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@
1616
import org.springframework.context.ApplicationContext;
1717
import org.springframework.context.annotation.Bean;
1818
import org.springframework.web.client.RestTemplate;
19-
import org.zowe.apiml.gateway.security.service.AuthenticationService;
20-
import org.zowe.apiml.gateway.security.service.AuthenticationServiceTest;
21-
import org.zowe.apiml.gateway.security.service.JwtSecurityInitializer;
19+
import org.zowe.apiml.gateway.security.service.*;
20+
import org.zowe.apiml.gateway.security.service.zosmf.TokenValidationStrategy;
2221
import org.zowe.apiml.gateway.security.service.zosmf.ZosmfService;
2322
import org.zowe.apiml.security.common.config.AuthConfigurationProperties;
2423
import org.zowe.apiml.util.CacheUtils;
@@ -59,7 +58,8 @@ public ZosmfService getZosmfService() {
5958
getAuthConfigurationProperties(),
6059
getDiscoveryClient(),
6160
getRestTemplate(),
62-
AuthenticationServiceTest.securityObjectMapper,applicationContext
61+
AuthenticationServiceTest.securityObjectMapper,applicationContext,
62+
mock(TokenValidationStrategy.class)
6363
);
6464
}
6565

gateway-service/src/test/java/org/zowe/apiml/gateway/security/login/zosmf/ZosmfAuthenticationProviderTest.java

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,20 @@
2020
import org.mockito.stubbing.Answer;
2121
import org.springframework.context.ApplicationContext;
2222
import org.springframework.http.*;
23-
import org.springframework.security.authentication.AbstractAuthenticationToken;
24-
import org.springframework.security.authentication.AuthenticationServiceException;
25-
import org.springframework.security.authentication.BadCredentialsException;
26-
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
23+
import org.springframework.security.authentication.*;
2724
import org.springframework.security.authentication.jaas.JaasAuthenticationToken;
2825
import org.springframework.security.core.Authentication;
2926
import org.springframework.test.util.ReflectionTestUtils;
30-
import org.springframework.web.client.ResourceAccessException;
31-
import org.springframework.web.client.RestClientException;
32-
import org.springframework.web.client.RestTemplate;
27+
import org.springframework.web.client.*;
3328
import org.zowe.apiml.gateway.security.service.AuthenticationService;
29+
import org.zowe.apiml.gateway.security.service.zosmf.TokenValidationStrategy;
3430
import org.zowe.apiml.gateway.security.service.zosmf.ZosmfService;
3531
import org.zowe.apiml.security.common.config.AuthConfigurationProperties;
3632
import org.zowe.apiml.security.common.error.ServiceNotAccessibleException;
3733
import org.zowe.apiml.security.common.token.TokenAuthentication;
3834

3935
import java.io.IOException;
40-
import java.util.Arrays;
41-
import java.util.Collections;
42-
import java.util.EnumMap;
36+
import java.util.*;
4337

4438
import static org.junit.jupiter.api.Assertions.*;
4539
import static org.mockito.Mockito.*;
@@ -100,7 +94,12 @@ private Application createApplication(InstanceInfo... instanceInfos) {
10094

10195
private ZosmfService createZosmfService() {
10296
ApplicationContext applicationContext = mock(ApplicationContext.class);
103-
ZosmfService zosmfService = new ZosmfService(authConfigurationProperties, discovery, restTemplate, securityObjectMapper, applicationContext);
97+
ZosmfService zosmfService = new ZosmfService(authConfigurationProperties,
98+
discovery,
99+
restTemplate,
100+
securityObjectMapper,
101+
applicationContext,
102+
mock(TokenValidationStrategy.class));
104103
ReflectionTestUtils.setField(zosmfService, "meAsProxy", zosmfService);
105104
ZosmfService output = spy(zosmfService);
106105
when(applicationContext.getBean(ZosmfService.class)).thenReturn(output);

gateway-service/src/test/java/org/zowe/apiml/gateway/security/query/SuccessfulQueryHandlerTest.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.springframework.web.client.RestTemplate;
2727
import org.zowe.apiml.gateway.security.service.AuthenticationService;
2828
import org.zowe.apiml.gateway.security.service.JwtSecurityInitializer;
29+
import org.zowe.apiml.gateway.security.service.zosmf.TokenValidationStrategy;
2930
import org.zowe.apiml.gateway.security.service.zosmf.ZosmfService;
3031
import org.zowe.apiml.security.SecurityUtils;
3132
import org.zowe.apiml.security.common.config.AuthConfigurationProperties;
@@ -36,6 +37,7 @@
3637
import java.security.KeyPair;
3738

3839
import static org.junit.jupiter.api.Assertions.*;
40+
import static org.mockito.Mockito.mock;
3941
import static org.mockito.Mockito.when;
4042

4143
@ExtendWith(MockitoExtension.class)
@@ -76,7 +78,12 @@ void setup() {
7678
if (keyPair != null) {
7779
privateKey = keyPair.getPrivate();
7880
}
79-
ZosmfService zosmfService = new ZosmfService(authConfigurationProperties, discoveryClient, restTemplate, new ObjectMapper(),applicationContext);
81+
ZosmfService zosmfService = new ZosmfService(authConfigurationProperties,
82+
discoveryClient,
83+
restTemplate,
84+
new ObjectMapper(),
85+
applicationContext,
86+
mock(TokenValidationStrategy.class));
8087
AuthenticationService authenticationService = new AuthenticationService(
8188
applicationContext, authConfigurationProperties, jwtSecurityInitializer, zosmfService,
8289
discoveryClient, restTemplate, cacheManager, new CacheUtils()

0 commit comments

Comments
 (0)