mirror of
https://codeberg.org/PGPainless/wkd-java.git
synced 2024-11-22 07:12:05 +01:00
Refactoring and dynamic test suite
This commit is contained in:
parent
38ef283313
commit
3af16baa20
23 changed files with 287 additions and 162 deletions
|
@ -10,6 +10,7 @@ group 'org.pgpainless'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
mavenLocal()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
@ -20,7 +21,7 @@ dependencies {
|
||||||
testImplementation 'com.ginsberg:junit5-system-exit:1.1.2'
|
testImplementation 'com.ginsberg:junit5-system-exit:1.1.2'
|
||||||
testImplementation 'org.mockito:mockito-core:4.3.1'
|
testImplementation 'org.mockito:mockito-core:4.3.1'
|
||||||
|
|
||||||
implementation("org.pgpainless:pgpainless-cert-d:0.1.0")
|
implementation("org.pgpainless:pgpainless-cert-d:0.1.1")
|
||||||
implementation project(':wkd-java')
|
implementation project(':wkd-java')
|
||||||
implementation "info.picocli:picocli:4.6.3"
|
implementation "info.picocli:picocli:4.6.3"
|
||||||
|
|
||||||
|
|
|
@ -12,14 +12,14 @@ import org.pgpainless.certificate_store.CertificateFactory;
|
||||||
import org.pgpainless.key.info.KeyRingInfo;
|
import org.pgpainless.key.info.KeyRingInfo;
|
||||||
import pgp.certificate_store.Certificate;
|
import pgp.certificate_store.Certificate;
|
||||||
import pgp.wkd.CertificateAndUserIds;
|
import pgp.wkd.CertificateAndUserIds;
|
||||||
import pgp.wkd.CertificateReader;
|
import pgp.wkd.discovery.CertificateParser;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class CertificateReaderImpl implements CertificateReader {
|
public class CertificateParserImpl implements CertificateParser {
|
||||||
@Override
|
@Override
|
||||||
public List<CertificateAndUserIds> read(InputStream inputStream) throws IOException {
|
public List<CertificateAndUserIds> read(InputStream inputStream) throws IOException {
|
||||||
List<CertificateAndUserIds> certificatesAndUserIds = new ArrayList<>();
|
List<CertificateAndUserIds> certificatesAndUserIds = new ArrayList<>();
|
|
@ -1,21 +1,21 @@
|
||||||
package pgp.wkd.cli;
|
package pgp.wkd.cli;
|
||||||
|
|
||||||
import pgp.wkd.AbstractDiscover;
|
import pgp.wkd.discovery.CertificateDiscoveryImplementation;
|
||||||
import pgp.wkd.CertificateReader;
|
import pgp.wkd.discovery.CertificateParser;
|
||||||
import pgp.wkd.HttpUrlConnectionWKDFetcher;
|
import pgp.wkd.discovery.HttpUrlConnectionCertificateFetcher;
|
||||||
import pgp.wkd.WKDFetcher;
|
import pgp.wkd.discovery.CertificateFetcher;
|
||||||
|
|
||||||
public class DiscoverImpl extends AbstractDiscover {
|
public class DiscoverImpl extends CertificateDiscoveryImplementation {
|
||||||
|
|
||||||
public DiscoverImpl() {
|
public DiscoverImpl() {
|
||||||
super(new CertificateReaderImpl(), new HttpUrlConnectionWKDFetcher());
|
super(new CertificateParserImpl(), new HttpUrlConnectionCertificateFetcher());
|
||||||
}
|
}
|
||||||
|
|
||||||
public DiscoverImpl(WKDFetcher fetcher) {
|
public DiscoverImpl(CertificateFetcher fetcher) {
|
||||||
super(new CertificateReaderImpl(), fetcher);
|
super(new CertificateParserImpl(), fetcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DiscoverImpl(CertificateReader certificateReader, WKDFetcher fetcher) {
|
public DiscoverImpl(CertificateParser certificateParser, CertificateFetcher fetcher) {
|
||||||
super(certificateReader, fetcher);
|
super(certificateParser, fetcher);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,13 +7,13 @@ package pgp.wkd.cli.command;
|
||||||
import org.bouncycastle.bcpg.ArmoredOutputStream;
|
import org.bouncycastle.bcpg.ArmoredOutputStream;
|
||||||
import org.bouncycastle.util.io.Streams;
|
import org.bouncycastle.util.io.Streams;
|
||||||
import pgp.certificate_store.Certificate;
|
import pgp.certificate_store.Certificate;
|
||||||
import pgp.wkd.Discover;
|
import pgp.wkd.discovery.CertificateDiscoverer;
|
||||||
import pgp.wkd.HttpUrlConnectionWKDFetcher;
|
import pgp.wkd.discovery.HttpUrlConnectionCertificateFetcher;
|
||||||
import pgp.wkd.MalformedUserIdException;
|
import pgp.wkd.MalformedUserIdException;
|
||||||
import pgp.wkd.WKDAddress;
|
import pgp.wkd.WKDAddress;
|
||||||
import pgp.wkd.WKDAddressHelper;
|
import pgp.wkd.WKDAddressHelper;
|
||||||
import pgp.wkd.WKDDiscoveryResult;
|
import pgp.wkd.discovery.DiscoveryResult;
|
||||||
import pgp.wkd.WKDFetcher;
|
import pgp.wkd.discovery.CertificateFetcher;
|
||||||
import pgp.wkd.cli.CertNotFetchableException;
|
import pgp.wkd.cli.CertNotFetchableException;
|
||||||
import pgp.wkd.cli.DiscoverImpl;
|
import pgp.wkd.cli.DiscoverImpl;
|
||||||
import picocli.CommandLine;
|
import picocli.CommandLine;
|
||||||
|
@ -42,14 +42,14 @@ public class Fetch implements Runnable {
|
||||||
boolean armor = false;
|
boolean armor = false;
|
||||||
|
|
||||||
// TODO: Better way to inject fetcher implementation
|
// TODO: Better way to inject fetcher implementation
|
||||||
public static WKDFetcher fetcher = new HttpUrlConnectionWKDFetcher();
|
public static CertificateFetcher fetcher = new HttpUrlConnectionCertificateFetcher();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Discover discover = new DiscoverImpl(fetcher);
|
CertificateDiscoverer certificateDiscoverer = new DiscoverImpl(fetcher);
|
||||||
|
|
||||||
WKDAddress address = addressFromUserId(userId);
|
WKDAddress address = addressFromUserId(userId);
|
||||||
WKDDiscoveryResult result = discover.discover(address);
|
DiscoveryResult result = certificateDiscoverer.discover(address);
|
||||||
|
|
||||||
if (!result.isSuccessful()) {
|
if (!result.isSuccessful()) {
|
||||||
throw new CertNotFetchableException("Cannot fetch cert.");
|
throw new CertNotFetchableException("Cannot fetch cert.");
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
|
|
||||||
package pgp.wkd.cli.test_suite;
|
package pgp.wkd.cli.test_suite;
|
||||||
|
|
||||||
import pgp.wkd.DiscoveryMethod;
|
import pgp.wkd.discovery.DiscoveryMethod;
|
||||||
import pgp.wkd.WKDAddress;
|
import pgp.wkd.WKDAddress;
|
||||||
import pgp.wkd.WKDFetcher;
|
import pgp.wkd.discovery.CertificateFetcher;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
@ -15,17 +15,17 @@ import java.io.InputStream;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
public class DirectoryBasedWkdFetcher implements WKDFetcher {
|
public class DirectoryBasedCertificateFetcher implements CertificateFetcher {
|
||||||
|
|
||||||
// The directory containing the .well-known subdirectory
|
// The directory containing the .well-known subdirectory
|
||||||
private final Path rootPath;
|
private final Path rootPath;
|
||||||
|
|
||||||
public DirectoryBasedWkdFetcher(Path rootPath) {
|
public DirectoryBasedCertificateFetcher(Path rootPath) {
|
||||||
this.rootPath = rootPath;
|
this.rootPath = rootPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream fetch(WKDAddress address, DiscoveryMethod method) throws IOException {
|
public InputStream fetchCertificate(WKDAddress address, DiscoveryMethod method) throws IOException {
|
||||||
URI uri = address.getUri(method);
|
URI uri = address.getUri(method);
|
||||||
String path = uri.getPath();
|
String path = uri.getPath();
|
||||||
File file = rootPath.resolve(path.substring(1)).toFile(); // get rid of leading slash at start of path
|
File file = rootPath.resolve(path.substring(1)).toFile(); // get rid of leading slash at start of path
|
|
@ -5,7 +5,9 @@
|
||||||
package pgp.wkd.cli.test_suite;
|
package pgp.wkd.cli.test_suite;
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.DynamicTest;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.TestFactory;
|
||||||
import org.junit.platform.commons.logging.Logger;
|
import org.junit.platform.commons.logging.Logger;
|
||||||
import org.junit.platform.commons.logging.LoggerFactory;
|
import org.junit.platform.commons.logging.LoggerFactory;
|
||||||
import pgp.wkd.cli.WKDCLI;
|
import pgp.wkd.cli.WKDCLI;
|
||||||
|
@ -17,6 +19,7 @@ import pgp.wkd.test_suite.TestSuiteGenerator;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||||
|
@ -39,24 +42,29 @@ public class TestSuiteTestRunner {
|
||||||
suite = generator.generateTestSuiteInDirectory(tempFile, TestSuiteGenerator.Method.direct);
|
suite = generator.generateTestSuiteInDirectory(tempFile, TestSuiteGenerator.Method.direct);
|
||||||
|
|
||||||
// Fetch certificates from a local directory instead of the internetzzz.
|
// Fetch certificates from a local directory instead of the internetzzz.
|
||||||
Fetch.fetcher = new DirectoryBasedWkdFetcher(tempPath);
|
Fetch.fetcher = new DirectoryBasedCertificateFetcher(tempPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@TestFactory
|
||||||
void runTestsAgainstTestSuite() {
|
public Iterable<DynamicTest> testsFromTestSuite() {
|
||||||
for (TestCase testCase : suite.getTestCases()) {
|
return suite.getTestCases()
|
||||||
LOGGER.info(() -> "Execute Test Case '" + testCase.getTestTitle() + "'");
|
.stream()
|
||||||
String mail = testCase.getLookupMailAddress();
|
.map(testCase -> DynamicTest.dynamicTest(
|
||||||
|
testCase.getTestTitle(),
|
||||||
|
() -> {
|
||||||
|
String mail = testCase.getLookupMailAddress();
|
||||||
|
|
||||||
int exitCode = WKDCLI.execute(new String[] {
|
int exitCode = WKDCLI.execute(new String[] {
|
||||||
"fetch", "--armor", mail
|
"fetch", "--armor", mail
|
||||||
});
|
});
|
||||||
|
|
||||||
if (testCase.isExpectSuccess()) {
|
if (testCase.isExpectSuccess()) {
|
||||||
assertEquals(0, exitCode, testCase.getTestDescription());
|
assertEquals(0, exitCode, testCase.getTestDescription());
|
||||||
} else {
|
} else {
|
||||||
assertNotEquals(0, exitCode, testCase.getTestDescription());
|
assertNotEquals(0, exitCode, testCase.getTestDescription());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
))
|
||||||
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,9 @@ dependencies {
|
||||||
|
|
||||||
// Z-Base32
|
// Z-Base32
|
||||||
implementation 'com.sandinh:zbase32-commons-codec:1.0.0'
|
implementation 'com.sandinh:zbase32-commons-codec:1.0.0'
|
||||||
|
|
||||||
|
// @Nullable etc.
|
||||||
|
implementation "com.google.code.findbugs:jsr305:3.0.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
|
|
|
@ -9,6 +9,9 @@ import pgp.certificate_store.Certificate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tuple class which bundles a {@link Certificate} and a list of its valid or expired user ids.
|
||||||
|
*/
|
||||||
public class CertificateAndUserIds {
|
public class CertificateAndUserIds {
|
||||||
|
|
||||||
private final Certificate certificate;
|
private final Certificate certificate;
|
||||||
|
@ -19,10 +22,20 @@ public class CertificateAndUserIds {
|
||||||
this.userIds = userIds;
|
this.userIds = userIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list containing the valid or expired user-ids of the certificate.
|
||||||
|
*
|
||||||
|
* @return user ids
|
||||||
|
*/
|
||||||
public List<String> getUserIds() {
|
public List<String> getUserIds() {
|
||||||
return new ArrayList<>(userIds);
|
return new ArrayList<>(userIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the certificate itself.
|
||||||
|
*
|
||||||
|
* @return certificate
|
||||||
|
*/
|
||||||
public Certificate getCertificate() {
|
public Certificate getCertificate() {
|
||||||
return certificate;
|
return certificate;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package pgp.wkd;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface Discover {
|
|
||||||
|
|
||||||
WKDDiscoveryItem discover(DiscoveryMethod method, WKDAddress address);
|
|
||||||
|
|
||||||
default WKDDiscoveryResult discover(WKDAddress address) {
|
|
||||||
List<WKDDiscoveryItem> results = new ArrayList<>();
|
|
||||||
|
|
||||||
// advanced method
|
|
||||||
WKDDiscoveryItem advanced = discover(DiscoveryMethod.advanced, address);
|
|
||||||
results.add(advanced);
|
|
||||||
|
|
||||||
if (advanced.isSuccessful()) {
|
|
||||||
return new WKDDiscoveryResult(results);
|
|
||||||
}
|
|
||||||
// direct method
|
|
||||||
results.add(discover(DiscoveryMethod.direct, address));
|
|
||||||
|
|
||||||
return new WKDDiscoveryResult(results);
|
|
||||||
}
|
|
||||||
|
|
||||||
default WKDDiscoveryResult discoverByEmail(String email) throws MalformedUserIdException {
|
|
||||||
return discover(WKDAddress.fromEmail(email));
|
|
||||||
}
|
|
||||||
|
|
||||||
default WKDDiscoveryResult discoverByUserId(String userId) throws MalformedUserIdException {
|
|
||||||
return discover(WKDAddressHelper.wkdAddressFromUserId(userId));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package pgp.wkd;
|
|
||||||
|
|
||||||
public enum DiscoveryMethod {
|
|
||||||
advanced,
|
|
||||||
direct,
|
|
||||||
;
|
|
||||||
}
|
|
|
@ -4,6 +4,10 @@
|
||||||
|
|
||||||
package pgp.wkd;
|
package pgp.wkd;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception that gets thrown when the application is presented with a malformed user-id.
|
||||||
|
* A malformed user-id is a user-id which does not contain an email address.
|
||||||
|
*/
|
||||||
public class MalformedUserIdException extends RuntimeException {
|
public class MalformedUserIdException extends RuntimeException {
|
||||||
|
|
||||||
public MalformedUserIdException(String message) {
|
public MalformedUserIdException(String message) {
|
||||||
|
|
|
@ -6,21 +6,36 @@ package pgp.wkd;
|
||||||
|
|
||||||
import pgp.certificate_store.Certificate;
|
import pgp.certificate_store.Certificate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A rejected OpenPGP certificate.
|
||||||
|
* The WKD specification requires that a certificate fetched via the Web Key Directory MUST contain the mail address
|
||||||
|
* that was used to look up the certificate as a user id.
|
||||||
|
*
|
||||||
|
* A rejected certificate may not have carried the lookup email address.
|
||||||
|
*/
|
||||||
public class RejectedCertificate {
|
public class RejectedCertificate {
|
||||||
|
|
||||||
private final Certificate certificate;
|
private final Certificate certificate;
|
||||||
private final Throwable failure;
|
private final Throwable reasonForRejection;
|
||||||
|
|
||||||
public RejectedCertificate(Certificate certificate, Throwable failure) {
|
public RejectedCertificate(Certificate certificate, Throwable reasonForRejection) {
|
||||||
this.certificate = certificate;
|
this.certificate = certificate;
|
||||||
this.failure = failure;
|
this.reasonForRejection = reasonForRejection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the certificate.
|
||||||
|
* @return certificate
|
||||||
|
*/
|
||||||
public Certificate getCertificate() {
|
public Certificate getCertificate() {
|
||||||
return certificate;
|
return certificate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Throwable getFailure() {
|
/**
|
||||||
return failure;
|
* Return the reason for rejection.
|
||||||
|
* @return rejection reason
|
||||||
|
*/
|
||||||
|
public Throwable getReasonForRejection() {
|
||||||
|
return reasonForRejection;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,9 @@
|
||||||
package pgp.wkd;
|
package pgp.wkd;
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.ZBase32;
|
import org.apache.commons.codec.binary.ZBase32;
|
||||||
|
import pgp.wkd.discovery.DiscoveryMethod;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
|
@ -26,7 +28,7 @@ public final class WKDAddress {
|
||||||
private static final String SCHEME = "https://";
|
private static final String SCHEME = "https://";
|
||||||
private static final String ADV_SUBDOMAIN = "openpgpkey.";
|
private static final String ADV_SUBDOMAIN = "openpgpkey.";
|
||||||
private static final String DIRECT_WELL_KNOWN = "/.well-known/openpgpkey/hu/";
|
private static final String DIRECT_WELL_KNOWN = "/.well-known/openpgpkey/hu/";
|
||||||
private static String ADV_WELL_KNOWN(String domain) {
|
private static String ADV_WELL_KNOWN(@Nonnull String domain) {
|
||||||
return "/.well-known/openpgpkey/" + domain + "/hu/";
|
return "/.well-known/openpgpkey/" + domain + "/hu/";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +42,7 @@ public final class WKDAddress {
|
||||||
private static final Pattern PATTERN_DOMAIN_PART = Pattern.compile("[a-zA-Z0-9.-]+$");
|
private static final Pattern PATTERN_DOMAIN_PART = Pattern.compile("[a-zA-Z0-9.-]+$");
|
||||||
|
|
||||||
// Android API lvl 10 does not yet know StandardCharsets.UTF_8 :/
|
// Android API lvl 10 does not yet know StandardCharsets.UTF_8 :/
|
||||||
|
@SuppressWarnings("CharsetObjectCanBeUsed")
|
||||||
private static final Charset utf8 = Charset.forName("UTF8");
|
private static final Charset utf8 = Charset.forName("UTF8");
|
||||||
// Z-Base32 encoding is described in https://www.rfc-editor.org/rfc/rfc6189.html#section-5.1.6
|
// Z-Base32 encoding is described in https://www.rfc-editor.org/rfc/rfc6189.html#section-5.1.6
|
||||||
private static final ZBase32 zBase32 = new ZBase32();
|
private static final ZBase32 zBase32 = new ZBase32();
|
||||||
|
@ -70,14 +73,17 @@ public final class WKDAddress {
|
||||||
* @param domainPart domain part of the email address, case-insensitive
|
* @param domainPart domain part of the email address, case-insensitive
|
||||||
*
|
*
|
||||||
* @return WKD address
|
* @return WKD address
|
||||||
|
* @throws IllegalArgumentException in case of malformed local or domain part
|
||||||
*/
|
*/
|
||||||
public static WKDAddress fromLocalAndDomainPart(String localPart, String domainPart) {
|
@Nonnull
|
||||||
|
public static WKDAddress fromLocalAndDomainPart(@Nonnull String localPart, @Nonnull String domainPart) {
|
||||||
if (!PATTERN_LOCAL_PART.matcher(localPart).matches()) {
|
if (!PATTERN_LOCAL_PART.matcher(localPart).matches()) {
|
||||||
throw new IllegalArgumentException("Invalid local part.");
|
throw new IllegalArgumentException("Invalid local part.");
|
||||||
}
|
}
|
||||||
if (!PATTERN_DOMAIN_PART.matcher(domainPart).matches()) {
|
if (!PATTERN_DOMAIN_PART.matcher(domainPart).matches()) {
|
||||||
throw new IllegalArgumentException("Invalid domain part.");
|
throw new IllegalArgumentException("Invalid domain part.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new WKDAddress(localPart, domainPart);
|
return new WKDAddress(localPart, domainPart);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,20 +93,35 @@ public final class WKDAddress {
|
||||||
* @param email email address, case sensitive
|
* @param email email address, case sensitive
|
||||||
* @return WKDAddress object
|
* @return WKDAddress object
|
||||||
*/
|
*/
|
||||||
public static WKDAddress fromEmail(String email) throws MalformedUserIdException {
|
public static WKDAddress fromEmail(@Nonnull String email) throws MalformedUserIdException {
|
||||||
MailAddress mailAddress = parseMailAddress(email);
|
MailAddress mailAddress = parseMailAddress(email);
|
||||||
return new WKDAddress(mailAddress.getLocalPart(), mailAddress.getDomainPart());
|
return new WKDAddress(mailAddress.getLocalPart(), mailAddress.getDomainPart());
|
||||||
}
|
}
|
||||||
|
|
||||||
public URI getUri(DiscoveryMethod method) {
|
/**
|
||||||
if (method == DiscoveryMethod.advanced) {
|
* Return the {@link URI} for the respective {@link DiscoveryMethod}.
|
||||||
return getAdvancedMethodURI();
|
*
|
||||||
} else if (method == DiscoveryMethod.direct) {
|
* @param method discovery method
|
||||||
return getDirectMethodURI();
|
* @return uri of the certificate
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public URI getUri(@Nonnull DiscoveryMethod method) {
|
||||||
|
switch (method) {
|
||||||
|
case advanced:
|
||||||
|
return getAdvancedMethodURI();
|
||||||
|
case direct:
|
||||||
|
return getDirectMethodURI();
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Invalid discovery method: " + method);
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException("Invalid discovery method.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the email address from which the {@link WKDAddress} was created.
|
||||||
|
*
|
||||||
|
* @return email address
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
public String getEmail() {
|
public String getEmail() {
|
||||||
return localPart + '@' + domainPart;
|
return localPart + '@' + domainPart;
|
||||||
}
|
}
|
||||||
|
@ -117,6 +138,7 @@ public final class WKDAddress {
|
||||||
*
|
*
|
||||||
* @return URI using the direct lookup method
|
* @return URI using the direct lookup method
|
||||||
*/
|
*/
|
||||||
|
@Nonnull
|
||||||
public URI getDirectMethodURI() {
|
public URI getDirectMethodURI() {
|
||||||
return URI.create(SCHEME + domainPart + DIRECT_WELL_KNOWN + zbase32LocalPart + "?l=" + percentEncodedLocalPart);
|
return URI.create(SCHEME + domainPart + DIRECT_WELL_KNOWN + zbase32LocalPart + "?l=" + percentEncodedLocalPart);
|
||||||
}
|
}
|
||||||
|
@ -133,6 +155,7 @@ public final class WKDAddress {
|
||||||
*
|
*
|
||||||
* @return URI using the advanced lookup method
|
* @return URI using the advanced lookup method
|
||||||
*/
|
*/
|
||||||
|
@Nonnull
|
||||||
public URI getAdvancedMethodURI() {
|
public URI getAdvancedMethodURI() {
|
||||||
return URI.create(SCHEME + ADV_SUBDOMAIN + domainPart + ADV_WELL_KNOWN(domainPart) + zbase32LocalPart + "?l=" + percentEncodedLocalPart);
|
return URI.create(SCHEME + ADV_SUBDOMAIN + domainPart + ADV_WELL_KNOWN(domainPart) + zbase32LocalPart + "?l=" + percentEncodedLocalPart);
|
||||||
}
|
}
|
||||||
|
@ -143,7 +166,8 @@ public final class WKDAddress {
|
||||||
* @param string string
|
* @param string string
|
||||||
* @return zbase32 encoded sha1 sum of the string
|
* @return zbase32 encoded sha1 sum of the string
|
||||||
*/
|
*/
|
||||||
private String sha1AndZBase32Encode(String string) {
|
@Nonnull
|
||||||
|
private String sha1AndZBase32Encode(@Nonnull String string) {
|
||||||
String lowerCase = string.toLowerCase();
|
String lowerCase = string.toLowerCase();
|
||||||
byte[] bytes = lowerCase.getBytes(utf8);
|
byte[] bytes = lowerCase.getBytes(utf8);
|
||||||
|
|
||||||
|
@ -166,7 +190,8 @@ public final class WKDAddress {
|
||||||
* @param string string
|
* @param string string
|
||||||
* @return percent encoded string
|
* @return percent encoded string
|
||||||
*/
|
*/
|
||||||
private String percentEncode(String string) {
|
@Nonnull
|
||||||
|
private String percentEncode(@Nonnull String string) {
|
||||||
try {
|
try {
|
||||||
return URLEncoder.encode(string, "UTF-8");
|
return URLEncoder.encode(string, "UTF-8");
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
@ -181,7 +206,9 @@ public final class WKDAddress {
|
||||||
* @param email email address string
|
* @param email email address string
|
||||||
* @return validated and split mail address
|
* @return validated and split mail address
|
||||||
*/
|
*/
|
||||||
private static MailAddress parseMailAddress(String email) throws MalformedUserIdException {
|
@Nonnull
|
||||||
|
private static MailAddress parseMailAddress(@Nonnull String email)
|
||||||
|
throws MalformedUserIdException {
|
||||||
Matcher matcher = PATTERN_EMAIL.matcher(email);
|
Matcher matcher = PATTERN_EMAIL.matcher(email);
|
||||||
if (!matcher.matches()) {
|
if (!matcher.matches()) {
|
||||||
throw new MalformedUserIdException("Invalid email address.");
|
throw new MalformedUserIdException("Invalid email address.");
|
||||||
|
@ -207,7 +234,7 @@ public final class WKDAddress {
|
||||||
* @param localPart local part
|
* @param localPart local part
|
||||||
* @param domainPart domain part
|
* @param domainPart domain part
|
||||||
*/
|
*/
|
||||||
MailAddress(String localPart, String domainPart) {
|
MailAddress(@Nonnull String localPart, @Nonnull String domainPart) {
|
||||||
this.localPart = localPart;
|
this.localPart = localPart;
|
||||||
this.domainPart = domainPart;
|
this.domainPart = domainPart;
|
||||||
}
|
}
|
||||||
|
@ -218,6 +245,7 @@ public final class WKDAddress {
|
||||||
*
|
*
|
||||||
* @return local part
|
* @return local part
|
||||||
*/
|
*/
|
||||||
|
@Nonnull
|
||||||
public String getLocalPart() {
|
public String getLocalPart() {
|
||||||
return localPart;
|
return localPart;
|
||||||
}
|
}
|
||||||
|
@ -228,6 +256,7 @@ public final class WKDAddress {
|
||||||
*
|
*
|
||||||
* @return domain part
|
* @return domain part
|
||||||
*/
|
*/
|
||||||
|
@Nonnull
|
||||||
public String getDomainPart() {
|
public String getDomainPart() {
|
||||||
return domainPart;
|
return domainPart;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
package pgp.wkd;
|
package pgp.wkd;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
@ -31,7 +32,9 @@ public class WKDAddressHelper {
|
||||||
* @throws IllegalArgumentException in case the user-id does not match the expected format
|
* @throws IllegalArgumentException in case the user-id does not match the expected format
|
||||||
* and does not contain an email address.
|
* and does not contain an email address.
|
||||||
*/
|
*/
|
||||||
public static String emailFromUserId(String userId) throws MalformedUserIdException {
|
@Nonnull
|
||||||
|
public static String emailFromUserId(String userId)
|
||||||
|
throws MalformedUserIdException {
|
||||||
Matcher matcher = PATTERN_USER_ID.matcher(userId);
|
Matcher matcher = PATTERN_USER_ID.matcher(userId);
|
||||||
if (!matcher.matches()) {
|
if (!matcher.matches()) {
|
||||||
throw new MalformedUserIdException("User-ID does not follow excepted pattern \"Firstname Lastname <email.address> [Optional Comment]\"");
|
throw new MalformedUserIdException("User-ID does not follow excepted pattern \"Firstname Lastname <email.address> [Optional Comment]\"");
|
||||||
|
@ -47,7 +50,9 @@ public class WKDAddressHelper {
|
||||||
* @param userId user-id
|
* @param userId user-id
|
||||||
* @return WKD address for the user-id's email address.
|
* @return WKD address for the user-id's email address.
|
||||||
*/
|
*/
|
||||||
public static WKDAddress wkdAddressFromUserId(String userId) throws MalformedUserIdException {
|
@Nonnull
|
||||||
|
public static WKDAddress wkdAddressFromUserId(String userId)
|
||||||
|
throws MalformedUserIdException {
|
||||||
String email = emailFromUserId(userId);
|
String email = emailFromUserId(userId);
|
||||||
return WKDAddress.fromEmail(email);
|
return WKDAddress.fromEmail(email);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,26 +2,27 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package pgp.wkd;
|
package pgp.wkd.discovery;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import pgp.wkd.WKDAddress;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
||||||
public abstract class AbstractUriWKDFetcher implements WKDFetcher {
|
public abstract class AbstractUriCertificateFetcher implements CertificateFetcher {
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(WKDFetcher.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(CertificateFetcher.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream fetch(WKDAddress address, DiscoveryMethod method) throws IOException {
|
public InputStream fetchCertificate(WKDAddress address, DiscoveryMethod method) throws IOException {
|
||||||
URI uri = address.getUri(method);
|
URI uri = address.getUri(method);
|
||||||
try {
|
try {
|
||||||
return fetchUri(uri);
|
return fetchFromUri(uri);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOGGER.debug("Could not fetch key using " + method + " method from " + uri.toString(), e);
|
LOGGER.debug("Could not fetch key using " + method + " method from " + uri, e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,6 +35,6 @@ public abstract class AbstractUriWKDFetcher implements WKDFetcher {
|
||||||
* @throws java.net.ConnectException in case the file or host does not exist
|
* @throws java.net.ConnectException in case the file or host does not exist
|
||||||
* @throws IOException in case of an IO-error
|
* @throws IOException in case of an IO-error
|
||||||
*/
|
*/
|
||||||
protected abstract InputStream fetchUri(URI uri) throws IOException;
|
protected abstract InputStream fetchFromUri(URI uri) throws IOException;
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package pgp.wkd.discovery;
|
||||||
|
|
||||||
|
import pgp.wkd.MalformedUserIdException;
|
||||||
|
import pgp.wkd.WKDAddress;
|
||||||
|
import pgp.wkd.WKDAddressHelper;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface CertificateDiscoverer {
|
||||||
|
|
||||||
|
DiscoveryResponse discover(DiscoveryMethod method, WKDAddress address);
|
||||||
|
|
||||||
|
default DiscoveryResult discover(WKDAddress address) {
|
||||||
|
List<DiscoveryResponse> results = new ArrayList<>();
|
||||||
|
|
||||||
|
// advanced method
|
||||||
|
DiscoveryResponse advanced = discover(DiscoveryMethod.advanced, address);
|
||||||
|
results.add(advanced);
|
||||||
|
|
||||||
|
if (advanced.isSuccessful()) {
|
||||||
|
return new DiscoveryResult(results);
|
||||||
|
}
|
||||||
|
// direct method
|
||||||
|
results.add(discover(DiscoveryMethod.direct, address));
|
||||||
|
|
||||||
|
return new DiscoveryResult(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
default DiscoveryResult discoverByEmail(String email) throws MalformedUserIdException {
|
||||||
|
return discover(WKDAddress.fromEmail(email));
|
||||||
|
}
|
||||||
|
|
||||||
|
default DiscoveryResult discoverByUserId(String userId) throws MalformedUserIdException {
|
||||||
|
return discover(WKDAddressHelper.wkdAddressFromUserId(userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,29 +2,33 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package pgp.wkd;
|
package pgp.wkd.discovery;
|
||||||
|
|
||||||
import pgp.certificate_store.Certificate;
|
import pgp.certificate_store.Certificate;
|
||||||
|
import pgp.wkd.CertificateAndUserIds;
|
||||||
|
import pgp.wkd.MissingUserIdException;
|
||||||
|
import pgp.wkd.RejectedCertificate;
|
||||||
|
import pgp.wkd.WKDAddress;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class AbstractDiscover implements Discover {
|
public class CertificateDiscoveryImplementation implements CertificateDiscoverer {
|
||||||
|
|
||||||
protected final CertificateReader reader;
|
protected final CertificateParser reader;
|
||||||
protected final WKDFetcher fetcher;
|
protected final CertificateFetcher fetcher;
|
||||||
|
|
||||||
public AbstractDiscover(CertificateReader reader, WKDFetcher fetcher) {
|
public CertificateDiscoveryImplementation(CertificateParser reader, CertificateFetcher fetcher) {
|
||||||
this.reader = reader;
|
this.reader = reader;
|
||||||
this.fetcher = fetcher;
|
this.fetcher = fetcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WKDDiscoveryItem discover(DiscoveryMethod method, WKDAddress address) {
|
public DiscoveryResponse discover(DiscoveryMethod method, WKDAddress address) {
|
||||||
try {
|
try {
|
||||||
InputStream inputStream = fetcher.fetch(address, method);
|
InputStream inputStream = fetcher.fetchCertificate(address, method);
|
||||||
List<CertificateAndUserIds> fetchedCertificates = reader.read(inputStream);
|
List<CertificateAndUserIds> fetchedCertificates = reader.read(inputStream);
|
||||||
|
|
||||||
List<RejectedCertificate> rejectedCertificates = new ArrayList<>();
|
List<RejectedCertificate> rejectedCertificates = new ArrayList<>();
|
||||||
|
@ -50,10 +54,10 @@ public class AbstractDiscover implements Discover {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return WKDDiscoveryItem.success(method, address, acceptableCertificates, rejectedCertificates);
|
return DiscoveryResponse.success(method, address, acceptableCertificates, rejectedCertificates);
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
return WKDDiscoveryItem.failure(method, address, e);
|
return DiscoveryResponse.failure(method, address, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,7 +2,9 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package pgp.wkd;
|
package pgp.wkd.discovery;
|
||||||
|
|
||||||
|
import pgp.wkd.WKDAddress;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -11,7 +13,7 @@ import java.io.InputStream;
|
||||||
* Abstract class for fetching OpenPGP certificates from the WKD.
|
* Abstract class for fetching OpenPGP certificates from the WKD.
|
||||||
* This class can be extended to fetch files from remote servers using different HTTP clients.
|
* This class can be extended to fetch files from remote servers using different HTTP clients.
|
||||||
*/
|
*/
|
||||||
public interface WKDFetcher {
|
public interface CertificateFetcher {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to fetch an OpenPGP certificate from the Web Key Directory.
|
* Attempt to fetch an OpenPGP certificate from the Web Key Directory.
|
||||||
|
@ -21,5 +23,5 @@ public interface WKDFetcher {
|
||||||
*
|
*
|
||||||
* @throws IOException in case of an error
|
* @throws IOException in case of an error
|
||||||
*/
|
*/
|
||||||
InputStream fetch(WKDAddress address, DiscoveryMethod method) throws IOException;
|
InputStream fetchCertificate(WKDAddress address, DiscoveryMethod method) throws IOException;
|
||||||
}
|
}
|
|
@ -2,13 +2,15 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package pgp.wkd;
|
package pgp.wkd.discovery;
|
||||||
|
|
||||||
|
import pgp.wkd.CertificateAndUserIds;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface CertificateReader {
|
public interface CertificateParser {
|
||||||
|
|
||||||
List<CertificateAndUserIds> read(InputStream inputStream) throws IOException;
|
List<CertificateAndUserIds> read(InputStream inputStream) throws IOException;
|
||||||
}
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package pgp.wkd.discovery;
|
||||||
|
|
||||||
|
public enum DiscoveryMethod {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Advanced method.
|
||||||
|
* This is the preferred method which MUST be checked first.
|
||||||
|
*
|
||||||
|
* @see <a href="https://www.ietf.org/archive/id/draft-koch-openpgp-webkey-service-13.html#section-3.1-5">OpenPGP Web Key Directory: Advanced Method</a>
|
||||||
|
*/
|
||||||
|
advanced,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Direct method.
|
||||||
|
* This is the fallback method.
|
||||||
|
*
|
||||||
|
* @see <a href="https://www.ietf.org/archive/id/draft-koch-openpgp-webkey-service-13.html#section-3.1-10">OpenPGP web Key Directory: Direct Method</a>
|
||||||
|
*/
|
||||||
|
direct
|
||||||
|
}
|
|
@ -2,78 +2,95 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package pgp.wkd;
|
package pgp.wkd.discovery;
|
||||||
|
|
||||||
import pgp.certificate_store.Certificate;
|
import pgp.certificate_store.Certificate;
|
||||||
|
import pgp.wkd.RejectedCertificate;
|
||||||
|
import pgp.wkd.WKDAddress;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public final class WKDDiscoveryItem {
|
public final class DiscoveryResponse {
|
||||||
|
|
||||||
private final DiscoveryMethod method;
|
private final DiscoveryMethod method;
|
||||||
private final WKDAddress address;
|
private final WKDAddress address;
|
||||||
private final List<Certificate> certificates;
|
private final List<Certificate> certificates;
|
||||||
private final List<RejectedCertificate> rejectedCertificates;
|
private final List<RejectedCertificate> rejectedCertificates;
|
||||||
private final Throwable failure;
|
private final Throwable fetchingFailure;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for a {@link WKDDiscoveryItem} object.
|
* Constructor for a {@link DiscoveryResponse} object.
|
||||||
* @param method discovery method
|
* @param method discovery method
|
||||||
* @param address wkd address used for discovery
|
* @param address wkd address used for discovery
|
||||||
* @param certificates list of successfully fetched certificates
|
* @param certificates list of successfully fetched certificates
|
||||||
* @param rejectedCertificates list of invalid fetched certificates (e.g. missing user-id)
|
* @param rejectedCertificates list of invalid fetched certificates (e.g. missing user-id)
|
||||||
* @param failure general fetching error (e.g. connection error, 404...)
|
* @param fetchingFailure general fetching error (e.g. connection error, 404...)
|
||||||
*/
|
*/
|
||||||
private WKDDiscoveryItem(
|
private DiscoveryResponse(
|
||||||
DiscoveryMethod method,
|
DiscoveryMethod method,
|
||||||
WKDAddress address,
|
WKDAddress address,
|
||||||
List<Certificate> certificates,
|
List<Certificate> certificates,
|
||||||
List<RejectedCertificate> rejectedCertificates,
|
List<RejectedCertificate> rejectedCertificates,
|
||||||
Throwable failure) {
|
Throwable fetchingFailure) {
|
||||||
this.method = method;
|
this.method = method;
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.certificates = certificates;
|
this.certificates = certificates;
|
||||||
this.rejectedCertificates = rejectedCertificates;
|
this.rejectedCertificates = rejectedCertificates;
|
||||||
this.failure = failure;
|
this.fetchingFailure = fetchingFailure;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static WKDDiscoveryItem success(DiscoveryMethod method, WKDAddress address, List<Certificate> certificates, List<RejectedCertificate> rejectedCertificates) {
|
public static DiscoveryResponse success(
|
||||||
return new WKDDiscoveryItem(method, address, certificates, rejectedCertificates, null);
|
@Nonnull DiscoveryMethod method,
|
||||||
|
@Nonnull WKDAddress address,
|
||||||
|
@Nonnull List<Certificate> certificates,
|
||||||
|
@Nonnull List<RejectedCertificate> rejectedCertificates) {
|
||||||
|
return new DiscoveryResponse(method, address, certificates, rejectedCertificates, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static WKDDiscoveryItem failure(DiscoveryMethod method, WKDAddress address, Throwable failure) {
|
public static DiscoveryResponse failure(
|
||||||
return new WKDDiscoveryItem(method, address, null, null, failure);
|
@Nonnull DiscoveryMethod method,
|
||||||
|
@Nonnull WKDAddress address,
|
||||||
|
@Nonnull Throwable fetchingFailure) {
|
||||||
|
return new DiscoveryResponse(method, address, Collections.emptyList(), Collections.emptyList(), fetchingFailure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public DiscoveryMethod getMethod() {
|
public DiscoveryMethod getMethod() {
|
||||||
return method;
|
return method;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public WKDAddress getAddress() {
|
public WKDAddress getAddress() {
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSuccessful() {
|
public boolean isSuccessful() {
|
||||||
return !hasFailure();
|
return !hasFetchingFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public List<Certificate> getCertificates() {
|
public List<Certificate> getCertificates() {
|
||||||
return certificates;
|
return certificates;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public List<RejectedCertificate> getRejectedCertificates() {
|
public List<RejectedCertificate> getRejectedCertificates() {
|
||||||
return rejectedCertificates;
|
return rejectedCertificates;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Throwable getFailure() {
|
@Nullable
|
||||||
return failure;
|
public Throwable getFetchingFailure() {
|
||||||
|
return fetchingFailure;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasCertificates() {
|
public boolean hasCertificates() {
|
||||||
return certificates != null && !certificates.isEmpty();
|
return certificates != null && !certificates.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasFailure() {
|
public boolean hasFetchingFailure() {
|
||||||
return failure != null;
|
return fetchingFailure != null;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,25 +2,27 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package pgp.wkd;
|
package pgp.wkd.discovery;
|
||||||
|
|
||||||
import pgp.certificate_store.Certificate;
|
import pgp.certificate_store.Certificate;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class WKDDiscoveryResult {
|
public class DiscoveryResult {
|
||||||
|
|
||||||
private List<WKDDiscoveryItem> items;
|
private List<DiscoveryResponse> items;
|
||||||
|
|
||||||
public WKDDiscoveryResult(List<WKDDiscoveryItem> items) {
|
public DiscoveryResult(@Nonnull List<DiscoveryResponse> items) {
|
||||||
this.items = items;
|
this.items = items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public List<Certificate> getCertificates() {
|
public List<Certificate> getCertificates() {
|
||||||
List<Certificate> certificates = new ArrayList<>();
|
List<Certificate> certificates = new ArrayList<>();
|
||||||
|
|
||||||
for (WKDDiscoveryItem item : items) {
|
for (DiscoveryResponse item : items) {
|
||||||
if (item.isSuccessful()) {
|
if (item.isSuccessful()) {
|
||||||
certificates.addAll(item.getCertificates());
|
certificates.addAll(item.getCertificates());
|
||||||
}
|
}
|
||||||
|
@ -29,7 +31,7 @@ public class WKDDiscoveryResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSuccessful() {
|
public boolean isSuccessful() {
|
||||||
for (WKDDiscoveryItem item : items) {
|
for (DiscoveryResponse item : items) {
|
||||||
if (item.isSuccessful() && item.hasCertificates()) {
|
if (item.isSuccessful() && item.hasCertificates()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -37,13 +39,15 @@ public class WKDDiscoveryResult {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<WKDDiscoveryItem> getItems() {
|
@Nonnull
|
||||||
|
public List<DiscoveryResponse> getItems() {
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<WKDDiscoveryItem> getFailedItems() {
|
@Nonnull
|
||||||
List<WKDDiscoveryItem> fails = new ArrayList<>();
|
public List<DiscoveryResponse> getFailedItems() {
|
||||||
for (WKDDiscoveryItem item : items) {
|
List<DiscoveryResponse> fails = new ArrayList<>();
|
||||||
|
for (DiscoveryResponse item : items) {
|
||||||
if (!item.isSuccessful()) {
|
if (!item.isSuccessful()) {
|
||||||
fails.add(item);
|
fails.add(item);
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package pgp.wkd;
|
package pgp.wkd.discovery;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -12,11 +12,11 @@ import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of {@link WKDFetcher} using Java's {@link HttpURLConnection}.
|
* Implementation of {@link CertificateFetcher} using Java's {@link HttpURLConnection}.
|
||||||
*/
|
*/
|
||||||
public class HttpUrlConnectionWKDFetcher extends AbstractUriWKDFetcher {
|
public class HttpUrlConnectionCertificateFetcher extends AbstractUriCertificateFetcher {
|
||||||
|
|
||||||
public InputStream fetchUri(URI uri) throws IOException {
|
public InputStream fetchFromUri(URI uri) throws IOException {
|
||||||
URL url = uri.toURL();
|
URL url = uri.toURL();
|
||||||
HttpURLConnection con = (HttpURLConnection) url.openConnection();
|
HttpURLConnection con = (HttpURLConnection) url.openConnection();
|
||||||
con.setRequestMethod("GET");
|
con.setRequestMethod("GET");
|
Loading…
Reference in a new issue