From e17f6f6139b7a08824279514bc98743dfc87e7d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubo=C5=A1=20Ra=C4=8Dansk=C3=BD?= Date: Wed, 5 Jun 2024 13:47:37 +0200 Subject: [PATCH] Fix #1560: Create Operation does not return proximity OTP (#1561) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix #1560: Create Operation does not return proximity OTP Co-authored-by: Zdeněk Černý --- .../tasks/OperationServiceBehavior.java | 36 ++++++++++---- .../tasks/OperationServiceBehaviorTest.java | 47 +++++++++++++++++-- .../tasks/OperationServiceBehaviorTest.sql | 5 +- 3 files changed, 73 insertions(+), 15 deletions(-) diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehavior.java index 92ac3460a..09484984c 100644 --- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehavior.java +++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehavior.java @@ -245,7 +245,7 @@ public OperationDetailResponse createOperation(OperationCreateRequest request) t final OperationEntity savedEntity = operationRepository.save(operationEntity); callbackUrlBehavior.notifyCallbackListenersOnOperationChange(savedEntity); - return convertFromEntity(savedEntity); + return convertFromEntityAndFillOtp(savedEntity); } catch (GenericServiceException ex) { // already logged throw ex; @@ -731,9 +731,8 @@ public OperationDetailResponse operationDetail(OperationDetailRequest request) t claimOperation(operationOptional.get(), userId, currentTimestamp), currentTimestamp ); - final OperationDetailResponse operationDetailResponse = convertFromEntity(operationEntity); + final OperationDetailResponse operationDetailResponse = convertFromEntityAndFillOtp(operationEntity); extendAndSetOperationDetailData(operationDetailResponse); - generateAndSetOtpToOperationDetail(operationEntity, operationDetailResponse); return operationDetailResponse; } catch (GenericServiceException ex) { // already logged @@ -821,8 +820,7 @@ public OperationListResponse findPendingOperationsForUser(OperationListForUserRe final OperationEntity operationEntity = expireOperation(op, currentTimestamp); // Skip operation that just expired if (OperationStatusDo.PENDING.equals(operationEntity.getStatus())) { - final OperationDetailResponse operationDetail = convertFromEntity(operationEntity); - generateAndSetOtpToOperationDetail(operationEntity, operationDetail); + final OperationDetailResponse operationDetail = convertFromEntityAndFillOtp(operationEntity); result.add(operationDetail); } }); @@ -883,6 +881,14 @@ public OperationListResponse findOperationsByExternalId(OperationExtIdRequest re } } + /** + * Convert the given entity to the response class. + * Mind that it does not fill the proximity OTP. If you need so, use {@link #convertFromEntityAndFillOtp(OperationEntity)} instead. + * + * @param source Entity to convert. + * @return response class + * @see #convertFromEntityAndFillOtp(OperationEntity) + */ private OperationDetailResponse convertFromEntity(OperationEntity source) { final OperationDetailResponse destination = new OperationDetailResponse(); destination.setId(source.getId()); @@ -1000,10 +1006,20 @@ private Map mapMerge(Map m1, Map return m3; } + /** + * Convert the given entity to the response class. + * Unlike {@link #convertFromEntity(OperationEntity)} also fill proximity OTP value if needed. + * + * @param source Entity to convert. + * @return response class + * @see #convertFromEntity(OperationEntity) + */ @SneakyThrows(GenericServiceException.class) - private void generateAndSetOtpToOperationDetail(final OperationEntity operation, final OperationDetailResponse operationDetailResponse) { - final String totp = generateTotp(operation, powerAuthServiceConfiguration.getProximityCheckOtpLength()); - operationDetailResponse.setProximityOtp(totp); + private OperationDetailResponse convertFromEntityAndFillOtp(final OperationEntity source) { + final String totp = generateTotp(source, powerAuthServiceConfiguration.getProximityCheckOtpLength()); + final OperationDetailResponse target = convertFromEntity(source); + target.setProximityOtp(totp); + return target; } private String generateTotp(final OperationEntity operation, final int otpLength) throws GenericServiceException { @@ -1016,9 +1032,9 @@ private String generateTotp(final OperationEntity operation, final int otpLength } try { - byte[] seedBytes = Base64.getDecoder().decode(seed); + final byte[] seedBytes = Base64.getDecoder().decode(seed); final Instant now = Instant.now(); - byte[] totp = Totp.generateTotpSha256(seedBytes, now, otpLength); + final byte[] totp = Totp.generateTotpSha256(seedBytes, now, otpLength); return new String(totp, StandardCharsets.UTF_8); } catch (CryptoProviderException | IllegalArgumentException e) { diff --git a/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehaviorTest.java b/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehaviorTest.java index dee96320a..126cabc17 100644 --- a/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehaviorTest.java +++ b/powerauth-java-server/src/test/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehaviorTest.java @@ -88,9 +88,50 @@ void testCreateOperationWithActivationId() throws Exception { request.setUserId(USER_ID); final OperationDetailResponse operationDetailResponse = operationService.createOperation(request); - final OperationEntity savedEntity = operationRepository.findOperation(operationDetailResponse.getId()).get(); - assertTrue(operationRepository.findOperation(operationDetailResponse.getId()).isPresent()); - assertEquals(ACTIVATION_ID, savedEntity.getActivationId()); + final Optional savedEntity = operationRepository.findOperation(operationDetailResponse.getId()); + + assertTrue(savedEntity.isPresent()); + assertEquals(ACTIVATION_ID, savedEntity.get().getActivationId()); + assertNull(operationDetailResponse.getProximityOtp()); + } + + @Test + void testCreateOperationWithoutActivationIdAndExplicitProximityCheck() throws Exception { + final OperationCreateRequest request = new OperationCreateRequest(); + request.setTemplateName("test-template"); + request.setApplications(List.of(APP_ID)); + request.setUserId("test-user"); + request.setProximityCheckEnabled(true); + + final OperationDetailResponse operationDetailResponse = operationService.createOperation(request); + assertNotNull(operationDetailResponse.getProximityOtp()); + + final OperationDetailRequest detailRequest = new OperationDetailRequest(); + detailRequest.setOperationId(operationDetailResponse.getId()); + + final OperationDetailResponse operationDetail = operationService.operationDetail(detailRequest); + assertNotNull(operationDetail); + assertNotNull(operationDetail.getProximityOtp()); + assertNull(operationDetail.getActivationId()); + } + + @Test + void testCreateOperationWithoutActivationIdAndImplicitProximityCheck() throws Exception { + final OperationCreateRequest request = new OperationCreateRequest(); + request.setTemplateName("test-template-proximity-check"); + request.setApplications(List.of(APP_ID)); + request.setUserId("test-user"); + + final OperationDetailResponse operationDetailResponse = operationService.createOperation(request); + assertNotNull(operationDetailResponse.getProximityOtp()); + + final OperationDetailRequest detailRequest = new OperationDetailRequest(); + detailRequest.setOperationId(operationDetailResponse.getId()); + + final OperationDetailResponse operationDetail = operationService.operationDetail(detailRequest); + assertNotNull(operationDetail); + assertNotNull(operationDetail.getProximityOtp()); + assertNull(operationDetail.getActivationId()); } @Test diff --git a/powerauth-java-server/src/test/resources/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehaviorTest.sql b/powerauth-java-server/src/test/resources/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehaviorTest.sql index 2efdeb76f..6ddd894fc 100644 --- a/powerauth-java-server/src/test/resources/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehaviorTest.sql +++ b/powerauth-java-server/src/test/resources/io/getlime/security/powerauth/app/server/service/behavior/tasks/OperationServiceBehaviorTest.sql @@ -1,5 +1,6 @@ -INSERT INTO pa_operation_template (id, template_name, operation_type, data_template, signature_type, max_failure_count, expiration, proximity_check_enabled) -VALUES (100, 'test-template', 'test-template', 'A2', 'POSSESSION_KNOWLEDGE', 5, 300, false); +INSERT INTO pa_operation_template (id, template_name, operation_type, data_template, signature_type, max_failure_count, expiration, proximity_check_enabled) VALUES + (100, 'test-template', 'test-template', 'A2', 'POSSESSION_KNOWLEDGE', 5, 300, false), + (101, 'test-template-proximity-check', 'test-template', 'A2', 'POSSESSION_KNOWLEDGE', 5, 300, true); INSERT INTO pa_application (id, name) VALUES (21, 'PA_Tests');