Skip to content

Commit

Permalink
[SELC-5887] added test for user-sdk-event
Browse files Browse the repository at this point in the history
  • Loading branch information
flaminiaScarciofolo committed Oct 30, 2024
1 parent c7b5eaa commit f1ca3b0
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public class UserInstitutionCdcService {
public static final String USERS_FIELD_LIST_WITHOUT_FISCAL_CODE = "name,familyName,email,workContacts";
private static final String PROD_FD = "prod-fd";
private static final String PROD_FD_GARANTITO = "prod-fd-garantito";
public static final String ERROR_DURING_SUBSCRIBE_COLLECTION_EXCEPTION_MESSAGE = "Error during subscribe collection, exception: {} , message: {}";


private final TelemetryClient telemetryClient;
Expand Down Expand Up @@ -140,26 +141,26 @@ private void initOrderStream(Boolean sendEventsEnabled, Boolean sendFdEventsEnab
publisher.subscribe().with(
this::consumerUserInstitutionRepositoryEvent,
failure -> {
log.error("Error during subscribe collection, exception: {} , message: {}", failure.toString(), failure.getMessage());
log.error(ERROR_DURING_SUBSCRIBE_COLLECTION_EXCEPTION_MESSAGE, failure.toString(), failure.getMessage());
telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(TrackEventInput.builder().exception(failure.getClass().toString()).build()), Map.of(USER_INFO_UPDATE_FAILURE, 1D));
Quarkus.asyncExit();
});

if (sendEventsEnabled) {
if (Boolean.TRUE.equals(sendEventsEnabled)) {
publisher.subscribe().with(
this::consumerToSendScUserEvent,
failure -> {
log.error("Error during subscribe collection, exception: {} , message: {}", failure.toString(), failure.getMessage());
log.error(ERROR_DURING_SUBSCRIBE_COLLECTION_EXCEPTION_MESSAGE, failure.toString(), failure.getMessage());
telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(TrackEventInput.builder().exception(failure.getClass().toString()).build()), Map.of(EVENTS_USER_INSTITUTION_FAILURE, 1D));
Quarkus.asyncExit();
});
}

if (sendFdEventsEnabled) {
if (Boolean.TRUE.equals(sendFdEventsEnabled)) {
publisher.subscribe().with(
this::consumerToSendUserEventForFD,
failure -> {
log.error("Error during subscribe collection, exception: {} , message: {}", failure.toString(), failure.getMessage());
log.error(ERROR_DURING_SUBSCRIBE_COLLECTION_EXCEPTION_MESSAGE, failure.toString(), failure.getMessage());
telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(TrackEventInput.builder().exception(failure.getClass().toString()).build()), Map.of(EVENTS_USER_INSTITUTION_FAILURE, 1D));
Quarkus.asyncExit();
});
Expand Down Expand Up @@ -241,34 +242,34 @@ public void consumerToSendScUserEvent(ChangeStreamDocument<UserInstitution> docu

public void consumerToSendUserEventForFD(ChangeStreamDocument<UserInstitution> document) {

assert document.getFullDocument() != null;
assert document.getDocumentKey() != null;
UserInstitution userInstitutionChanged = document.getFullDocument();

log.info("Starting consumerToSendUserEventForFd ... ");

userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, userInstitutionChanged.getUserId())
.onFailure(this::checkIfIsRetryableException)
.retry().withBackOff(Duration.ofSeconds(retryMinBackOff), Duration.ofSeconds(retryMaxBackOff)).atMost(maxRetry)
.onItem().transformToUni(userResource -> Uni.createFrom().item(UserUtils.retrieveFdProductIfItChanged(userInstitutionChanged.getProducts(), List.of(PROD_FD, PROD_FD_GARANTITO)))
.onItem().ifNotNull().transform(onboardedProduct -> notificationMapper.toFdUserNotificationToSend(userInstitutionChanged, onboardedProduct, userResource, evaluateType(onboardedProduct)))
.onItem().ifNotNull().transformToUni(fdUserNotificationToSend -> {
log.info("Sending message to EventHubFdRestClient ... ");
return eventHubFdRestClient.sendMessage(fdUserNotificationToSend)
.onFailure().retry().withBackOff(Duration.ofSeconds(retryMinBackOff), Duration.ofSeconds(retryMaxBackOff)).atMost(maxRetry)
.onItem().invoke(() -> telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInput(fdUserNotificationToSend)), Map.of(EVENTS_USER_INSTITUTION_PRODUCT_SUCCESS, 1D)))
.onFailure().invoke(() -> telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInput(fdUserNotificationToSend)), Map.of(EVENTS_USER_INSTITUTION_PRODUCT_FAILURE, 1D)));
}
))
.subscribe().with(
result -> {
log.info("SendFdEvents successfully performed from UserInstitution document having id: {}", document.getDocumentKey().toJson());
telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInputByUserInstitution(userInstitutionChanged)), Map.of(EVENTS_USER_INSTITUTION_SUCCESS, 1D));
},
failure -> {
log.error("Error during SendFdEvents from UserInstitution document having id: {} , message: {}", document.getDocumentKey().toJson(), failure.getMessage());
telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInputByUserInstitution(userInstitutionChanged)), Map.of(EVENTS_USER_INSTITUTION_FAILURE, 1D));
});
if (Objects.nonNull(document.getFullDocument()) && Objects.nonNull(document.getDocumentKey()) {
UserInstitution userInstitutionChanged = document.getFullDocument();

log.info("Starting consumerToSendUserEventForFd ... ");

userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, userInstitutionChanged.getUserId())
.onFailure(this::checkIfIsRetryableException)
.retry().withBackOff(Duration.ofSeconds(retryMinBackOff), Duration.ofSeconds(retryMaxBackOff)).atMost(maxRetry)
.onItem().transformToUni(userResource -> Uni.createFrom().item(UserUtils.retrieveFdProductIfItChanged(userInstitutionChanged.getProducts(), List.of(PROD_FD, PROD_FD_GARANTITO)))
.onItem().ifNotNull().transform(onboardedProduct -> notificationMapper.toFdUserNotificationToSend(userInstitutionChanged, onboardedProduct, userResource, evaluateType(onboardedProduct)))
.onItem().ifNotNull().transformToUni(fdUserNotificationToSend -> {
log.info("Sending message to EventHubFdRestClient ... ");
return eventHubFdRestClient.sendMessage(fdUserNotificationToSend)
.onFailure().retry().withBackOff(Duration.ofSeconds(retryMinBackOff), Duration.ofSeconds(retryMaxBackOff)).atMost(maxRetry)
.onItem().invoke(() -> telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInput(fdUserNotificationToSend)), Map.of(EVENTS_USER_INSTITUTION_PRODUCT_SUCCESS, 1D)))
.onFailure().invoke(() -> telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInput(fdUserNotificationToSend)), Map.of(EVENTS_USER_INSTITUTION_PRODUCT_FAILURE, 1D)));
}
))
.subscribe().with(
result -> {
log.info("SendFdEvents successfully performed from UserInstitution document having id: {}", document.getDocumentKey().toJson());
telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInputByUserInstitution(userInstitutionChanged)), Map.of(EVENTS_USER_INSTITUTION_SUCCESS, 1D));
},
failure -> {
log.error("Error during SendFdEvents from UserInstitution document having id: {} , message: {}", document.getDocumentKey().toJson(), failure.getMessage());
telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInputByUserInstitution(userInstitutionChanged)), Map.of(EVENTS_USER_INSTITUTION_FAILURE, 1D));
});
}
}

private NotificationUserType evaluateType(OnboardedProduct onboardedProduct) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
import it.pagopa.selfcare.user.model.OnboardedProduct;
import it.pagopa.selfcare.user.model.TrackEventInput;
import it.pagopa.selfcare.user.model.constants.OnboardedProductState;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
Expand All @@ -16,6 +18,7 @@
import static java.util.Comparator.naturalOrder;
import static java.util.Comparator.nullsLast;

@Slf4j
public class UserUtils {

/**
Expand Down Expand Up @@ -93,40 +96,28 @@ public static String getSASToken(String resourceUri, String keyName, String key)
int week = 60 * 60 * 24 * 7;
String expiry = Long.toString(epoch + week);

String sasToken = null;
try {
String stringToSign = URLEncoder.encode(resourceUri, "UTF-8") + "\n" + expiry;
String signature = getHMAC256(key, stringToSign);
sasToken = "SharedAccessSignature sr=" + URLEncoder.encode(resourceUri, "UTF-8") + "&sig=" +
URLEncoder.encode(signature, "UTF-8") + "&se=" + expiry + "&skn=" + keyName;
} catch (UnsupportedEncodingException e) {

e.printStackTrace();
}

String sasToken;
String stringToSign = URLEncoder.encode(resourceUri, StandardCharsets.UTF_8) + "\n" + expiry;
String signature = getHMAC256(key, stringToSign);
sasToken = "SharedAccessSignature sr=" + URLEncoder.encode(resourceUri, StandardCharsets.UTF_8) + "&sig=" +
URLEncoder.encode(signature, StandardCharsets.UTF_8) + "&se=" + expiry + "&skn=" + keyName;
return sasToken;
}


public static String getHMAC256(String key, String input) {
Mac sha256_HMAC;
Mac sha256HMAC;
String hash = null;
try {
sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes(), "HmacSHA256");
sha256_HMAC.init(secret_key);
sha256HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "HmacSHA256");
sha256HMAC.init(secretKey);
Base64.Encoder encoder = Base64.getEncoder();

hash = new String(encoder.encode(sha256_HMAC.doFinal(input.getBytes("UTF-8"))));
hash = new String(encoder.encode(sha256HMAC.doFinal(input.getBytes(StandardCharsets.UTF_8))));

} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException e) {
log.error("Exception: {}", e.getMessage(), e);
}

return hash;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

@JsonInclude(JsonInclude.Include.NON_NULL)
@Data
@SuppressWarnings("java:S1068")
public class FdUserNotificationToSend {

private String id;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package it.pagopa.selfcare.user.model;

@SuppressWarnings("java:S1068")
public enum NotificationUserType {

ACTIVE_USER,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package it.pagopa.selfcare.user;

import it.pagopa.selfcare.user.model.TrackEventInput;
import it.pagopa.selfcare.user.model.UserGroupNotificationToSend;
import it.pagopa.selfcare.user.model.UserNotificationToSend;
import it.pagopa.selfcare.user.model.UserToNotify;
import it.pagopa.selfcare.user.model.*;
import org.junit.jupiter.api.Test;

import java.time.Instant;
Expand Down Expand Up @@ -34,6 +31,26 @@ void toTrackEventInput_withUserNotification() {
assertEquals("productRole", result.getProductRole());
}

@Test
void toTrackEventInput_withUserFdNotification() {
FdUserNotificationToSend userNotification = new FdUserNotificationToSend();
userNotification.setId("docKey");
UserToNotify user = new UserToNotify();
user.setUserId("userId");
user.setProductRole("productRole");
userNotification.setUser(user);
userNotification.setInstitutionId("institutionId");
userNotification.setProduct("productId");

TrackEventInput result = TrackEventInput.toTrackEventInput(userNotification);

assertEquals("docKey", result.getDocumentKey());
assertEquals("userId", result.getUserId());
assertEquals("institutionId", result.getInstitutionId());
assertEquals("productId", result.getProductId());
assertEquals("productRole", result.getProductRole());
}

@Test
void toTrackEventInputForUserGroup_withUserGroupNotification() {
UserGroupNotificationToSend userGroupNotification = new UserGroupNotificationToSend();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,40 @@ OnboardedProduct dummyOnboardedProduct(String productRole, OnboardedProductState
onboardedProduct.setStatus(state);
return onboardedProduct;
}

@Test
void getSASToken_withValidInputs_shouldReturnValidToken() {
String resourceUri = "https://example.com/resource";
String keyName = "keyName";
String key = "secretKey";

Check failure

Code scanning / CodeQL

Hard-coded credential in API call Critical test

Hard-coded value flows to
sensitive API call
.

String sasToken = UserUtils.getSASToken(resourceUri, keyName, key);

assertNotNull(sasToken);
assertTrue(sasToken.contains("SharedAccessSignature sr="));
assertTrue(sasToken.contains("&sig="));
assertTrue(sasToken.contains("&se="));
assertTrue(sasToken.contains("&skn="));
}

@Test
void getSASToken_withInvalidEncoding_shouldHandleException() {
String resourceUri = "https://example.com/resource";
String keyName = "keyName";
String key = "secretKey";

Check failure

Code scanning / CodeQL

Hard-coded credential in API call Critical test

Hard-coded value flows to
sensitive API call
.

String sasToken = UserUtils.getSASToken(resourceUri, keyName, key);

assertNotNull(sasToken);
}

@Test
void getHMAC256_withValidInputs_shouldReturnValidHash() {
String key = "secretKey";

Check failure

Code scanning / CodeQL

Hard-coded credential in API call Critical test

Hard-coded value flows to
sensitive API call
.
String input = "inputString";

String hash = UserUtils.getHMAC256(key, input);

assertNotNull(hash);
}
}

0 comments on commit f1ca3b0

Please sign in to comment.