diff --git a/docs/Configuration-Properties.md b/docs/Configuration-Properties.md
index 46718a26..ce1df243 100644
--- a/docs/Configuration-Properties.md
+++ b/docs/Configuration-Properties.md
@@ -9,7 +9,6 @@ The Enrollment Server uses the following public configuration properties:
| `spring.datasource.url` | `_empty_` | Database JDBC URL |
| `spring.datasource.username` | `_empty_` | Database JDBC username |
| `spring.datasource.password` | `_empty_` | Database JDBC password |
-| `spring.datasource.driver-class-name` | `_empty_` | Datasource JDBC class name |
| `spring.jpa.hibernate.ddl-auto` | `none` | Configuration of automatic database schema creation |
| `spring.jpa.properties.hibernate.connection.characterEncoding` | `_empty_` | Character encoding |
| `spring.jpa.properties.hibernate.connection.useUnicode` | `_empty_` | Character encoding - Unicode support |
@@ -63,6 +62,8 @@ logging.pattern.console=%clr(%d{${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd HH:mm:ss.SSS
## Monitoring and Observability
-
+| Property | Default | Note |
+|-------------------------------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `management.tracing.sampling.probability` | `1.0` | Specifies the proportion of requests that are sampled for tracing. A value of 1.0 means that 100% of requests are sampled, while a value of 0 effectively disables tracing. |
The WAR file includes the `micrometer-registry-prometheus` dependency.
Discuss its configuration with the [Spring Boot documentation](https://docs.spring.io/spring-boot/docs/3.1.x/reference/html/actuator.html#actuator.metrics).
diff --git a/docs/onboarding/Configuration-Properties.md b/docs/onboarding/Configuration-Properties.md
index 799d45a1..3f1881e6 100644
--- a/docs/onboarding/Configuration-Properties.md
+++ b/docs/onboarding/Configuration-Properties.md
@@ -9,7 +9,6 @@ The Onboarding Server uses the following public configuration properties:
| `spring.datasource.url` | `jdbc:postgresql://localhost:5432/powerauth` | Database JDBC URL |
| `spring.datasource.username` | `powerauth` | Database JDBC username |
| `spring.datasource.password` | `_empty_` | Database JDBC password |
-| `spring.datasource.driver-class-name` | `org.postgresql.Driver` | Datasource JDBC class name |
| `spring.jpa.hibernate.ddl-auto` | `none` | Configuration of automatic database schema creation |
| `spring.jpa.properties.hibernate.connection.characterEncoding` | `utf8` | Character encoding |
| `spring.jpa.properties.hibernate.connection.useUnicode` | `true` | Character encoding - Unicode support |
@@ -46,7 +45,6 @@ The Onboarding Server uses the following public configuration properties:
| `enrollment-server-onboarding.identity-verification.otp.enabled` | `true` | Whether OTP verification is enabled during identity verification. |
| `enrollment-server-onboarding.identity-verification.max-failed-attempts` | `5` | Maximum failed attempts for identity verification. |
| `enrollment-server-onboarding.identity-verification.max-failed-attempts-document-upload` | `5` | Maximum failed attempts for document upload. |
-| `enrollment-server-onboarding.client-evaluation.max-failed-attempts` | `5` | Maximum failed attempts for client evaluation. |
## Digital Onboarding Adapter Configuration
@@ -69,6 +67,7 @@ The Onboarding Server uses the following public configuration properties:
| Property | Default | Note |
|---|---|---|
| `enrollment-server-onboarding.client-evaluation.max-failed-attempts` | 5 | Number of maximum failed attempts for client evaluation. |
+| `enrollment-server-onboarding.client-evaluation.include-extracted-data` | `false` | Include extracted data to the evaluate client request. The format of extracted data is defined by the provider of document verification. |
## Document Verification Provider Configuration
@@ -170,6 +169,8 @@ logging.pattern.console=%clr(%d{${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd HH:mm:ss.SSS
## Monitoring and Observability
-
+| Property | Default | Note |
+|-------------------------------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `management.tracing.sampling.probability` | `1.0` | Specifies the proportion of requests that are sampled for tracing. A value of 1.0 means that 100% of requests are sampled, while a value of 0 effectively disables tracing. |
The WAR file includes the `micrometer-registry-prometheus` dependency.
Discuss its configuration with the [Spring Boot documentation](https://docs.spring.io/spring-boot/docs/3.1.x/reference/html/actuator.html#actuator.metrics).
diff --git a/docs/onboarding/Configuration-Verification-Providers.md b/docs/onboarding/Configuration-Verification-Providers.md
index ed358b8b..09d04519 100644
--- a/docs/onboarding/Configuration-Verification-Providers.md
+++ b/docs/onboarding/Configuration-Verification-Providers.md
@@ -11,7 +11,7 @@ The document verification process is currently supported for following providers
### ZenID
-#### Configuration - API key
+#### API key
The authorization of all API calls is secured by an API key value. It has to be sent as the `Authorization: api_key VALUE` header value.
Check the bottom of the `Manual/Configuration` page for more details.
@@ -21,7 +21,7 @@ The API key value can be configured/get from the `Access` page configuration:
- Condition: `ApiKeyEqualsValue`
- Value: the value here is the value of the API key
-#### Configuration - Validators
+#### Validators
It is recommended to create a custom validation profile. The sensitivity of selected validators can be tuned-up or disabled completely at the `Sensitivity` page.
The profile can be then set as the default or specified in the configuration properties.
@@ -32,6 +32,41 @@ When calling `document-verification/init-sdk` following implementation fields ar
- Init token - send a token value `sdk-init-token` in the request body `attributes` map field
- SDK response - receive the value under `zenid-sdk-init-response` from the response `attributes` map field
+### Innovatrics
+
+Innovatrics documentation for developers can be found at [this link](https://developers.innovatrics.com/digital-onboarding/technical/remote/dot-dis/latest/documentation/).
+
+#### OCR Threshold
+
+During a document validation Innovatrics provides a list of fields extracted from the document, that have OCR
+confidence lower than configurable threshold. If the list is not empty, there is a high probability that some
+information is read incorrectly. For that reason, this document will be rejected. The OCR confidence threshold is `0.92`
+by default, and can be tuned using `innovatrics.dot.dis.customer.document.inspection.ocr-text-field-threshold`.
+
+#### Text Consistency
+
+For each document Innovatrics tries to read visual zone, machine-readable zone and barcode. These isolated parts are
+cross-checked during a document validation by Innovatrics. If there are inconsistency between visual zone and
+machine-readable zone, or between visual-zone and barcode, the document will be rejected. However, some editions of
+identification documents are inconsistent by design. To prevent false rejection of those document modify the
+configuration.
+Following example excludes `issuingAuthority` field of Czech identity card 2005 edition from text consistency check:
+
+```yml
+innovatrics:
+ dot:
+ dis:
+ customer:
+ document:
+ inspection:
+ text-consistency-check:
+ CZE_identity-card_2005-01-01:
+ exclusions:
+ - issuingAuthority
+```
+
+The format of the document name is `{country}_{type}_{edition}` according to the response of `/metadata` request.
+
## Presence Check
The document verification process is currently supported for following providers:
@@ -39,7 +74,7 @@ The document verification process is currently supported for following providers
- [Innovatrics](https://www.innovatrics.com/) - use value `innovatrics` in configuration
- Mock - useful for simple testing and local runs - use value `mock` in configuration
-#### Configuration
+### iProov
There are a few needed configuration changes to bring a successful integration. All the following configuration tuning
has to be requested from the iProov's [support team](https://iproov.freshdesk.com/support/login) on a per-service basis:
diff --git a/enrollment-server-api-model/pom.xml b/enrollment-server-api-model/pom.xml
index a798a371..ec7badbe 100644
--- a/enrollment-server-api-model/pom.xml
+++ b/enrollment-server-api-model/pom.xml
@@ -30,7 +30,7 @@
com.wultra.security
enrollment-server-parent
- 1.6.0-SNAPSHOT
+ 1.7.0-SNAPSHOT
@@ -43,6 +43,11 @@
io.swagger.core.v3
swagger-annotations-jakarta
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+
diff --git a/enrollment-server-api-model/src/main/java/com/wultra/app/enrollmentserver/api/model/enrollment/request/PushRegisterRequest.java b/enrollment-server-api-model/src/main/java/com/wultra/app/enrollmentserver/api/model/enrollment/request/PushRegisterRequest.java
index 21dee883..c42ed56e 100644
--- a/enrollment-server-api-model/src/main/java/com/wultra/app/enrollmentserver/api/model/enrollment/request/PushRegisterRequest.java
+++ b/enrollment-server-api-model/src/main/java/com/wultra/app/enrollmentserver/api/model/enrollment/request/PushRegisterRequest.java
@@ -18,19 +18,41 @@
package com.wultra.app.enrollmentserver.api.model.enrollment.request;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
import lombok.Data;
+import lombok.ToString;
/**
- * Class representing a device registration request. The supported platform
- * values are 'ios' and 'android'. The push token is the value received from
- * APNS or FCM services without any modification.
+ * Class representing a device registration request.
*
* @author Petr Dvorak, petr@wultra.com
*/
@Data
public class PushRegisterRequest {
- private String platform;
+ /**
+ * The platform.
+ */
+ @NotNull
+ private Platform platform;
+
+ /**
+ * The push token is the value received from APNS or FCM services without any modification.
+ */
+ @NotBlank
+ @ToString.Exclude
+ @Schema(description = "The push token is the value received from APNS or FCM services without any modification.")
private String token;
+ public enum Platform {
+ @JsonProperty("ios")
+ IOS,
+
+ @JsonProperty("android")
+ ANDROID
+ }
+
}
diff --git a/enrollment-server-onboarding-adapter-mock/pom.xml b/enrollment-server-onboarding-adapter-mock/pom.xml
index 99f5897a..b80b2924 100644
--- a/enrollment-server-onboarding-adapter-mock/pom.xml
+++ b/enrollment-server-onboarding-adapter-mock/pom.xml
@@ -24,7 +24,7 @@
com.wultra.security
enrollment-server-parent
- 1.6.0-SNAPSHOT
+ 1.7.0-SNAPSHOT
enrollment-server-onboarding-adapter-mock
diff --git a/enrollment-server-onboarding-api-model/pom.xml b/enrollment-server-onboarding-api-model/pom.xml
index b22e220c..fcc57e0b 100644
--- a/enrollment-server-onboarding-api-model/pom.xml
+++ b/enrollment-server-onboarding-api-model/pom.xml
@@ -7,7 +7,7 @@
com.wultra.security
enrollment-server-parent
- 1.6.0-SNAPSHOT
+ 1.7.0-SNAPSHOT
enrollment-server-onboarding-api-model
diff --git a/enrollment-server-onboarding-api/pom.xml b/enrollment-server-onboarding-api/pom.xml
index 48cb44c2..40396bfc 100644
--- a/enrollment-server-onboarding-api/pom.xml
+++ b/enrollment-server-onboarding-api/pom.xml
@@ -25,7 +25,7 @@
com.wultra.security
enrollment-server-parent
- 1.6.0-SNAPSHOT
+ 1.7.0-SNAPSHOT
com.wultra.security
diff --git a/enrollment-server-onboarding-common/pom.xml b/enrollment-server-onboarding-common/pom.xml
index d6d88b13..2cc80fe4 100644
--- a/enrollment-server-onboarding-common/pom.xml
+++ b/enrollment-server-onboarding-common/pom.xml
@@ -24,7 +24,7 @@
com.wultra.security
enrollment-server-parent
- 1.6.0-SNAPSHOT
+ 1.7.0-SNAPSHOT
enrollment-server-onboarding-common
diff --git a/enrollment-server-onboarding-common/src/test/resources/application-test.properties b/enrollment-server-onboarding-common/src/test/resources/application-test.properties
index 308c2b3a..98bb47ca 100644
--- a/enrollment-server-onboarding-common/src/test/resources/application-test.properties
+++ b/enrollment-server-onboarding-common/src/test/resources/application-test.properties
@@ -1,5 +1,4 @@
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.username=sa
spring.datasource.password=password
-spring.datasource.driver-class-name=org.h2.Driver
spring.jpa.hibernate.ddl-auto=create
diff --git a/enrollment-server-onboarding-domain-model/pom.xml b/enrollment-server-onboarding-domain-model/pom.xml
index 05d934ce..2f5edb9e 100644
--- a/enrollment-server-onboarding-domain-model/pom.xml
+++ b/enrollment-server-onboarding-domain-model/pom.xml
@@ -30,7 +30,7 @@
com.wultra.security
enrollment-server-parent
- 1.6.0-SNAPSHOT
+ 1.7.0-SNAPSHOT
@@ -38,6 +38,19 @@
io.getlime.security
powerauth-java-crypto
+
+
+
+ org.bouncycastle
+ bcprov-jdk18on
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
diff --git a/enrollment-server-onboarding-domain-model/src/main/java/com/wultra/app/enrollmentserver/model/integration/OwnerId.java b/enrollment-server-onboarding-domain-model/src/main/java/com/wultra/app/enrollmentserver/model/integration/OwnerId.java
index dc5df814..3992bfef 100644
--- a/enrollment-server-onboarding-domain-model/src/main/java/com/wultra/app/enrollmentserver/model/integration/OwnerId.java
+++ b/enrollment-server-onboarding-domain-model/src/main/java/com/wultra/app/enrollmentserver/model/integration/OwnerId.java
@@ -17,13 +17,14 @@
*/
package com.wultra.app.enrollmentserver.model.integration;
-import com.google.common.io.BaseEncoding;
import io.getlime.security.powerauth.crypto.lib.util.Hash;
import lombok.AccessLevel;
import lombok.Data;
import lombok.Setter;
import lombok.ToString;
+import org.bouncycastle.util.encoders.Base32;
+import java.nio.charset.StandardCharsets;
import java.util.Date;
/**
@@ -73,9 +74,8 @@ public String getUserIdSecured() {
throw new IllegalStateException("Missing userId value");
}
if (userIdSecured == null) {
- userIdSecured = BaseEncoding.base32()
- .omitPadding()
- .encode(Hash.sha256(userId));
+ userIdSecured = new String(Base32.encode(Hash.sha256(userId)), StandardCharsets.UTF_8)
+ .replace("=", "");
if (userIdSecured.length() > USER_ID_MAX_LENGTH) {
userIdSecured = userIdSecured.substring(0, USER_ID_MAX_LENGTH);
}
diff --git a/enrollment-server-onboarding-domain-model/src/test/java/com/wultra/app/enrollmentserver/model/integration/OwnerIdTest.java b/enrollment-server-onboarding-domain-model/src/test/java/com/wultra/app/enrollmentserver/model/integration/OwnerIdTest.java
new file mode 100644
index 00000000..b763f651
--- /dev/null
+++ b/enrollment-server-onboarding-domain-model/src/test/java/com/wultra/app/enrollmentserver/model/integration/OwnerIdTest.java
@@ -0,0 +1,40 @@
+/*
+ * PowerAuth Enrollment Server
+ * Copyright (C) 2024 Wultra s.r.o.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package com.wultra.app.enrollmentserver.model.integration;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Test for {@link OwnerId}.
+ *
+ * @author Lubos Racansky, lubos.racansky@wultra.com
+ */
+class OwnerIdTest {
+
+ @Test
+ void testUserIdSecured() {
+ final OwnerId tested = new OwnerId();
+ tested.setUserId("Joe");
+
+ final String result = tested.getUserIdSecured();
+
+ assertEquals("NXMLPV6TYXCGRGZT4UNZ6EF4NKN6RH7I7IVBE7EMNQB42BOWRLHA", result);
+ }
+}
diff --git a/enrollment-server-onboarding-provider-innovatrics/pom.xml b/enrollment-server-onboarding-provider-innovatrics/pom.xml
index bec000cb..629a295f 100644
--- a/enrollment-server-onboarding-provider-innovatrics/pom.xml
+++ b/enrollment-server-onboarding-provider-innovatrics/pom.xml
@@ -25,7 +25,7 @@
com.wultra.security
enrollment-server-parent
- 1.6.0-SNAPSHOT
+ 1.7.0-SNAPSHOT
com.wultra.security
diff --git a/enrollment-server-onboarding-provider-innovatrics/src/main/java/com/wultra/app/onboardingserver/provider/innovatrics/InnovatricsApiService.java b/enrollment-server-onboarding-provider-innovatrics/src/main/java/com/wultra/app/onboardingserver/provider/innovatrics/InnovatricsApiService.java
index 916c08fb..bc3caebd 100644
--- a/enrollment-server-onboarding-provider-innovatrics/src/main/java/com/wultra/app/onboardingserver/provider/innovatrics/InnovatricsApiService.java
+++ b/enrollment-server-onboarding-provider-innovatrics/src/main/java/com/wultra/app/onboardingserver/provider/innovatrics/InnovatricsApiService.java
@@ -222,14 +222,6 @@ public CreateSelfieResponse createSelfie(final String customerId, final String l
}
}
- // TODO remove - temporal test call
-// @PostConstruct
-// public void testCall() throws RestClientException {
-// logger.info("Trying a test call");
-// final ResponseEntity response = restClient.get("/api/v1/metadata", STRING_TYPE_REFERENCE);
-// logger.info("Result of test call: {}", response.getBody());
-// }
-
/**
* Create a new customer resource.
* @param ownerId owner identification.
diff --git a/enrollment-server-onboarding-provider-innovatrics/src/main/java/com/wultra/app/onboardingserver/provider/innovatrics/InnovatricsDocumentVerificationProvider.java b/enrollment-server-onboarding-provider-innovatrics/src/main/java/com/wultra/app/onboardingserver/provider/innovatrics/InnovatricsDocumentVerificationProvider.java
index 5f660561..d2f6a81d 100644
--- a/enrollment-server-onboarding-provider-innovatrics/src/main/java/com/wultra/app/onboardingserver/provider/innovatrics/InnovatricsDocumentVerificationProvider.java
+++ b/enrollment-server-onboarding-provider-innovatrics/src/main/java/com/wultra/app/onboardingserver/provider/innovatrics/InnovatricsDocumentVerificationProvider.java
@@ -20,11 +20,10 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.base.Strings;
import com.wultra.app.enrollmentserver.model.enumeration.DocumentType;
import com.wultra.app.enrollmentserver.model.enumeration.DocumentVerificationStatus;
-import com.wultra.app.enrollmentserver.model.integration.*;
import com.wultra.app.enrollmentserver.model.integration.Image;
+import com.wultra.app.enrollmentserver.model.integration.*;
import com.wultra.app.onboardingserver.api.errorhandling.DocumentVerificationException;
import com.wultra.app.onboardingserver.api.provider.DocumentVerificationProvider;
import com.wultra.app.onboardingserver.common.database.entity.DocumentResultEntity;
@@ -33,10 +32,10 @@
import com.wultra.app.onboardingserver.provider.innovatrics.model.api.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
-import org.springframework.util.StringUtils;
import java.util.*;
import java.util.stream.Collectors;
@@ -99,7 +98,7 @@ public DocumentsSubmitResult submitDocuments(OwnerId id, List
}
final Optional primaryPage = results.getResults().stream()
- .filter(result -> Strings.isNullOrEmpty(result.getRejectReason()) && Strings.isNullOrEmpty(result.getErrorDetail()))
+ .filter(result -> StringUtils.isBlank(result.getRejectReason()) && StringUtils.isBlank(result.getErrorDetail()))
.findFirst();
if (primaryPage.isPresent()) {
@@ -132,9 +131,9 @@ public DocumentsVerificationResult verifyDocuments(OwnerId id, List uplo
final String rejectReasons = results.getResults().stream()
.map(DocumentVerificationResult::getRejectReason)
- .filter(StringUtils::hasText)
+ .filter(StringUtils::isNotBlank)
.collect(Collectors.joining(";"));
- if (StringUtils.hasText(rejectReasons)) {
+ if (StringUtils.isNotBlank(rejectReasons)) {
logger.debug("Some documents were rejected: rejectReasons={}, {}", rejectReasons, id);
results.setStatus(DocumentVerificationStatus.REJECTED);
results.setRejectReason(rejectReasons);
@@ -172,7 +171,7 @@ public void cleanupDocuments(OwnerId id, List uploadIds) throws RemoteCo
public List parseRejectionReasons(DocumentResultEntity docResult) throws DocumentVerificationException {
logger.debug("Parsing rejection reasons of {}", docResult);
final String rejectionReasons = docResult.getRejectReason();
- if (!StringUtils.hasText(rejectionReasons)) {
+ if (StringUtils.isBlank(rejectionReasons)) {
return Collections.emptyList();
}
diff --git a/enrollment-server-onboarding-provider-innovatrics/src/main/java/com/wultra/app/onboardingserver/provider/innovatrics/InnovatricsLivenessService.java b/enrollment-server-onboarding-provider-innovatrics/src/main/java/com/wultra/app/onboardingserver/provider/innovatrics/InnovatricsLivenessService.java
index 8b97aa52..cdf5061c 100644
--- a/enrollment-server-onboarding-provider-innovatrics/src/main/java/com/wultra/app/onboardingserver/provider/innovatrics/InnovatricsLivenessService.java
+++ b/enrollment-server-onboarding-provider-innovatrics/src/main/java/com/wultra/app/onboardingserver/provider/innovatrics/InnovatricsLivenessService.java
@@ -19,7 +19,6 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.base.Strings;
import com.wultra.app.enrollmentserver.model.integration.OwnerId;
import com.wultra.app.enrollmentserver.model.integration.SessionInfo;
import com.wultra.app.onboardingserver.common.database.IdentityVerificationRepository;
@@ -125,7 +124,7 @@ private static String fetchCustomerId(final OwnerId id, final IdentityVerificati
}
final String customerId = (String) sessionInfo.getSessionAttributes().get(SessionInfo.ATTRIBUTE_PRIMARY_DOCUMENT_REFERENCE);
- if (Strings.isNullOrEmpty(customerId)) {
+ if (StringUtils.isBlank(customerId)) {
throw new IdentityVerificationException("Missing a customer ID value for calling Innovatrics, " + id);
}
return customerId;
diff --git a/enrollment-server-onboarding-provider-innovatrics/src/main/java/com/wultra/app/onboardingserver/provider/innovatrics/InnovatricsPresenceCheckProvider.java b/enrollment-server-onboarding-provider-innovatrics/src/main/java/com/wultra/app/onboardingserver/provider/innovatrics/InnovatricsPresenceCheckProvider.java
index 007c06c6..b4dca0c5 100644
--- a/enrollment-server-onboarding-provider-innovatrics/src/main/java/com/wultra/app/onboardingserver/provider/innovatrics/InnovatricsPresenceCheckProvider.java
+++ b/enrollment-server-onboarding-provider-innovatrics/src/main/java/com/wultra/app/onboardingserver/provider/innovatrics/InnovatricsPresenceCheckProvider.java
@@ -17,7 +17,6 @@
*/
package com.wultra.app.onboardingserver.provider.innovatrics;
-import com.google.common.base.Strings;
import com.wultra.app.enrollmentserver.model.enumeration.PresenceCheckStatus;
import com.wultra.app.enrollmentserver.model.integration.Image;
import com.wultra.app.enrollmentserver.model.integration.OwnerId;
@@ -31,6 +30,7 @@
import com.wultra.app.onboardingserver.provider.innovatrics.model.api.SelfieSimilarityWith;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
@@ -151,7 +151,7 @@ private static Optional fail(final String errorDetail) {
private static String fetchCustomerId(final OwnerId id, final SessionInfo sessionInfo) throws PresenceCheckException {
final String customerId = (String) sessionInfo.getSessionAttributes().get(SessionInfo.ATTRIBUTE_PRIMARY_DOCUMENT_REFERENCE);
- if (Strings.isNullOrEmpty(customerId)) {
+ if (StringUtils.isBlank(customerId)) {
throw new PresenceCheckException("Missing a customer ID value for calling Innovatrics, " + id);
}
return customerId;
diff --git a/enrollment-server-onboarding-provider-iproov/pom.xml b/enrollment-server-onboarding-provider-iproov/pom.xml
index 3822d8b3..b7cbfe87 100644
--- a/enrollment-server-onboarding-provider-iproov/pom.xml
+++ b/enrollment-server-onboarding-provider-iproov/pom.xml
@@ -25,7 +25,7 @@
com.wultra.security
enrollment-server-parent
- 1.6.0-SNAPSHOT
+ 1.7.0-SNAPSHOT
com.wultra.security
diff --git a/enrollment-server-onboarding-provider-iproov/src/main/java/com/wultra/app/onboardingserver/provider/iproov/IProovPresenceCheckProvider.java b/enrollment-server-onboarding-provider-iproov/src/main/java/com/wultra/app/onboardingserver/provider/iproov/IProovPresenceCheckProvider.java
index d3457922..2521216b 100644
--- a/enrollment-server-onboarding-provider-iproov/src/main/java/com/wultra/app/onboardingserver/provider/iproov/IProovPresenceCheckProvider.java
+++ b/enrollment-server-onboarding-provider-iproov/src/main/java/com/wultra/app/onboardingserver/provider/iproov/IProovPresenceCheckProvider.java
@@ -19,7 +19,6 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.base.Strings;
import com.wultra.app.enrollmentserver.model.enumeration.PresenceCheckStatus;
import com.wultra.app.enrollmentserver.model.integration.Image;
import com.wultra.app.enrollmentserver.model.integration.OwnerId;
@@ -34,6 +33,7 @@
import com.wultra.app.onboardingserver.provider.iproov.model.api.EnrolResponse;
import com.wultra.core.rest.client.base.RestClientException;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -161,7 +161,7 @@ public SessionInfo startPresenceCheck(OwnerId id) throws PresenceCheckException,
@Override
public PresenceCheckResult getResult(OwnerId id, SessionInfo sessionInfo) throws PresenceCheckException, RemoteCommunicationException {
final String token = (String) sessionInfo.getSessionAttributes().get(VERIFICATION_TOKEN);
- if (Strings.isNullOrEmpty(token)) {
+ if (StringUtils.isBlank(token)) {
throw new PresenceCheckException("Missing a token value for verification validation in iProov, " + id);
}
diff --git a/enrollment-server-onboarding-provider-zenid/pom.xml b/enrollment-server-onboarding-provider-zenid/pom.xml
index 00c1a27c..80f1bb3d 100644
--- a/enrollment-server-onboarding-provider-zenid/pom.xml
+++ b/enrollment-server-onboarding-provider-zenid/pom.xml
@@ -25,7 +25,7 @@
com.wultra.security
enrollment-server-parent
- 1.6.0-SNAPSHOT
+ 1.7.0-SNAPSHOT
com.wultra.security
diff --git a/enrollment-server-onboarding-provider-zenid/src/main/java/com/wultra/app/onboardingserver/provider/zenid/ZenidDocumentVerificationProvider.java b/enrollment-server-onboarding-provider-zenid/src/main/java/com/wultra/app/onboardingserver/provider/zenid/ZenidDocumentVerificationProvider.java
index 4dc71cfc..3571c78e 100644
--- a/enrollment-server-onboarding-provider-zenid/src/main/java/com/wultra/app/onboardingserver/provider/zenid/ZenidDocumentVerificationProvider.java
+++ b/enrollment-server-onboarding-provider-zenid/src/main/java/com/wultra/app/onboardingserver/provider/zenid/ZenidDocumentVerificationProvider.java
@@ -20,22 +20,22 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.base.Preconditions;
import com.wultra.app.enrollmentserver.model.enumeration.CardSide;
import com.wultra.app.enrollmentserver.model.enumeration.DocumentType;
import com.wultra.app.enrollmentserver.model.enumeration.DocumentVerificationStatus;
import com.wultra.app.enrollmentserver.model.integration.*;
+import com.wultra.app.onboardingserver.api.errorhandling.DocumentVerificationException;
+import com.wultra.app.onboardingserver.api.provider.DocumentVerificationProvider;
import com.wultra.app.onboardingserver.common.database.DocumentVerificationRepository;
import com.wultra.app.onboardingserver.common.database.entity.DocumentResultEntity;
import com.wultra.app.onboardingserver.common.database.entity.DocumentVerificationEntity;
import com.wultra.app.onboardingserver.common.errorhandling.RemoteCommunicationException;
import com.wultra.app.onboardingserver.provider.zenid.model.api.*;
-import com.wultra.app.onboardingserver.api.errorhandling.DocumentVerificationException;
-import com.wultra.app.onboardingserver.api.provider.DocumentVerificationProvider;
import com.wultra.core.rest.client.base.RestClientException;
import jakarta.annotation.Nullable;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -336,7 +336,7 @@ public List parseRejectionReasons(DocumentResultEntity docResult) throws
@Override
public VerificationSdkInfo initVerificationSdk(OwnerId id, Map initAttributes) throws RemoteCommunicationException, DocumentVerificationException {
- Preconditions.checkArgument(initAttributes.containsKey(SDK_INIT_TOKEN), "Missing initialization token for ZenID SDK");
+ Validate.isTrue(initAttributes.containsKey(SDK_INIT_TOKEN), "Missing initialization token for ZenID SDK");
String token = initAttributes.get(SDK_INIT_TOKEN);
ResponseEntity responseEntity;
diff --git a/enrollment-server-onboarding-provider-zenid/src/main/java/com/wultra/app/onboardingserver/provider/zenid/ZenidRestApiService.java b/enrollment-server-onboarding-provider-zenid/src/main/java/com/wultra/app/onboardingserver/provider/zenid/ZenidRestApiService.java
index c98964a2..200cf86f 100644
--- a/enrollment-server-onboarding-provider-zenid/src/main/java/com/wultra/app/onboardingserver/provider/zenid/ZenidRestApiService.java
+++ b/enrollment-server-onboarding-provider-zenid/src/main/java/com/wultra/app/onboardingserver/provider/zenid/ZenidRestApiService.java
@@ -17,7 +17,6 @@
*/
package com.wultra.app.onboardingserver.provider.zenid;
-import com.google.common.base.Preconditions;
import com.wultra.app.enrollmentserver.model.enumeration.CardSide;
import com.wultra.app.enrollmentserver.model.enumeration.DocumentType;
import com.wultra.app.enrollmentserver.model.integration.OwnerId;
@@ -27,6 +26,7 @@
import com.wultra.core.rest.client.base.RestClientException;
import jakarta.annotation.Nullable;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.Validate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -104,7 +104,7 @@ public ZenidRestApiService(
*/
public ResponseEntity uploadSample(OwnerId ownerId, SubmittedDocument document)
throws RestClientException {
- Preconditions.checkNotNull(document.getPhoto(), "Missing photo in " + document);
+ Validate.notNull(document.getPhoto(), "Missing photo in " + document);
final MultiValueMap queryParams = buildQueryParams(ownerId, document);
@@ -148,9 +148,8 @@ public ResponseEntity syncSample(String documentId
* @param sampleIds Ids of previously uploaded samples.
* @return Response entity with the investigation result
*/
- public ResponseEntity investigateSamples(List sampleIds)
- throws RestClientException {
- Preconditions.checkArgument(sampleIds.size() > 0, "Missing sample ids for investigation");
+ public ResponseEntity investigateSamples(List sampleIds) throws RestClientException {
+ Validate.notEmpty(sampleIds, "Missing sample ids for investigation");
MultiValueMap queryParams = new LinkedMultiValueMap<>();
sampleIds.forEach(sampleId -> queryParams.add("sampleIDs", sampleId));
diff --git a/enrollment-server-onboarding/pom.xml b/enrollment-server-onboarding/pom.xml
index 3eed482d..1d883cea 100644
--- a/enrollment-server-onboarding/pom.xml
+++ b/enrollment-server-onboarding/pom.xml
@@ -29,7 +29,7 @@
com.wultra.security
enrollment-server-parent
- 1.6.0-SNAPSHOT
+ 1.7.0-SNAPSHOT
@@ -101,6 +101,11 @@
spring-boot-starter-validation
+
+ com.github.ben-manes.caffeine
+ caffeine
+
+
jakarta.servlet
jakarta.servlet-api
@@ -148,6 +153,16 @@
micrometer-registry-prometheus
+
+ io.projectreactor
+ reactor-core-micrometer
+
+
+
+ io.micrometer
+ micrometer-tracing-bridge-otel
+
+
org.springframework.boot
diff --git a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/configuration/IdentityVerificationConfig.java b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/configuration/IdentityVerificationConfig.java
index 37e1d40b..379db3cd 100644
--- a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/configuration/IdentityVerificationConfig.java
+++ b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/configuration/IdentityVerificationConfig.java
@@ -79,6 +79,9 @@ public class IdentityVerificationConfig {
@Value("${enrollment-server-onboarding.client-evaluation.max-failed-attempts:5}")
private int clientEvaluationMaxFailedAttempts;
+ @Value("${enrollment-server-onboarding.client-evaluation.include-extracted-data:false}")
+ private boolean sendingExtractedDataEnabled;
+
@PostConstruct
void validate() {
// Once in the future, we may replace OTP in SCA by NFC document reading
diff --git a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/docverify/mock/provider/WultraMockDocumentVerificationProvider.java b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/docverify/mock/provider/WultraMockDocumentVerificationProvider.java
index 69283098..b4b384d6 100644
--- a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/docverify/mock/provider/WultraMockDocumentVerificationProvider.java
+++ b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/docverify/mock/provider/WultraMockDocumentVerificationProvider.java
@@ -17,18 +17,18 @@
*/
package com.wultra.app.onboardingserver.docverify.mock.provider;
-import com.google.common.base.Ascii;
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
import com.wultra.app.enrollmentserver.model.enumeration.DocumentType;
import com.wultra.app.enrollmentserver.model.enumeration.DocumentVerificationStatus;
import com.wultra.app.enrollmentserver.model.integration.*;
+import com.wultra.app.onboardingserver.api.errorhandling.DocumentVerificationException;
+import com.wultra.app.onboardingserver.api.provider.DocumentVerificationProvider;
import com.wultra.app.onboardingserver.common.database.entity.DocumentResultEntity;
import com.wultra.app.onboardingserver.common.database.entity.DocumentVerificationEntity;
import com.wultra.app.onboardingserver.docverify.mock.MockConst;
-import com.wultra.app.onboardingserver.api.errorhandling.DocumentVerificationException;
-import com.wultra.app.onboardingserver.api.provider.DocumentVerificationProvider;
import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
@@ -65,11 +65,12 @@ public class WultraMockDocumentVerificationProvider implements DocumentVerificat
public WultraMockDocumentVerificationProvider() {
logger.warn("Using mocked version of {}", DocumentVerificationProvider.class.getName());
- submittedDocs = CacheBuilder.newBuilder()
+ // TODO (racansky, 2024-01-04) consider removing Caffeine dependency and replace it by simple LinkedHashMap#removeEldestEntry
+ submittedDocs = Caffeine.newBuilder()
.expireAfterWrite(Duration.ofHours(1))
.build();
- verificationUploadIds = CacheBuilder.newBuilder()
+ verificationUploadIds = Caffeine.newBuilder()
.expireAfterWrite(Duration.ofHours(1))
.build();
}
@@ -242,7 +243,7 @@ private DocumentSubmitResult toDocumentSubmitResult(final SubmittedDocument docu
if (docId.startsWith("upload")) {
uploadedDocId = docId;
} else {
- uploadedDocId = Ascii.truncate("uploaded-" + docId, 36, "...");
+ uploadedDocId = StringUtils.truncate("uploaded-" + docId, 33) + "...";
}
submitResult.setUploadId(uploadedDocId);
submitResult.setValidationResult("{\"validationResult\": { \"data\": \"" + docId + "\" } }");
diff --git a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/ClientEvaluationService.java b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/ClientEvaluationService.java
index 640e1cde..cd4b19f2 100644
--- a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/ClientEvaluationService.java
+++ b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/ClientEvaluationService.java
@@ -21,7 +21,9 @@
import com.wultra.app.enrollmentserver.model.enumeration.ErrorOrigin;
import com.wultra.app.enrollmentserver.model.enumeration.IdentityVerificationPhase;
import com.wultra.app.enrollmentserver.model.enumeration.IdentityVerificationStatus;
+import com.wultra.app.enrollmentserver.model.integration.DocumentSubmitResult;
import com.wultra.app.enrollmentserver.model.integration.OwnerId;
+import com.wultra.app.onboardingserver.common.database.entity.DocumentResultEntity;
import com.wultra.app.onboardingserver.common.database.entity.DocumentVerificationEntity;
import com.wultra.app.onboardingserver.common.database.entity.IdentityVerificationEntity;
import com.wultra.app.onboardingserver.common.service.AuditService;
@@ -33,6 +35,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import java.util.List;
import java.util.Set;
import static com.wultra.app.enrollmentserver.model.enumeration.IdentityVerificationStatus.ACCEPTED;
@@ -86,20 +89,27 @@ public ClientEvaluationService(
public void processClientEvaluation(final IdentityVerificationEntity identityVerification, final OwnerId ownerId) {
logger.debug("Client evaluation started for {}", identityVerification);
+ final Set acceptedDocuments = selectAcceptedDocuments(identityVerification);
+
final String verificationId;
try {
- verificationId = getVerificationId(identityVerification);
+ verificationId = fetchVerificationId(identityVerification, acceptedDocuments);
} catch (Exception e) {
processVerificationIdError(identityVerification, ownerId, e);
return;
}
- final EvaluateClientRequest request = EvaluateClientRequest.builder()
+ final EvaluateClientRequest.EvaluateClientRequestBuilder requestBuilder = EvaluateClientRequest.builder()
.processId(identityVerification.getProcessId())
.userId(identityVerification.getUserId())
.identityVerificationId(identityVerification.getId())
.verificationId(verificationId)
- .build();
+ .provider(config.getDocumentVerificationProvider());
+
+ if (config.isSendingExtractedDataEnabled()) {
+ requestBuilder.extractedData(fetchDocumentsExtractedData(acceptedDocuments, identityVerification));
+ }
+ final EvaluateClientRequest request = requestBuilder.build();
final int maxFailedAttempts = config.getClientEvaluationMaxFailedAttempts();
for (int i = 0; i < maxFailedAttempts; i++) {
@@ -117,10 +127,29 @@ public void processClientEvaluation(final IdentityVerificationEntity identityVer
processTooManyEvaluationError(identityVerification, ownerId);
}
- private static String getVerificationId(final IdentityVerificationEntity identityVerification) {
- final Set verificationIds = identityVerification.getDocumentVerifications().stream()
+ private static Set selectAcceptedDocuments(final IdentityVerificationEntity identityVerification) {
+ return identityVerification.getDocumentVerifications().stream()
.filter(DocumentVerificationEntity::isUsedForVerification)
.filter(it -> it.getStatus() == DocumentStatus.ACCEPTED)
+ .collect(toSet());
+ }
+
+ private static List fetchDocumentsExtractedData(final Set documents, final IdentityVerificationEntity identityVerification) {
+ return documents.stream()
+ .map(doc -> selectLatestDocumentResult(doc, identityVerification))
+ .map(DocumentResultEntity::getExtractedData)
+ .filter(data -> !DocumentSubmitResult.NO_DATA_EXTRACTED.equals(data))
+ .toList();
+ }
+
+ private static DocumentResultEntity selectLatestDocumentResult(final DocumentVerificationEntity documentVerificationEntity, final IdentityVerificationEntity identityVerification) {
+ return documentVerificationEntity.getResults().stream()
+ .findFirst()
+ .orElseThrow(() -> new IllegalStateException("Missing document result for %s of %s".formatted(documentVerificationEntity, identityVerification)));
+ }
+
+ private static String fetchVerificationId(final IdentityVerificationEntity identityVerification, final Set documents) {
+ final Set verificationIds = documents.stream()
.map(DocumentVerificationEntity::getVerificationId)
.collect(toSet());
diff --git a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/IdentityVerificationService.java b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/IdentityVerificationService.java
index f4c8c2d8..907d9ad4 100644
--- a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/IdentityVerificationService.java
+++ b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/IdentityVerificationService.java
@@ -530,21 +530,9 @@ public Image getPhotoById(final String photoId, final OwnerId ownerId) throws Do
}
public List createDocsMetadata(List entities) {
- List docsMetadata = new ArrayList<>();
- entities.forEach(entity -> {
- DocumentMetadataResponseDto docMetadata = toDocumentMetadata(entity);
-
- if (DocumentStatus.REJECTED.equals(entity.getStatus())) {
- List errors = collectRejectionErrors(entity);
- if (docMetadata.getErrors() == null) {
- docMetadata.setErrors(new ArrayList<>());
- }
- docMetadata.getErrors().addAll(errors);
- }
-
- docsMetadata.add(docMetadata);
- });
- return docsMetadata;
+ return entities.stream()
+ .map(this::toDocumentMetadata)
+ .toList();
}
/**
@@ -616,8 +604,9 @@ private List collectRejectionErrors(DocumentVerificationEntity entity) {
private DocumentMetadataResponseDto toDocumentMetadata(DocumentVerificationEntity entity) {
DocumentMetadataResponseDto docMetadata = new DocumentMetadataResponseDto();
docMetadata.setId(entity.getId());
- if (StringUtils.isNotBlank(entity.getErrorDetail())) {
- docMetadata.setErrors(List.of(entity.getErrorDetail()));
+ // Hide specific error reason if any.
+ if (StringUtils.isNotBlank(entity.getErrorDetail()) || StringUtils.isNotBlank(entity.getRejectReason())) {
+ docMetadata.setErrors(List.of("Error verifying the document."));
}
docMetadata.setFilename(entity.getFilename());
docMetadata.setSide(entity.getSide());
diff --git a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/ImageProcessor.java b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/ImageProcessor.java
index 2b4db63e..3dd5d7d6 100644
--- a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/ImageProcessor.java
+++ b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/ImageProcessor.java
@@ -17,7 +17,6 @@
*/
package com.wultra.app.onboardingserver.impl.service;
-import com.google.common.io.Files;
import com.wultra.app.enrollmentserver.model.integration.Image;
import com.wultra.app.enrollmentserver.model.integration.OwnerId;
import com.wultra.app.onboardingserver.api.errorhandling.PresenceCheckException;
@@ -74,7 +73,7 @@ public Image upscaleImage(final OwnerId ownerId, final Image sourceImage, final
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageIO.write(bufferedOutputImage, TYPE_PNG, outputStream);
- final String filenamePng = Files.getNameWithoutExtension(filename) + SUFFIX_PNG;
+ final String filenamePng = getFilenameWithoutExtension(filename) + SUFFIX_PNG;
final byte[] targetData = outputStream.toByteArray();
logger.debug("Image: {}, size: {} KB, {}", filenamePng, targetData.length / KILOBYTE, ownerId);
@@ -90,4 +89,12 @@ public Image upscaleImage(final OwnerId ownerId, final Image sourceImage, final
throw new PresenceCheckException("Unable to read image", e);
}
}
+
+ private static String getFilenameWithoutExtension(final String filename) {
+ if (filename.contains(".")) {
+ return filename.substring(0, filename.lastIndexOf("."));
+ } else {
+ return filename;
+ }
+ }
}
diff --git a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/PresenceCheckService.java b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/PresenceCheckService.java
index 12471f92..4c356a58 100644
--- a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/PresenceCheckService.java
+++ b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/PresenceCheckService.java
@@ -17,8 +17,6 @@
*/
package com.wultra.app.onboardingserver.impl.service;
-import com.google.common.base.Ascii;
-import com.google.common.base.Preconditions;
import com.wultra.app.enrollmentserver.model.enumeration.*;
import com.wultra.app.enrollmentserver.model.integration.*;
import com.wultra.app.onboardingserver.api.errorhandling.DocumentVerificationException;
@@ -40,6 +38,7 @@
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
@@ -151,7 +150,7 @@ private void submitSelfiePhoto(final OwnerId ownerId, final IdentityVerification
final SubmittedDocument submittedDoc = new SubmittedDocument();
// TODO use different random id approach
submittedDoc.setDocumentId(
- Ascii.truncate("selfie-photo-" + ownerId.getActivationId(), 36, "...")
+ StringUtils.truncate("selfie-photo-" + ownerId.getActivationId(), 33) + "..."
);
submittedDoc.setPhoto(photo);
submittedDoc.setType(DocumentType.SELFIE_PHOTO);
@@ -290,7 +289,7 @@ private List getDocsWithPhoto(final IdentityVerifica
}
docsWithPhoto.forEach(docWithPhoto ->
- Preconditions.checkNotNull(docWithPhoto.getPhotoId(), "Expected photoId value in " + docWithPhoto)
+ Validate.notNull(docWithPhoto.getPhotoId(), "Expected photoId value in " + docWithPhoto)
);
return docsWithPhoto;
diff --git a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/document/DocumentProcessingService.java b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/document/DocumentProcessingService.java
index 7dae9452..99a91b9d 100644
--- a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/document/DocumentProcessingService.java
+++ b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/service/document/DocumentProcessingService.java
@@ -17,7 +17,6 @@
*/
package com.wultra.app.onboardingserver.impl.service.document;
-import com.google.common.base.Strings;
import com.wultra.app.enrollmentserver.api.model.onboarding.request.DocumentSubmitRequest;
import com.wultra.app.enrollmentserver.model.Document;
import com.wultra.app.enrollmentserver.model.DocumentMetadata;
@@ -239,7 +238,7 @@ private void checkDocumentResubmit(final OwnerId ownerId, final DocumentSubmitRe
* @param docVerification Resubmitted document.
*/
private void handleResubmit(final OwnerId ownerId, final String originalDocumentId, final DocumentVerificationEntity docVerification) {
- if (Strings.isNullOrEmpty(originalDocumentId)) {
+ if (StringUtils.isBlank(originalDocumentId)) {
logger.debug("Document {} is not a resubmit {}", docVerification, ownerId);
return;
}
diff --git a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/util/ConditionalOnPropertyNotEmpty.java b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/util/ConditionalOnPropertyNotEmpty.java
index 69ee892f..bbd818c6 100644
--- a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/util/ConditionalOnPropertyNotEmpty.java
+++ b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/impl/util/ConditionalOnPropertyNotEmpty.java
@@ -18,7 +18,7 @@
package com.wultra.app.onboardingserver.impl.util;
-import com.google.common.base.Strings;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
@@ -52,7 +52,7 @@ public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata)
Map attrs = metadata.getAnnotationAttributes(ConditionalOnPropertyNotEmpty.class.getName());
String propertyName = (String) Objects.requireNonNull(attrs).get("value");
String val = context.getEnvironment().getProperty(propertyName);
- return !Strings.nullToEmpty(val).trim().isEmpty();
+ return StringUtils.isNotBlank(val);
}
}
diff --git a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/provider/model/request/EvaluateClientRequest.java b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/provider/model/request/EvaluateClientRequest.java
index b175d88d..3ee5c759 100644
--- a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/provider/model/request/EvaluateClientRequest.java
+++ b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/provider/model/request/EvaluateClientRequest.java
@@ -22,6 +22,8 @@
import com.wultra.core.annotations.PublicApi;
import lombok.*;
+import java.util.List;
+
/**
* Request object for {@link OnboardingProvider#evaluateClient(EvaluateClientRequest)}.
*
@@ -45,4 +47,11 @@ public final class EvaluateClientRequest {
@NonNull
private String verificationId;
+
+ private String provider;
+
+ /**
+ * Data extracted from each document/page. Format is defined by the document verification provider used.
+ */
+ private List extractedData;
}
diff --git a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/provider/rest/ClientEvaluateRequestDto.java b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/provider/rest/ClientEvaluateRequestDto.java
index f636460d..8e73a915 100644
--- a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/provider/rest/ClientEvaluateRequestDto.java
+++ b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/provider/rest/ClientEvaluateRequestDto.java
@@ -19,6 +19,8 @@
import lombok.Data;
+import java.util.List;
+
/**
* Request object for client evaluation.
*
@@ -39,4 +41,9 @@ class ClientEvaluateRequestDto {
private String verificationId;
private String provider;
+
+ /**
+ * Data extracted from each document/page. Format is defined by the document verification provider used.
+ */
+ private List extractedData;
}
diff --git a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/provider/rest/RestOnboardingProvider.java b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/provider/rest/RestOnboardingProvider.java
index 814ebd87..d6607bed 100644
--- a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/provider/rest/RestOnboardingProvider.java
+++ b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/provider/rest/RestOnboardingProvider.java
@@ -245,6 +245,8 @@ private static ClientEvaluateRequestDto convert(final EvaluateClientRequest sour
target.setIdentityVerificationId(source.getIdentityVerificationId());
target.setUserId(source.getUserId());
target.setVerificationId(source.getVerificationId());
+ target.setProvider(source.getProvider());
+ target.setExtractedData(source.getExtractedData());
return target;
}
}
diff --git a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/statemachine/util/StateContextUtil.java b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/statemachine/util/StateContextUtil.java
index 8b45bac1..085db8d4 100644
--- a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/statemachine/util/StateContextUtil.java
+++ b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/statemachine/util/StateContextUtil.java
@@ -16,11 +16,11 @@
*/
package com.wultra.app.onboardingserver.statemachine.util;
-import com.google.common.base.Preconditions;
import com.wultra.app.onboardingserver.statemachine.consts.ExtendedStateVariable;
import com.wultra.app.onboardingserver.statemachine.enums.OnboardingEvent;
import com.wultra.app.onboardingserver.statemachine.enums.OnboardingState;
import io.getlime.core.rest.model.base.response.Response;
+import org.apache.commons.lang3.Validate;
import org.springframework.http.HttpStatus;
import org.springframework.statemachine.StateContext;
@@ -38,7 +38,7 @@ private StateContextUtil() {
}
public static void setResponseOk(final StateContext context, final Response response) {
- Preconditions.checkArgument(
+ Validate.isTrue(
!context.getStateMachine().hasStateMachineError(),
String.format("Found state machine error in %s, when expected ok", context)
);
diff --git a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/task/cleaning/CleaningService.java b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/task/cleaning/CleaningService.java
index 7d32cdcd..cbf91a4f 100644
--- a/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/task/cleaning/CleaningService.java
+++ b/enrollment-server-onboarding/src/main/java/com/wultra/app/onboardingserver/task/cleaning/CleaningService.java
@@ -17,7 +17,6 @@
*/
package com.wultra.app.onboardingserver.task.cleaning;
-import com.google.common.collect.Lists;
import com.wultra.app.enrollmentserver.model.enumeration.DocumentStatus;
import com.wultra.app.enrollmentserver.model.enumeration.ErrorOrigin;
import com.wultra.app.enrollmentserver.model.enumeration.OnboardingStatus;
@@ -33,8 +32,11 @@
import org.springframework.transaction.annotation.Transactional;
import java.time.Duration;
+import java.util.Collection;
import java.util.Date;
import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
/**
* Service with cleaning functionality.
@@ -120,7 +122,7 @@ public void terminateExpiredOtpCodes() {
final Date createdDateExpiredOtp = DateUtil.convertExpirationToCreatedDate(otpExpiration);
final List otpIds = onboardingOtpRepository.findExpiredIds(createdDateExpiredOtp);
final Date now = new Date();
- for (List otpIdChunk : Lists.partition(otpIds, BATCH_SIZE)) {
+ for (List otpIdChunk : ListUtils.partition(otpIds, BATCH_SIZE)) {
terminateAndAuditOtps(otpIdChunk, now);
}
}
@@ -139,7 +141,7 @@ public void terminateExpiredProcesses() {
return;
}
logger.info("Terminating {} expired processes", ids.size());
- for (List idsChunk : Lists.partition(ids, BATCH_SIZE)) {
+ for (List idsChunk : ListUtils.partition(ids, BATCH_SIZE)) {
terminateAndAuditProcesses(idsChunk, now, OnboardingProcessEntity.ERROR_PROCESS_EXPIRED_ONBOARDING, ErrorOrigin.PROCESS_LIMIT_CHECK);
}
}
@@ -165,7 +167,7 @@ public void terminateExpiredDocumentVerifications() {
}
final Date now = new Date();
- for (List idsChunk : Lists.partition(ids, BATCH_SIZE)) {
+ for (List idsChunk : ListUtils.partition(ids, BATCH_SIZE)) {
logger.info("Terminating {} expired document verifications", idsChunk.size());
terminateAndAuditDocuments(idsChunk, now, ERROR_MESSAGE_DOCUMENT_VERIFICATION_EXPIRED, ErrorOrigin.PROCESS_LIMIT_CHECK);
}
@@ -184,7 +186,7 @@ public void terminateExpiredIdentityVerifications() {
final Date now = new Date();
final ErrorOrigin errorOrigin = ErrorOrigin.PROCESS_LIMIT_CHECK;
- for (List idsChunk : Lists.partition(ids, BATCH_SIZE)) {
+ for (List idsChunk : ListUtils.partition(ids, BATCH_SIZE)) {
logger.info("Terminating {} expired identity verifications", idsChunk.size());
terminateAndAuditIdentityVerifications(idsChunk, now, OnboardingProcessEntity.ERROR_PROCESS_EXPIRED_ONBOARDING, errorOrigin);
}
@@ -207,7 +209,7 @@ private void terminateProcessesAndRelatedEntities(final List processIds,
final Date now = new Date();
final ErrorOrigin errorOrigin = ErrorOrigin.PROCESS_LIMIT_CHECK;
- for (List processIdChunk : Lists.partition(processIds, BATCH_SIZE)) {
+ for (List processIdChunk : ListUtils.partition(processIds, BATCH_SIZE)) {
logger.info("Terminating {} processes", processIdChunk.size());
terminateAndAuditProcesses(processIdChunk, now, errorDetail, errorOrigin);
@@ -248,4 +250,21 @@ private void terminateAndAuditDocuments(final List documentIds, final Da
documentVerificationRepository.findById(documentId).ifPresent(document ->
auditService.audit(document, "Expired Document verification for user: {}, {}", document.getIdentityVerification().getUserId(), errorDetail)));
}
+
+ protected static final class ListUtils {
+
+ private ListUtils() {
+ throw new IllegalStateException("Utility class");
+ }
+
+ public static Collection> partition(final List source, final int partitionSize) {
+ if (source.size() <= partitionSize) {
+ return List.of(source);
+ }
+ return IntStream.range(0, source.size())
+ .boxed()
+ .collect(Collectors.groupingBy(partition -> (partition / partitionSize), Collectors.mapping(source::get, Collectors.toList())))
+ .values();
+ }
+ }
}
diff --git a/enrollment-server-onboarding/src/main/resources/application.properties b/enrollment-server-onboarding/src/main/resources/application.properties
index 77e71e94..fe3aa094 100644
--- a/enrollment-server-onboarding/src/main/resources/application.properties
+++ b/enrollment-server-onboarding/src/main/resources/application.properties
@@ -28,7 +28,6 @@ banner.application.version=@project.version@
spring.datasource.url=jdbc:postgresql://localhost:5432/powerauth
spring.datasource.username=powerauth
spring.datasource.password=
-spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.hikari.auto-commit=false
spring.jpa.properties.hibernate.connection.characterEncoding=utf8
spring.jpa.properties.hibernate.connection.useUnicode=true
@@ -37,7 +36,6 @@ spring.jpa.properties.hibernate.connection.useUnicode=true
#spring.datasource.url=jdbc:oracle:thin:@//127.0.0.1:1521/powerauth
#spring.datasource.username=powerauth
#spring.datasource.password=
-#spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
# Hibernate Configuration
spring.jpa.hibernate.ddl-auto=none
@@ -90,6 +88,7 @@ enrollment-server-onboarding.onboarding-process.max-error-score=15
# Client Evaluation Configuration
enrollment-server-onboarding.client-evaluation.max-failed-attempts=5
+enrollment-server-onboarding.client-evaluation.include-extracted-data=false
# Identity Verification Configuration
enrollment-server-onboarding.identity-verification.enabled=false
@@ -222,6 +221,7 @@ powerauth.service.correlation-header.value.validation-regexp=[a-zA-Z0-9\\-]{8,10
#logging.pattern.console=%clr(%d{${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:%5p}) [%X{X-Correlation-ID}] %clr(%5p) %clr(${PID: }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}
# Monitoring
+management.tracing.sampling.probability=1.0
#management.endpoint.metrics.enabled=true
#management.endpoints.web.exposure.include=health, prometheus
#management.endpoint.prometheus.enabled=true
diff --git a/enrollment-server-onboarding/src/test/java/com/wultra/app/onboardingserver/impl/service/ClientEvaluationServiceTest.java b/enrollment-server-onboarding/src/test/java/com/wultra/app/onboardingserver/impl/service/ClientEvaluationServiceTest.java
index 28caa5d0..55dd8ec5 100644
--- a/enrollment-server-onboarding/src/test/java/com/wultra/app/onboardingserver/impl/service/ClientEvaluationServiceTest.java
+++ b/enrollment-server-onboarding/src/test/java/com/wultra/app/onboardingserver/impl/service/ClientEvaluationServiceTest.java
@@ -19,7 +19,9 @@
import com.wultra.app.enrollmentserver.model.enumeration.DocumentStatus;
import com.wultra.app.enrollmentserver.model.enumeration.ErrorOrigin;
+import com.wultra.app.enrollmentserver.model.integration.DocumentSubmitResult;
import com.wultra.app.enrollmentserver.model.integration.OwnerId;
+import com.wultra.app.onboardingserver.common.database.entity.DocumentResultEntity;
import com.wultra.app.onboardingserver.common.database.entity.DocumentVerificationEntity;
import com.wultra.app.onboardingserver.common.database.entity.IdentityVerificationEntity;
import com.wultra.app.onboardingserver.common.service.AuditService;
@@ -34,6 +36,7 @@
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
+import java.util.List;
import java.util.Set;
import java.util.UUID;
@@ -71,12 +74,15 @@ class ClientEvaluationServiceTest {
void testProcessClientEvaluation_successful() throws Exception {
when(identityVerificationConfig.getClientEvaluationMaxFailedAttempts())
.thenReturn(1);
+ when(identityVerificationConfig.isSendingExtractedDataEnabled())
+ .thenReturn(true);
final EvaluateClientRequest evaluateClientRequest = EvaluateClientRequest.builder()
.processId("p1")
.userId("u1")
.identityVerificationId("i1")
.verificationId("v1")
+ .extractedData(List.of("d1_data"))
.build();
final EvaluateClientResponse evaluateClientResponse = EvaluateClientResponse.builder()
.accepted(true)
@@ -90,8 +96,8 @@ void testProcessClientEvaluation_successful() throws Exception {
identityVerification.setUserId("u1");
identityVerification.setPhase(CLIENT_EVALUATION);
identityVerification.setDocumentVerifications(Set.of(
- createDocumentVerification("d1", DocumentStatus.ACCEPTED, "v1"),
- createDocumentVerification("d2", DocumentStatus.ACCEPTED, "v1"),
+ createDocumentVerificationWithResults("d1", DocumentStatus.ACCEPTED, "v1", "d1_data"),
+ createDocumentVerificationWithResults("d2", DocumentStatus.ACCEPTED, "v1", DocumentSubmitResult.NO_DATA_EXTRACTED),
createDocumentVerification("d3", DocumentStatus.DISPOSED, "v2")));
final OwnerId ownerId = new OwnerId();
@@ -166,4 +172,13 @@ private static DocumentVerificationEntity createDocumentVerification(final Strin
documentVerification.setUsedForVerification(true);
return documentVerification;
}
+
+ private static DocumentVerificationEntity createDocumentVerificationWithResults(final String id, final DocumentStatus status, final String verificationId, final String extractedData) {
+ final DocumentResultEntity documentResult = new DocumentResultEntity();
+ documentResult.setExtractedData(extractedData);
+
+ final DocumentVerificationEntity documentVerification = createDocumentVerification(id, status, verificationId);
+ documentVerification.setResults(Set.of(documentResult));
+ return documentVerification;
+ }
}
diff --git a/enrollment-server-onboarding/src/test/java/com/wultra/app/onboardingserver/impl/service/IdentityVerificationServiceTest.java b/enrollment-server-onboarding/src/test/java/com/wultra/app/onboardingserver/impl/service/IdentityVerificationServiceTest.java
index 02fda8c0..a9fb95c7 100644
--- a/enrollment-server-onboarding/src/test/java/com/wultra/app/onboardingserver/impl/service/IdentityVerificationServiceTest.java
+++ b/enrollment-server-onboarding/src/test/java/com/wultra/app/onboardingserver/impl/service/IdentityVerificationServiceTest.java
@@ -17,9 +17,13 @@
*/
package com.wultra.app.onboardingserver.impl.service;
+import com.wultra.app.enrollmentserver.model.enumeration.DocumentStatus;
import com.wultra.app.enrollmentserver.model.enumeration.ErrorOrigin;
import com.wultra.app.enrollmentserver.model.integration.OwnerId;
+import com.wultra.app.onboardingserver.api.provider.DocumentVerificationProvider;
import com.wultra.app.onboardingserver.common.database.IdentityVerificationRepository;
+import com.wultra.app.onboardingserver.common.database.entity.DocumentResultEntity;
+import com.wultra.app.onboardingserver.common.database.entity.DocumentVerificationEntity;
import com.wultra.app.onboardingserver.common.database.entity.IdentityVerificationEntity;
import com.wultra.app.onboardingserver.common.service.AuditService;
import org.junit.jupiter.api.Test;
@@ -28,6 +32,10 @@
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.util.CollectionUtils;
+
+import java.util.List;
+import java.util.Set;
import static com.wultra.app.enrollmentserver.model.enumeration.IdentityVerificationPhase.COMPLETED;
import static com.wultra.app.enrollmentserver.model.enumeration.IdentityVerificationPhase.OTP_VERIFICATION;
@@ -35,6 +43,7 @@
import static com.wultra.app.enrollmentserver.model.enumeration.IdentityVerificationStatus.FAILED;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
/**
@@ -54,6 +63,9 @@ class IdentityVerificationServiceTest {
@Mock
private IdentityVerificationPrecompleteCheck identityVerificationPrecompleteCheck;
+ @Mock
+ private DocumentVerificationProvider documentVerificationProvider;
+
@InjectMocks
private IdentityVerificationService tested;
@@ -96,4 +108,49 @@ void testProcessDocumentVerificationResult_invalidPrecompleteGuard() throws Exce
assertThat(savedIdentityVerification.getErrorDetail(), equalTo("documentVerificationFailed"));
assertThat(savedIdentityVerification.getErrorOrigin(), equalTo(ErrorOrigin.FINAL_VALIDATION));
}
+
+ @Test
+ void testCreateDocsMetadata_hideRejectedErrorDetail() {
+ final DocumentVerificationEntity doc = new DocumentVerificationEntity();
+ doc.setStatus(DocumentStatus.REJECTED);
+ doc.setErrorDetail("Hide specific error occurred.");
+
+ final List errors = tested.createDocsMetadata(List.of(doc)).get(0).getErrors();
+ assertHidden(errors);
+ }
+
+ @Test
+ void testCreateDocsMetadata_hideRejectedRejectReason() {
+ final DocumentVerificationEntity doc = new DocumentVerificationEntity();
+ doc.setStatus(DocumentStatus.REJECTED);
+ doc.setRejectReason("Hide specific rejection reason.");
+
+ final List errors = tested.createDocsMetadata(List.of(doc)).get(0).getErrors();
+ assertHidden(errors);
+ }
+
+ @Test
+ void testCreateDocsMetadata_hideFailedErrorDetail() {
+ final DocumentVerificationEntity doc = new DocumentVerificationEntity();
+ doc.setStatus(DocumentStatus.FAILED);
+ doc.setErrorDetail("Hide some error occurred.");
+
+ final List errors = tested.createDocsMetadata(List.of(doc)).get(0).getErrors();
+ assertHidden(errors);
+ }
+
+ @Test
+ void testCreateDocsMetadata_accepted() {
+ final DocumentVerificationEntity doc = new DocumentVerificationEntity();
+ doc.setStatus(DocumentStatus.ACCEPTED);
+
+ final List errors = tested.createDocsMetadata(List.of(doc)).get(0).getErrors();
+ assertTrue(CollectionUtils.isEmpty(errors));
+ }
+
+ private static void assertHidden(final List errors) {
+ assertEquals(1, errors.size());
+ assertEquals("Error verifying the document.", errors.get(0));
+ }
+
}
diff --git a/enrollment-server-onboarding/src/test/java/com/wultra/app/onboardingserver/task/cleaning/CleaningServiceTest.java b/enrollment-server-onboarding/src/test/java/com/wultra/app/onboardingserver/task/cleaning/CleaningServiceTest.java
index 12ccf3fa..8022720c 100644
--- a/enrollment-server-onboarding/src/test/java/com/wultra/app/onboardingserver/task/cleaning/CleaningServiceTest.java
+++ b/enrollment-server-onboarding/src/test/java/com/wultra/app/onboardingserver/task/cleaning/CleaningServiceTest.java
@@ -28,6 +28,11 @@
import org.springframework.test.context.jdbc.Sql;
import org.springframework.transaction.annotation.Transactional;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
import static com.wultra.app.enrollmentserver.model.enumeration.ErrorOrigin.PROCESS_LIMIT_CHECK;
import static org.junit.jupiter.api.Assertions.*;
@@ -227,6 +232,30 @@ void testTerminateExpiredProcessActivations() {
assertEquals(PROCESS_LIMIT_CHECK, documentVerification.getErrorOrigin());
}
+ @Test
+ void testPartition() {
+ final List source = Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h");
+
+ final Collection> result = CleaningService.ListUtils.partition(source, 3);
+
+ assertEquals(3, result.size());
+
+ final Iterator> iterator = result.iterator();
+ assertEquals(List.of("a", "b", "c"), iterator.next());
+ assertEquals(List.of("d", "e", "f"), iterator.next());
+ assertEquals(List.of("g", "h"), iterator.next());
+ }
+
+ @Test
+ void testPartition_tooSmall() {
+ final List source = Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h");
+
+ final Collection> result = CleaningService.ListUtils.partition(source, 10);
+
+ assertEquals(1, result.size());
+ assertEquals(List.of("a", "b", "c", "d", "e", "f", "g", "h"), result.iterator().next());
+ }
+
private void assertStatus(final String id, final DocumentStatus status) {
final DocumentVerificationEntity documentVerification = fetchDocumentVerification(id);
assertEquals(status, documentVerification.getStatus(), "status of " + id);
diff --git a/enrollment-server-onboarding/src/test/resources/application-test.properties b/enrollment-server-onboarding/src/test/resources/application-test.properties
index 5dff6a04..5ee3baba 100644
--- a/enrollment-server-onboarding/src/test/resources/application-test.properties
+++ b/enrollment-server-onboarding/src/test/resources/application-test.properties
@@ -18,7 +18,6 @@
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.username=sa
spring.datasource.password=password
-spring.datasource.driver-class-name=org.h2.Driver
spring.jpa.hibernate.ddl-auto=create
spring.liquibase.enabled=false
diff --git a/enrollment-server/pom.xml b/enrollment-server/pom.xml
index 995cfb9f..f438a954 100644
--- a/enrollment-server/pom.xml
+++ b/enrollment-server/pom.xml
@@ -30,7 +30,7 @@
com.wultra.security
enrollment-server-parent
- 1.6.0-SNAPSHOT
+ 1.7.0-SNAPSHOT
@@ -128,6 +128,16 @@
micrometer-registry-prometheus
+
+ io.projectreactor
+ reactor-core-micrometer
+
+
+
+ io.micrometer
+ micrometer-tracing-bridge-otel
+
+
org.springframework.boot
diff --git a/enrollment-server/src/main/java/com/wultra/app/enrollmentserver/impl/service/PushRegistrationService.java b/enrollment-server/src/main/java/com/wultra/app/enrollmentserver/impl/service/PushRegistrationService.java
index ecaf31ce..7b9705e9 100644
--- a/enrollment-server/src/main/java/com/wultra/app/enrollmentserver/impl/service/PushRegistrationService.java
+++ b/enrollment-server/src/main/java/com/wultra/app/enrollmentserver/impl/service/PushRegistrationService.java
@@ -72,17 +72,11 @@ public Response registerDevice(
throw new InvalidRequestObjectException();
}
- // Get the values from the request
- final String platform = requestObject.getPlatform();
+ final MobilePlatform platform = convert(requestObject.getPlatform());
final String token = requestObject.getToken();
- // Register the device and return response
- MobilePlatform mobilePlatform = MobilePlatform.Android;
- if ("ios".equalsIgnoreCase(platform)) {
- mobilePlatform = MobilePlatform.iOS;
- }
try {
- final boolean result = client.createDevice(applicationId, token, mobilePlatform, activationId);
+ final boolean result = client.createDevice(applicationId, token, platform, activationId);
if (result) {
logger.info("Push registration succeeded, user ID: {}", userId);
return new Response();
@@ -96,4 +90,11 @@ public Response registerDevice(
}
}
+ private static MobilePlatform convert(final PushRegisterRequest.Platform source) {
+ return switch (source) {
+ case IOS -> MobilePlatform.IOS;
+ case ANDROID -> MobilePlatform.ANDROID;
+ };
+ }
+
}
diff --git a/enrollment-server/src/main/java/com/wultra/app/enrollmentserver/impl/util/ConditionalOnPropertyNotEmpty.java b/enrollment-server/src/main/java/com/wultra/app/enrollmentserver/impl/util/ConditionalOnPropertyNotEmpty.java
index 89a6e877..e96aa8c4 100644
--- a/enrollment-server/src/main/java/com/wultra/app/enrollmentserver/impl/util/ConditionalOnPropertyNotEmpty.java
+++ b/enrollment-server/src/main/java/com/wultra/app/enrollmentserver/impl/util/ConditionalOnPropertyNotEmpty.java
@@ -18,7 +18,7 @@
package com.wultra.app.enrollmentserver.impl.util;
-import com.google.common.base.Strings;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
@@ -52,7 +52,7 @@ public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata)
Map attrs = metadata.getAnnotationAttributes(ConditionalOnPropertyNotEmpty.class.getName());
String propertyName = (String) Objects.requireNonNull(attrs).get("value");
String val = context.getEnvironment().getProperty(propertyName);
- return !Strings.nullToEmpty(val).trim().isEmpty();
+ return StringUtils.isNotBlank(val);
}
}
diff --git a/enrollment-server/src/main/java/com/wultra/app/enrollmentserver/model/validator/PushRegisterRequestValidator.java b/enrollment-server/src/main/java/com/wultra/app/enrollmentserver/model/validator/PushRegisterRequestValidator.java
index aa0fe01c..e0a731f9 100644
--- a/enrollment-server/src/main/java/com/wultra/app/enrollmentserver/model/validator/PushRegisterRequestValidator.java
+++ b/enrollment-server/src/main/java/com/wultra/app/enrollmentserver/model/validator/PushRegisterRequestValidator.java
@@ -42,11 +42,8 @@ public static String validate(PushRegisterRequest request) {
}
// Validate mobile platform
- final String platform = request.getPlatform();
- if (StringUtils.isBlank(platform)) {
+ if (request.getPlatform() == null) {
return "No mobile platform was provided when registering for push messages.";
- } else if (!"ios".equalsIgnoreCase(platform) && !"android".equalsIgnoreCase(platform)) { // must be iOS or Android
- return "Unknown mobile platform was provided when registering for push messages.";
}
// Validate push token
@@ -56,7 +53,6 @@ public static String validate(PushRegisterRequest request) {
}
return null;
-
}
}
diff --git a/enrollment-server/src/main/resources/application.properties b/enrollment-server/src/main/resources/application.properties
index 12e3db53..a15ca8e1 100644
--- a/enrollment-server/src/main/resources/application.properties
+++ b/enrollment-server/src/main/resources/application.properties
@@ -28,7 +28,6 @@ banner.application.version=@project.version@
spring.datasource.url=jdbc:postgresql://localhost:5432/powerauth
spring.datasource.username=powerauth
spring.datasource.password=
-spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.hikari.auto-commit=false
spring.jpa.properties.hibernate.connection.characterEncoding=utf8
spring.jpa.properties.hibernate.connection.useUnicode=true
@@ -37,7 +36,6 @@ spring.jpa.properties.hibernate.connection.useUnicode=true
#spring.datasource.url=jdbc:oracle:thin:@//127.0.0.1:1521/powerauth
#spring.datasource.username=powerauth
#spring.datasource.password=
-#spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
# Hibernate Configuration
spring.jpa.hibernate.ddl-auto=none
@@ -90,7 +88,10 @@ powerauth.service.correlation-header.value.validation-regexp=[a-zA-Z0-9\\-]{8,10
#logging.pattern.console=%clr(%d{${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:%5p}) [%X{X-Correlation-ID}] %clr(%5p) %clr(${PID: }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}
# Monitoring
+management.tracing.sampling.probability=1.0
#management.endpoint.metrics.enabled=true
#management.endpoints.web.exposure.include=health, prometheus
#management.endpoint.prometheus.enabled=true
#management.prometheus.metrics.export.enabled=true
+
+spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration
diff --git a/enrollment-server/src/test/java/com/wultra/app/enrollmentserver/impl/service/converter/MobileTokenConverterTest.java b/enrollment-server/src/test/java/com/wultra/app/enrollmentserver/impl/service/converter/MobileTokenConverterTest.java
index c17ce661..16e32c57 100644
--- a/enrollment-server/src/test/java/com/wultra/app/enrollmentserver/impl/service/converter/MobileTokenConverterTest.java
+++ b/enrollment-server/src/test/java/com/wultra/app/enrollmentserver/impl/service/converter/MobileTokenConverterTest.java
@@ -18,7 +18,6 @@
package com.wultra.app.enrollmentserver.impl.service.converter;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.collect.ImmutableMap;
import com.wultra.app.enrollmentserver.database.entity.OperationTemplateEntity;
import com.wultra.app.enrollmentserver.errorhandling.MobileTokenConfigurationException;
import com.wultra.security.powerauth.client.model.enumeration.OperationStatus;
@@ -362,27 +361,27 @@ void testConvertUiPostApprovalGenericMessageWithSubstitutedDangerousChars() thro
@Test
void testConvertAttributes() throws Exception {
final OperationDetailResponse operationDetail = createOperationDetailResponse();
- operationDetail.setParameters(ImmutableMap.builder()
- .put("amount", "13.7")
- .put("currency", "EUR")
- .put("iban", "AT483200000012345864")
- .put("note", "Remember me")
- .put("headingLevel", "3")
- .put("thumbnailUrl", "https://example.com/123_thumb.jpeg")
- .put("originalUrl", "https://example.com/123.jpeg")
- .put("sourceAmount", "1.26")
- .put("sourceCurrency", "ETH")
- .put("targetAmount", "1710.98")
- .put("targetCurrency", "USD")
- .put("dynamic", "true")
- .put("partyLogoUrl", "https://example.com/img/logo/logo.svg")
- .put("partyName", "Example Ltd.")
- .put("partyDescription", "Find out more about Example...")
- .put("partyUrl", "https://example.com/hello")
- .put("alertType", "WARNING")
- .put("alertTitle", "Insufficient Balance")
- .put("alertMessage", "You have only $1.00 on your account with number 238400856/0300.")
- .build());
+ operationDetail.setParameters(Map.ofEntries(
+ Map.entry("amount", "13.7"),
+ Map.entry("currency", "EUR"),
+ Map.entry("iban", "AT483200000012345864"),
+ Map.entry("note", "Remember me"),
+ Map.entry("headingLevel", "3"),
+ Map.entry("thumbnailUrl", "https://example.com/123_thumb.jpeg"),
+ Map.entry("originalUrl", "https://example.com/123.jpeg"),
+ Map.entry("sourceAmount", "1.26"),
+ Map.entry("sourceCurrency", "ETH"),
+ Map.entry("targetAmount", "1710.98"),
+ Map.entry("targetCurrency", "USD"),
+ Map.entry("dynamic", "true"),
+ Map.entry("partyLogoUrl", "https://example.com/img/logo/logo.svg"),
+ Map.entry("partyName", "Example Ltd."),
+ Map.entry("partyDescription", "Find out more about Example..."),
+ Map.entry("partyUrl", "https://example.com/hello"),
+ Map.entry("alertType", "WARNING"),
+ Map.entry("alertTitle", "Insufficient Balance"),
+ Map.entry("alertMessage", "You have only $1.00 on your account with number 238400856/0300.")
+ ));
final OperationTemplateEntity operationTemplate = new OperationTemplateEntity();
operationTemplate.setAttributes("""
@@ -518,10 +517,10 @@ void testConvertAttributes() throws Exception {
@Test
void testConvertAmount_notANumber() throws Exception {
final OperationDetailResponse operationDetail = createOperationDetailResponse();
- operationDetail.setParameters(ImmutableMap.builder()
- .put("amount", "not a number")
- .put("currency", "CZK")
- .build());
+ operationDetail.setParameters(Map.of(
+ "amount", "not a number",
+ "currency", "CZK"
+ ));
final OperationTemplateEntity operationTemplate = new OperationTemplateEntity();
operationTemplate.setAttributes("""
@@ -558,13 +557,13 @@ void testConvertAmount_notANumber() throws Exception {
@Test
void testConvertAmountConversion_sourceNotANumber() throws Exception {
final OperationDetailResponse operationDetail = createOperationDetailResponse();
- operationDetail.setParameters(ImmutableMap.builder()
- .put("sourceAmount", "source not a number")
- .put("sourceCurrency", "EUR")
- .put("targetAmount", "1710.98")
- .put("targetCurrency", "USD")
- .put("dynamic", "true")
- .build());
+ operationDetail.setParameters(Map.of(
+ "sourceAmount", "source not a number",
+ "sourceCurrency", "EUR",
+ "targetAmount", "1710.98",
+ "targetCurrency", "USD",
+ "dynamic", "true"
+ ));
final OperationTemplateEntity operationTemplate = new OperationTemplateEntity();
operationTemplate.setAttributes("""
@@ -610,13 +609,13 @@ void testConvertAmountConversion_sourceNotANumber() throws Exception {
@Test
void testConvertAmountConversion_targetNotANumber() throws Exception {
final OperationDetailResponse operationDetail = createOperationDetailResponse();
- operationDetail.setParameters(ImmutableMap.builder()
- .put("sourceAmount", "1710.98")
- .put("sourceCurrency", "USD")
- .put("targetAmount", "target not a number")
- .put("targetCurrency", "EUR")
- .put("dynamic", "true")
- .build());
+ operationDetail.setParameters(Map.of(
+ "sourceAmount", "1710.98",
+ "sourceCurrency", "USD",
+ "targetAmount", "target not a number",
+ "targetCurrency", "EUR",
+ "dynamic", "true"
+ ));
final OperationTemplateEntity operationTemplate = new OperationTemplateEntity();
operationTemplate.setAttributes("""
diff --git a/mtoken-model/pom.xml b/mtoken-model/pom.xml
index a95dd784..369b2419 100644
--- a/mtoken-model/pom.xml
+++ b/mtoken-model/pom.xml
@@ -26,7 +26,7 @@
com.wultra.security
enrollment-server-parent
- 1.6.0-SNAPSHOT
+ 1.7.0-SNAPSHOT
diff --git a/pom.xml b/pom.xml
index a5f9bbd4..606baa41 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,13 +26,13 @@
com.wultra.security
enrollment-server-parent
- 1.6.0-SNAPSHOT
+ 1.7.0-SNAPSHOT
pom
org.springframework.boot
spring-boot-starter-parent
- 3.1.6
+ 3.2.2
@@ -94,20 +94,15 @@
4.0.0
2.2.20
2.3.0
- 1.4.2
+ 1.4.4
-
- 3.13.0
-
- 1.8.0
- 1.6.0
- 1.6.0
- 1.6.0
+ 1.9.0-SNAPSHOT
+ 1.7.0-SNAPSHOT
+ 1.7.0-SNAPSHOT
+ 1.7.0-SNAPSHOT
1.77
7.4
-
- 1.4.14
@@ -316,13 +311,6 @@
org.apache.maven.plugins
maven-enforcer-plugin
-
-
- de.skuzzle.enforcer
- restrict-imports-enforcer-rule
- 2.4.0
-
-
enforce-banned-dependencies
@@ -336,26 +324,12 @@
org.apache.tomcat.embed:*:*:*:compile
org.bouncycastle:bcpkix-jdk15on:*:*:compile
org.bouncycastle:bcprov-jdk15on:*:*:compile
+ com.google.guava:guava*:*:*:compile
-
- enforce-banned-java-imports
-
- enforce
-
-
-
-
-
- Guava depends on jsr305 but we prefer jakarta in our code
- javax.annotation.**
-
-
-
-