Skip to content

Commit 2da761a

Browse files
balhar-jakubCarsonCookjandadav
authored
fix(authentication): Support specific zOSMF version, allow override (#1241)
* Setup jwt builder endpoint test Signed-off-by: Carson Cook <carson.cook@ibm.com> * Implement zosmf jwt check Signed-off-by: Carson Cook <carson.cook@ibm.com> * Make zosmf jwt endpoint configurable Signed-off-by: Carson Cook <carson.cook@ibm.com> * Update Jwt keys endpoint behavior. Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com> * Split Jwt related logic to separate Controller Add Apar specific for JWt keys Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com> * Add authenticate endpoint as separate Apar Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com> * Add another set of tests for the Authenticate endpoint without JWT support. Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com> * JWT configuration override Signed-off-by: jandadav <janda.david@gmail.com> * Update the authentication endpoint apar Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com> * ZosmfAuthenticationProvider respect override Signed-off-by: jandadav <janda.david@gmail.com> * consume zowe config Signed-off-by: jandadav <janda.david@gmail.com> * Improve the name of the Action Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com> * Remove zosmf, used only for testing locally Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com> * Remove smell Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com> * Add unit tests for Authentication Apar Signed-off-by: Jakub Balhar <jakub.balhar@broadcom.com> * Remove hardcoded jwt builder path Signed-off-by: Carson Cook <carson.cook@ibm.com> * Fix styling Signed-off-by: Carson Cook <carson.cook@ibm.com> * Fix AuthenticateApar and its unit tests Signed-off-by: Carson Cook <carson.cook@ibm.com> Co-authored-by: Carson Cook <carson.cook@ibm.com> Co-authored-by: jandadav <janda.david@gmail.com>
1 parent 1cfee86 commit 2da761a

File tree

28 files changed

+795
-118
lines changed

28 files changed

+795
-118
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# This workflow will build a Java project with Gradle
2+
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
3+
4+
name: CI Testing with zOSMF without JWT APAR applied with /zosmf/services/authenticate present
5+
6+
on:
7+
push:
8+
branches: [ master ]
9+
pull_request:
10+
branches: [ master ]
11+
12+
jobs:
13+
build:
14+
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- uses: actions/checkout@v2
19+
with:
20+
ref: ${{ github.head_ref }}
21+
- name: Set up JDK 1.8
22+
uses: actions/setup-java@v1
23+
with:
24+
java-version: 1.8
25+
- name: Grant execute permission for gradlew
26+
run: chmod +x gradlew
27+
- name: Cache Gradle packages
28+
uses: actions/cache@v2
29+
with:
30+
path: |
31+
~/.gradle/caches
32+
~/.gradle/wrapper
33+
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
34+
restore-keys: |
35+
${{ runner.os }}-gradle-
36+
- name: Build with Gradle
37+
run: ./gradlew build runCITests -x test --scan --info -Pgradle.cache.push=true -DexternalJenkinsToggle="true" -Penabler=v1 -DauxiliaryUserList.value="unauthorized,USER1,validPassword;servicesinfo-authorized,USER,validPassword;servicesinfo-unauthorized,USER1,validPassword" -Dcredentials.user=USER -Dcredentials.password=validPassword -Dzosmf.host=localhost -Dzosmf.port=10013 -Dzosmf.serviceId=mockzosmf -Dinternal.gateway.port=10017 -Dzosmf.appliedApars=AuthenticateApar
38+
- name: Store results
39+
uses: actions/upload-artifact@v2
40+
with:
41+
name: Package
42+
path: |
43+
api-catalog-services/build/reports/tests/test/
44+
discovery-service/build/reports/tests/test/
45+
gateway-service/build/reports/tests/test/
46+
./**/test-results/**/*.xml
47+
api-catalog-ui/frontend/test-results/
48+
api-layer.tar.gz
49+
- name: Cleanup Gradle Cache
50+
# Remove some files from the Gradle cache, so they aren't cached by GitHub Actions.
51+
# Restoring these files from a GitHub Actions cache might cause problems for future builds.
52+
run: |
53+
rm -f ~/.gradle/caches/modules-2/modules-2.lock
54+
rm -f ~/.gradle/caches/modules-2/gc.properties
55+

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
package org.zowe.apiml.security.common.config;
1111

1212
import lombok.Data;
13+
import org.springframework.beans.factory.annotation.Value;
1314
import org.springframework.boot.context.properties.ConfigurationProperties;
1415
import org.springframework.security.authentication.AuthenticationServiceException;
1516
import org.springframework.stereotype.Component;
@@ -54,6 +55,9 @@ public class AuthConfigurationProperties {
5455

5556
private String jwtKeyAlias;
5657

58+
@Value("${apiml.security.auth.zosmfJwtEndpoint:/jwt/ibm/api/zOSMFBuilder/jwk}")
59+
private String zosmfJwtEndpoint;
60+
5761
//Token properties
5862
@Data
5963
public static class TokenProperties {

gateway-package/src/main/resources/bin/start.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ _BPX_JOBNAME=${ZOWE_PREFIX}${GATEWAY_CODE} java \
107107
-Dserver.ssl.trustStore=${TRUSTSTORE} \
108108
-Dserver.ssl.trustStoreType=${KEYSTORE_TYPE} \
109109
-Dserver.ssl.trustStorePassword=${KEYSTORE_PASSWORD} \
110+
-Dapiml.security.auth.zosmfJwtAutoconfiguration=${APIML_SECURITY_ZOSMF_JWT_AUTOCONFIGURATION_MODE:auto}
110111
-Dapiml.security.x509.enabled=${APIML_SECURITY_X509_ENABLED:-false} \
111112
-Dapiml.security.x509.externalMapperUrl=http://localhost:${ZOWE_ZSS_SERVER_PORT}/certificate/x509/map \
112113
-Dapiml.security.x509.externalMapperUser=ZWESVUSR \

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.springframework.context.annotation.*;
1414
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
1515
import org.zowe.apiml.gateway.security.login.Providers;
16+
import org.zowe.apiml.gateway.security.login.zosmf.ZosmfConfiguration;
1617
import org.zowe.apiml.gateway.security.service.zosmf.ZosmfService;
1718
import org.zowe.apiml.passticket.PassTicketService;
1819
import org.zowe.apiml.security.common.config.AuthConfigurationProperties;
@@ -50,9 +51,10 @@ public Providers loginProviders(
5051
DiscoveryClient discoveryClient,
5152
AuthConfigurationProperties authConfigurationProperties,
5253
ZosmfService zosmfService,
53-
@Lazy CompoundAuthProvider compoundAuthProvider
54+
@Lazy CompoundAuthProvider compoundAuthProvider,
55+
ZosmfConfiguration zosmfConfiguration
5456
) {
55-
return new Providers(discoveryClient, authConfigurationProperties, compoundAuthProvider, zosmfService);
57+
return new Providers(discoveryClient, authConfigurationProperties, compoundAuthProvider, zosmfService, zosmfConfiguration);
5658
}
5759

5860
}

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.springframework.cloud.client.discovery.DiscoveryClient;
1515
import org.springframework.security.authentication.AuthenticationServiceException;
1616
import org.zowe.apiml.gateway.security.config.CompoundAuthProvider;
17+
import org.zowe.apiml.gateway.security.login.zosmf.ZosmfConfiguration;
1718
import org.zowe.apiml.gateway.security.service.zosmf.ZosmfService;
1819
import org.zowe.apiml.security.common.config.AuthConfigurationProperties;
1920
import org.zowe.apiml.security.common.error.ServiceNotAccessibleException;
@@ -25,6 +26,7 @@ public class Providers {
2526
private final AuthConfigurationProperties authConfigurationProperties;
2627
private final CompoundAuthProvider compoundAuthProvider;
2728
private final ZosmfService zosmfService;
29+
private final ZosmfConfiguration zosmfConfiguration;
2830

2931
/**
3032
* This method decides whether the Zosmf service is available.
@@ -68,6 +70,14 @@ public boolean isZosfmUsed() {
6870
* @return True is the instance support JWT
6971
*/
7072
public boolean zosmfSupportsJwt() {
71-
return zosmfService.loginEndpointExists();
73+
switch (zosmfConfiguration.jwtAutoconfigurationMode) {
74+
case JWT:
75+
return true;
76+
case LTPA:
77+
return false;
78+
default: // AUTO
79+
return zosmfService.loginEndpointExists() && zosmfService.jwtBuilderEndpointExists();
80+
}
81+
7282
}
7383
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ public static void main(String[] args) {
3737

3838
String jwkUrl = args[0];
3939
String filename = args[1];
40-
ZosmfJwkToPublicKey zosmfJwkToPublicKey = new ZosmfJwkToPublicKey(restTemplate);
40+
String zosmfJwtBuilderPath = System.getProperty("apiml.security.auth.zosmfJwtEndpoint", "/jwt/ibm/api/zOSMFBuilder/jwk");
41+
ZosmfJwkToPublicKey zosmfJwkToPublicKey = new ZosmfJwkToPublicKey(restTemplate, zosmfJwtBuilderPath);
4142

4243
System.out.printf("Loading public key of z/OSMF at %s%n", jwkUrl);
4344
try {

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

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,15 @@
1010
package org.zowe.apiml.gateway.security.login.zosmf;
1111

1212
import lombok.RequiredArgsConstructor;
13-
import org.springframework.security.authentication.AuthenticationProvider;
14-
import org.springframework.security.authentication.BadCredentialsException;
15-
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
13+
import org.springframework.security.authentication.*;
1614
import org.springframework.security.core.Authentication;
1715
import org.springframework.stereotype.Component;
1816
import org.zowe.apiml.gateway.security.service.AuthenticationService;
1917
import org.zowe.apiml.gateway.security.service.zosmf.ZosmfService;
18+
import org.zowe.apiml.security.common.token.TokenAuthentication;
2019

21-
import static org.zowe.apiml.gateway.security.service.zosmf.ZosmfService.TokenType.JWT;
22-
import static org.zowe.apiml.gateway.security.service.zosmf.ZosmfService.TokenType.LTPA;
20+
import static org.zowe.apiml.gateway.security.service.zosmf.ZosmfService.TokenType.JWT;
21+
import static org.zowe.apiml.gateway.security.service.zosmf.ZosmfService.TokenType.LTPA;
2322

2423
/**
2524
* Authentication provider that verifies credentials against z/OSMF service
@@ -30,6 +29,7 @@ public class ZosmfAuthenticationProvider implements AuthenticationProvider {
3029

3130
private final AuthenticationService authenticationService;
3231
private final ZosmfService zosmfService;
32+
private final ZosmfConfiguration zosmfConfiguration;
3333

3434
/**
3535
* Authenticate the credentials with the z/OSMF service
@@ -43,23 +43,43 @@ public Authentication authenticate(Authentication authentication) {
4343

4444
final ZosmfService.AuthenticationResponse ar = zosmfService.authenticate(authentication);
4545

46-
// if z/OSMF support JWT, use it as Zowe JWT token
47-
if (ar.getTokens().containsKey(JWT)) {
48-
return authenticationService.createTokenAuthentication(user, ar.getTokens().get(JWT));
49-
}
50-
51-
if (ar.getTokens().containsKey(LTPA)) {
52-
// construct own JWT token, including LTPA from z/OSMF
53-
final String domain = ar.getDomain();
54-
final String jwtToken = authenticationService.createJwtToken(user, domain, ar.getTokens().get(LTPA));
46+
switch (zosmfConfiguration.jwtAutoconfigurationMode) {
47+
case LTPA:
48+
if (ar.getTokens().containsKey(LTPA)) {
49+
return getApimlJwtToken(user, ar);
50+
}
51+
break;
52+
case JWT:
53+
if (ar.getTokens().containsKey(JWT)) {
54+
return getZosmfJwtToken(user, ar);
55+
}
56+
break;
57+
default: //AUTO
58+
if (ar.getTokens().containsKey(JWT)) {
59+
return getZosmfJwtToken(user, ar);
60+
}
5561

56-
return authenticationService.createTokenAuthentication(user, jwtToken);
62+
if (ar.getTokens().containsKey(LTPA)) {
63+
return getApimlJwtToken(user, ar);
64+
}
65+
break;
5766
}
5867

5968
// JWT and LTPA tokens are missing, authentication was wrong
6069
throw new BadCredentialsException("Username or password are invalid.");
6170
}
6271

72+
public TokenAuthentication getZosmfJwtToken(String user, ZosmfService.AuthenticationResponse ar) {
73+
return authenticationService.createTokenAuthentication(user, ar.getTokens().get(JWT));
74+
}
75+
76+
private TokenAuthentication getApimlJwtToken(String user, ZosmfService.AuthenticationResponse ar) {
77+
final String domain = ar.getDomain();
78+
final String jwtToken = authenticationService.createJwtToken(user, domain, ar.getTokens().get(LTPA));
79+
80+
return authenticationService.createTokenAuthentication(user, jwtToken);
81+
}
82+
6383
@Override
6484
public boolean supports(Class<?> auth) {
6585
return auth == UsernamePasswordAuthenticationToken.class;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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.login.zosmf;
12+
13+
import org.springframework.beans.factory.annotation.Value;
14+
import org.springframework.context.annotation.Configuration;
15+
16+
@Configuration
17+
public class ZosmfConfiguration {
18+
19+
public enum JWT_AUTOCONFIGURATION_MODE {
20+
AUTO,
21+
LTPA,
22+
JWT
23+
}
24+
25+
@Value("${apiml.security.auth.zosmfJwtAutoconfiguration:AUTO}")
26+
public JWT_AUTOCONFIGURATION_MODE jwtAutoconfigurationMode;
27+
28+
public static ZosmfConfiguration of(JWT_AUTOCONFIGURATION_MODE mode) {
29+
ZosmfConfiguration configuration = new ZosmfConfiguration();
30+
configuration.jwtAutoconfigurationMode = mode;
31+
return configuration;
32+
}
33+
34+
}

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

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,43 +9,43 @@
99
*/
1010
package org.zowe.apiml.gateway.security.login.zosmf;
1111

12-
import java.io.FileNotFoundException;
13-
import java.io.PrintWriter;
14-
15-
import org.springframework.stereotype.Component;
12+
import lombok.RequiredArgsConstructor;
13+
import lombok.extern.slf4j.Slf4j;
1614
import org.springframework.web.client.HttpClientErrorException;
1715
import org.springframework.web.client.RestTemplate;
1816

19-
import lombok.RequiredArgsConstructor;
20-
import lombok.extern.slf4j.Slf4j;
17+
import java.io.FileNotFoundException;
18+
import java.io.PrintWriter;
2119

22-
@Component
2320
@RequiredArgsConstructor
2421
@Slf4j
2522
public class ZosmfJwkToPublicKey {
2623

2724
protected final RestTemplate restTemplateWithoutKeystore;
25+
private final String zosmfJwtBuilderPath;
2826

2927
/**
3028
* Write public key that can be used to validate z/OSMF JWT tokens.
29+
*
3130
* @param zosmfUrl Base URL of z/OSMF without trailing /
3231
* @param filename File name of the resulting PEM file
3332
* @return True when the file has been updated
3433
* @throws FileNotFoundException when the filename is invalid
3534
*/
3635
public boolean updateJwtPublicKeyFile(String zosmfUrl, String filename, String caAlias, String caKeyStore,
37-
String caKeyStoreType, char[] caKeyStorePassword, char[] caKeyPassword) throws FileNotFoundException {
36+
String caKeyStoreType, char[] caKeyStorePassword, char[] caKeyPassword) throws FileNotFoundException {
3837
try {
39-
String jwkJson = restTemplateWithoutKeystore.getForObject(zosmfUrl + "/jwt/ibm/api/zOSMFBuilder/jwk",
40-
String.class);
38+
String jwkJson = restTemplateWithoutKeystore.getForObject(zosmfUrl + zosmfJwtBuilderPath,
39+
String.class);
4140
JwkToPublicKeyConverter converter = new JwkToPublicKeyConverter();
42-
String pem = converter.convertFirstPublicKeyJwkToPem(jwkJson, caAlias, caKeyStore, caKeyStoreType, caKeyStorePassword, caKeyPassword) ;
41+
String pem = converter.convertFirstPublicKeyJwkToPem(jwkJson, caAlias, caKeyStore, caKeyStoreType, caKeyStorePassword, caKeyPassword);
4342
try (PrintWriter out = new PrintWriter(filename)) {
4443
out.println(pem);
4544
}
4645
return true;
4746
} catch (HttpClientErrorException.NotFound e) {
48-
log.warn("Unable to read z/OSMF JWT public key. JWT support might be not configured in z/OSMF: {}", e.getMessage());;
47+
log.warn("Unable to read z/OSMF JWT public key. JWT support might be not configured in z/OSMF: {}", e.getMessage());
48+
;
4949
return false;
5050
}
5151
}

0 commit comments

Comments
 (0)