From 1bc5cb5005bef82d5a6fd5783102fd5ae3908e79 Mon Sep 17 00:00:00 2001 From: Lubos Racansky Date: Thu, 8 Dec 2022 10:12:41 +0100 Subject: [PATCH] Fix #558: SMS does not respect accept-language --- docs/onboarding/Database-Structure.md | 1 + docs/sql/mysql/onboarding/create-schema.sql | 1 + docs/sql/oracle/onboarding/create-schema.sql | 1 + .../postgresql/onboarding/create-schema.sql | 1 + .../entity/OnboardingProcessEntity.java | 11 ++++++++++ .../IdentityVerificationOtpService.java | 21 +++++++++++++++++-- .../impl/service/OnboardingServiceImpl.java | 17 ++++++++++++++- .../ActivationCleaningServiceTest.sql | 8 +++---- ...rviceTest.testTerminateExpiredOtpCodes.sql | 4 ++-- ...testTerminateExpiredProcessActivations.sql | 10 ++++----- ...stTerminateExpiredProcessVerifications.sql | 10 ++++----- ...viceTest.testTerminateExpiredProcesses.sql | 8 +++---- 12 files changed, 70 insertions(+), 23 deletions(-) diff --git a/docs/onboarding/Database-Structure.md b/docs/onboarding/Database-Structure.md index 70658a4c3..294564c16 100644 --- a/docs/onboarding/Database-Structure.md +++ b/docs/onboarding/Database-Structure.md @@ -46,6 +46,7 @@ Stores onboarding processes created for tracking status of user onboarding. |---|---|---|---| | `id` | `VARCHAR(36)` | `NOT NULL PRIMARY KEY` | Autogenerated record identifier (UUID). | | `identification_data` | `VARCHAR(1024)` | `NOT NULL` | Data sent by the customer as JSON. | +| `custom_data` | `VARCHAR(1024)` | `NOT NULL` | Custom data as JSON. | | `user_id` | `VARCHAR(256)` | | Resolved user identifier. | | `activation_id` | `VARCHAR(36)` | | Identifier of created activation. | | `status` | `VARCHAR(32)` | `NOT NULL` | Status of onboarding process (`ACTIVATION_IN_PROGRESS`, `VERIFICATION_IN_PROGRESS`, `FINISHED`, `FAILED`). | diff --git a/docs/sql/mysql/onboarding/create-schema.sql b/docs/sql/mysql/onboarding/create-schema.sql index 4c8ef6d6d..f687d9f1e 100644 --- a/docs/sql/mysql/onboarding/create-schema.sql +++ b/docs/sql/mysql/onboarding/create-schema.sql @@ -26,6 +26,7 @@ CREATE TABLE es_onboarding_process ( error_detail VARCHAR(256), error_origin VARCHAR(256), error_score INTEGER NOT NULL DEFAULT 0, + custom_data VARCHAR(1024) NOT NULL, timestamp_created DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, timestamp_last_updated DATETIME, timestamp_finished DATETIME, diff --git a/docs/sql/oracle/onboarding/create-schema.sql b/docs/sql/oracle/onboarding/create-schema.sql index cedfa75f6..8810bc8b7 100644 --- a/docs/sql/oracle/onboarding/create-schema.sql +++ b/docs/sql/oracle/onboarding/create-schema.sql @@ -28,6 +28,7 @@ CREATE TABLE ES_ONBOARDING_PROCESS ( ERROR_DETAIL VARCHAR2(256 CHAR), ERROR_ORIGIN VARCHAR2(256 CHAR), ERROR_SCORE INTEGER DEFAULT 0 NOT NULL, + CUSTOM_DATA VARCHAR2(1024 CHAR) NOT NULL, TIMESTAMP_CREATED TIMESTAMP(6) NOT NULL, TIMESTAMP_LAST_UPDATED TIMESTAMP(6), TIMESTAMP_FINISHED TIMESTAMP(6), diff --git a/docs/sql/postgresql/onboarding/create-schema.sql b/docs/sql/postgresql/onboarding/create-schema.sql index fcc6c66fd..72ad6db45 100644 --- a/docs/sql/postgresql/onboarding/create-schema.sql +++ b/docs/sql/postgresql/onboarding/create-schema.sql @@ -32,6 +32,7 @@ CREATE TABLE es_onboarding_process ( error_detail VARCHAR(256), error_origin VARCHAR(256), error_score INTEGER NOT NULL DEFAULT 0, + custom_data VARCHAR(1024) NOT NULL, timestamp_created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, timestamp_last_updated TIMESTAMP, timestamp_finished TIMESTAMP, diff --git a/enrollment-server-onboarding-common/src/main/java/com/wultra/app/onboardingserver/common/database/entity/OnboardingProcessEntity.java b/enrollment-server-onboarding-common/src/main/java/com/wultra/app/onboardingserver/common/database/entity/OnboardingProcessEntity.java index abbbedc07..921cf5543 100644 --- a/enrollment-server-onboarding-common/src/main/java/com/wultra/app/onboardingserver/common/database/entity/OnboardingProcessEntity.java +++ b/enrollment-server-onboarding-common/src/main/java/com/wultra/app/onboardingserver/common/database/entity/OnboardingProcessEntity.java @@ -57,6 +57,11 @@ public class OnboardingProcessEntity implements Serializable { public static final String ERROR_MAX_PROCESS_ERROR_SCORE_EXCEEDED = "maxProcessErrorScoreExceeded"; public static final String ERROR_USER_LOOKUP = "userLookupFailed"; + /** + * Key for {@link #customData} storing locale. + */ + public static final String CUSTOM_DATA_LOCALE = "locale"; + @Id @GeneratedValue(generator = "uuid") @GenericGenerator(name = "uuid", strategy = "uuid2") @@ -66,6 +71,12 @@ public class OnboardingProcessEntity implements Serializable { @Column(name = "identification_data", nullable = false) private String identificationData; + /** + * Json with custom data such as preferred locale. + */ + @Column(name = "custom_data", nullable = false) + private String customData = "{}"; + @Column(name = "user_id") private String userId; diff --git a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/IdentityVerificationOtpService.java b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/IdentityVerificationOtpService.java index a404d1cf6..02a7b058d 100644 --- a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/IdentityVerificationOtpService.java +++ b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/IdentityVerificationOtpService.java @@ -17,6 +17,8 @@ */ package com.wultra.app.onboardingserver.impl.service; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.wultra.app.enrollmentserver.api.model.onboarding.response.OtpVerifyResponse; import com.wultra.app.enrollmentserver.model.enumeration.*; import com.wultra.app.enrollmentserver.model.integration.OwnerId; @@ -37,11 +39,12 @@ import com.wultra.app.onboardingserver.provider.model.request.SendOtpCodeRequest; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Date; +import java.util.Locale; +import java.util.Map; import static com.wultra.app.enrollmentserver.model.enumeration.IdentityVerificationPhase.OTP_VERIFICATION; import static com.wultra.app.enrollmentserver.model.enumeration.IdentityVerificationPhase.PRESENCE_CHECK; @@ -73,6 +76,8 @@ public class IdentityVerificationOtpService { private final AuditService auditService; + private final ObjectMapper mapper = new ObjectMapper(); + /** * Service constructor. * @@ -263,7 +268,7 @@ private void sendOtpCode(String processId, boolean isResend) throws OnboardingPr .userId(userId) .otpCode(otpCode) .resend(isResend) - .locale(LocaleContextHolder.getLocale()) + .locale(getLocale(process)) .otpType(SendOtpCodeRequest.OtpType.USER_VERIFICATION) .build(); try { @@ -276,6 +281,18 @@ private void sendOtpCode(String processId, boolean isResend) throws OnboardingPr auditService.auditOnboardingProvider(process, "{} user verification OTP for user: {}", resentPrefix, userId); } + @SuppressWarnings("unchecked") // unchecked readValue + private Locale getLocale(final OnboardingProcessEntity process) throws OnboardingProcessException { + try { + logger.debug("Getting locale from custom_data: {} of process ID: {}", process.getCustomData(), process.getId()); + final Map json = mapper.readValue(process.getCustomData(), Map.class); + final String language = json.get(OnboardingProcessEntity.CUSTOM_DATA_LOCALE).toString(); + return new Locale(language); + } catch (JsonProcessingException e) { + throw new OnboardingProcessException("Problem to parse custom_data of process ID: " + process.getId(), e); + } + } + private String createOtpCode(final boolean isResend, final OnboardingProcessEntity process) throws OnboardingOtpDeliveryException, OnboardingProcessException { if (isResend) { return otpService.createOtpCodeForResend(process, OtpType.USER_VERIFICATION); diff --git a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/OnboardingServiceImpl.java b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/OnboardingServiceImpl.java index df4c2a4ed..a71f46591 100644 --- a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/OnboardingServiceImpl.java +++ b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/OnboardingServiceImpl.java @@ -413,6 +413,7 @@ private String lookupUser(final OnboardingProcessEntity process, final Map identification, final String identificationData) { final OnboardingProcessEntity process = createNewProcess(identificationData); logger.debug("Created process ID: {}", process.getId()); @@ -422,11 +423,12 @@ private OnboardingProcessEntity createNewProcess(final Map ident return process; } - private OnboardingProcessEntity createNewProcess(final String identificationData) { + private OnboardingProcessEntity createNewProcess(final String identificationData) throws OnboardingProcessException { final OnboardingProcessEntity process = new OnboardingProcessEntity(); process.setIdentificationData(identificationData); process.setStatus(OnboardingStatus.ACTIVATION_IN_PROGRESS); process.setTimestampCreated(new Date()); + setLocaleToCustomData(process); return onboardingProcessRepository.save(process); } @@ -434,6 +436,7 @@ private OnboardingProcessEntity createNewProcess(final String identificationData private OnboardingProcessEntity resumeExistingProcess(final OnboardingProcessEntity process, final Map identification) { logger.debug("Resuming process ID: {}", process.getId()); process.setTimestampLastUpdated(new Date()); + setLocaleToCustomData(process); final String userId = lookupUser(process, identification); if (!process.getUserId().equals(userId)) { throw new OnboardingProcessException( @@ -444,6 +447,18 @@ private OnboardingProcessEntity resumeExistingProcess(final OnboardingProcessEnt return process; } + @SuppressWarnings("unchecked") // unchecked readValue + private void setLocaleToCustomData(final OnboardingProcessEntity process) throws OnboardingProcessException { + try { + logger.debug("Setting locale to custom_data: {} of process ID: {}", process.getCustomData(), process.getId()); + final Map json = normalizedMapper.readValue(process.getCustomData(), Map.class); + json.put(OnboardingProcessEntity.CUSTOM_DATA_LOCALE, LocaleContextHolder.getLocale().getLanguage()); + process.setCustomData(normalizedMapper.writeValueAsString(json)); + } catch (JsonProcessingException e) { + throw new OnboardingProcessException("Problem to parse custom_data of process ID: " + process.getId(), e); + } + } + private void removeActivation(final OnboardingProcessEntity process) throws OnboardingProcessException { final String activationId = process.getActivationId(); if (activationId != null) { diff --git a/enrollment-server-onboarding/src/test/resources/com/wultra/app/onboardingserver/task/cleaning/ActivationCleaningServiceTest.sql b/enrollment-server-onboarding/src/test/resources/com/wultra/app/onboardingserver/task/cleaning/ActivationCleaningServiceTest.sql index dc30c06b0..f6c7c418b 100644 --- a/enrollment-server-onboarding/src/test/resources/com/wultra/app/onboardingserver/task/cleaning/ActivationCleaningServiceTest.sql +++ b/enrollment-server-onboarding/src/test/resources/com/wultra/app/onboardingserver/task/cleaning/ActivationCleaningServiceTest.sql @@ -1,4 +1,4 @@ -INSERT INTO es_onboarding_process(id, identification_data, status, activation_id, activation_removed, error_score, timestamp_created) VALUES - ('11111111-df91-4053-bb3d-3970979baf5d', '{}', 'ACTIVATION_IN_PROGRESS', 'a1', false, 0, now()), - ('22222222-df91-4053-bb3d-3970979baf5d', '{}', 'FAILED', 'a2', false, 0, now()), - ('33333333-df91-4053-bb3d-3970979baf5d', '{}', 'FAILED', 'a3', true, 0, now()); +INSERT INTO es_onboarding_process(id, identification_data, status, activation_id, activation_removed, error_score, custom_data, timestamp_created) VALUES + ('11111111-df91-4053-bb3d-3970979baf5d', '{}', 'ACTIVATION_IN_PROGRESS', 'a1', false, 0, '{}', now()), + ('22222222-df91-4053-bb3d-3970979baf5d', '{}', 'FAILED', 'a2', false, 0, '{}', now()), + ('33333333-df91-4053-bb3d-3970979baf5d', '{}', 'FAILED', 'a3', true, 0, '{}', now()); diff --git a/enrollment-server-onboarding/src/test/resources/com/wultra/app/onboardingserver/task/cleaning/CleaningServiceTest.testTerminateExpiredOtpCodes.sql b/enrollment-server-onboarding/src/test/resources/com/wultra/app/onboardingserver/task/cleaning/CleaningServiceTest.testTerminateExpiredOtpCodes.sql index cd7370aec..fd980ed1b 100644 --- a/enrollment-server-onboarding/src/test/resources/com/wultra/app/onboardingserver/task/cleaning/CleaningServiceTest.testTerminateExpiredOtpCodes.sql +++ b/enrollment-server-onboarding/src/test/resources/com/wultra/app/onboardingserver/task/cleaning/CleaningServiceTest.testTerminateExpiredOtpCodes.sql @@ -1,5 +1,5 @@ -INSERT INTO es_onboarding_process(id, identification_data, status, error_score, timestamp_created) VALUES - ('b4662611-df91-4053-bb3d-3970979baf5d', '{}', 'VERIFICATION_IN_PROGRESS', 0, now()); +INSERT INTO es_onboarding_process(id, identification_data, status, error_score, custom_data, timestamp_created) VALUES + ('b4662611-df91-4053-bb3d-3970979baf5d', '{}', 'VERIFICATION_IN_PROGRESS', 0, '{}', now()); INSERT INTO es_onboarding_otp(id, process_id, otp_code, failed_attempts, status, type, timestamp_created, timestamp_expiration) VALUES ('f50b8c04-649d-43a7-8079-4dbf9b0bbc72', 'b4662611-df91-4053-bb3d-3970979baf5d', '123', 0, 'ACTIVE', 'USER_VERIFICATION', now(), now()), diff --git a/enrollment-server-onboarding/src/test/resources/com/wultra/app/onboardingserver/task/cleaning/CleaningServiceTest.testTerminateExpiredProcessActivations.sql b/enrollment-server-onboarding/src/test/resources/com/wultra/app/onboardingserver/task/cleaning/CleaningServiceTest.testTerminateExpiredProcessActivations.sql index 436ee79e8..23fb3c7f6 100644 --- a/enrollment-server-onboarding/src/test/resources/com/wultra/app/onboardingserver/task/cleaning/CleaningServiceTest.testTerminateExpiredProcessActivations.sql +++ b/enrollment-server-onboarding/src/test/resources/com/wultra/app/onboardingserver/task/cleaning/CleaningServiceTest.testTerminateExpiredProcessActivations.sql @@ -1,8 +1,8 @@ -INSERT INTO es_onboarding_process(id, identification_data, status, error_score, timestamp_created) VALUES - ('11111111-df91-4053-bb3d-3970979baf5d', '{}', 'ACTIVATION_IN_PROGRESS', 0, now() - interval '301' second), - ('22222222-df91-4053-bb3d-3970979baf5d', '{}', 'ACTIVATION_IN_PROGRESS', 0, now() - interval '301' second), - ('33333333-df91-4053-bb3d-3970979baf5d', '{}', 'ACTIVATION_IN_PROGRESS', 0, now() - interval '301' second), - ('44444444-df91-4053-bb3d-3970979baf5d', '{}', 'ACTIVATION_IN_PROGRESS', 0, now()); -- to be kept +INSERT INTO es_onboarding_process(id, identification_data, status, error_score, custom_data, timestamp_created) VALUES + ('11111111-df91-4053-bb3d-3970979baf5d', '{}', 'ACTIVATION_IN_PROGRESS', 0, '{}', now() - interval '301' second), + ('22222222-df91-4053-bb3d-3970979baf5d', '{}', 'ACTIVATION_IN_PROGRESS', 0, '{}', now() - interval '301' second), + ('33333333-df91-4053-bb3d-3970979baf5d', '{}', 'ACTIVATION_IN_PROGRESS', 0, '{}', now() - interval '301' second), + ('44444444-df91-4053-bb3d-3970979baf5d', '{}', 'ACTIVATION_IN_PROGRESS', 0, '{}', now()); -- to be kept INSERT INTO es_identity_verification(id, activation_id, user_id, process_id, status, phase, timestamp_created, timestamp_last_updated) VALUES ('11111111-4ac0-45dd-b68e-29f4cd991a5c', 'a1', 'u1', '11111111-df91-4053-bb3d-3970979baf5d', 'IN_PROGRESS', 'PRESENCE_CHECK', now(), now()), diff --git a/enrollment-server-onboarding/src/test/resources/com/wultra/app/onboardingserver/task/cleaning/CleaningServiceTest.testTerminateExpiredProcessVerifications.sql b/enrollment-server-onboarding/src/test/resources/com/wultra/app/onboardingserver/task/cleaning/CleaningServiceTest.testTerminateExpiredProcessVerifications.sql index 7b67da8c4..9fdd0cc0d 100644 --- a/enrollment-server-onboarding/src/test/resources/com/wultra/app/onboardingserver/task/cleaning/CleaningServiceTest.testTerminateExpiredProcessVerifications.sql +++ b/enrollment-server-onboarding/src/test/resources/com/wultra/app/onboardingserver/task/cleaning/CleaningServiceTest.testTerminateExpiredProcessVerifications.sql @@ -1,8 +1,8 @@ -INSERT INTO es_onboarding_process(id, identification_data, status, error_score, timestamp_created) VALUES - ('11111111-df91-4053-bb3d-3970979baf5d', '{}', 'VERIFICATION_IN_PROGRESS', 0, now() - interval '10801' second), - ('22222222-df91-4053-bb3d-3970979baf5d', '{}', 'VERIFICATION_IN_PROGRESS', 0, now() - interval '10801' second), - ('33333333-df91-4053-bb3d-3970979baf5d', '{}', 'VERIFICATION_IN_PROGRESS', 0, now() - interval '10801' second), - ('44444444-df91-4053-bb3d-3970979baf5d', '{}', 'VERIFICATION_IN_PROGRESS', 0, now()); -- to be kept +INSERT INTO es_onboarding_process(id, identification_data, status, error_score, custom_data, timestamp_created) VALUES + ('11111111-df91-4053-bb3d-3970979baf5d', '{}', 'VERIFICATION_IN_PROGRESS', 0, '{}', now() - interval '10801' second), + ('22222222-df91-4053-bb3d-3970979baf5d', '{}', 'VERIFICATION_IN_PROGRESS', 0, '{}', now() - interval '10801' second), + ('33333333-df91-4053-bb3d-3970979baf5d', '{}', 'VERIFICATION_IN_PROGRESS', 0, '{}', now() - interval '10801' second), + ('44444444-df91-4053-bb3d-3970979baf5d', '{}', 'VERIFICATION_IN_PROGRESS', 0, '{}', now()); -- to be kept INSERT INTO es_identity_verification(id, activation_id, user_id, process_id, status, phase, timestamp_created, timestamp_last_updated) VALUES ('11111111-4ac0-45dd-b68e-29f4cd991a5c', 'a1', 'u1', '11111111-df91-4053-bb3d-3970979baf5d', 'IN_PROGRESS', 'PRESENCE_CHECK', now(), now()), diff --git a/enrollment-server-onboarding/src/test/resources/com/wultra/app/onboardingserver/task/cleaning/CleaningServiceTest.testTerminateExpiredProcesses.sql b/enrollment-server-onboarding/src/test/resources/com/wultra/app/onboardingserver/task/cleaning/CleaningServiceTest.testTerminateExpiredProcesses.sql index cf5c4160b..42a8863b3 100644 --- a/enrollment-server-onboarding/src/test/resources/com/wultra/app/onboardingserver/task/cleaning/CleaningServiceTest.testTerminateExpiredProcesses.sql +++ b/enrollment-server-onboarding/src/test/resources/com/wultra/app/onboardingserver/task/cleaning/CleaningServiceTest.testTerminateExpiredProcesses.sql @@ -1,4 +1,4 @@ -INSERT INTO es_onboarding_process(id, identification_data, status, error_score, timestamp_created) VALUES - ('11111111-df91-4053-bb3d-3970979baf5d', '{}', 'ACTIVATION_IN_PROGRESS', 0, now() - interval '10801' second), - ('22222222-df91-4053-bb3d-3970979baf5d', '{}', 'ACTIVATION_IN_PROGRESS', 0, now()), - ('33333333-df91-4053-bb3d-3970979baf5d', '{}', 'FINISHED', 0, now() - interval '301' second); +INSERT INTO es_onboarding_process(id, identification_data, status, error_score, custom_data, timestamp_created) VALUES + ('11111111-df91-4053-bb3d-3970979baf5d', '{}', 'ACTIVATION_IN_PROGRESS', 0, '{}', now() - interval '10801' second), + ('22222222-df91-4053-bb3d-3970979baf5d', '{}', 'ACTIVATION_IN_PROGRESS', 0, '{}', now()), + ('33333333-df91-4053-bb3d-3970979baf5d', '{}', 'FINISHED', 0, '{}', now() - interval '301' second);