ops: add CI/CD pipeline, a/b rolling deploy, Gitea Actions workflow
Deploy to Production / deploy (push) Failing after 10s
Deploy to Production / deploy (push) Failing after 10s
- .gitea/workflows/deploy.yml — push-to-main triggers rolling deploy - scripts/deploy-bluegreen.sh — a-stack then b-stack restart; Maven runs in Docker (no JDK needed on runner host); Caddy reload at end - scripts/deploy-all.ps1 — emergency manual deploy from dev machine - infra/docker-compose.yml — a/b pairs per service; wget health checks; Gitea service; Prometheus/Grafana/DB ports restricted to localhost - infra/Caddyfile — dual upstreams with health-based routing - infra/Dockerfile.* — one per service - infra/prometheus.yml + grafana provisioning Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+108
@@ -0,0 +1,108 @@
|
||||
package org.botstandards.apix.verification;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.botstandards.apix.common.OLevel;
|
||||
import org.botstandards.apix.common.VerificationResult;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
public class O1DnsVerifier {
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
record DohResponse(int Status, List<DohRecord> Answer) {
|
||||
DohResponse() { this(0, List.of()); }
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
record DohRecord(String data) {
|
||||
DohRecord() { this(null); }
|
||||
}
|
||||
|
||||
private final VerificationConfig config;
|
||||
private final HttpClient http;
|
||||
private final ObjectMapper mapper;
|
||||
|
||||
public O1DnsVerifier(VerificationConfig config) {
|
||||
this.config = config;
|
||||
this.http = HttpClient.newBuilder()
|
||||
.connectTimeout(Duration.ofMillis(config.httpTimeoutMs()))
|
||||
.build();
|
||||
this.mapper = new ObjectMapper();
|
||||
}
|
||||
|
||||
public VerificationResult verify(String domain, String expectedToken) {
|
||||
VerificationResult txtResult = checkTxtRecord(domain, expectedToken);
|
||||
if (!txtResult.succeeded()) {
|
||||
return txtResult;
|
||||
}
|
||||
return checkMxRecord(domain);
|
||||
}
|
||||
|
||||
private VerificationResult checkTxtRecord(String domain, String expectedToken) {
|
||||
String url = config.dohUrl() + "?name=_apix-verification." + domain + "&type=TXT";
|
||||
try {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(url))
|
||||
.timeout(Duration.ofMillis(config.httpTimeoutMs()))
|
||||
.header("Accept", "application/dns-json")
|
||||
.GET()
|
||||
.build();
|
||||
HttpResponse<String> response = http.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
DohResponse doh = mapper.readValue(response.body(), DohResponse.class);
|
||||
|
||||
if (doh.Status() == 3) {
|
||||
return VerificationResult.failure(OLevel.UNVERIFIED, "DNS_TXT",
|
||||
"NXDOMAIN for _apix-verification." + domain);
|
||||
}
|
||||
if (doh.Answer() == null || doh.Answer().isEmpty()) {
|
||||
return VerificationResult.failure(OLevel.UNVERIFIED, "DNS_TXT",
|
||||
"No TXT record found at _apix-verification." + domain);
|
||||
}
|
||||
|
||||
String expected = "apix-token=" + expectedToken;
|
||||
boolean found = doh.Answer().stream()
|
||||
.map(DohRecord::data)
|
||||
.filter(d -> d != null)
|
||||
.map(d -> d.startsWith("\"") && d.endsWith("\"") ? d.substring(1, d.length() - 1) : d)
|
||||
.anyMatch(d -> d.equals(expected));
|
||||
|
||||
if (!found) {
|
||||
return VerificationResult.failure(OLevel.UNVERIFIED, "DNS_TXT",
|
||||
"TXT record at _apix-verification." + domain + " does not contain " + expected);
|
||||
}
|
||||
return VerificationResult.success(OLevel.IDENTITY_VERIFIED);
|
||||
} catch (Exception e) {
|
||||
return VerificationResult.failure(OLevel.UNVERIFIED, "DNS_TXT",
|
||||
"HTTP error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private VerificationResult checkMxRecord(String domain) {
|
||||
String url = config.dohUrl() + "?name=" + domain + "&type=MX";
|
||||
try {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(url))
|
||||
.timeout(Duration.ofMillis(config.httpTimeoutMs()))
|
||||
.header("Accept", "application/dns-json")
|
||||
.GET()
|
||||
.build();
|
||||
HttpResponse<String> response = http.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
DohResponse doh = mapper.readValue(response.body(), DohResponse.class);
|
||||
|
||||
if (doh.Answer() == null || doh.Answer().isEmpty()) {
|
||||
return VerificationResult.failure(OLevel.UNVERIFIED, "DNS_MX",
|
||||
"No MX record found for " + domain);
|
||||
}
|
||||
return VerificationResult.success(OLevel.IDENTITY_VERIFIED);
|
||||
} catch (Exception e) {
|
||||
return VerificationResult.failure(OLevel.UNVERIFIED, "DNS_MX",
|
||||
"HTTP error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package org.botstandards.apix.verification;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.botstandards.apix.common.OLevel;
|
||||
import org.botstandards.apix.common.VerificationResult;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
public class O2GleifVerifier {
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
record GleifResponse(List<GleifRecord> data) {
|
||||
GleifResponse() { this(List.of()); }
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
record GleifRecord(String id) {
|
||||
GleifRecord() { this(null); }
|
||||
}
|
||||
|
||||
private final VerificationConfig config;
|
||||
private final HttpClient http;
|
||||
private final ObjectMapper mapper;
|
||||
|
||||
public O2GleifVerifier(VerificationConfig config) {
|
||||
this.config = config;
|
||||
this.http = HttpClient.newBuilder()
|
||||
.connectTimeout(Duration.ofMillis(config.httpTimeoutMs()))
|
||||
.build();
|
||||
this.mapper = new ObjectMapper();
|
||||
}
|
||||
|
||||
public VerificationResult verify(String legalName, String jurisdiction) {
|
||||
String encodedName = URLEncoder.encode(legalName, StandardCharsets.UTF_8);
|
||||
String url = config.gleifApiUrl() + "/lei-records"
|
||||
+ "?filter[entity.legalName]=" + encodedName
|
||||
+ "&filter[entity.jurisdiction]=" + jurisdiction;
|
||||
try {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(url))
|
||||
.timeout(Duration.ofMillis(config.httpTimeoutMs()))
|
||||
.header("Accept", "application/json")
|
||||
.GET()
|
||||
.build();
|
||||
HttpResponse<String> response = http.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
if (response.statusCode() == 200) {
|
||||
GleifResponse gleif = mapper.readValue(response.body(), GleifResponse.class);
|
||||
if (gleif.data() != null && !gleif.data().isEmpty()) {
|
||||
String lei = gleif.data().get(0).id();
|
||||
return VerificationResult.success(OLevel.LEGAL_ENTITY_VERIFIED, lei);
|
||||
}
|
||||
}
|
||||
return VerificationResult.failure(OLevel.IDENTITY_VERIFIED, "GLEIF",
|
||||
"No GLEIF record found");
|
||||
} catch (Exception e) {
|
||||
return VerificationResult.failure(OLevel.IDENTITY_VERIFIED, "GLEIF",
|
||||
"HTTP error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package org.botstandards.apix.verification;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.botstandards.apix.common.OLevel;
|
||||
import org.botstandards.apix.common.VerificationResult;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
public class O2OpenCorporatesVerifier {
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
record OcResponse(OcResults results) {
|
||||
OcResponse() { this(null); }
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
record OcResults(List<Object> companies) {
|
||||
OcResults() { this(List.of()); }
|
||||
}
|
||||
|
||||
private final VerificationConfig config;
|
||||
private final HttpClient http;
|
||||
private final ObjectMapper mapper;
|
||||
|
||||
public O2OpenCorporatesVerifier(VerificationConfig config) {
|
||||
this.config = config;
|
||||
this.http = HttpClient.newBuilder()
|
||||
.connectTimeout(Duration.ofMillis(config.httpTimeoutMs()))
|
||||
.build();
|
||||
this.mapper = new ObjectMapper();
|
||||
}
|
||||
|
||||
public VerificationResult verify(String legalName, String jurisdiction) {
|
||||
String encodedName = URLEncoder.encode(legalName, StandardCharsets.UTF_8);
|
||||
String url = config.openCorporatesApiUrl() + "/companies/search"
|
||||
+ "?q=" + encodedName
|
||||
+ "&jurisdiction_code=" + jurisdiction
|
||||
+ "&api_token=" + config.openCorporatesApiKey();
|
||||
try {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(url))
|
||||
.timeout(Duration.ofMillis(config.httpTimeoutMs()))
|
||||
.header("Accept", "application/json")
|
||||
.GET()
|
||||
.build();
|
||||
HttpResponse<String> response = http.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
if (response.statusCode() == 200) {
|
||||
OcResponse oc = mapper.readValue(response.body(), OcResponse.class);
|
||||
if (oc.results() != null
|
||||
&& oc.results().companies() != null
|
||||
&& !oc.results().companies().isEmpty()) {
|
||||
return VerificationResult.success(OLevel.LEGAL_ENTITY_VERIFIED);
|
||||
}
|
||||
}
|
||||
return VerificationResult.failure(OLevel.IDENTITY_VERIFIED, "OPENCORPORATES",
|
||||
"No company record found");
|
||||
} catch (Exception e) {
|
||||
return VerificationResult.failure(OLevel.IDENTITY_VERIFIED, "OPENCORPORATES",
|
||||
"HTTP error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
package org.botstandards.apix.verification;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.botstandards.apix.common.OLevel;
|
||||
import org.botstandards.apix.common.VerificationResult;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
public class O3HygieneVerifier {
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
record DohResponse(int Status, List<DohRecord> Answer) {
|
||||
DohResponse() { this(0, List.of()); }
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
record DohRecord(String data) {
|
||||
DohRecord() { this(null); }
|
||||
}
|
||||
|
||||
private final VerificationConfig config;
|
||||
private final HttpClient http;
|
||||
private final ObjectMapper mapper;
|
||||
|
||||
public O3HygieneVerifier(VerificationConfig config) {
|
||||
this.config = config;
|
||||
this.http = HttpClient.newBuilder()
|
||||
.connectTimeout(Duration.ofMillis(config.httpTimeoutMs()))
|
||||
.build();
|
||||
this.mapper = new ObjectMapper();
|
||||
}
|
||||
|
||||
public VerificationResult verify(String domain) {
|
||||
VerificationResult secResult = checkSecurityTxt(domain);
|
||||
if (!secResult.succeeded()) {
|
||||
return secResult;
|
||||
}
|
||||
VerificationResult dmarcResult = checkDmarc(domain);
|
||||
if (!dmarcResult.succeeded()) {
|
||||
return dmarcResult;
|
||||
}
|
||||
return checkSpf(domain);
|
||||
}
|
||||
|
||||
private VerificationResult checkSecurityTxt(String domain) {
|
||||
String url = config.securityTxtUrlTemplate().replace("{domain}", domain);
|
||||
try {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(url))
|
||||
.timeout(Duration.ofMillis(config.httpTimeoutMs()))
|
||||
.GET()
|
||||
.build();
|
||||
HttpResponse<String> response = http.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
if (response.statusCode() != 200) {
|
||||
return VerificationResult.failure(OLevel.LEGAL_ENTITY_VERIFIED, "SECURITY_TXT",
|
||||
"security.txt not found at " + url + " (HTTP " + response.statusCode() + ")");
|
||||
}
|
||||
return VerificationResult.success(OLevel.HYGIENE_VERIFIED);
|
||||
} catch (Exception e) {
|
||||
return VerificationResult.failure(OLevel.LEGAL_ENTITY_VERIFIED, "SECURITY_TXT",
|
||||
"HTTP error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private VerificationResult checkDmarc(String domain) {
|
||||
String url = config.dohUrl() + "?name=_dmarc." + domain + "&type=TXT";
|
||||
try {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(url))
|
||||
.timeout(Duration.ofMillis(config.httpTimeoutMs()))
|
||||
.header("Accept", "application/dns-json")
|
||||
.GET()
|
||||
.build();
|
||||
HttpResponse<String> response = http.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
DohResponse doh = mapper.readValue(response.body(), DohResponse.class);
|
||||
|
||||
boolean found = doh.Answer() != null && doh.Answer().stream()
|
||||
.map(DohRecord::data)
|
||||
.filter(d -> d != null)
|
||||
.map(d -> d.startsWith("\"") && d.endsWith("\"") ? d.substring(1, d.length() - 1) : d)
|
||||
.anyMatch(d -> d.contains("v=DMARC1"));
|
||||
|
||||
if (!found) {
|
||||
return VerificationResult.failure(OLevel.LEGAL_ENTITY_VERIFIED, "DMARC",
|
||||
"No DMARC TXT record found for _dmarc." + domain);
|
||||
}
|
||||
return VerificationResult.success(OLevel.HYGIENE_VERIFIED);
|
||||
} catch (Exception e) {
|
||||
return VerificationResult.failure(OLevel.LEGAL_ENTITY_VERIFIED, "DMARC",
|
||||
"HTTP error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private VerificationResult checkSpf(String domain) {
|
||||
String url = config.dohUrl() + "?name=" + domain + "&type=TXT";
|
||||
try {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(url))
|
||||
.timeout(Duration.ofMillis(config.httpTimeoutMs()))
|
||||
.header("Accept", "application/dns-json")
|
||||
.GET()
|
||||
.build();
|
||||
HttpResponse<String> response = http.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
DohResponse doh = mapper.readValue(response.body(), DohResponse.class);
|
||||
|
||||
boolean found = doh.Answer() != null && doh.Answer().stream()
|
||||
.map(DohRecord::data)
|
||||
.filter(d -> d != null)
|
||||
.map(d -> d.startsWith("\"") && d.endsWith("\"") ? d.substring(1, d.length() - 1) : d)
|
||||
.anyMatch(d -> d.contains("v=spf1"));
|
||||
|
||||
if (!found) {
|
||||
return VerificationResult.failure(OLevel.LEGAL_ENTITY_VERIFIED, "SPF",
|
||||
"No SPF TXT record found for " + domain);
|
||||
}
|
||||
return VerificationResult.success(OLevel.HYGIENE_VERIFIED);
|
||||
} catch (Exception e) {
|
||||
return VerificationResult.failure(OLevel.LEGAL_ENTITY_VERIFIED, "SPF",
|
||||
"HTTP error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
package org.botstandards.apix.verification;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Verifies that an agent has published the expected rotation challenge token
|
||||
* at the well-known DNS location: _apix-rotation.{domain} TXT "apix-rotate={token}"
|
||||
*
|
||||
* This is the machine-native key rotation path: rotation secret (first factor)
|
||||
* + DNS control (second factor, analogous to ACME DNS-01).
|
||||
* The challenge token is intentionally public — it is valueless without the rotation secret.
|
||||
*/
|
||||
public class RotationChallengeVerifier {
|
||||
|
||||
private static final String TXT_PREFIX = "apix-rotate=";
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
record DohResponse(int Status, List<DohRecord> Answer) {
|
||||
DohResponse() { this(0, List.of()); }
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
record DohRecord(String data) {
|
||||
DohRecord() { this(null); }
|
||||
}
|
||||
|
||||
private final VerificationConfig config;
|
||||
private final HttpClient http;
|
||||
private final ObjectMapper mapper;
|
||||
|
||||
public RotationChallengeVerifier(VerificationConfig config) {
|
||||
this.config = config;
|
||||
this.http = HttpClient.newBuilder()
|
||||
.connectTimeout(Duration.ofMillis(config.httpTimeoutMs()))
|
||||
.build();
|
||||
this.mapper = new ObjectMapper();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if _apix-rotation.{domain} TXT contains "apix-rotate={expectedToken}".
|
||||
* Throws on HTTP/parse error so the caller can surface a meaningful error.
|
||||
*/
|
||||
public boolean verify(String domain, String expectedToken) {
|
||||
String url = config.dohUrl() + "?name=_apix-rotation." + domain + "&type=TXT";
|
||||
try {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(url))
|
||||
.timeout(Duration.ofMillis(config.httpTimeoutMs()))
|
||||
.header("Accept", "application/dns-json")
|
||||
.GET()
|
||||
.build();
|
||||
HttpResponse<String> response = http.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
DohResponse doh = mapper.readValue(response.body(), DohResponse.class);
|
||||
|
||||
if (doh.Answer() == null || doh.Answer().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
String expected = TXT_PREFIX + expectedToken;
|
||||
return doh.Answer().stream()
|
||||
.map(DohRecord::data)
|
||||
.filter(d -> d != null)
|
||||
.map(d -> d.startsWith("\"") && d.endsWith("\"") ? d.substring(1, d.length() - 1) : d)
|
||||
.anyMatch(d -> d.equals(expected));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("DoH query failed for _apix-rotation." + domain + ": " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package org.botstandards.apix.verification;
|
||||
|
||||
public record VerificationConfig(
|
||||
String dohUrl,
|
||||
String gleifApiUrl,
|
||||
String openCorporatesApiKey,
|
||||
String openCorporatesApiUrl,
|
||||
String securityTxtUrlTemplate,
|
||||
long httpTimeoutMs
|
||||
) {}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package org.botstandards.apix.verification;
|
||||
|
||||
import org.botstandards.apix.common.OLevel;
|
||||
import org.botstandards.apix.common.VerificationResult;
|
||||
|
||||
public class VerificationPipeline {
|
||||
|
||||
private final O1DnsVerifier o1;
|
||||
private final O2GleifVerifier o2Gleif;
|
||||
private final O2OpenCorporatesVerifier o2Oc;
|
||||
private final O3HygieneVerifier o3;
|
||||
|
||||
public VerificationPipeline(VerificationConfig config) {
|
||||
this.o1 = new O1DnsVerifier(config);
|
||||
this.o2Gleif = new O2GleifVerifier(config);
|
||||
this.o2Oc = new O2OpenCorporatesVerifier(config);
|
||||
this.o3 = new O3HygieneVerifier(config);
|
||||
}
|
||||
|
||||
public VerificationResult run(OLevel targetLevel, String domain, String dnsToken,
|
||||
String legalName, String jurisdiction) {
|
||||
if (targetLevel == OLevel.UNVERIFIED) {
|
||||
return VerificationResult.success(OLevel.UNVERIFIED);
|
||||
}
|
||||
|
||||
if (targetLevel == OLevel.OPERATIONALLY_VERIFIED || targetLevel == OLevel.AUDITED) {
|
||||
return VerificationResult.failure(OLevel.UNVERIFIED, "MANUAL_REVIEW",
|
||||
"requires BSF manual review");
|
||||
}
|
||||
|
||||
VerificationResult o1Result = o1.verify(domain, dnsToken);
|
||||
if (!o1Result.succeeded()) {
|
||||
return o1Result;
|
||||
}
|
||||
if (targetLevel == OLevel.IDENTITY_VERIFIED) {
|
||||
return o1Result;
|
||||
}
|
||||
|
||||
VerificationResult o2Result = o2Gleif.verify(legalName, jurisdiction);
|
||||
if (!o2Result.succeeded()) {
|
||||
o2Result = o2Oc.verify(legalName, jurisdiction);
|
||||
}
|
||||
if (!o2Result.succeeded()) {
|
||||
return VerificationResult.failure(OLevel.IDENTITY_VERIFIED,
|
||||
o2Result.blockedAtStep(), o2Result.message());
|
||||
}
|
||||
if (targetLevel == OLevel.LEGAL_ENTITY_VERIFIED) {
|
||||
return o2Result;
|
||||
}
|
||||
|
||||
return o3.verify(domain);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user