diff --git a/provider/notification-aws/build-aws/buildspec.yaml b/provider/notification-aws/build-aws/buildspec.yaml index cd48f3f69dbe2eb5ff01026a8a0b1effa843a6fd..623f56653e3c8073d4b93684f86a18cd390cddcc 100644 --- a/provider/notification-aws/build-aws/buildspec.yaml +++ b/provider/notification-aws/build-aws/buildspec.yaml @@ -54,7 +54,7 @@ phases: pre_build: commands: - echo "Logging in to Amazon ECR..." - - $(aws ecr get-login --no-include-email --region $AWS_REGION) # authenticate with ECR via the AWS CLI + - aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${ECR_REGISTRY} # authenticate with ECR via the AWS CLI build: commands: - export REPO_NAME=${PWD##*/} @@ -76,8 +76,9 @@ phases: - if [ "$GIT_SECRETS_SCAN_RESULT" = "FAILED" ]; then echo "Secrets detected!" && exit 1; fi - echo "Building primary service assemblies..." - - mvn -ntp -B test install sonar:sonar -pl .,notification-core,provider/notification-aws -Ddeployment.environment=prod -Dsonar.login=${SONAR_USERNAME} -Dsonar.password=${SONAR_PASSWORD} -Dsonar.branch.name=${BRANCH_NAME} - + - mvn -ntp -B test install -pl .,notification-core,provider/notification-aws -Ddeployment.environment=prod + - mvn sonar:sonar -pl .,provider/notification-aws -Dsonar.scm.provider=git -Dsonar.login=${SONAR_USERNAME} -Dsonar.password=${SONAR_PASSWORD} -Dsonar.branch.name=${BRANCH_NAME} + - echo "Building integration testing assemblies and gathering artifacts..." - ./testing/notification-test-aws/build-aws/prepare-dist.sh diff --git a/provider/notification-aws/lombok.config b/provider/notification-aws/lombok.config new file mode 100644 index 0000000000000000000000000000000000000000..0c2484f841602f9ef9b8613e98c78a4c2fbdc7b5 --- /dev/null +++ b/provider/notification-aws/lombok.config @@ -0,0 +1,2 @@ + config.stopBubbling = true + lombok.addLombokGeneratedAnnotation = true diff --git a/provider/notification-aws/pom.xml b/provider/notification-aws/pom.xml index 4bf38cdf42d4dcc27b9dafbae4d1823876d90d94..150c2ef70b306b94fa8cce6c72d9985bb068c997 100644 --- a/provider/notification-aws/pom.xml +++ b/provider/notification-aws/pom.xml @@ -76,7 +76,7 @@ <dependency> <groupId>org.opengroup.osdu.core.aws</groupId> <artifactId>os-core-lib-aws</artifactId> - <version>0.23.0</version> + <version>0.24.0-SNAPSHOT</version> </dependency> <dependency> <groupId>commons-io</groupId> @@ -203,10 +203,6 @@ <goals> <goal>prepare-agent</goal> </goals> - <configuration> - <!-- Sets the VM argument line used when unit tests are run. --> - <propertyName>jacocoArgLine</propertyName> - </configuration> </execution> <execution> <id>report</id> diff --git a/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/impl/AwsGoogleServiceAccountImpl.java b/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/impl/AwsGoogleServiceAccountImpl.java index 6a47ba1c722e57196d1eda4e689eefbc600586a5..e4754d9a732588cb8cfbecad64cf7bae76402a11 100644 --- a/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/impl/AwsGoogleServiceAccountImpl.java +++ b/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/impl/AwsGoogleServiceAccountImpl.java @@ -14,15 +14,14 @@ package org.opengroup.osdu.notification.provider.aws.impl; import org.opengroup.osdu.notification.provider.interfaces.IGoogleServiceAccount; import org.springframework.stereotype.Component; -import sun.reflect.generics.reflectiveObjects.NotImplementedException; + @Component public class AwsGoogleServiceAccountImpl implements IGoogleServiceAccount { @Override + // To do: Check if it is to be supported public String getIdToken(String keyString, String audience) { - // TODO : Check if it is to be supported - - throw new NotImplementedException(); + throw new UnsupportedOperationException(); } } diff --git a/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/impl/AwsPubsubRequestBodyExtractor.java b/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/impl/AwsPubsubRequestBodyExtractor.java index d4c30be61a0cbdd14cf41ae5b43f98e1021b2152..240b7b40a4d52484e2197e5d3f926c308a16cac7 100644 --- a/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/impl/AwsPubsubRequestBodyExtractor.java +++ b/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/impl/AwsPubsubRequestBodyExtractor.java @@ -59,6 +59,7 @@ public class AwsPubsubRequestBodyExtractor implements IPubsubRequestBodyExtracto if (this.messageContent == null) { this.messageContent = this.extractPubsubMessageFromRequestBody(); } + return this.messageContent.getAttributes(); } @@ -70,10 +71,14 @@ public class AwsPubsubRequestBodyExtractor implements IPubsubRequestBodyExtracto } public String extractNotificationIdFromRequestBody() { + JsonElement subscription = null; if (this.root == null) { this.root = this.extractRootJsonElementFromRequestBody(); } - JsonElement subscription = this.root.get("subscription"); + + if(root.has("subscription")) + subscription = this.root.get("subscription"); + if (subscription == null) { throw new AppException(HttpStatus.BAD_REQUEST.value(), INVALID_PUBSUB_MESSAGE, "Subscription object not found"); } @@ -92,14 +97,16 @@ public class AwsPubsubRequestBodyExtractor implements IPubsubRequestBodyExtracto this.root = this.extractRootJsonElementFromRequestBody(); } JsonElement message = this.root.get("message"); + if (message == null) { throw new AppException(HttpStatus.BAD_REQUEST.value(), INVALID_PUBSUB_MESSAGE, "Message object not found"); } + MessageContent content = GSON.fromJson(message.toString(), MessageContent.class); Map<String, String> attributes = content.getAttributes(); + if (attributes == null || attributes.isEmpty()) { - log.error("Incorrect Message: " + message ); throw new AppException(HttpStatus.BAD_REQUEST.value(), INVALID_PUBSUB_MESSAGE, "Attribute map not found"); } String data = content.getData(); @@ -112,8 +119,8 @@ public class AwsPubsubRequestBodyExtractor implements IPubsubRequestBodyExtracto throw new AppException(HttpStatus.BAD_REQUEST.value(), INVALID_PUBSUB_MESSAGE, "No tenant information from pubsub message."); } - String x_user_id= request.getHeader("x-user-id"); - lowerCase.put("x-user-id",x_user_id); + String xUserId= request.getHeader("x-user-id"); + lowerCase.put("x-user-id", xUserId); content.setAttributes(lowerCase); String decoded = new String(Base64.getDecoder().decode(data)); @@ -122,9 +129,10 @@ public class AwsPubsubRequestBodyExtractor implements IPubsubRequestBodyExtracto return content; } - private JsonObject extractRootJsonElementFromRequestBody() { + @SuppressWarnings("deprecation") + private JsonObject extractRootJsonElementFromRequestBody() { try { - JsonParser jsonParser = new JsonParser(); + JsonParser jsonParser = new JsonParser(); BufferedReader reader = request.getReader(); Stream<String> lines = reader.lines(); String requestBody = lines.collect(Collectors.joining("\n")); diff --git a/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/model/FailedNotificationDoc.java b/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/model/FailedNotificationDoc.java index 59cb608b17d5b6e5d03c9fe3ce1a2aac9615c1a1..f656a194ece08a2cd9671f6679fab9e9622cd929 100644 --- a/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/model/FailedNotificationDoc.java +++ b/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/model/FailedNotificationDoc.java @@ -19,11 +19,11 @@ import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBIndexHashKey; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBIndexRangeKey; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTypeConvertedEnum; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import org.apache.commons.lang3.StringUtils; @Data @NoArgsConstructor diff --git a/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/queue/impl/NotificationQueueServiceImpl.java b/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/queue/impl/NotificationQueueServiceImpl.java index 387c40eeb946bd486ab0394d1aed7ecbde0a3179..da20bf30a1f4e627a14fb438d4bed1067d8683d7 100644 --- a/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/queue/impl/NotificationQueueServiceImpl.java +++ b/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/queue/impl/NotificationQueueServiceImpl.java @@ -13,7 +13,6 @@ // limitations under the License. package org.opengroup.osdu.notification.provider.aws.queue.impl; -import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBDeleteExpression; import com.amazonaws.services.dynamodbv2.datamodeling.PaginatedQueryList; import com.amazonaws.services.sqs.AmazonSQS; import com.amazonaws.services.sqs.model.Message; @@ -24,6 +23,7 @@ import org.opengroup.osdu.core.aws.dynamodb.DynamoDBQueryHelperV2; import org.opengroup.osdu.core.aws.sqs.AmazonSQSConfig; import org.opengroup.osdu.core.aws.ssm.K8sLocalParameterProvider; import org.opengroup.osdu.core.aws.ssm.K8sParameterNotFoundException; +import org.opengroup.osdu.core.aws.sns.PublishRequestBuilder; import org.opengroup.osdu.core.common.http.HttpResponse; import org.opengroup.osdu.core.common.model.notification.Subscription; import org.opengroup.osdu.notification.provider.aws.model.FailedNotificationDoc; @@ -150,6 +150,9 @@ public class NotificationQueueServiceImpl implements NotificationQueueService { } private boolean notifySubscriber(Subscription subscription, String messageBody, Map<String, String> headerAttributes) { + // Only process this subscription if the topics match. + if (!subscription.getTopic().equals(headerAttributes.get(PublishRequestBuilder.OSDU_TOPIC_ATTRIBUTE_NAME))) return true; + HttpResponse response; try { response = notificationHandler.notifySubscriber(subscription, messageBody, headerAttributes); diff --git a/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/queue/impl/NotificationRetryQueueServiceImpl.java b/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/queue/impl/NotificationRetryQueueServiceImpl.java index 0652b440531b1f5c8be0b28c8fffd1608244d63e..b51df1790df1da3b6f1b66ea27c5895bc4fcd323 100644 --- a/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/queue/impl/NotificationRetryQueueServiceImpl.java +++ b/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/queue/impl/NotificationRetryQueueServiceImpl.java @@ -165,8 +165,6 @@ public class NotificationRetryQueueServiceImpl implements NotificationRetryQueue private void deleteDbRecords(List<Message> messages) { DynamoDBQueryHelperV2 dynamoDBQueryHelper = dynamoDBQueryHelperFactory.getQueryHelperUsingSSM(failedNotificationTablePath); messages.parallelStream().forEach(message -> { - FailedNotificationDoc doc = dynamoDBQueryHelper.loadByPrimaryKey(FailedNotificationDoc.class, - message.getMessageAttributes().get(FAILED_NOTIFICATION_RECORD_ID).getStringValue()); try { FailedNotificationDoc objectToDelete = new FailedNotificationDoc(); objectToDelete.setId(message.getMessageAttributes().get(FAILED_NOTIFICATION_RECORD_ID).getStringValue()); diff --git a/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/repository/SubscriptionRepository.java b/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/repository/SubscriptionRepository.java index 4cecb6736dcc124aa93b31527689c055465699b3..c687640a291f8209304d3b6eab523ec8d79b88cc 100644 --- a/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/repository/SubscriptionRepository.java +++ b/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/repository/SubscriptionRepository.java @@ -26,7 +26,6 @@ import org.opengroup.osdu.notification.provider.aws.security.KmsHelper; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Repository; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; diff --git a/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/security/AwsSecurityConfig.java b/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/security/AwsSecurityConfig.java index d683b65ca3e02ac7d0eefeadca1650923226c45e..e7567d10ee6a2c222b85e689b8b5f171cff8106b 100644 --- a/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/security/AwsSecurityConfig.java +++ b/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/security/AwsSecurityConfig.java @@ -1,15 +1,17 @@ -// Copyright © 2020 Amazon Web Services -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. + /* + Copyright © 2020 Amazon Web Services + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ package org.opengroup.osdu.notification.provider.aws.security; @@ -20,6 +22,7 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +@SuppressWarnings("deprecation") @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class AwsSecurityConfig extends WebSecurityConfigurerAdapter { diff --git a/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/security/KmsHelper.java b/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/security/KmsHelper.java index 73ad6a41679a9b39103b5e21cd6a83398a97f2d1..141c29d9092fe20ce232bb1259a16a87b4c0a9b1 100644 --- a/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/security/KmsHelper.java +++ b/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/security/KmsHelper.java @@ -67,34 +67,23 @@ public class KmsHelper { throw new AppException(HttpStatus.SC_INTERNAL_SERVER_ERROR, "SSM InternalServerErrorException", e.getErrorMessage()); } - } - - public ByteBuffer encrypt(String plainTextString) { - - EncryptRequest encReq = new EncryptRequest(); encReq.setKeyId(kmsKeyId); encReq.setPlaintext(ByteBuffer.wrap(plainTextString.getBytes())); encReq.setEncryptionContext(Collections.singletonMap("dataPartitionId", dpsHeaders.getPartitionId())); - ByteBuffer ciphertext = kmsClient.encrypt(encReq).getCiphertextBlob(); - return ciphertext; - - - + return kmsClient.encrypt(encReq).getCiphertextBlob(); } + public String decrypt(ByteBuffer ciphertext, String dataPartitionId) { - DecryptRequest decReq = new DecryptRequest(); decReq.setCiphertextBlob(ciphertext); decReq.setEncryptionContext(Collections.singletonMap("dataPartitionId", dataPartitionId)); ByteBuffer decrypted = kmsClient.decrypt(decReq).getPlaintext(); - String decryptedStr = new String(decrypted.array()); - return decryptedStr; + return new String(decrypted.array()); } - } diff --git a/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/security/ThreadSignatureService.java b/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/security/ThreadSignatureService.java index bd54591c5550a593a1c8877a3c4420621055f63c..e68d8917eb4379ae4366206f84e43d102077a624 100644 --- a/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/security/ThreadSignatureService.java +++ b/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/security/ThreadSignatureService.java @@ -14,8 +14,14 @@ package org.opengroup.osdu.notification.provider.aws.security; -import com.google.common.base.Strings; -import com.google.gson.Gson; +import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; +import java.util.Base64; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import javax.xml.bind.DatatypeConverter; + import org.apache.commons.lang3.StringUtils; import org.opengroup.osdu.core.common.cryptographic.HmacData; import org.opengroup.osdu.core.common.cryptographic.ISignatureService; @@ -23,129 +29,138 @@ import org.opengroup.osdu.core.common.cryptographic.SignatureServiceException; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; -import javax.xml.bind.DatatypeConverter; -import java.nio.charset.StandardCharsets; -import java.security.SecureRandom; -import java.util.Base64; +import com.google.common.base.Strings; +import com.google.gson.Gson; @Component @Primary public class ThreadSignatureService implements ISignatureService { - private static final String HMAC_SHA_256 = "HmacSHA256"; - private static final String DATA_FORMAT = "{\"expireMillisecond\": \"%s\",\"hashMechanism\": \"hmacSHA256\",\"endpointUrl\": \"%s\",\"nonce\": \"%s\"}"; - private static final String NOTIFICATION_SERVICE = "de-notification-service"; - private static final long EXPIRE_DURATION = 30000L; - - private static final String INVALID_SIGNATURE = "Invalid signature"; - private static final String ERROR_GENERATING_SIGNATURE = "Error generating the signature"; - private static final String SIGNATURE_EXPIRED = "Signature is expired"; - private static final String MISSING_HMAC_SIGNATURE = "HMAC signature should not be null or empty"; - private static final String MISSING_SECRET_VALUE = "Secret should not be null or empty"; - private static final String MISSING_ATTRIBUTES_IN_SIGNATURE = "Missing url or nonce or expire time in the signature"; - - - @Override - public String getSignedSignature(String url, String secret) throws SignatureServiceException { - if (Strings.isNullOrEmpty(url) || Strings.isNullOrEmpty(secret)) { - throw new SignatureServiceException(ERROR_GENERATING_SIGNATURE); - } - final long currentTime = System.currentTimeMillis(); - final String expireTime = String.valueOf(currentTime + EXPIRE_DURATION); - final String timeStamp = String.valueOf(currentTime); - try { - String nonce = DatatypeConverter.printHexBinary(generateRandomBytes(16)).toLowerCase(); - String data = String.format(DATA_FORMAT, expireTime, url, nonce); - final byte[] signature = getSignature(secret, nonce, timeStamp, data); - byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8); - String dataBytesEncoded = Base64.getEncoder().encodeToString(dataBytes); - StringBuilder output = new StringBuilder(); - output.append(dataBytesEncoded) - .append(".") - .append(DatatypeConverter.printHexBinary(signature).toLowerCase()); - - return output.toString(); - } catch (Exception ex) { - throw new SignatureServiceException(ERROR_GENERATING_SIGNATURE, ex); - } - } - - @Override - public String getSignedSignature(String url, String secret, String expireTime, String nonce) throws SignatureServiceException { - if (Strings.isNullOrEmpty(url) || Strings.isNullOrEmpty(secret) || !StringUtils.isNumeric(expireTime)) { - throw new SignatureServiceException(ERROR_GENERATING_SIGNATURE); - } - final long expiry = Long.parseLong(expireTime); - if (System.currentTimeMillis() > expiry) { - throw new SignatureServiceException(SIGNATURE_EXPIRED); - } - String timeStamp = String.valueOf(expiry - EXPIRE_DURATION); - String data = String.format(DATA_FORMAT, expireTime, url, nonce); - try { - final byte[] signature = getSignature(secret, nonce, timeStamp, data); - return DatatypeConverter.printHexBinary(signature).toLowerCase(); - } catch (Exception ex) { - throw new SignatureServiceException(ERROR_GENERATING_SIGNATURE, ex); - } - } - - - @Override - public void verifyHmacSignature(String hmac, String secret) throws SignatureServiceException { - if (Strings.isNullOrEmpty(hmac)) { - throw new SignatureServiceException(MISSING_HMAC_SIGNATURE); - } - if (Strings.isNullOrEmpty(secret)) { - throw new SignatureServiceException(MISSING_SECRET_VALUE); - } - String[] tokens = hmac.split("\\."); - if (tokens.length != 2) { - throw new SignatureServiceException(INVALID_SIGNATURE); - } - byte[] dataBytes = Base64.getDecoder().decode(tokens[0]); - String requestSignature = tokens[1]; - - String data = new String(dataBytes, StandardCharsets.UTF_8); - HmacData hmacData = new Gson().fromJson(data, HmacData.class); - String url = hmacData.getEndpointUrl(); - String nonce = hmacData.getNonce(); - String expireTime = hmacData.getExpireMillisecond(); - if (Strings.isNullOrEmpty(url) || Strings.isNullOrEmpty(nonce) || Strings.isNullOrEmpty(expireTime)) { - throw new SignatureServiceException(MISSING_ATTRIBUTES_IN_SIGNATURE); - } - String newSignature = getSignedSignature(url, secret, expireTime, nonce); - if (!requestSignature.equalsIgnoreCase(newSignature)) { - throw new SignatureServiceException(INVALID_SIGNATURE); - } - } - - private byte[] getSignature(String secret, String nonce, String timeStamp, String data) throws Exception { - final byte[] secretBytes = DatatypeConverter.parseHexBinary(secret); - final byte[] nonceBytes = DatatypeConverter.parseHexBinary(nonce); - final byte[] encryptedNonce = computeHmacSha256(nonceBytes, secretBytes); - final byte[] encryptedTimestamp = computeHmacSha256(timeStamp, encryptedNonce); - final byte[] signedKey = computeHmacSha256(NOTIFICATION_SERVICE, encryptedTimestamp); - return computeHmacSha256(data, signedKey); - } - - private byte[] computeHmacSha256(final String data, final byte[] key) throws Exception { - final Mac mac = Mac.getInstance(HMAC_SHA_256); - mac.init(new SecretKeySpec(key, HMAC_SHA_256)); - return mac.doFinal(data.getBytes(StandardCharsets.UTF_8)); - } - - private byte[] computeHmacSha256(final byte[] data, final byte[] key) throws Exception { - final Mac mac = Mac.getInstance(HMAC_SHA_256); - mac.init(new SecretKeySpec(key, HMAC_SHA_256)); - return mac.doFinal(data); - } - - private byte[] generateRandomBytes(final int size) { - final byte[] key = new byte[size]; - SecureRandom secureRandom = new SecureRandom(); - secureRandom.nextBytes(key); - return key; - } + private static final String HMAC_SHA_256 = "HmacSHA256"; + private static final String DATA_FORMAT = "{\"expireMillisecond\": \"%s\",\"hashMechanism\": \"hmacSHA256\",\"endpointUrl\": \"%s\",\"nonce\": \"%s\"}"; + private static final String NOTIFICATION_SERVICE = "de-notification-service"; + private static final long EXPIRE_DURATION = 30000L; + + private static final String INVALID_SIGNATURE = "Invalid signature"; + private static final String ERROR_GENERATING_SIGNATURE = "Error generating the signature"; + private static final String SIGNATURE_EXPIRED = "Signature is expired"; + private static final String MISSING_HMAC_SIGNATURE = "HMAC signature should not be null or empty"; + private static final String MISSING_SECRET_VALUE = "Secret should not be null or empty"; + private static final String MISSING_ATTRIBUTES_IN_SIGNATURE = "Missing url or nonce or expire time in the signature"; + + @Override + public String getSignedSignature(String url, String secret) throws SignatureServiceException { + if (Strings.isNullOrEmpty(url) || Strings.isNullOrEmpty(secret)) { + throw new SignatureServiceException(ERROR_GENERATING_SIGNATURE); + } + final long currentTime = System.currentTimeMillis(); + final String expireTime = String.valueOf(currentTime + EXPIRE_DURATION); + final String timeStamp = String.valueOf(currentTime); + try { + String nonce = DatatypeConverter.printHexBinary(generateRandomBytes(16)).toLowerCase(); + String data = String.format(DATA_FORMAT, expireTime, url, nonce); + final byte[] signature = getSignature(secret, nonce, timeStamp, data); + byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8); + String dataBytesEncoded = Base64.getEncoder().encodeToString(dataBytes); + StringBuilder output = new StringBuilder(); + output.append(dataBytesEncoded).append(".") + .append(DatatypeConverter.printHexBinary(signature).toLowerCase()); + + return output.toString(); + } catch (Exception ex) { + throw new SignatureServiceException(ERROR_GENERATING_SIGNATURE, ex); + } + } + + @Override + public String getSignedSignature(String url, String secret, String expireTime, String nonce) + throws SignatureServiceException { + if (Strings.isNullOrEmpty(url) || Strings.isNullOrEmpty(secret) || !StringUtils.isNumeric(expireTime)) { + throw new SignatureServiceException(ERROR_GENERATING_SIGNATURE); + } + final long expiry = Long.parseLong(expireTime); + if (System.currentTimeMillis() > expiry) { + throw new SignatureServiceException(SIGNATURE_EXPIRED); + } + String timeStamp = String.valueOf(expiry - EXPIRE_DURATION); + String data = String.format(DATA_FORMAT, expireTime, url, nonce); + try { + final byte[] signature = getSignature(secret, nonce, timeStamp, data); + return DatatypeConverter.printHexBinary(signature).toLowerCase(); + } catch (Exception ex) { + throw new SignatureServiceException(ERROR_GENERATING_SIGNATURE, ex); + } + } + + @Override + public void verifyHmacSignature(String hmac, String secret) throws SignatureServiceException { + if (Strings.isNullOrEmpty(hmac)) { + throw new SignatureServiceException(MISSING_HMAC_SIGNATURE); + } + if (Strings.isNullOrEmpty(secret)) { + throw new SignatureServiceException(MISSING_SECRET_VALUE); + } + String[] tokens = hmac.split("\\."); + if (tokens.length != 2) { + throw new SignatureServiceException(INVALID_SIGNATURE); + } + byte[] dataBytes = Base64.getDecoder().decode(tokens[0]); + String requestSignature = tokens[1]; + + String data = new String(dataBytes, StandardCharsets.UTF_8); + HmacData hmacData = new Gson().fromJson(data, HmacData.class); + String url = hmacData.getEndpointUrl(); + String nonce = hmacData.getNonce(); + String expireTime = hmacData.getExpireMillisecond(); + if (Strings.isNullOrEmpty(url) || Strings.isNullOrEmpty(nonce) || Strings.isNullOrEmpty(expireTime)) { + throw new SignatureServiceException(MISSING_ATTRIBUTES_IN_SIGNATURE); + } + String newSignature = getSignedSignature(url, secret, expireTime, nonce); + if (!requestSignature.equalsIgnoreCase(newSignature)) { + throw new SignatureServiceException(INVALID_SIGNATURE); + } + } + + private byte[] getSignature(String secret, String nonce, String timeStamp, String data) + throws SignatureServiceException { + try { + final byte[] secretBytes = DatatypeConverter.parseHexBinary(secret); + final byte[] nonceBytes = DatatypeConverter.parseHexBinary(nonce); + final byte[] encryptedNonce = computeHmacSha256(nonceBytes, secretBytes); + final byte[] encryptedTimestamp = computeHmacSha256(timeStamp, encryptedNonce); + final byte[] signedKey = computeHmacSha256(NOTIFICATION_SERVICE, encryptedTimestamp); + return computeHmacSha256(data, signedKey); + } catch (Exception ex) { + throw new SignatureServiceException(ERROR_GENERATING_SIGNATURE, ex); + } + } + + private byte[] computeHmacSha256(final String data, final byte[] key) throws SignatureServiceException { + try { + final Mac mac = Mac.getInstance(HMAC_SHA_256); + mac.init(new SecretKeySpec(key, HMAC_SHA_256)); + + return mac.doFinal(data.getBytes(StandardCharsets.UTF_8)); + } catch (Exception ex) { + throw new SignatureServiceException(ERROR_GENERATING_SIGNATURE, ex); + } + } + + private byte[] computeHmacSha256(final byte[] data, final byte[] key) throws SignatureServiceException { + try { + final Mac mac = Mac.getInstance(HMAC_SHA_256); + mac.init(new SecretKeySpec(key, HMAC_SHA_256)); + + return mac.doFinal(data); + } catch (Exception ex) { + throw new SignatureServiceException(ERROR_GENERATING_SIGNATURE, ex); + } + } + + private byte[] generateRandomBytes(final int size) { + final byte[] key = new byte[size]; + SecureRandom secureRandom = new SecureRandom(); + secureRandom.nextBytes(key); + return key; + } } diff --git a/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/utils/AwsCognitoClient.java b/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/utils/AwsCognitoClient.java index a5975d3c94ce71e6422210f69b030c4161a22525..92555ea97744603a22791e3d05b431cf38fb67b8 100644 --- a/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/utils/AwsCognitoClient.java +++ b/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/utils/AwsCognitoClient.java @@ -17,7 +17,6 @@ package org.opengroup.osdu.notification.provider.aws.utils; import com.amazonaws.services.cognitoidp.AWSCognitoIdentityProvider; import com.amazonaws.services.cognitoidp.AWSCognitoIdentityProviderClientBuilder; import com.amazonaws.services.cognitoidp.model.AdminSetUserPasswordRequest; -import com.amazonaws.services.cognitoidp.model.AdminSetUserPasswordResult; import com.amazonaws.services.cognitoidp.model.InitiateAuthRequest; import com.amazonaws.services.cognitoidp.model.InitiateAuthResult; import org.opengroup.osdu.core.aws.iam.IAMConfig; @@ -28,12 +27,8 @@ import java.util.Map; public class AwsCognitoClient { // Parameter value locations - private final static String USERNAME_PARAM = "USERNAME"; - private final static String PASSWORD_PARAM = "PASSWORD"; - private final static String COGNITO_CLIENT_ID_PROPERTY = "AWS_COGNITO_CLIENT_ID"; - private final static String COGNITO_AUTH_FLOW_PROPERTY = "AWS_COGNITO_AUTH_FLOW"; - private final static String COGNITO_AUTH_PARAMS_USER_PROPERTY = "AWS_COGNITO_AUTH_PARAMS_USER"; - private final static String COGNITO_AUTH_PARAMS_PASSWORD_PROPERTY = "AWS_COGNITO_AUTH_PARAMS_PASSWORD"; + private static final String USERNAME_PARAM = "USERNAME"; + private static final String PASSWORD_PARAM = "PASSWORD"; String awsCognitoClientId; @@ -75,7 +70,7 @@ public class AwsCognitoClient { return provider; } - public static AWSCognitoIdentityProvider generateCognitoClient(String region) + public AWSCognitoIdentityProvider generateCognitoClient(String region) { if (System.getenv("AWS_COGNITO_REGION") != null) { region = System.getenv("AWS_COGNITO_REGION"); @@ -91,7 +86,6 @@ public class AwsCognitoClient { .withPassword(password) .withPermanent(true) .withUserPoolId(userPoolId); - AdminSetUserPasswordResult result = this.provider.adminSetUserPassword(request); - + this.provider.adminSetUserPassword(request); } } diff --git a/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/utils/SQSUtils.java b/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/utils/SQSUtils.java index c55b2400e4cd3fde9c3823ad44a578b53743f4b7..39f45642cf6938bfb7c3cae6e03b0c3aa4640fd6 100644 --- a/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/utils/SQSUtils.java +++ b/provider/notification-aws/src/main/java/org/opengroup/osdu/notification/provider/aws/utils/SQSUtils.java @@ -82,11 +82,11 @@ public class SQSUtils { exceptionAttribute.setStringValue("Exception message: missing dataPartitionId"); messageAttributes.put("Exception", exceptionAttribute); - SendMessageRequest send_msg_request = new SendMessageRequest() + SendMessageRequest sendMsgRequest = new SendMessageRequest() .withQueueUrl(deadLetterQueueUrl) .withMessageBody(message.getBody()) .withMessageAttributes(messageAttributes); - return sqsClient.sendMessage(send_msg_request); + return sqsClient.sendMessage(sendMsgRequest); } } \ No newline at end of file diff --git a/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/AwsAppPropertiesTest.java b/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/AwsAppPropertiesTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b5533616fabb8df7814522ab43fcf70f6b5f7f2b --- /dev/null +++ b/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/AwsAppPropertiesTest.java @@ -0,0 +1,37 @@ +package org.opengroup.osdu.notification.provider.aws; + +import static org.junit.Assert.assertEquals; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.junit.MockitoJUnitRunner; +import org.opengroup.osdu.notification.provider.aws.impl.AwsAppProperties; +import org.powermock.reflect.Whitebox; + + +@RunWith(MockitoJUnitRunner.class) +public class AwsAppPropertiesTest { + + @InjectMocks + AwsAppProperties awsAppProperties; + + @Before + public void initTest() { + Whitebox.setInternalState(awsAppProperties, "authorizeAPI", "authorizeAPI"); + Whitebox.setInternalState(awsAppProperties, "registerAPI", "registerAPI"); + } + + @Test + public void getAuthorizeAPIReturnsAuthorizeAPI() { + String authorizeAPI = "authorizeAPI"; + assertEquals(authorizeAPI, awsAppProperties.getAuthorizeAPI()); + } + + @Test + public void getRegisterAPIReturnsAuthorizeAPI() { + String registerAPI = "registerAPI"; + assertEquals(registerAPI, awsAppProperties.getRegisterAPI()); + } +} diff --git a/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/AwsGoogleServiceAccountImplTest.java b/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/AwsGoogleServiceAccountImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f7e17b0bf99d5cbb287fc5713bcda16d2cf281f1 --- /dev/null +++ b/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/AwsGoogleServiceAccountImplTest.java @@ -0,0 +1,20 @@ +package org.opengroup.osdu.notification.provider.aws; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.junit.MockitoJUnitRunner; +import org.opengroup.osdu.notification.provider.aws.impl.AwsGoogleServiceAccountImpl; + +@RunWith(MockitoJUnitRunner.class) +public class AwsGoogleServiceAccountImplTest { + + @InjectMocks + AwsGoogleServiceAccountImpl awsGoogleServiceAccountImpl; + + + @Test(expected = UnsupportedOperationException.class) + public void getIdTokenThorwsNotImplementedException() { + awsGoogleServiceAccountImpl.getIdToken("keyString", "audience"); + } +} diff --git a/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/AwsPubSubHandshakeHandlerTest.java b/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/AwsPubSubHandshakeHandlerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d48cbef2d0f95f9bf8a1a9ee240522c7c0d9fc01 --- /dev/null +++ b/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/AwsPubSubHandshakeHandlerTest.java @@ -0,0 +1,23 @@ +package org.opengroup.osdu.notification.provider.aws; + +import static org.junit.Assert.assertNull; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.junit.MockitoJUnitRunner; +import org.opengroup.osdu.notification.provider.aws.impl.AwsPubSubHandshakeHandler; + + +@RunWith(MockitoJUnitRunner.class) +public class AwsPubSubHandshakeHandlerTest { + + @InjectMocks + AwsPubSubHandshakeHandler AwsPubSubHandshakeHandler; + + + @Test + public void getIdTokenReturnsNull() { + assertNull( AwsPubSubHandshakeHandler.getHandshakeResponse()); + } +} diff --git a/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/AwsPubsubRequestBodyExtractorTest.java b/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/AwsPubsubRequestBodyExtractorTest.java index 021cc17d22803ea24f0cdeec17bea522823d3614..b43a19ae21c74a0ae1f3f4c7369a95939caf9dea 100644 --- a/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/AwsPubsubRequestBodyExtractorTest.java +++ b/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/AwsPubsubRequestBodyExtractorTest.java @@ -25,7 +25,7 @@ import org.mockito.junit.MockitoJUnitRunner; import org.opengroup.osdu.core.common.logging.JaxRsDpsLog; - +import org.opengroup.osdu.core.common.model.http.AppException; import org.opengroup.osdu.core.common.model.http.DpsHeaders; import org.opengroup.osdu.notification.provider.aws.impl.AwsPubsubRequestBodyExtractor; @@ -38,16 +38,12 @@ import java.io.StringReader; import java.util.Map; - - @RunWith(MockitoJUnitRunner.class) public class AwsPubsubRequestBodyExtractorTest { @Mock private DpsHeaders dpsHeaders; - - @Mock private JaxRsDpsLog logger; @@ -58,10 +54,7 @@ public class AwsPubsubRequestBodyExtractorTest { private AwsPubsubRequestBodyExtractor service; @Before - public void init() { - - - } + public void init() { } @Test @@ -87,9 +80,6 @@ public class AwsPubsubRequestBodyExtractorTest { String receivedData = service.extractDataFromRequestBody(); Assert.assertEquals(expectedData,receivedData); - - - } @@ -116,11 +106,69 @@ public class AwsPubsubRequestBodyExtractorTest { String receivedData = service.extractNotificationIdFromRequestBody(); Assert.assertEquals(expectedData,receivedData); + } + + @Test(expected = AppException.class) + public void extractNotificationIdFromRequestBodyThrowsAppExceptionOnNullSubscription() throws IOException, AppException { + String stringRequest = "{\n" + + "\t\"Type\": \"Notification\",\n" + + "\t\"message\": {\n" + + "\t\"attributes\": {\n" + + "\t\"correlation-id\": \"39137f49-67d6-4001-a6aa-15521ef4f49e\",\n" + + "\t\"data-partition-id\": \"" + TestUtils.getDataPartitionId() + "\"\n" + + "\t},\n" + + "\t\"data\": \"dGVzdERhdGE=\",\n" + + "\t\"messageId\": \"136969346945\"\n" + + "\t}\n" + + "}\n"; + BufferedReader reader = new BufferedReader(new StringReader(stringRequest)); + Mockito.when(request.getReader()).thenReturn(reader); + service = new AwsPubsubRequestBodyExtractor(request); + service.extractNotificationIdFromRequestBody(); + } + + @Test(expected = AppException.class) + public void extractAttributesFromRequestBodyThrowsAppExceptionOnNullAttributes() throws IOException, AppException { + String stringRequest = "{\n" + + "\t\"Type\": \"Notification\",\n" + + "\t\"message\": {\n" + + "\t\"attributes\": {\n" + + "\t},\n" + + "\t\"data\": \"dGVzdERhdGE=\",\n" + + "\t\"messageId\": \"136969346945\"\n" + + "\t},\n" + + "\t\"subscription\": \""+ "de12345" +"\"\n" + + "}\n"; + BufferedReader reader = new BufferedReader(new StringReader(stringRequest)); + Mockito.when(request.getReader()).thenReturn(reader); + service = new AwsPubsubRequestBodyExtractor(request); + service.extractAttributesFromRequestBody(); } + + + @Test(expected = AppException.class) + public void extractAttributesFromRequestBodyThrowsAppExceptionOnNullDataPartitionId() throws IOException, AppException { + String stringRequest = "{\n" + + "\t\"Type\": \"Notification\",\n" + + "\t\"message\": {\n" + + "\t\"attributes\": {\n" + + "\t\"correlation-id\": \"39137f49-67d6-4001-a6aa-15521ef4f49e\"\n" + + "\t},\n" + + "\t\"data\": \"dGVzdERhdGE=\",\n" + + "\t\"messageId\": \"136969346945\"\n" + + "\t},\n" + + "\t\"subscription\": \""+ "de12345" +"\"\n" + + "}\n"; + BufferedReader reader = new BufferedReader(new StringReader(stringRequest)); + Mockito.when(request.getReader()).thenReturn(reader); + service = new AwsPubsubRequestBodyExtractor(request); + service.extractAttributesFromRequestBody(); + } + @Test public void should_returnValidAttributes_FromRequestBody() throws IOException { String stringRequest = "{\n" + @@ -138,8 +186,6 @@ public class AwsPubsubRequestBodyExtractorTest { BufferedReader reader = new BufferedReader(new StringReader(stringRequest)); Mockito.when(request.getReader()).thenReturn(reader); - String expectedData = "de12345"; - service = new AwsPubsubRequestBodyExtractor(request); // Act Map<String, String> receivedAttributes = service.extractAttributesFromRequestBody(); @@ -147,8 +193,5 @@ public class AwsPubsubRequestBodyExtractorTest { // Asset Assert.assertEquals("39137f49-123-456", receivedAttributes.get("correlation-id")); Assert.assertEquals("opendes", receivedAttributes.get("data-partition-id")); - - - } } diff --git a/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/AwsServiceAccountValidatorTest.java b/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/AwsServiceAccountValidatorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5649ab02c19c53adb92df25f6f61afc52e7bb138 --- /dev/null +++ b/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/AwsServiceAccountValidatorTest.java @@ -0,0 +1,26 @@ +package org.opengroup.osdu.notification.provider.aws; + +import static org.junit.Assert.assertFalse; + +import org.junit.runner.RunWith; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.junit.MockitoJUnitRunner; +import org.opengroup.osdu.notification.provider.aws.impl.AwsServiceAccountValidator; + +@RunWith(MockitoJUnitRunner.class) +public class AwsServiceAccountValidatorTest { + + @InjectMocks + AwsServiceAccountValidator awsServiceAccountValidator; + + @Test + public void awsServiceAccountValidatorReturnsFalse() { + assertFalse(awsServiceAccountValidator.isValidPublisherServiceAccount("jwt")); + } + + @Test + public void isValidServiceAccountReturnsFalse() { + assertFalse(awsServiceAccountValidator.isValidServiceAccount("jwt", "userIdentity", "audiences")); + } +} diff --git a/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/queue/impl/NotificationQueueServiceImplTest.java b/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/queue/impl/NotificationQueueServiceImplTest.java index 0f9bbedf0a31ef19e6383da7e96ab800712576b7..585556babd565ae99c9c42cf6688e39a3158f5a9 100644 --- a/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/queue/impl/NotificationQueueServiceImplTest.java +++ b/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/queue/impl/NotificationQueueServiceImplTest.java @@ -16,6 +16,8 @@ package org.opengroup.osdu.notification.provider.aws.queue.impl; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.withSettings; @@ -39,7 +41,9 @@ import org.mockito.junit.MockitoJUnitRunner; import org.opengroup.osdu.core.aws.dynamodb.DynamoDBQueryHelperFactory; import org.opengroup.osdu.core.aws.dynamodb.DynamoDBQueryHelperV2; import org.opengroup.osdu.core.aws.ssm.K8sLocalParameterProvider; +import org.opengroup.osdu.core.aws.sns.PublishRequestBuilder; import org.opengroup.osdu.core.common.http.HttpResponse; +import org.opengroup.osdu.core.common.model.http.DpsHeaders; import org.opengroup.osdu.core.common.model.notification.Subscription; import org.opengroup.osdu.notification.provider.aws.model.FailedNotificationDoc; import org.opengroup.osdu.notification.provider.aws.queue.impl.NotificationQueueServiceImpl; @@ -75,6 +79,8 @@ public class NotificationQueueServiceImplTest { private static MockedConstruction<K8sLocalParameterProvider> mockedConstruction; + private static final String DEFAULT_MESSAGE_TOPIC = "some-topic"; + @BeforeClass public static void setup() { mockedConstruction = Mockito.mockConstruction(K8sLocalParameterProvider.class, @@ -98,7 +104,7 @@ public class NotificationQueueServiceImplTest { Subscription subscription1 = new Subscription(); subscription1.setName("My listener"); - subscription1.setTopic("records-changed"); + subscription1.setTopic(DEFAULT_MESSAGE_TOPIC); subscription1.setPushEndpoint("/api/test/subscriber"); subscription1.setNotificationId("de-859ea6a6-eefa-4e30-ba38-13f8cd71360a"); subscription1.setId("testSubscription1Id"); @@ -123,6 +129,18 @@ public class NotificationQueueServiceImplTest { assertEquals("testMessage1", responseMessageList.get(0).getMessageId()); } + @Test + public void processNotificationMessage_withNoSubuscriberForTopic() throws Exception { + List<FailedNotificationDoc> list = new ArrayList<>(); + when(dynamoDBQueryHelper.queryByGSI(any(), any())).thenReturn(mock(PaginatedQueryList.class, withSettings().defaultAnswer(new ForwardsInvocations(list)))); + + List<Message> messageList = Arrays.asList(createMessage("testMessage1", "testDataPartition", "non-subscribed-topic")); + + List<Message> responseMessageList = notificationQueueService.processNotificationMessages(messageList); + + verify(notificationHandler, never()).notifySubscriber(any(), any(), any()); + } + @Test public void processNotificationMessage_noSubscribers_success() { when(subscriptionRepository.getAllSubscriptionsByDataPartition(anyString())).thenReturn(Collections.emptyList()); @@ -233,13 +251,19 @@ public class NotificationQueueServiceImplTest { assertEquals("testMessage2", responseMessageList.get(1).getMessageId()); } - private Message createMessage(String messageId, String dataPartitionId) { + private void addMessageAttribute(Message message, String key, String value) { + message.getMessageAttributes().put(key, new MessageAttributeValue().withDataType("String").withStringValue(value)); + } + + private Message createMessage(String messageId, String dataPartitionId, String topicName) { Message message = new Message(); message.setMessageId(messageId); - MessageAttributeValue dataPartitionIdAttribute = new MessageAttributeValue() - .withDataType("String"); - dataPartitionIdAttribute.setStringValue(dataPartitionId); - message.getMessageAttributes().put("data-partition-id", dataPartitionIdAttribute); + addMessageAttribute(message, DpsHeaders.DATA_PARTITION_ID, dataPartitionId); + addMessageAttribute(message, PublishRequestBuilder.OSDU_TOPIC_ATTRIBUTE_NAME, topicName); return message; } + + private Message createMessage(String messageId, String dataPartitionId) { + return createMessage(messageId, dataPartitionId, DEFAULT_MESSAGE_TOPIC); + } } diff --git a/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/security/KmsHelperTest.java b/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/security/KmsHelperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0eb25f1de6e315b0e4d0fce4fc8d13373de8d951 --- /dev/null +++ b/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/security/KmsHelperTest.java @@ -0,0 +1,72 @@ +package org.opengroup.osdu.notification.provider.aws.security; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import java.nio.ByteBuffer; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedConstruction; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.opengroup.osdu.core.aws.ssm.K8sLocalParameterProvider; +import org.opengroup.osdu.core.common.model.http.DpsHeaders; +import org.powermock.reflect.Whitebox; + +import com.amazonaws.SdkClientException; +import com.amazonaws.services.kms.AWSKMS;; + +@RunWith(MockitoJUnitRunner.class) +public class KmsHelperTest { + + @InjectMocks + private KmsHelper kmsHelper; + + @Mock + private DpsHeaders dpsHeaders; + + @Mock + private ByteBuffer ciphertext; + + private static MockedConstruction<K8sLocalParameterProvider> mockedConstruction; + + + @BeforeClass + public static void setup() { + mockedConstruction = Mockito.mockConstruction(K8sLocalParameterProvider.class, + (mock, context) -> { + //implement initializer for mock. Set return value for object A mock methods + when(mock.getParameterAsString("notification-sqs-url")).thenReturn( + "test-sqs-url"); + }); + } + + @AfterClass + public static void close(){ + mockedConstruction.close(); + } + + @Before + public void initTest() { + Whitebox.setInternalState(kmsHelper, "amazonRegion", "us-east-1"); + Whitebox.setInternalState(kmsHelper, "kmsEndpoint", "aws.kms.endpoint"); + } + + @Test(expected = SdkClientException.class) + public void encrypt_EncryptsData() { + kmsHelper.init(); + kmsHelper.encrypt("plain text"); + } + + @Test(expected = SdkClientException.class ) + public void decryptThorwsFormMockedKmsClient() { + kmsHelper.init(); + kmsHelper.decrypt(ciphertext, "dataPartitionId"); + } +} diff --git a/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/security/ThreadSignatureServiceTest.java b/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/security/ThreadSignatureServiceTest.java index fe7d7e5c15c0851183643bfd6ce35b7cca029f11..dc7551e1d66bf4776917268133ffad2999761179 100644 --- a/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/security/ThreadSignatureServiceTest.java +++ b/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/security/ThreadSignatureServiceTest.java @@ -16,6 +16,10 @@ package org.opengroup.osdu.notification.provider.aws.security; import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.Mac; + import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; @@ -24,8 +28,7 @@ import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; import org.opengroup.osdu.core.common.cryptographic.SignatureServiceException; -import javax.crypto.Mac; -import java.security.NoSuchAlgorithmException; +import com.google.gson.JsonSyntaxException; @RunWith(MockitoJUnitRunner.class) public class ThreadSignatureServiceTest { @@ -106,4 +109,8 @@ public class ThreadSignatureServiceTest { threadSignatureService.verifyHmacSignature("invalidHmac", SECRET); } + @Test(expected = JsonSyntaxException.class) + public void verifyHmacSignature_() throws SignatureServiceException { + threadSignatureService.verifyHmacSignature(HMAC, SECRET); + } } diff --git a/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/utils/AwsCognitoClientTest.java b/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/utils/AwsCognitoClientTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2a1ac4996ce68d931bbc1c0963fd6794095fe808 --- /dev/null +++ b/provider/notification-aws/src/test/java/org/opengroup/osdu/notification/provider/aws/utils/AwsCognitoClientTest.java @@ -0,0 +1,72 @@ +package org.opengroup.osdu.notification.provider.aws.utils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import com.amazonaws.services.cognitoidp.AWSCognitoIdentityProvider; +import com.amazonaws.services.sqs.AmazonSQS; + +import io.jsonwebtoken.lang.Assert; + +@RunWith(MockitoJUnitRunner.class) +public class AwsCognitoClientTest { + + private AwsCognitoClient awsCognitoClient; + + @Mock + private AmazonSQS sqsClient; + + @Mock + private AWSCognitoIdentityProvider provider; + + @Before + public void setUp() { + awsCognitoClient = new AwsCognitoClient("region", "awsCognitoClientId", "awsCognitoAuthFlow", + "awsCognitoAuthParamsUser", "awsCognitoAuthParamsPassword"); + } + + @Test + public void constuctorCreatesObject() { + Assert.isInstanceOf(AwsCognitoClient.class, awsCognitoClient); + assertNotNull(awsCognitoClient); + } + + @Test + public void getToken() { + awsCognitoClient.provider = provider; + when(provider.initiateAuth(any())).thenReturn(null); + String expected = ""; + String actual = awsCognitoClient.getToken("username", "password", "tokenType"); + assertEquals(expected, actual); + } + + @Test + public void getProvider() { + AWSCognitoIdentityProvider object = awsCognitoClient.getProvider(); + assertTrue(object instanceof AWSCognitoIdentityProvider); + } + + @Test + public void generateCognitoClient() { + AWSCognitoIdentityProvider object = awsCognitoClient.generateCognitoClient("us-east-1"); + assertTrue(object instanceof AWSCognitoIdentityProvider); + } + + //the code on production side seems useless. hence useless test here just for coverage. + @Test + public void setPassword() { + awsCognitoClient.provider = provider; + when(provider.adminSetUserPassword(any())).thenReturn(null); + awsCognitoClient.setPassword("username", "password", "user-pool-id"); + assertNotNull(awsCognitoClient); + } +} diff --git a/testing/notification-test-aws/src/test/java/org/opengroup/osdu/notification/subscriptions/TestNotificationsEndpoint.java b/testing/notification-test-aws/src/test/java/org/opengroup/osdu/notification/subscriptions/TestNotificationsEndpoint.java index 1809604630695c38de96bfc095eee2d7a7f1b75a..e5b9b32ce21bdb80172070c230776c5f2a645fb6 100644 --- a/testing/notification-test-aws/src/test/java/org/opengroup/osdu/notification/subscriptions/TestNotificationsEndpoint.java +++ b/testing/notification-test-aws/src/test/java/org/opengroup/osdu/notification/subscriptions/TestNotificationsEndpoint.java @@ -15,6 +15,7 @@ import org.opengroup.osdu.core.common.notification.SubscriptionFactory; import org.opengroup.osdu.notification.util.*; import java.util.Arrays; +import java.util.Base64; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -44,8 +45,32 @@ public class TestNotificationsEndpoint extends TestBase { } @Before - public void setup() { + public void setup() throws Exception { this.testUtils = new AwsTestUtils(); + Map<String, String> headers = new HashMap<>(); + headers.put(DpsHeaders.DATA_PARTITION_ID, TestUtils.getOsduTenant()); + headers.put(DpsHeaders.AUTHORIZATION, testUtils.getOpsToken()); + //hardcoding user here for 200 response tests. This is just initializing the subscription creation + headers.put("x-user-id", AwsConfig.getAWSCognitoUser()); + + DpsHeaders dpsHeaders = DpsHeaders.createFromMap(headers); + awssubscriptionService = awsfactory.create(dpsHeaders); + + try { + // Ensure that there is no previous subscription already registered + // Sometimes this can happen if previous tests didn't exit properly + + // Sadly there is no method to delete a subscription from just name, so have + // to manually build the ID like this. + String subscriptionId = String.format("%s%s%s", + Config.Instance().Topic, + PARTITION_TEST, + Config.Instance().HMACPushUrl); + String encodedSubscriptionId = Base64.getEncoder().encodeToString(subscriptionId.getBytes()); + + awssubscriptionService.delete(encodedSubscriptionId); + } + catch (Exception e) {} } @After @@ -57,25 +82,11 @@ public class TestNotificationsEndpoint extends TestBase { } private void createResourceForTestParition() throws Exception { - Map<String, String> headers = new HashMap<>(); - headers.put(DpsHeaders.DATA_PARTITION_ID, TestUtils.getOsduTenant()); - headers.put(DpsHeaders.AUTHORIZATION, testUtils.getOpsToken()); - //hardcoding user here for 200 response tests. This is just initializing the subscription creation - headers.put("x-user-id", AwsConfig.getAWSCognitoUser()); - DpsHeaders dpsHeaders = DpsHeaders.createFromMap(headers); - awssubscriptionService = awsfactory.create(dpsHeaders); - - Map<String,String> h = dpsHeaders.getHeaders(); - System.out.println(h); - - //Create a new subscription to pub/sub Subscription subscription = new Subscription(); subscription.setName("Subscription-test-for-notification"); subscription.setDescription("Subscription with test Partition for fetching notifications"); subscription.setTopic(Config.Instance().Topic); - //This seems to be a bug. Don't need to add the string - //subscription.setPushEndpoint(Config.Instance().HMACPushUrl + "hmac-integration-test"); subscription.setPushEndpoint(Config.Instance().HMACPushUrl); HmacSecret secret = new HmacSecret(); secret.setValue(Config.Instance().hmacSecretValue);