Skip to content

Commit

Permalink
Merge pull request #130 from /issues/129-merge-upstream
Browse files Browse the repository at this point in the history
Merge upstream
  • Loading branch information
banterCZ authored Dec 7, 2023
2 parents 34525e3 + d4b53cb commit e1b23a8
Show file tree
Hide file tree
Showing 21 changed files with 544 additions and 87 deletions.
3 changes: 1 addition & 2 deletions docs/Mobile-Token-API.md
Original file line number Diff line number Diff line change
Expand Up @@ -395,8 +395,7 @@ Claim an operation for a user.
```json
{
"requestObject": {
"id": "7e0ba60f-bf22-4ff5-b999-2733784e5eaa",
"userId": "user12345"
"id": "7e0ba60f-bf22-4ff5-b999-2733784e5eaa"
}
}
```
Expand Down
2 changes: 2 additions & 0 deletions docs/onboarding/Configuration-Properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ The Onboarding Server uses the following public configuration properties:
| `enrollment-server-onboarding.provider.innovatrics.serviceBaseUrl` | | Base REST service URL for Innovatrics. |
| `enrollment-server-onboarding.provider.innovatrics.serviceToken` | | Authentication token for Innovatrics. |
| `enrollment-server-onboarding.provider.innovatrics.serviceUserAgent` | `Wultra/OnboardingServer` | User agent to use when making HTTP calls to Innovatrics REST service. |
| `enrollment-server-onboarding.provider.innovatrics.presenceCheck.score` | 0.875 | Presence check minimal score threshold. |
| `enrollment-server-onboarding.provider.innovatrics.restClientConfig.acceptInvalidSslCertificate` | `false` | Whether invalid SSL certificate is accepted when calling Zen ID REST service. |
| `enrollment-server-onboarding.provider.innovatrics.restClientConfig.maxInMemorySize` | `10485760` | Maximum in memory size of HTTP requests when calling Innovatrics REST service. |
| `enrollment-server-onboarding.provider.innovatrics.restClientConfig.proxyEnabled` | `false` | Whether proxy server is enabled when calling Innovatrics REST service. |
Expand All @@ -150,6 +151,7 @@ The Onboarding Server uses the following public configuration properties:
| `enrollment-server-onboarding.provider.innovatrics.restClientConfig.proxyUsername` | | Proxy username to be used when calling Innovatrics REST service. |
| `enrollment-server-onboarding.provider.innovatrics.restClientConfig.proxyPassword` | | Proxy password to be used when calling Innovatrics REST service. |

See [Innovatrics documentation](https://developers.innovatrics.com/digital-onboarding/docs/functionalities/face/active-liveness-check/#magnifeye-liveness) for details how the score affects false acceptances (FAR) and false rejections (FRR).

## Correlation HTTP Header Configuration

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ public interface DocumentVerificationProvider {
*/
DocumentsSubmitResult submitDocuments(OwnerId id, List<SubmittedDocument> documents) throws RemoteCommunicationException, DocumentVerificationException;

/**
* A feature flag whether the selfie result gained by {@link PresenceCheckProvider} should be stored by {@link #submitDocuments(OwnerId, List)}.
* <p>
* Some implementation may require this cross-sending between providers by the Onboarding server, some providers may handle it internally.
*
* @return {@code true} if cross-sending between providers should be handled by Onboarding server, {@code false} otherwise.
*/
boolean shouldStoreSelfie();

/**
* Analyze previously submitted documents, detect frauds, return binary result
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ public interface PresenceCheckProvider {
*/
void initPresenceCheck(OwnerId id, Image photo) throws PresenceCheckException, RemoteCommunicationException;

/**
* A feature flag whether the trusted photo of the user should be passed to {@link #initPresenceCheck(OwnerId, Image)}.
* <p>
* Some implementation may require specific source to be called by Onboarding server, some providers may handle it internally.
*
* @return {@code true} if the trusted photo should be provided, {@code false} otherwise.
*/
boolean shouldProvideTrustedPhoto();

/**
* Starts the presence check process. The process has to be initialized before this call.
*
Expand All @@ -67,9 +76,10 @@ public interface PresenceCheckProvider {
* Cleans up all presence check data related to the identity.
*
* @param id Owner identification.
* @param sessionInfo Session info with presence check relevant data.
* @throws PresenceCheckException In case of business logic error.
* @throws RemoteCommunicationException In case of remote communication error.
*/
void cleanupIdentityData(OwnerId id) throws PresenceCheckException, RemoteCommunicationException;
void cleanupIdentityData(OwnerId id, SessionInfo sessionInfo) throws PresenceCheckException, RemoteCommunicationException;

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,22 @@
*/
package com.wultra.app.onboardingserver.provider.innovatrics;

import com.wultra.app.enrollmentserver.model.integration.OwnerId;
import com.wultra.app.onboardingserver.common.errorhandling.RemoteCommunicationException;
import com.wultra.app.onboardingserver.provider.innovatrics.model.api.CustomerInspectResponse;
import com.wultra.app.onboardingserver.provider.innovatrics.model.api.EvaluateCustomerLivenessRequest;
import com.wultra.app.onboardingserver.provider.innovatrics.model.api.EvaluateCustomerLivenessResponse;
import com.wultra.core.rest.client.base.RestClient;
import com.wultra.core.rest.client.base.RestClientException;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

/**
* Implementation of the REST service to<a href="https://www.innovatrics.com/">Innovatrics</a>.
Expand All @@ -43,7 +49,9 @@
@Slf4j
class InnovatricsApiService {

private static final ParameterizedTypeReference<String> STRING_TYPE_REFERENCE = new ParameterizedTypeReference<>() { };
private static final MultiValueMap<String, String> EMPTY_ADDITIONAL_HEADERS = new LinkedMultiValueMap<>();

private static final MultiValueMap<String, String> EMPTY_QUERY_PARAMS = new LinkedMultiValueMap<>();

/**
* REST client for Innovatrics calls.
Expand All @@ -60,11 +68,81 @@ public InnovatricsApiService(@Qualifier("restClientInnovatrics") final RestClien
this.restClient = restClient;
}

// TODO remove - temporal test call
@PostConstruct
public void testCall() throws RestClientException {
logger.info("Trying a test call");
final ResponseEntity<String> response = restClient.get("/api/v1/metadata", STRING_TYPE_REFERENCE);
logger.info("Result of test call: {}", response.getBody());
public EvaluateCustomerLivenessResponse evaluateLiveness(final String customerId, final OwnerId ownerId) throws RemoteCommunicationException {
final EvaluateCustomerLivenessRequest request = new EvaluateCustomerLivenessRequest();
request.setType(EvaluateCustomerLivenessRequest.TypeEnum.MAGNIFEYE_LIVENESS);

final String apiPath = "/api/v1/customers/%s/liveness/evaluation".formatted(customerId);

try {
logger.info("Calling liveness/evaluation, {}", ownerId);
logger.debug("Calling {}, {}", apiPath, request);
final ResponseEntity<EvaluateCustomerLivenessResponse> response = restClient.post(apiPath, request, EMPTY_QUERY_PARAMS, EMPTY_ADDITIONAL_HEADERS, new ParameterizedTypeReference<>() {});
logger.info("Got {} for liveness/evaluation, {}", response.getStatusCode(), ownerId);
logger.debug("{} response status code: {}", apiPath, response.getStatusCode());
logger.trace("{} response: {}", apiPath, response);
return response.getBody();
} catch (RestClientException e) {
throw new RemoteCommunicationException(
String.format("Failed REST call to evaluate liveness for customerId=%s, statusCode=%s, responseBody='%s'", customerId, e.getStatusCode(), e.getResponse()), e);
} catch (Exception e) {
throw new RemoteCommunicationException("Unexpected error when evaluating liveness for customerId=" + customerId, e);
}
}

public CustomerInspectResponse inspectCustomer(final String customerId, final OwnerId ownerId) throws RemoteCommunicationException {
final String apiPath = "/api/v1/customers/%s/inspect".formatted(customerId);

try {
logger.info("Calling /inspect, {}", ownerId);
logger.debug("Calling {}", apiPath);
final ResponseEntity<CustomerInspectResponse> response = restClient.post(apiPath, null, EMPTY_QUERY_PARAMS, EMPTY_ADDITIONAL_HEADERS, new ParameterizedTypeReference<>() {});
logger.info("Got {} for /inspect, {}", response.getStatusCode(), ownerId);
logger.debug("{} response status code: {}", apiPath, response.getStatusCode());
logger.trace("{} response: {}", apiPath, response);
return response.getBody();
} catch (RestClientException e) {
throw new RemoteCommunicationException(
String.format("Failed REST call to evaluate inspect for customerId=%s, statusCode=%s, responseBody='%s'", customerId, e.getStatusCode(), e.getResponse()), e);
} catch (Exception e) {
throw new RemoteCommunicationException("Unexpected error when evaluating inspect for customerId=" + customerId, e);
}
}

public void deleteLiveness(final String customerId, final OwnerId ownerId) throws RemoteCommunicationException {
final String apiPath = "/api/v1/customers/%s/liveness".formatted(customerId);

try {
logger.info("Deleting liveness, {}", ownerId);
logger.debug("Deleting {}", apiPath);
final ResponseEntity<Void> response = restClient.delete(apiPath, new ParameterizedTypeReference<>() {});
logger.info("Got {} for liveness delete, {}", response.getStatusCode(), ownerId);
logger.debug("{} response status code: {}", apiPath, response.getStatusCode());
logger.trace("{} response: {}", apiPath, response);
} catch (RestClientException e) {
throw new RemoteCommunicationException(
String.format("Failed REST call to delete liveness for customerId=%s, statusCode=%s, responseBody='%s'", customerId, e.getStatusCode(), e.getResponse()), e);
} catch (Exception e) {
throw new RemoteCommunicationException("Unexpected error when deleting liveness for customerId=" + customerId, e);
}
}

public void deleteSelfie(final String customerId, final OwnerId ownerId) throws RemoteCommunicationException {
final String apiPath = "/api/v1/customers/%s/selfie".formatted(customerId);

try {
logger.info("Deleting selfie, {}", ownerId);
logger.debug("Deleting {}", apiPath);
final ResponseEntity<Void> response = restClient.delete(apiPath, new ParameterizedTypeReference<>() {});
logger.info("Got {} for selfie delete, {}", response.getStatusCode(), ownerId);
logger.debug("{} response status code: {}", apiPath, response.getStatusCode());
logger.trace("{} response: {}", apiPath, response);
} catch (RestClientException e) {
throw new RemoteCommunicationException(
String.format("Failed REST call to delete selfie for customerId=%s, statusCode=%s, responseBody='%s'", customerId, e.getStatusCode(), e.getResponse()), e);
} catch (Exception e) {
throw new RemoteCommunicationException("Unexpected error when deleting selfie for customerId=" + customerId, e);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,14 @@ class InnovatricsConfigProps {
*/
private RestClientConfiguration restClientConfig;

private PresenceCheckConfiguration presenceCheckConfiguration;

@Getter @Setter
public static class PresenceCheckConfiguration {
/**
* Presence check minimal score threshold.
*/
private double score = 0.875;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ public DocumentsSubmitResult submitDocuments(OwnerId id, List<SubmittedDocument>
return null;
}

@Override
public boolean shouldStoreSelfie() {
return false;
}

@Override
public DocumentsVerificationResult verifyDocuments(OwnerId id, List<String> uploadIds) throws RemoteCommunicationException, DocumentVerificationException {
return null;
Expand Down
Loading

0 comments on commit e1b23a8

Please sign in to comment.