Skip to content

Commit

Permalink
fix: Make the SAF IDT properties configurable in Zowe (#2610)
Browse files Browse the repository at this point in the history
* fix saf impl props propagation

Signed-off-by: at670475 <[email protected]>

* Return correct message when not being able to connect to ZSS

Signed-off-by: at670475 <[email protected]>

* set default value for safidt provider in the manifest

Signed-off-by: at670475 <[email protected]>

* set value to empty

Signed-off-by: at670475 <[email protected]>

* use defualt value urls through GW

Signed-off-by: at670475 <[email protected]>

* Add unit tests

Signed-off-by: at670475 <[email protected]>

* Fix gw port name

Signed-off-by: at670475 <[email protected]>

* add validation schema

Signed-off-by: at670475 <[email protected]>

* Address PR review

Signed-off-by: at670475 <[email protected]>

* change condition

Signed-off-by: at670475 <[email protected]>

* fix sonar

Signed-off-by: at670475 <[email protected]>

* Fix schema

Signed-off-by: at670475 <[email protected]>

Signed-off-by: at670475 <[email protected]>
  • Loading branch information
taban03 authored Oct 7, 2022
1 parent 336d3b4 commit b28a9dd
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 1 deletion.
3 changes: 3 additions & 0 deletions gateway-package/src/main/resources/bin/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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} \
Expand Down
5 changes: 5 additions & 0 deletions gateway-package/src/main/resources/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.*;
Expand All @@ -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;
Expand Down Expand Up @@ -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");
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,31 @@ void givenBadResponse() {
assertThrows(SafIdtException.class,
() -> underTest.generate(VALID_USERNAME, new char[1], "ANYAPPL"));
}

@Test
void givenInternalErrorResponseWithEmptyBody() {
ResponseEntity<SafRestAuthenticationService.Token> 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<SafRestAuthenticationService.Token> 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"));
}
}
}
}
Expand Down Expand Up @@ -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"));
}
}
}

}
26 changes: 26 additions & 0 deletions schemas/gateway-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -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."
}
}
}
}
}
}
},
Expand Down

0 comments on commit b28a9dd

Please sign in to comment.