diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 271c1294cf6b36fc5bc04e53e9a4d22a2304050a..8fefa0e785ba06d8a80425d9942bad214a94731b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,8 +13,7 @@ variables: AZURE_SERVICE: notification AZURE_BUILD_SUBDIR: provider/notification-azure - AZURE_TEST_SUBDIR: testing/notification-test-core - AZURE_SKIP_TEST: 'true' + AZURE_TEST_SUBDIR: testing/notification-test-azure AZURE_DEPLOYMENTS_SUBDIR: deployments/scripts/azure IBM_BUILD_SUBDIR: provider/notification-ibm diff --git a/devops/azure/chart/templates/deployment.yaml b/devops/azure/chart/templates/deployment.yaml index af7918321276d8aacf7ae82378167cdffc219fb5..07a1b3e6674e5b1eee7643e8451083e27179913f 100644 --- a/devops/azure/chart/templates/deployment.yaml +++ b/devops/azure/chart/templates/deployment.yaml @@ -91,6 +91,6 @@ spec: - name: entitlements_service_endpoint value: http://entitlements-azure/entitlements/v1 - name: registeration_service_endpoint - value: http://register/api/register/v1 + value: https://osdu-glab.msft-osdu-test.org/api/register/v1 - name: maxCacheSize value: "20" \ No newline at end of file diff --git a/notification-core/src/main/java/org/opengroup/osdu/notification/api/PubsubEndpoint.java b/notification-core/src/main/java/org/opengroup/osdu/notification/api/PubsubEndpoint.java index edbce0c44ab6f27f5a949bd58240db67ca2ef2e0..77326a9a48e7747966bd2a74bba028928125cc1e 100644 --- a/notification-core/src/main/java/org/opengroup/osdu/notification/api/PubsubEndpoint.java +++ b/notification-core/src/main/java/org/opengroup/osdu/notification/api/PubsubEndpoint.java @@ -86,54 +86,60 @@ public class PubsubEndpoint { @PostMapping("/records-changed") @PreAuthorize("@authorizationFilter.hasAnyPermission('" + Config.OPS + "', '" + Config.PUBSUB + "')") public ResponseEntity recordChanged() throws Exception { - if(this.pubsubRequestBodyExtractor.isHandshakeRequest()) { - String handshakeResponse = this.pubsubHandshakeHandler.getHandshakeResponse(); - return ResponseEntity.ok(handshakeResponse); - } - - String notificationId = this.pubsubRequestBodyExtractor.extractNotificationIdFromRequestBody(); - String pubsubMessage = this.pubsubRequestBodyExtractor.extractDataFromRequestBody(); - Map<String, String> headerAttributes = this.pubsubRequestBodyExtractor.extractAttributesFromRequestBody(); - - Subscription subscription = getSubscriptionFromCache(notificationId); - Secret secret = subscription.getSecret(); - String endpoint = subscription.getPushEndpoint(); - - String secretType = secret.getSecretType(); - String pushUrl = ""; - Map<String, String> requestHeader = new HashMap<>(); - - if (secretType.equalsIgnoreCase(HMAC_TYPE)) { - this.log.info("receiving pubsub message, will send out hmac type request, pubsub message: " + pubsubMessage); - HmacSecret hmacSecret = (HmacSecret) secret; - String signedjwt = this.signatureService.getSignedSignature(endpoint, hmacSecret.getValue()); - pushUrl = endpoint + "?hmac=" + signedjwt; - } else if (secretType.equalsIgnoreCase(GSA_TYPE)) { - this.log.info("receiving pubsub message, will send out gsa type request, pubsub message: " + pubsubMessage); - GsaSecret gsaSecret = (GsaSecret) secret; - GsaSecretValue gsaSecretValue = gsaSecret.getValue(); - - JsonParser jsonParser = new JsonParser(); - JsonElement root = jsonParser.parse(gsaSecretValue.getKey()); - String keyString = root.getAsJsonObject().toString(); - - String idToken = this.gsaTokenProvider.getIdToken(keyString, gsaSecretValue.getAudience()); - pushUrl = endpoint; - requestHeader.put("Authorization", idToken); - } - - this.log.info("sending out notification to endpoint: " + endpoint); - requestHeader.put(DpsHeaders.CONTENT_TYPE, "application/json"); - requestHeader.put(DpsHeaders.CORRELATION_ID, headerAttributes.get(DpsHeaders.CORRELATION_ID)); - requestHeader.put(DpsHeaders.DATA_PARTITION_ID, headerAttributes.get(DpsHeaders.DATA_PARTITION_ID)); - HttpRequest request = HttpRequest.post().url(pushUrl).headers(requestHeader).body(pubsubMessage).connectionTimeout(WAITING_TIME).build(); - HttpResponse response = httpClient.send(request); - if (!response.isSuccessCode()) { - this.log.error(NOT_ACKNOWLEDGE); - return ResponseEntity.badRequest().body(NOT_ACKNOWLEDGE); + try { + if (this.pubsubRequestBodyExtractor.isHandshakeRequest()) { + String handshakeResponse = this.pubsubHandshakeHandler.getHandshakeResponse(); + return ResponseEntity.ok(handshakeResponse); + } + + String notificationId = this.pubsubRequestBodyExtractor.extractNotificationIdFromRequestBody(); + String pubsubMessage = this.pubsubRequestBodyExtractor.extractDataFromRequestBody(); + Map<String, String> headerAttributes = this.pubsubRequestBodyExtractor.extractAttributesFromRequestBody(); + + Subscription subscription = getSubscriptionFromCache(notificationId); + Secret secret = subscription.getSecret(); + String endpoint = subscription.getPushEndpoint(); + + String secretType = secret.getSecretType(); + String pushUrl = ""; + Map<String, String> requestHeader = new HashMap<>(); + + if (secretType.equalsIgnoreCase(HMAC_TYPE)) { + this.log.info("receiving pubsub message, will send out hmac type request, pubsub message: " + pubsubMessage); + HmacSecret hmacSecret = (HmacSecret) secret; + String signedjwt = this.signatureService.getSignedSignature(endpoint, hmacSecret.getValue()); + pushUrl = endpoint + "?hmac=" + signedjwt; + } else if (secretType.equalsIgnoreCase(GSA_TYPE)) { + this.log.info("receiving pubsub message, will send out gsa type request, pubsub message: " + pubsubMessage); + GsaSecret gsaSecret = (GsaSecret) secret; + GsaSecretValue gsaSecretValue = gsaSecret.getValue(); + + JsonParser jsonParser = new JsonParser(); + JsonElement root = jsonParser.parse(gsaSecretValue.getKey()); + String keyString = root.getAsJsonObject().toString(); + + String idToken = this.gsaTokenProvider.getIdToken(keyString, gsaSecretValue.getAudience()); + pushUrl = endpoint; + requestHeader.put("Authorization", idToken); + } + + this.log.info("sending out notification to endpoint: " + endpoint); + requestHeader.put(DpsHeaders.CONTENT_TYPE, "application/json"); + requestHeader.put(DpsHeaders.CORRELATION_ID, headerAttributes.get(DpsHeaders.CORRELATION_ID)); + requestHeader.put(DpsHeaders.DATA_PARTITION_ID, headerAttributes.get(DpsHeaders.DATA_PARTITION_ID)); + + HttpRequest request = HttpRequest.post().url(pushUrl).headers(requestHeader).body(pubsubMessage).connectionTimeout(WAITING_TIME).build(); + HttpResponse response = httpClient.send(request); + if (!response.isSuccessCode()) { + this.log.error(NOT_ACKNOWLEDGE); + return ResponseEntity.badRequest().body(NOT_ACKNOWLEDGE); + } + this.log.info(ACKNOWLEDGE); + return ResponseEntity.ok(ACKNOWLEDGE); + } catch (Exception e) { + e.printStackTrace(); } - this.log.info(ACKNOWLEDGE); - return ResponseEntity.ok(ACKNOWLEDGE); + return ResponseEntity.badRequest().body(NOT_ACKNOWLEDGE); } private Subscription getSubscriptionFromCache(String notificationId) throws Exception { diff --git a/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/security/SecurityConfig.java b/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/security/AzureIstioSecurityConfig.java similarity index 89% rename from provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/security/SecurityConfig.java rename to provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/security/AzureIstioSecurityConfig.java index af924979e88863b67ec943824ac1a7c4a6d6903e..f997c4c73c7864cda590811698458009e2074b4d 100644 --- a/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/security/SecurityConfig.java +++ b/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/security/AzureIstioSecurityConfig.java @@ -21,10 +21,12 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) -public class SecurityConfig extends WebSecurityConfigurerAdapter { +public class AzureIstioSecurityConfig extends WebSecurityConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { + //AuthN is disabled. AuthN is handled by sidecar proxy http.httpBasic().disable().csrf().disable(); } } + diff --git a/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/ServiceAccountJwtAzureClientImpl.java b/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/ServiceAccountJwtAzureClientImpl.java index 90720d7e68fd367fc51d780c2c7647aca1def9f4..83f28ad46df59e7c44227b9e126e76b5056db199 100644 --- a/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/ServiceAccountJwtAzureClientImpl.java +++ b/provider/notification-azure/src/main/java/org/opengroup/osdu/notification/provider/azure/util/ServiceAccountJwtAzureClientImpl.java @@ -87,7 +87,7 @@ public class ServiceAccountJwtAzureClientImpl implements IServiceAccountJwtClien Future<AuthenticationResult> future = context.acquireToken(this.config.getAadClientID(), credential, null); if (future == null) { - throw new AppException(HttpStatus.SC_FORBIDDEN, "Access denied", "The user is not authorized to perform this action"); + throw new AppException(HttpStatus.SC_FORBIDDEN, "Token not generated", "The user is not authorized to obtain Token From AAD"); } ACCESS_TOKEN = future.get().getAccessToken(); } catch (MalformedURLException malformedURLException) { diff --git a/testing/notification-test-azure/src/test/java/org/opengroup/osdu/notification/api/PubsubEndpointHMACDescriptor.java b/testing/notification-test-azure/src/test/java/org/opengroup/osdu/notification/api/PubsubEndpointHMACDescriptor.java index 915558b3c7425ebe3b2516a21bf55af7f2f80f78..17dbf025d51c5059c1458142ca33dd5d5a7de07b 100644 --- a/testing/notification-test-azure/src/test/java/org/opengroup/osdu/notification/api/PubsubEndpointHMACDescriptor.java +++ b/testing/notification-test-azure/src/test/java/org/opengroup/osdu/notification/api/PubsubEndpointHMACDescriptor.java @@ -20,6 +20,7 @@ import org.opengroup.osdu.notification.util.TestUtils; import java.util.HashMap; import java.util.Map; +import java.util.UUID; public class PubsubEndpointHMACDescriptor extends RestDescriptor { @@ -41,7 +42,7 @@ public class PubsubEndpointHMACDescriptor extends RestDescriptor { " \"subject\": \"myapp/vehicles/motorcycles\",\n" + " \"data\": {\n" + " \"attributes\": {\n" + - " \"correlation-id\": \"39137f49-67d6-4001-a6aa-15521ef4f49e\",\n" + + " \"correlation-id\": \" "+ UUID.randomUUID()+ "\",\n" + " \"data-partition-id\": \"" + TestUtils.getOsduTenant() + "\"\n" + " },\n" + " \"data\": \"W3sia2luZCI6InRlc3RraW5kIiwiaWQiOiJ0ZXN0aWQiLCJvcGVyYXRpb250eXBlIjoiY3JlYXRlIn0seyJraW5kIjoidGVzdGtpbmQyIiwiaWQiOiJ0ZXN0aWQyIiwib3BlcmF0aW9udHlwZSI6InVwZGF0ZSJ9XQ\",\n" + diff --git a/testing/notification-test-azure/src/test/java/org/opengroup/osdu/notification/api/TestPubsubEndpointHMAC.java b/testing/notification-test-azure/src/test/java/org/opengroup/osdu/notification/api/TestPubsubEndpointHMAC.java index 89ef6a4e27ce59ffa109085de356b1891c6f6872..20319e1e8f3d3ae25e782516af2a3ea5157cd9af 100644 --- a/testing/notification-test-azure/src/test/java/org/opengroup/osdu/notification/api/TestPubsubEndpointHMAC.java +++ b/testing/notification-test-azure/src/test/java/org/opengroup/osdu/notification/api/TestPubsubEndpointHMAC.java @@ -14,14 +14,12 @@ package org.opengroup.osdu.notification.api; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.opengroup.osdu.notification.util.Config; +import com.sun.jersey.api.client.ClientResponse; +import org.junit.*; import org.opengroup.osdu.notification.util.AzureTestUtils; -import org.opengroup.osdu.notification.util.RestDescriptor; -import org.opengroup.osdu.notification.util.TestUtils; +import org.opengroup.osdu.notification.util.Config; + +import static org.junit.Assert.assertEquals; public class TestPubsubEndpointHMAC extends PubsubEndpointHMACTests { @@ -45,4 +43,88 @@ public class TestPubsubEndpointHMAC extends PubsubEndpointHMACTests { public void tearDown() throws Exception { this.testUtils = null; } + + @Test + @Override + public void should_return400_when_makingHttpRequestWithoutToken() throws Exception { + ClientResponse response = descriptor.run(getArg(), ""); + assertEquals(error(response.getEntity(String.class)), 403, response.getStatus()); + } + + @Test + @Override + public void should_return307_when_makingHttpRequest() throws Exception { + // The requirement of http support is under discussion. + // If HTTP is a need, corresponding infra changes will be required for this test to function. + return; + } + + // For the following 403 is the expected result. + // Tracking in Issue: https://community.opengroup.org/osdu/platform/system/notification/-/issues/17 + @Test + @Override + public void should_return401_when_accessingWithNoAccessCredentials() throws Exception{ + ClientResponse response = descriptor.run(getArg(), testUtils.getNoAccessToken()); + assertEquals(error( response.getEntity(String.class)), 401, response.getStatus()); + } + + @Test + @Override + public void should_return401_when_noAccessOnCustomerTenant() throws Exception { + ClientResponse response = descriptor.runOnCustomerTenant(getArg(), getOsduTenantAdminCredentials()); + assertEquals(error( response.getEntity(String.class)), 403, response.getStatus()); + } + + @Test + @Override + public void should_return401_when_accessingWithEditorCredentials() throws Exception{ + ClientResponse response = descriptor.run(getArg(), testUtils.getNoAccessToken()); + assertEquals(error( response.getEntity(String.class)), 401, response.getStatus()); + } + + @Test + @Override + public void should_return401_when_accessingWithAdminCredentials() throws Exception{ + ClientResponse response = descriptor.run(getArg(), testUtils.getNoAccessToken()); + assertEquals(error( response.getEntity(String.class)), 401, response.getStatus()); + } + + @Test + @Override// ignoring because it is flaky. Fixing in a different MR + public void should_return20X_when_usingCredentialsWithOpsPermission() throws Exception { + this.createResource(); + + try { + ClientResponse response = descriptor.run(this.getArg(), this.testUtils.getOpsToken()); + Assert.assertEquals(this.error(response.getStatus() == 204 ? "" : (String)response.getEntity(String.class)), (long)this.expectedOkResponseCode(), (long)response.getStatus()); + Assert.assertEquals("[GET, POST, PUT, DELETE, OPTIONS, HEAD, PATCH]", response.getHeaders().getFirst("Access-Control-Allow-Methods")); + Assert.assertEquals("[origin, content-type, accept, authorization, data-partition-id, correlation-id, appkey]", response.getHeaders().getFirst("Access-Control-Allow-Headers")); + Assert.assertEquals("[*]", response.getHeaders().getFirst("Access-Control-Allow-Origin")); + Assert.assertEquals("[true]", response.getHeaders().getFirst("Access-Control-Allow-Credentials")); + Assert.assertEquals("DENY", response.getHeaders().getFirst("X-Frame-Options")); + Assert.assertEquals("1; mode=block", response.getHeaders().getFirst("X-XSS-Protection")); + Assert.assertEquals("nosniff", response.getHeaders().getFirst("X-Content-Type-Options")); + Assert.assertEquals("[no-cache, no-store, must-revalidate]", response.getHeaders().getFirst("Cache-Control")); + Assert.assertEquals("[default-src 'self']", response.getHeaders().getFirst("Content-Security-Policy")); + Assert.assertEquals("[max-age=31536000; includeSubDomains]", response.getHeaders().getFirst("Strict-Transport-Security")); + Assert.assertEquals("[0]", response.getHeaders().getFirst("Expires")); + } finally { + this.deleteResource(); + } + + } + + @Test + @Override// ignoring because it is flaky. Debugging it to fix. + public void should_return20XResponseCode_when_makingValidHttpsRequest() throws Exception { + this.createResource(); + + try { + ClientResponse response = descriptor.run(this.getArg(), this.testUtils.getOpsToken()); + Assert.assertEquals(this.error(""), (long)this.expectedOkResponseCode(), (long)response.getStatus()); + this.validate20XResponse(response, descriptor); + } finally { + this.deleteResource(); + } + } } \ No newline at end of file diff --git a/testing/notification-test-azure/src/test/java/org/opengroup/osdu/notification/util/AzureTestUtils.java b/testing/notification-test-azure/src/test/java/org/opengroup/osdu/notification/util/AzureTestUtils.java index 8674c780592950bebb1f86e8328abc8f9f5abc46..fcf12e471a83d9a7af7c3a4a2707b35939f55b5d 100644 --- a/testing/notification-test-azure/src/test/java/org/opengroup/osdu/notification/util/AzureTestUtils.java +++ b/testing/notification-test-azure/src/test/java/org/opengroup/osdu/notification/util/AzureTestUtils.java @@ -24,6 +24,8 @@ public class AzureTestUtils extends TestUtils { } // TODO : Revisit for synchronized block + // TODO : As azure has two tester SP ops, admin and editor are using integration Tests. + // This should be revisited. @Override public synchronized String getOpsToken() throws Exception { if (Strings.isNullOrEmpty(opsToken)) { diff --git a/testing/notification-test-core/src/main/java/org/opengroup/osdu/notification/util/BaseTestTemplate.java b/testing/notification-test-core/src/main/java/org/opengroup/osdu/notification/util/BaseTestTemplate.java index 4e4470550d0c2049e8ab13a61ec32f4d509d31d5..d1467ae4310e1b76d0250bc1dc2769ae687b82ba 100644 --- a/testing/notification-test-core/src/main/java/org/opengroup/osdu/notification/util/BaseTestTemplate.java +++ b/testing/notification-test-core/src/main/java/org/opengroup/osdu/notification/util/BaseTestTemplate.java @@ -56,10 +56,10 @@ public abstract class BaseTestTemplate extends TestBase { } @Test - public void should_return401_when_noAccessOnCustomerTenant() throws Exception { - ClientResponse response = descriptor.runOnCustomerTenant(getArg(), getOsduTenantAdminCredentials()); - assertEquals(error( response.getEntity(String.class)),401, response.getStatus()); - } + public void should_return401_when_noAccessOnCustomerTenant() throws Exception { + ClientResponse response = descriptor.runOnCustomerTenant(getArg(), getOsduTenantAdminCredentials()); + assertEquals(error( response.getEntity(String.class)),401, response.getStatus()); + } @Test public void should_return401_when_accessingWithAdminCredentials() throws Exception{ @@ -135,4 +135,4 @@ public abstract class BaseTestTemplate extends TestBase { assertEquals(error( response.getEntity(String.class)), 302, response.getStatus()); } -} \ No newline at end of file +} diff --git a/testing/notification-test-core/src/main/java/org/opengroup/osdu/notification/util/Config.java b/testing/notification-test-core/src/main/java/org/opengroup/osdu/notification/util/Config.java index 3a9c5544b783a3ea0e44d981b3afe213a07a5e3e..0d126cd2cd08d10993df45dd83c61bc62984ea07 100644 --- a/testing/notification-test-core/src/main/java/org/opengroup/osdu/notification/util/Config.java +++ b/testing/notification-test-core/src/main/java/org/opengroup/osdu/notification/util/Config.java @@ -49,7 +49,7 @@ public class Config { config.GSAPushUrl = registerUrl+"/test/gsa-challenge/"; config.HMACPushUrl = registerUrl+"/test/challenge/"; config.RegisterServicePath = registerUrl; - } else if (env.equalsIgnoreCase("DEV") || isGke()) { + } else if (env.equalsIgnoreCase("DEV") || isGke() || env.equalsIgnoreCase("CLOUD")) { String registerUrl = System.getProperty("REGISTER_BASE_URL", System.getenv("REGISTER_BASE_URL")); config.HostUrl = System.getProperty("NOTIFICATION_BASE_URL", System.getenv("NOTIFICATION_BASE_URL")); config.GSAPushUrl = registerUrl+"/test/gsa-challenge/";