Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Integration tests runnable with API ML v2 on z/OS #4022

Open
wants to merge 24 commits into
base: v2.x.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 23 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1352,8 +1352,6 @@ jobs:
image: ghcr.io/balhar-jakub/gateway-service:${{ github.run_id }}-${{ github.run_number }}
mock-services:
image: ghcr.io/balhar-jakub/mock-services:${{ github.run_id }}-${{ github.run_number }}
env:
ZOSMF_APPLIEDAPARS: AuthenticateApar

steps:
- uses: actions/checkout@v4
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
package org.zowe.apiml.security.common.auth.saf;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.Authentication;

import java.lang.invoke.MethodHandle;
Expand Down Expand Up @@ -96,6 +97,9 @@ private boolean checkPermission(String userId, String resourceType, String resou
@Override
public boolean hasSafResourceAccess(Authentication authentication, String resourceClass, String resourceName, String accessLevel) {
String userid = authentication.getName();
if (StringUtils.isEmpty(userid)) {
return false;
}
AccessLevel level = AccessLevel.valueOf(accessLevel);
log.debug("Evaluating access of user {} to resource {} in class {} level {}", userid, resourceClass, resourceName, level);
return checkPermission(userid, resourceClass, resourceName, level.getValue(), true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;

import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;

Expand Down Expand Up @@ -88,6 +90,11 @@ void testHasSafResourceAccess_whenNoResponse_thenTrue() {
assertTrue(safResourceAccessVerifying.hasSafResourceAccess(authentication, CLASS, RESOURCE, LEVEL.name()));
}

@Test
void testHasSafResourceAccess_whenUseridEmpty_thenFalse() {
assertFalse(safResourceAccessVerifying.hasSafResourceAccess(new UsernamePasswordAuthenticationToken("", "token"), CLASS, RESOURCE, LEVEL.name()));
}

@Builder
public static class TestPlatformReturned {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public enum ErrorCode {
ERR_8_12_16(8, 12, 16, HttpStatus.SC_INTERNAL_SERVER_ERROR, "Invocation of the Security Server Network Authentication Service Program Call (PC) interface failed with a 'local services are not available' return code. This indicates that the Security Server Network Authentication Service started task (SKRBKDC) address space has not been started or is terminating."),
ERR_8_12_20(8, 12, 20, HttpStatus.SC_INTERNAL_SERVER_ERROR, "Invocation of the Security Server Network Authentication Service Program Call (PC) interface failed with an 'abend in the PC service routine' return code. The symptom record associated with this abend can be found in the logrec data set."),
ERR_8_12_24(8, 12, 24, HttpStatus.SC_INTERNAL_SERVER_ERROR, "Invocation of the Security Server Network Authentication Service Program Call (PC) interface failed with an 'unable to obtain control lock' return code. This can occur if the task holding the lock is not being dispatched (for example, a dump is in progress)."),
ERR_8_16_28(8, 16, 28, HttpStatus.SC_BAD_REQUEST, "Unable to generate PassTicket. Verify that the secured signon (PassTicket) function and application ID is configured properly by referring to Using PassTickets in z/OS Security Server RACF Security Administrator's Guide."),
ERR_8_16_28(8, 16, 28, HttpStatus.SC_INTERNAL_SERVER_ERROR, "Unable to generate PassTicket. Verify that the secured signon (PassTicket) function and application ID is configured properly by referring to Using PassTickets in z/OS Security Server RACF Security Administrator's Guide."),
ERR_8_16_32(8, 16, 32, HttpStatus.SC_INTERNAL_SERVER_ERROR, "PassTicket evaluation failure. Possible reasons include: " +
"PassTicket to be evaluated is not a successful PassTicket. "
+ "The PassTicket to be evaluated was already evaluated before and replay protection is in effect. "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ void testErrorCode() {

te = new TestException(8, 16, 28);
assertSame(AbstractIRRPassTicketException.ErrorCode.ERR_8_16_28, te.getErrorCode());
assertEquals(HttpStatus.SC_BAD_REQUEST, te.getHttpStatus());
assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, te.getHttpStatus());
}

class TestException extends AbstractIRRPassTicketException {
Expand Down
5 changes: 5 additions & 0 deletions config/local/gateway-service.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ server:
ssl:
keyAlias: localhost-multi
keyStore: keystore/localhost/localhost-multi.keystore.p12

webSocket:
requestBufferSize: 16348
max-http-request-header-size: 16348
Comment on lines +58 to +60
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these are v3-specific, we need to verify

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://www.baeldung.com/spring-boot-max-http-header-size#servermax-http-request-header-size

It seems there should be server.max-http-header-size.

The websocket config is probably only Zowe related (see WebSocketConfig). I would rather remove both.


ssl:
keyAlias: localhost
keyPassword: password
Expand Down
5 changes: 5 additions & 0 deletions discoverable-client/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ plugins {
alias(libs.plugins.gradle.git.properties)
}

java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

normalization {
runtimeClasspath {
ignore("**/*git.properties*")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ public ResponseEntity<String> revokeAllUserAccessTokens(@RequestBody(required =
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
}
String userId = SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString();
log.debug("revokeAllUserAccessTokens: userId={}", userId);
long timeStamp = 0;
if (rulesRequestModel != null) {
timeStamp = rulesRequestModel.getTimestamp();
Expand All @@ -143,6 +144,7 @@ public ResponseEntity<String> revokeAccessTokensForUser(@RequestBody() RulesRequ
if (userId == null) {
return badRequestForPATInvalidation();
}
log.debug("revokeAccessTokensForUser: userId={}", userId);
tokenProvider.invalidateAllTokensForUser(userId, timeStamp);

return new ResponseEntity<>(HttpStatus.NO_CONTENT);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,6 @@ public SecurityFilterChain authProtectedEndpointsFilterChain(HttpSecurity http)
)))
.authorizeRequests(requests -> requests
.anyRequest().authenticated())
.x509(x509 -> x509.userDetailsService(x509UserDetailsService()))
.authenticationProvider(compoundAuthProvider) // for authenticating credentials
.apply(new CustomSecurityFilters());
return http.build();
Expand Down Expand Up @@ -314,7 +313,6 @@ public SecurityFilterChain authZaasEndpointsFilterChain(HttpSecurity http) throw
)))
.authorizeRequests(requests -> requests
.anyRequest().authenticated())
.x509(x509 -> x509.userDetailsService(x509UserDetailsService()))
.addFilterAfter(new CategorizeCertsFilter(publicKeyCertificatesBase64, certificateValidator), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class)
.addFilterAfter(new ExtractAuthSourceFilter(authSourceService, authExceptionHandler), org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter.class)
.addFilterAfter(new ZaasAuthenticationFilter(authSourceService, authExceptionHandler), CategorizeCertsFilter.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,12 @@ public boolean isValid(AuthSource authSource) {
serviceId = ((PATAuthSource) authSource).getDefaultServiceId();
}
boolean validForScopes = tokenProvider.isValidForScopes(token, serviceId);
logger.log(MessageType.DEBUG, "PAT is %s for scope: %s ", validForScopes ? "valid" : "not valid", serviceId);
logger.log(MessageType.DEBUG, "PAT is {} for scope: {} ", validForScopes ? "valid" : "not valid", serviceId);
boolean invalidate = tokenProvider.isInvalidated(token);
logger.log(MessageType.DEBUG, "PAT was %s", invalidate ? "invalidated" : "not invalidated");
logger.log(MessageType.DEBUG, "PAT was {}", invalidate ? "invalidated" : "not invalidated");
return validForScopes && !invalidate;
} catch (Exception e) {
logger.log(MessageType.ERROR, "PAT is not valid due to the exception: %s", e.getMessage());
logger.log(MessageType.ERROR, "PAT is not valid due to the exception: {}", e.getMessage());
return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,15 @@ public void invalidateToken(String token) throws CachingServiceClientException,
}

public void invalidateAllTokensForUser(String userId, long timestamp) throws CachingServiceClientException {
String hashedUserId = getHash(userId);
String hashedUserId = getHash(userId.trim().toUpperCase());
if (timestamp == 0) {
timestamp = System.currentTimeMillis();
}
cachingServiceClient.appendList(INVALID_USERS_KEY, new CachingServiceClient.KeyValue(hashedUserId, Long.toString(timestamp)));
}

public void invalidateAllTokensForService(String serviceId, long timestamp) throws CachingServiceClientException {
String hashedServiceId = getHash(serviceId);
String hashedServiceId = getHash(serviceId.trim().toLowerCase());
if (timestamp == 0) {
timestamp = System.currentTimeMillis();
}
Expand All @@ -82,7 +82,7 @@ public void invalidateAllTokensForService(String serviceId, long timestamp) thro
public boolean isInvalidated(String token) throws CachingServiceClientException {
QueryResponse parsedToken = authenticationService.parseJwtWithSignature(token);
String hashedToken = getHash(token);
String hashedUserId = getHash(parsedToken.getUserId());
String hashedUserId = getHash(parsedToken.getUserId().trim().toUpperCase());
List<String> hashedServiceIds = parsedToken.getScopes().stream().map(this::getHash).collect(Collectors.toList());

Map<String, Map<String, String>> cacheMap = cachingServiceClient.readAllMaps();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ void shouldFailWhenGenerationFails() throws JsonProcessingException, Unsupported
successfulTicketHandlerHandler.onAuthenticationSuccess(httpServletRequest, httpServletResponse, tokenAuthentication);

assertEquals(MediaType.APPLICATION_JSON_VALUE, httpServletResponse.getContentType());
assertEquals(HttpStatus.BAD_REQUEST.value(), httpServletResponse.getStatus());
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.value(), httpServletResponse.getStatus());
assertTrue(httpServletResponse.getContentAsString().contains("ZWEAG141E"));
assertTrue(httpServletResponse.isCommitted());
}
Expand Down
68 changes: 61 additions & 7 deletions integration-tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ test {

task startUpCheck(type: Test) {
group 'integration tests'
description "Check that the API Mediation Layer is up and runnig"
description "Check that the API Mediation Layer is up and running"

systemProperties System.properties
systemProperty "environment.offPlatform", true
Expand All @@ -100,7 +100,6 @@ task environmentCheck(type: Test) {
outputs.upToDateWhen { false }
}


task runStartUpCheck(type: Test) {
group 'integration tests'
description "Check that the API Mediation Layer is up and running"
Expand Down Expand Up @@ -176,14 +175,14 @@ task runAllIntegrationTests(type: Test) {
outputs.upToDateWhen { false }
}

task runAllIntegrationTestsForZoweTestingOnZos(type: Test) {
task runAllIntegrationTestsForZoweNonHaTestingOnZos(type: Test) {
// This task is intended to run on z/OS systems with some limitations:
// Only 1 Gateway
// Only 1 Gateway (Non-HA mode)
// z/OSMF Authentication provider only
// No support for SAF ID Tokens

group "Integration tests"
description "Run all integration tests for Zowe testing on z/OS (limited)"
description "Run all integration tests for Zowe Non-HA testing on z/OS (limited)"

def targetSystem = System.getenv("ZOS_TARGET_SYS") ? "-" + System.getenv("ZOS_TARGET_SYS") : ""
systemProperty "environment.config", targetSystem
Expand All @@ -210,7 +209,58 @@ task runAllIntegrationTestsForZoweTestingOnZos(type: Test) {
'SafIdTokenTest'
)
}

debugOptions {
port = 5005
suspend = true
server = true
}
outputs.upToDateWhen { false }
}

task runAllIntegrationTestsForZoweHaTestingOnZos(type: Test) {
// This task is intended to run on z/OS systems with some limitations:
// More than 1 Gateway (HA mode)
// z/OSMF Authentication provider only
// No support for SAF ID Tokens

group "Integration tests"
description "Run all integration tests for Zowe HA testing on z/OS (limited)"

def targetSystem = System.getenv("ZOS_TARGET_SYS") ? "-" + System.getenv("ZOS_TARGET_SYS") : ""
systemProperty "environment.config", targetSystem
systemProperty "environment.zos.target", "true"
systemProperty "environment.ha", true
systemProperties System.properties
systemProperties.remove('java.endorsed.dirs')

useJUnitPlatform {
excludeTags(
'StartupCheck',
'EnvironmentCheck',
'AdditionalLocalTest',
'TestsNotMeantForZowe',
'DiscoverableClientDependentTest',
'OktaOauth2Test',
'MultipleRegistrationsTest',
'NotForMainframeTest',
'ApiCatalogStandaloneTest',
'SAFProviderTest',
'CloudGatewayProxyTest',
'SafIdTokenTest',
'ChaoticHATest',
'GraphQLTest'
)
}

debugOptions {
port = 5005
suspend = true
server = true
}

outputs.upToDateWhen { false }
outputs.cacheIf { false }
}

task runAllIntegrationTestsForZoweTesting(type: Test) {
Expand Down Expand Up @@ -362,6 +412,7 @@ task runCloudGatewayProxyTest(type: Test) {
)
}
}

task runCloudGatewayServiceRoutingTest(type: Test) {
group "integration tests"
description "Run tests verifying cloud gateway can locate service and translate auth scheme"
Expand Down Expand Up @@ -529,11 +580,14 @@ task runLbHaTests(type: Test) {
task runChaoticHATests(type: Test) {
group "Integration tests"
description "Run Chaotic tests verifying High Availability"
dependsOn startUpCheck

def targetSystem = System.getenv("ZOS_TARGET_SYS") ? "-" + System.getenv("ZOS_TARGET_SYS") : ""
systemProperty "environment.config", targetSystem
systemProperty "environment.zos.target", "true"
systemProperty "environment.ha", true

outputs.cacheIf { false }

systemProperty "environment.ha", true
systemProperties System.getProperties()
useJUnitPlatform {
includeTags(
Expand Down
Loading
Loading