From b28a9ddd9475aa9f774d40b654a2c72a57cef4a6 Mon Sep 17 00:00:00 2001 From: Andrea Tabone <39694626+taban03@users.noreply.github.com> Date: Fri, 7 Oct 2022 15:14:27 +0200 Subject: [PATCH] fix: Make the SAF IDT properties configurable in Zowe (#2610) * fix saf impl props propagation Signed-off-by: at670475 * Return correct message when not being able to connect to ZSS Signed-off-by: at670475 * set default value for safidt provider in the manifest Signed-off-by: at670475 * set value to empty Signed-off-by: at670475 * use defualt value urls through GW Signed-off-by: at670475 * Add unit tests Signed-off-by: at670475 * Fix gw port name Signed-off-by: at670475 * add validation schema Signed-off-by: at670475 * Address PR review Signed-off-by: at670475 * change condition Signed-off-by: at670475 * fix sonar Signed-off-by: at670475 * Fix schema Signed-off-by: at670475 Signed-off-by: at670475 --- .../src/main/resources/bin/start.sh | 3 ++ .../src/main/resources/manifest.yaml | 5 +++ .../saf/SafRestAuthenticationService.java | 14 ++++++- .../saf/SafRestAuthenticationServiceTest.java | 40 +++++++++++++++++++ schemas/gateway-schema.json | 26 ++++++++++++ 5 files changed, 87 insertions(+), 1 deletion(-) diff --git a/gateway-package/src/main/resources/bin/start.sh b/gateway-package/src/main/resources/bin/start.sh index 383b7ddd4d..5273fa8c9f 100755 --- a/gateway-package/src/main/resources/bin/start.sh +++ b/gateway-package/src/main/resources/bin/start.sh @@ -213,6 +213,9 @@ _BPX_JOBNAME=${ZWE_zowe_job_prefix}${GATEWAY_CODE} java \ -Dapiml.security.authorization.provider=${ZWE_configs_apiml_security_authorization_provider:-} \ -Dapiml.security.authorization.endpoint.enabled=${ZWE_configs_apiml_security_authorization_endpoint_enabled:-false} \ -Dapiml.security.authorization.endpoint.url=${ZWE_configs_apiml_security_authorization_endpoint_url:-"https://${ZWE_haInstance_hostname:-localhost}:${ZWE_configs_port:-7554}/zss/api/v1/saf-auth"} \ + -Dapiml.security.saf.provider=${ZWE_configs_apiml_security_saf_provider:-"rest"} \ + -Dapiml.security.saf.urls.authenticate=${ZWE_configs_apiml_security_saf_urls_authenticate:-"https://${ZWE_haInstance_hostname:-localhost}:${ZWE_configs_port:-7554}/zss/api/v1/saf/authenticate"} \ + -Dapiml.security.saf.urls.verify=${ZWE_configs_apiml_security_saf_urls_verify:-"https://${ZWE_haInstance_hostname:-localhost}:${ZWE_configs_port:-7554}/zss/api/v1/saf/verify"} \ -Dapiml.security.authorization.resourceClass=${ZWE_configs_apiml_security_authorization_resourceClass:-ZOWE} \ -Dapiml.security.authorization.resourceNamePrefix=${ZWE_configs_apiml_security_authorization_resourceNamePrefix:-APIML.} \ -Dapiml.security.zosmf.applid=${ZWE_configs_apiml_security_zosmf_applid:-IZUDFLT} \ diff --git a/gateway-package/src/main/resources/manifest.yaml b/gateway-package/src/main/resources/manifest.yaml index 17a74251c4..0995748797 100644 --- a/gateway-package/src/main/resources/manifest.yaml +++ b/gateway-package/src/main/resources/manifest.yaml @@ -53,6 +53,11 @@ configs: externalMapperUrl: # default value is Zowe runtime user defined in zowe.yaml "zowe.setup.security.users.zowe" externalMapperUser: + saf: + provider: + urls: + authenticate: + verify: server: internal: diff --git a/gateway-service/src/main/java/org/zowe/apiml/gateway/security/service/saf/SafRestAuthenticationService.java b/gateway-service/src/main/java/org/zowe/apiml/gateway/security/service/saf/SafRestAuthenticationService.java index 73440d7576..1f3233943f 100644 --- a/gateway-service/src/main/java/org/zowe/apiml/gateway/security/service/saf/SafRestAuthenticationService.java +++ b/gateway-service/src/main/java/org/zowe/apiml/gateway/security/service/saf/SafRestAuthenticationService.java @@ -13,6 +13,7 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.StdArraySerializers; import lombok.*; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.*; @@ -35,6 +36,7 @@ * - apiml.security.saf.urls.verify - URL to verify the validity of the token */ @RequiredArgsConstructor +@Slf4j public class SafRestAuthenticationService implements SafIdtProvider { private final RestTemplate restTemplate; @@ -65,7 +67,13 @@ public String generate(String username, char[] password, String applId) { HttpMethod.POST, new HttpEntity<>(authentication, HEADERS), Token.class); - + if (HttpStatus.INTERNAL_SERVER_ERROR.equals(response.getStatusCode())) { + log.debug("The request with URL {} used to generate the SAF IDT token failed with response code {}.", authenticationUrl, response.getStatusCode()); + if (response.getBody() != null) { //NOSONAR tests return null + throw new SafIdtException(response.getBody().toString()); //NOSONAR tests return null + } + throw new SafIdtException("Cannot connect to ZSS authentication service and generate the SAF IDT token. Please, verify your configuration."); + } Token responseBody = response.getBody(); if (responseBody == null || StringUtils.isEmpty(responseBody.getJwt())) { throw new SafIdtException("ZSS authentication service has not returned the Identity token"); @@ -90,6 +98,10 @@ public boolean verify(String safToken, String applid) { new HttpEntity<>(new Token(safToken, applid), HEADERS), Void.class); + if (HttpStatus.INTERNAL_SERVER_ERROR.equals(response.getStatusCode())) { + log.debug("The request with URL {} used to validate the SAF IDT token failed with response code {}.", verifyUrl, response.getStatusCode()); + throw new SafIdtException("Cannot connect to ZSS authentication service and validate the SAF IDT token. Please, verify your configuration."); + } return response.getStatusCode().is2xxSuccessful(); } catch (RestClientException e) { return false; diff --git a/gateway-service/src/test/java/org/zowe/apiml/gateway/security/service/saf/SafRestAuthenticationServiceTest.java b/gateway-service/src/test/java/org/zowe/apiml/gateway/security/service/saf/SafRestAuthenticationServiceTest.java index 94d6952aee..eadfd1fb59 100644 --- a/gateway-service/src/test/java/org/zowe/apiml/gateway/security/service/saf/SafRestAuthenticationServiceTest.java +++ b/gateway-service/src/test/java/org/zowe/apiml/gateway/security/service/saf/SafRestAuthenticationServiceTest.java @@ -103,6 +103,31 @@ void givenBadResponse() { assertThrows(SafIdtException.class, () -> underTest.generate(VALID_USERNAME, new char[1], "ANYAPPL")); } + + @Test + void givenInternalErrorResponseWithEmptyBody() { + ResponseEntity response = + new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); + when(restTemplate.exchange(any(), eq(HttpMethod.POST), any(), eq(SafRestAuthenticationService.Token.class))) + .thenReturn(response); + + assertThrows(SafIdtException.class, + () -> underTest.generate(VALID_USERNAME, new char[1], "ANYAPPL")); + } + + @Test + void givenInternalErrorResponse() { + String validSafToken = "validSafToken"; + SafRestAuthenticationService.Token responseBody = + new SafRestAuthenticationService.Token(validSafToken, "applid"); + ResponseEntity response = + new ResponseEntity<>(responseBody, HttpStatus.INTERNAL_SERVER_ERROR); + when(restTemplate.exchange(any(), eq(HttpMethod.POST), any(), eq(SafRestAuthenticationService.Token.class))) + .thenReturn(response); + + assertThrows(SafIdtException.class, + () -> underTest.generate(VALID_USERNAME, new char[1], "ANYAPPL")); + } } } } @@ -152,6 +177,21 @@ void givenCorrectResponse() { assertThat(underTest.verify("validSafToken", "applid"), is(true)); } } + + @Nested + @DisplayName("Return Exception") + class ReturnExceptionTest { + @Test + void givenInternalErrorResponse() { + ResponseEntity response = + new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); + when(restTemplate.exchange(any(), eq(HttpMethod.POST), any(), eq(Void.class))) + .thenReturn(response); + + assertThrows(SafIdtException.class, + () -> underTest.verify("validSafToken", "applid")); + } + } } } diff --git a/schemas/gateway-schema.json b/schemas/gateway-schema.json index bfb3fe6d43..cbdb9f1dc0 100644 --- a/schemas/gateway-schema.json +++ b/schemas/gateway-schema.json @@ -111,6 +111,32 @@ "description": "User that has permission to do such mapping. Zowe user is used if no value is provided." } } + }, + "saf": { + "type": "object", + "description": "SAF IDT provider.", + "properties": { + "provider": { + "type": "string", + "description": "Method of communication used by the SAF IDT provider implementation. REST is used if no value is provided.", + "enum": ["rest"], + "default": "rest" + }, + "urls": { + "type": "string", + "description": "URLs of the SAF IDT provider used for the token generation and verification.", + "properties": { + "authenticate": { + "type": "string", + "description": "URL of the SAF IDT provider used to generate the SAF token on behalf of the specified user." + }, + "verify": { + "type": "string", + "description": "URL of the SAF IDT provider used to validate the SAF token." + } + } + } + } } } },