Skip to content

Commit

Permalink
BATM-7170 Onfido: Allow webhook URL configuration via configuration f…
Browse files Browse the repository at this point in the history
…ile (#979)
  • Loading branch information
filipocelka authored Feb 27, 2025
1 parent e1127cc commit c47f839
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 52 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.generalbytes.batm.server.extensions.extra.identityverification.onfido;

import com.generalbytes.batm.server.extensions.IExtensionContext;
import com.google.common.base.Strings;
import lombok.AllArgsConstructor;

import java.util.function.Supplier;

@AllArgsConstructor
public class OnfidoConfigurationService {

/**
* Configuration file for Onfido in '/batm/config' directory.
*/
private static final String ONFIDO_CONFIG_FILE = "onfido";

private final IExtensionContext ctx;

/**
* Get callback URL for verification site webhook.
*
* @return URL from configuration file or default value based on configured hostname and port 7743
*/
public String getVerificationSiteCallbackUrl() {
return getFromConfigOrDefault(getVerificationSiteWebhookConfig(), this::getMasterServerApiAddress);
}

/**
* Get callback URL for Onfido webhook.
*
* @return URL from configuration file or default value based on configured hostname and port 8743
*/
public String getOnfidoCallbackUrl() {
return getFromConfigOrDefault(getOnfidoWebhookConfig(), this::getMasterServerProxyAddress);
}

private String getMasterServerApiAddress() {
return "https://" + getHostname() + ":" + 7743;
}

/**
* @return this server address with hostname form /batm/config/hostname,
* port number and path set to the one used by nginx proxy installed with {@code batm-manage install-reverse-proxy}.
*/
private String getMasterServerProxyAddress() {
return "https://" + getHostname() + ":" + 8743 + "/server";
}

private String getHostname() {
String hostname = Strings.nullToEmpty(ctx.getConfigFileContent("hostname")).trim();
if (hostname.isEmpty()) {
throw new RuntimeException("Hostname not configured in /batm/config");
}
return hostname;
}

private String getFromConfigOrDefault(String configValue, Supplier<String> defaultValue) {
return configValue != null ? configValue : defaultValue.get();
}

private String getOnfidoWebhookConfig() {
return getConfiguredPropertyOrNull("webhook.onfido");
}

private String getVerificationSiteWebhookConfig() {
return getConfiguredPropertyOrNull("webhook.verificationSite");
}

private String getConfiguredPropertyOrNull(String key) {
return ctx.getConfigProperty(ONFIDO_CONFIG_FILE, key, null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import com.generalbytes.batm.server.extensions.aml.verification.IIdentityVerificationProvider;
import com.generalbytes.batm.server.extensions.aml.verification.IdentityCheckWebhookException;
import com.generalbytes.batm.server.extensions.extra.identityverification.onfido.verificationsite.VerificationSiteClient;
import com.google.common.base.Strings;
import com.onfido.Onfido;
import com.onfido.exceptions.OnfidoException;
import com.onfido.models.Applicant;
Expand All @@ -30,40 +29,35 @@ public class OnfidoIdentityVerificationProvider implements IIdentityVerification
private final OnfidoWebhookProcessor webhookProcessor;
private final String gbApiKey;
private final IExtensionContext ctx;
private final OnfidoConfigurationService configurationService;

public OnfidoIdentityVerificationProvider(String onfidoApiKey, String verificationSiteUrl, OnfidoRegion region, String gbApiKey, IExtensionContext ctx) {
this(
buildOnfido(onfidoApiKey, region),
verificationSiteUrl,
gbApiKey,
ctx);
}

private static Onfido buildOnfido(String apiKey, OnfidoRegion region) {
Objects.requireNonNull(apiKey, "onfido api key cannot be null");
Onfido.Builder builder = Onfido.builder().apiToken(apiKey);
Objects.requireNonNull(region, "region cannot be null");
switch (region) {
case US:
return builder.regionUS().build();
case CA:
return builder.regionCA().build();
case EU:
default:
return builder.regionEU().build();
}
}

public OnfidoIdentityVerificationProvider(Onfido onfido, String verificationSiteUrl, String gbApiKey, IExtensionContext ctx) {
public OnfidoIdentityVerificationProvider(String onfidoApiKey,
String verificationSiteUrl,
OnfidoRegion region,
String gbApiKey,
IExtensionContext ctx) {
if (verificationSiteUrl == null) {
throw new IllegalArgumentException("verificationSiteUrl must be configured!");
}

this.ctx = ctx;
this.onfido = onfido;
this.referrer = getReferrer(verificationSiteUrl);
this.verificationSiteUrl = verificationSiteUrl;
this.gbApiKey = gbApiKey;
this.webhookProcessor = new OnfidoWebhookProcessor(onfido, getMasterServerProxyAddress());
onfido = buildOnfido(onfidoApiKey, region);
referrer = getReferrer(verificationSiteUrl);
configurationService = new OnfidoConfigurationService(ctx);
webhookProcessor = new OnfidoWebhookProcessor(onfido, configurationService.getOnfidoCallbackUrl());
}

private static Onfido buildOnfido(String apiKey, OnfidoRegion region) {
Objects.requireNonNull(apiKey, "onfido api key cannot be null");
Onfido.Builder builder = Onfido.builder().apiToken(apiKey);
Objects.requireNonNull(region, "region cannot be null");
return switch (region) {
case US -> builder.regionUS().build();
case CA -> builder.regionCA().build();
default -> builder.regionEU().build();
};
}

@Override
Expand All @@ -87,32 +81,12 @@ public CreateApplicantResponse createApplicant(String customerLanguage, String i
}

private VerificationSiteClient createVerificationSiteClient() {
String callbackUrl = getMasterServerApiAddress();
String callbackUrl = configurationService.getVerificationSiteCallbackUrl();
// After the documents are submitted, the verification website will call this url (with added path and applicant ID)
log.info("Creating new verification-site client for url {} with callbackUrl {}(/serverapi/apiv1/identity-check/submit/<applicantId>)", this.verificationSiteUrl, callbackUrl);
return new VerificationSiteClient(this.verificationSiteUrl, callbackUrl);
}

private String getMasterServerApiAddress() {
return "https://" + getHostname() + ":" + 7743;
}

/**
* @return this server address with hostname form /batm/config/hostname,
* port number and path set to the one used by nginx proxy installed with {@code batm-manage install-reverse-proxy}.
*/
private String getMasterServerProxyAddress() {
return "https://" + getHostname() + ":" + 8743 + "/server";
}

private String getHostname() {
String hostname = Strings.nullToEmpty(ctx.getConfigFileContent("hostname")).trim();
if (hostname.isEmpty()) {
throw new RuntimeException("Hostname not configured in /batm/config");
}
return hostname;
}

private String getVerificationUrl(String customerLanguage, String applicantId) {
String verificationUrl = this.verificationSiteUrl + "?a=" + applicantId;
if (customerLanguage != null) {
Expand All @@ -121,8 +95,6 @@ private String getVerificationUrl(String customerLanguage, String applicantId) {
return verificationUrl;
}



/**
* Called by the verification website after all documents and data are uploaded by the user.
* Starts verification process on onfido.
Expand Down Expand Up @@ -159,7 +131,7 @@ private String getReferrer(String verificationSiteUrl) {
URL url = new URL(verificationSiteUrl);
return url.getProtocol() + "://" + url.getHost() + "/*";
} catch (MalformedURLException e) {
log.error("Error parsing url " + verificationSiteUrl);
log.error("Error parsing url {}", verificationSiteUrl);
return verificationSiteUrl + "*";
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.generalbytes.batm.server.extensions.extra.identityverification.onfido;

import com.generalbytes.batm.server.extensions.IExtensionContext;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class OnfidoConfigurationServiceTest {
@Mock
private IExtensionContext ctx;
@InjectMocks
private OnfidoConfigurationService configurationService;

@Test
void testGetVerificationSiteCallbackUrl_fromConfig() {
String configuredUrl = "https://my.server.com:1234/verification";
when(ctx.getConfigProperty("onfido", "webhook.verificationSite", null)).thenReturn(configuredUrl);
String verificationSiteCallbackUrl = configurationService.getVerificationSiteCallbackUrl();
assertEquals(configuredUrl, verificationSiteCallbackUrl);
}

@Test
void testGetVerificationSiteCallbackUrl_defaultValue() {
when(ctx.getConfigProperty("onfido", "webhook.verificationSite", null)).thenReturn(null);
when(ctx.getConfigFileContent("hostname")).thenReturn("default.server.com");
String verificationSiteCallbackUrl = configurationService.getVerificationSiteCallbackUrl();
assertEquals("https://default.server.com:7743", verificationSiteCallbackUrl);
}

@Test
void testGetOnfidoCallbackUrl_fromConfig() {
String configuredUrl = "https://my.server.com:1234/onfido";
when(ctx.getConfigProperty("onfido", "webhook.onfido", null)).thenReturn(configuredUrl);
String onfidoCallbackUrl = configurationService.getOnfidoCallbackUrl();
assertEquals(configuredUrl, onfidoCallbackUrl);
}

@Test
void testGetOnfidoCallbackUrl_defaultValue() {
when(ctx.getConfigProperty("onfido", "webhook.onfido", null)).thenReturn(null);
when(ctx.getConfigFileContent("hostname")).thenReturn("default.server.com");
String onfidoCallbackUrl = configurationService.getOnfidoCallbackUrl();
assertEquals("https://default.server.com:8743/server", onfidoCallbackUrl);
}

@Test
void testHostnameNotConfigured() {
when(ctx.getConfigFileContent("hostname")).thenReturn(null);
try {
configurationService.getVerificationSiteCallbackUrl();
} catch (RuntimeException e) {
assertEquals("Hostname not configured in /batm/config", e.getMessage());
}
}
}

0 comments on commit c47f839

Please sign in to comment.