From bcb0716fc2f70d4df89b7f6e87075d5b273e7142 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 6 Apr 2022 13:38:14 +0200 Subject: [PATCH] Some more cleanup --- config/checkstyle/checkstyle.xml | 218 ++++++++++++++++++ config/checkstyle/suppressions.xml | 19 ++ vks-java/build.gradle | 6 +- .../java/pgp/vks/client/RequestVerify.java | 48 +++- .../src/main/java/pgp/vks/client/Status.java | 28 +++ .../src/main/java/pgp/vks/client/Upload.java | 57 ++++- .../vks/client/exception/package-info.java} | 8 +- .../java/pgp/vks/client/package-info.java | 8 + ...rorResponse.java => ErrorResponseDto.java} | 4 +- ...loadRequest.java => UploadRequestDto.java} | 23 +- ...adResponse.java => UploadResponseDto.java} | 15 +- ...quest.java => VerificationRequestDto.java} | 8 +- ...onse.java => VerificationResponseDto.java} | 14 +- .../v1/dto/{Status.java => package-info.java} | 10 +- .../vks/client/v1/impl/RequestVerifyImpl.java | 70 ++++-- .../pgp/vks/client/v1/impl/UploadImpl.java | 28 +-- .../pgp/vks/client/v1/impl/package-info.java | 8 + .../java/pgp/vks/client/v1/package-info.java | 8 + .../client/v1/dto/ErrorResponseDtoTest.java | 28 +++ .../dto/UploadRequestDtoTest.java} | 15 +- .../{impl => }/v1/dto/UploadResponseTest.java | 9 +- .../v1/dto/VerificationRequestDtoTest.java | 39 ++++ .../v1/dto/VerificationResponseDtoTest.java | 42 ++++ .../impl/URLMapperTest.java} | 5 +- .../client/{impl/v1 => v1/impl}/VKSTest.java | 99 ++++++-- 25 files changed, 690 insertions(+), 127 deletions(-) create mode 100644 config/checkstyle/checkstyle.xml create mode 100644 config/checkstyle/suppressions.xml create mode 100644 vks-java/src/main/java/pgp/vks/client/Status.java rename vks-java/src/{test/java/pgp/vks/client/impl/v1/dummy_vks/DummyVks.java => main/java/pgp/vks/client/exception/package-info.java} (61%) create mode 100644 vks-java/src/main/java/pgp/vks/client/package-info.java rename vks-java/src/main/java/pgp/vks/client/v1/dto/{ErrorResponse.java => ErrorResponseDto.java} (78%) rename vks-java/src/main/java/pgp/vks/client/v1/dto/{UploadRequest.java => UploadRequestDto.java} (53%) rename vks-java/src/main/java/pgp/vks/client/v1/dto/{UploadResponse.java => UploadResponseDto.java} (63%) rename vks-java/src/main/java/pgp/vks/client/v1/dto/{VerificationRequest.java => VerificationRequestDto.java} (72%) rename vks-java/src/main/java/pgp/vks/client/v1/dto/{VerificationResponse.java => VerificationResponseDto.java} (59%) rename vks-java/src/main/java/pgp/vks/client/v1/dto/{Status.java => package-info.java} (63%) create mode 100644 vks-java/src/main/java/pgp/vks/client/v1/impl/package-info.java create mode 100644 vks-java/src/main/java/pgp/vks/client/v1/package-info.java create mode 100644 vks-java/src/test/java/pgp/vks/client/v1/dto/ErrorResponseDtoTest.java rename vks-java/src/test/java/pgp/vks/client/{impl/v1/dto/UploadRequestTest.java => v1/dto/UploadRequestDtoTest.java} (93%) rename vks-java/src/test/java/pgp/vks/client/{impl => }/v1/dto/UploadResponseTest.java (80%) create mode 100644 vks-java/src/test/java/pgp/vks/client/v1/dto/VerificationRequestDtoTest.java create mode 100644 vks-java/src/test/java/pgp/vks/client/v1/dto/VerificationResponseDtoTest.java rename vks-java/src/test/java/pgp/vks/client/{impl/v1/V1APITest.java => v1/impl/URLMapperTest.java} (94%) rename vks-java/src/test/java/pgp/vks/client/{impl/v1 => v1/impl}/VKSTest.java (56%) diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 0000000..bf1878e --- /dev/null +++ b/config/checkstyle/checkstyle.xml @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml new file mode 100644 index 0000000..1314d44 --- /dev/null +++ b/config/checkstyle/suppressions.xml @@ -0,0 +1,19 @@ + + + + + + + + + + diff --git a/vks-java/build.gradle b/vks-java/build.gradle index 57c150a..9099f01 100644 --- a/vks-java/build.gradle +++ b/vks-java/build.gradle @@ -16,8 +16,10 @@ dependencies { testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion" - api("org.pgpainless:pgp-certificate-store:0.1.0") - api("org.pgpainless:pgpainless-core:1.1.4") + implementation("org.bouncycastle:bcutil-jdk15on:1.70") + + // api("org.pgpainless:pgp-certificate-store:0.1.0") + // api("org.pgpainless:pgpainless-core:1.1.4") // @Nonnull, @Nullable... implementation "com.google.code.findbugs:jsr305:3.0.2" diff --git a/vks-java/src/main/java/pgp/vks/client/RequestVerify.java b/vks-java/src/main/java/pgp/vks/client/RequestVerify.java index ed059e0..3d7fb5d 100644 --- a/vks-java/src/main/java/pgp/vks/client/RequestVerify.java +++ b/vks-java/src/main/java/pgp/vks/client/RequestVerify.java @@ -4,19 +4,53 @@ package pgp.vks.client; -import pgp.vks.client.v1.dto.VerificationResponse; - import java.io.IOException; import java.util.Arrays; import java.util.List; +import java.util.Map; public interface RequestVerify { - default VerificationResponse forEmailAddresses(List emailAddresses, String uploadToken) - throws IOException { - return forEmailAddresses(emailAddresses, uploadToken, Arrays.asList("en_US", "en_GB")); + default ForEmailAddresses forEmailAddress(String emailAddress) { + return forEmailAddresses(emailAddress); } - VerificationResponse forEmailAddresses(List emailAddresses, String uploadToken, List locale) - throws IOException; + ForEmailAddresses forEmailAddresses(String... emailAddresses); + + interface ForEmailAddresses { + + default Response execute(String token) throws IOException { + return execute(token, Arrays.asList("en_US", "en_GB")); + } + + Response execute(String token, List locale) throws IOException; + + } + + class Response { + + private final String keyFingerprint; + private final Map status; + private final String token; + + public Response(String keyFingerprint, + Map status, + String token) { + this.keyFingerprint = keyFingerprint; + this.status = status; + this.token = token; + } + + public String getKeyFingerprint() { + return keyFingerprint; + } + + public Map getStatus() { + return status; + } + + public String getToken() { + return token; + } + } } diff --git a/vks-java/src/main/java/pgp/vks/client/Status.java b/vks-java/src/main/java/pgp/vks/client/Status.java new file mode 100644 index 0000000..a8ead89 --- /dev/null +++ b/vks-java/src/main/java/pgp/vks/client/Status.java @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2022 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package pgp.vks.client; + +public enum Status { + + /** + * User-ID is not published. + */ + unpublished, + + /** + * User-ID is published. + */ + published, + + /** + * User-ID is revoked. + */ + revoked, + + /** + * A verification mail for the User-ID was requested, but the verification is still pending. + */ + pending +} diff --git a/vks-java/src/main/java/pgp/vks/client/Upload.java b/vks-java/src/main/java/pgp/vks/client/Upload.java index ad074cb..f5d4892 100644 --- a/vks-java/src/main/java/pgp/vks/client/Upload.java +++ b/vks-java/src/main/java/pgp/vks/client/Upload.java @@ -4,14 +4,65 @@ package pgp.vks.client; -import pgp.vks.client.v1.dto.UploadResponse; - import javax.annotation.Nonnull; import java.io.IOException; import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; public interface Upload { - UploadResponse cert(@Nonnull InputStream certInStream) throws IOException; + /** + * Upload a certificate to the key server. + * The
certInStream
can either contain the binary, or the ASCII armored representation of a transferable + * public key (certificate). + * + * @param certInStream inputStream containing the certificate + * @return server response + * @throws IOException in case of an IO error + */ + Response cert(@Nonnull InputStream certInStream) throws IOException; + class Response { + + private final String keyFingerprint; + private final Map status; + private final String token; + + public Response(String keyFingerprint, + Map status, + String token) { + this.keyFingerprint = keyFingerprint; + this.status = status; + this.token = token; + } + + /** + * Return the fingerprint of the uploaded certificate. + * + * @return fingerprint + */ + public String getKeyFingerprint() { + return keyFingerprint; + } + + /** + * Return an access token which, for a limited time, can be used to request verifications (see {@link RequestVerify}). + * + * @return access token + */ + public String getToken() { + return token; + } + + /** + * Return a map containing the {@link Status} of each of the keys addresses. + * + * @return status map + */ + public Map getStatus() { + return new HashMap<>(status); + } + } } + diff --git a/vks-java/src/test/java/pgp/vks/client/impl/v1/dummy_vks/DummyVks.java b/vks-java/src/main/java/pgp/vks/client/exception/package-info.java similarity index 61% rename from vks-java/src/test/java/pgp/vks/client/impl/v1/dummy_vks/DummyVks.java rename to vks-java/src/main/java/pgp/vks/client/exception/package-info.java index 3a446a4..28f92e1 100644 --- a/vks-java/src/test/java/pgp/vks/client/impl/v1/dummy_vks/DummyVks.java +++ b/vks-java/src/main/java/pgp/vks/client/exception/package-info.java @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: Apache-2.0 -package pgp.vks.client.impl.v1.dummy_vks; - -public class DummyVks { -} +/** + * Exception classes. + */ +package pgp.vks.client.exception; diff --git a/vks-java/src/main/java/pgp/vks/client/package-info.java b/vks-java/src/main/java/pgp/vks/client/package-info.java new file mode 100644 index 0000000..0d46c7e --- /dev/null +++ b/vks-java/src/main/java/pgp/vks/client/package-info.java @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: 2022 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +/** + * Client API for communicating with Verifying Key Servers. + */ +package pgp.vks.client; diff --git a/vks-java/src/main/java/pgp/vks/client/v1/dto/ErrorResponse.java b/vks-java/src/main/java/pgp/vks/client/v1/dto/ErrorResponseDto.java similarity index 78% rename from vks-java/src/main/java/pgp/vks/client/v1/dto/ErrorResponse.java rename to vks-java/src/main/java/pgp/vks/client/v1/dto/ErrorResponseDto.java index 1ac0e2a..3208791 100644 --- a/vks-java/src/main/java/pgp/vks/client/v1/dto/ErrorResponse.java +++ b/vks-java/src/main/java/pgp/vks/client/v1/dto/ErrorResponseDto.java @@ -6,11 +6,11 @@ package pgp.vks.client.v1.dto; import com.fasterxml.jackson.annotation.JsonProperty; -public class ErrorResponse { +public class ErrorResponseDto { private final String error; - public ErrorResponse(@JsonProperty("error") String error) { + public ErrorResponseDto(@JsonProperty("error") String error) { this.error = error; } diff --git a/vks-java/src/main/java/pgp/vks/client/v1/dto/UploadRequest.java b/vks-java/src/main/java/pgp/vks/client/v1/dto/UploadRequestDto.java similarity index 53% rename from vks-java/src/main/java/pgp/vks/client/v1/dto/UploadRequest.java rename to vks-java/src/main/java/pgp/vks/client/v1/dto/UploadRequestDto.java index 7acaaeb..e351a39 100644 --- a/vks-java/src/main/java/pgp/vks/client/v1/dto/UploadRequest.java +++ b/vks-java/src/main/java/pgp/vks/client/v1/dto/UploadRequestDto.java @@ -7,22 +7,33 @@ package pgp.vks.client.v1.dto; import com.fasterxml.jackson.annotation.JsonProperty; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Base64; +import org.bouncycastle.util.io.Streams; -import java.nio.charset.StandardCharsets; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; -public class UploadRequest { +public class UploadRequestDto { - private static final byte[] ARMOR_HEADER = "-----BEGIN PGP PUBLIC KEY BLOCK-----".getBytes(StandardCharsets.UTF_8); + private static final byte[] ARMOR_HEADER = "-----BEGIN PGP PUBLIC KEY BLOCK-----".getBytes(Charset.forName("UTF8")); private final String keytext; - public UploadRequest(@JsonProperty("keytext") String keytext) { + public UploadRequestDto(@JsonProperty("keytext") String keytext) { this.keytext = keytext; } - public static UploadRequest fromBytes(byte[] keytext) { + public static UploadRequestDto fromInputStream(InputStream certInStream) throws IOException { + ByteArrayOutputStream certBuf = new ByteArrayOutputStream(); + Streams.pipeAll(certInStream, certBuf); + certInStream.close(); + return fromBytes(certBuf.toByteArray()); + } + + public static UploadRequestDto fromBytes(byte[] keytext) { String armoredOrBase64 = new String(base64IfNecessary(keytext)); - return new UploadRequest(armoredOrBase64); + return new UploadRequestDto(armoredOrBase64); } private static byte[] base64IfNecessary(byte[] certBytes) { diff --git a/vks-java/src/main/java/pgp/vks/client/v1/dto/UploadResponse.java b/vks-java/src/main/java/pgp/vks/client/v1/dto/UploadResponseDto.java similarity index 63% rename from vks-java/src/main/java/pgp/vks/client/v1/dto/UploadResponse.java rename to vks-java/src/main/java/pgp/vks/client/v1/dto/UploadResponseDto.java index 1aca588..b2298c9 100644 --- a/vks-java/src/main/java/pgp/vks/client/v1/dto/UploadResponse.java +++ b/vks-java/src/main/java/pgp/vks/client/v1/dto/UploadResponseDto.java @@ -4,21 +4,22 @@ package pgp.vks.client.v1.dto; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import pgp.vks.client.Status; +import pgp.vks.client.Upload; import java.util.HashMap; import java.util.Map; -public class UploadResponse { +public class UploadResponseDto { private final String key_fpr; private final Map status; private final String token; - public UploadResponse(@JsonProperty("key_fpr") String key_fpr, - @JsonProperty("status") Map status, - @JsonProperty("token") String token) { + public UploadResponseDto(@JsonProperty("key_fpr") String key_fpr, + @JsonProperty("status") Map status, + @JsonProperty("token") String token) { this.key_fpr = key_fpr; this.status = status; this.token = token; @@ -38,4 +39,8 @@ public class UploadResponse { public Map getStatus() { return new HashMap<>(status); } + + public Upload.Response toEntity() { + return new Upload.Response(getKeyFingerprint(), getStatus(), getToken()); + } } diff --git a/vks-java/src/main/java/pgp/vks/client/v1/dto/VerificationRequest.java b/vks-java/src/main/java/pgp/vks/client/v1/dto/VerificationRequestDto.java similarity index 72% rename from vks-java/src/main/java/pgp/vks/client/v1/dto/VerificationRequest.java rename to vks-java/src/main/java/pgp/vks/client/v1/dto/VerificationRequestDto.java index 62bbfe2..999be64 100644 --- a/vks-java/src/main/java/pgp/vks/client/v1/dto/VerificationRequest.java +++ b/vks-java/src/main/java/pgp/vks/client/v1/dto/VerificationRequestDto.java @@ -8,15 +8,15 @@ import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; -public class VerificationRequest { +public class VerificationRequestDto { private final String token; private final List addresses; private final List locale; - public VerificationRequest(@JsonProperty("token") String token, - @JsonProperty("addresses") List addresses, - @JsonProperty("locale") List locale) { + public VerificationRequestDto(@JsonProperty("token") String token, + @JsonProperty("addresses") List addresses, + @JsonProperty("locale") List locale) { this.token = token; this.addresses = addresses; this.locale = locale; diff --git a/vks-java/src/main/java/pgp/vks/client/v1/dto/VerificationResponse.java b/vks-java/src/main/java/pgp/vks/client/v1/dto/VerificationResponseDto.java similarity index 59% rename from vks-java/src/main/java/pgp/vks/client/v1/dto/VerificationResponse.java rename to vks-java/src/main/java/pgp/vks/client/v1/dto/VerificationResponseDto.java index ffee3ea..78a9597 100644 --- a/vks-java/src/main/java/pgp/vks/client/v1/dto/VerificationResponse.java +++ b/vks-java/src/main/java/pgp/vks/client/v1/dto/VerificationResponseDto.java @@ -5,18 +5,20 @@ package pgp.vks.client.v1.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import pgp.vks.client.RequestVerify; +import pgp.vks.client.Status; import java.util.Map; -public class VerificationResponse { +public class VerificationResponseDto { private final String key_fpr; private final Map status; private final String token; - public VerificationResponse(@JsonProperty("key_fpr") String key_fpr, - @JsonProperty("status") Map status, - @JsonProperty("token") String token) { + public VerificationResponseDto(@JsonProperty("key_fpr") String key_fpr, + @JsonProperty("status") Map status, + @JsonProperty("token") String token) { this.key_fpr = key_fpr; this.status = status; this.token = token; @@ -36,4 +38,8 @@ public class VerificationResponse { public Map getStatus() { return status; } + + public RequestVerify.Response toEntity() { + return new RequestVerify.Response(getKeyFingerprint(), getStatus(), getToken()); + } } diff --git a/vks-java/src/main/java/pgp/vks/client/v1/dto/Status.java b/vks-java/src/main/java/pgp/vks/client/v1/dto/package-info.java similarity index 63% rename from vks-java/src/main/java/pgp/vks/client/v1/dto/Status.java rename to vks-java/src/main/java/pgp/vks/client/v1/dto/package-info.java index c6fde2c..3094cae 100644 --- a/vks-java/src/main/java/pgp/vks/client/v1/dto/Status.java +++ b/vks-java/src/main/java/pgp/vks/client/v1/dto/package-info.java @@ -2,11 +2,7 @@ // // SPDX-License-Identifier: Apache-2.0 +/** + * JSON DTOs for v1 API. + */ package pgp.vks.client.v1.dto; - -public enum Status { - unpublished, - published, - revoked, - pending -} diff --git a/vks-java/src/main/java/pgp/vks/client/v1/impl/RequestVerifyImpl.java b/vks-java/src/main/java/pgp/vks/client/v1/impl/RequestVerifyImpl.java index a548c49..7faa6d6 100644 --- a/vks-java/src/main/java/pgp/vks/client/v1/impl/RequestVerifyImpl.java +++ b/vks-java/src/main/java/pgp/vks/client/v1/impl/RequestVerifyImpl.java @@ -7,15 +7,17 @@ package pgp.vks.client.v1.impl; import com.fasterxml.jackson.databind.ObjectMapper; import pgp.vks.client.RequestVerify; import pgp.vks.client.exception.CertCannotBePublishedException; -import pgp.vks.client.v1.dto.ErrorResponse; -import pgp.vks.client.v1.dto.VerificationResponse; -import pgp.vks.client.v1.dto.VerificationRequest; +import pgp.vks.client.v1.dto.ErrorResponseDto; +import pgp.vks.client.v1.dto.VerificationRequestDto; +import pgp.vks.client.v1.dto.VerificationResponseDto; +import javax.annotation.Nonnull; import javax.net.ssl.HttpsURLConnection; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; +import java.util.Arrays; import java.util.List; public class RequestVerifyImpl implements RequestVerify { @@ -28,31 +30,49 @@ public class RequestVerifyImpl implements RequestVerify { } @Override - public VerificationResponse forEmailAddresses(List emailAddresses, String token, List locale) - throws IOException { - VerificationRequest request = new VerificationRequest(token, emailAddresses, locale); + public ForEmailAddresses forEmailAddresses(String... emailAddresses) { + if (emailAddresses == null || emailAddresses.length == 0) { + throw new IllegalArgumentException("At least one email address is required."); + } + List emailList = Arrays.asList(emailAddresses); - URL url = api.postRequestVerify(); - HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); - connection.setRequestMethod("POST"); - connection.setDoOutput(true); - connection.setRequestProperty("Content-Type", "application/json"); + return new ForEmailAddressesImpl(emailList); + } - OutputStream out = connection.getOutputStream(); - json.writeValue(out, request); - out.flush(); - out.close(); + private class ForEmailAddressesImpl implements ForEmailAddresses { - int status = connection.getResponseCode(); - InputStream responseIn; - if (status >= 400) { - responseIn = connection.getErrorStream(); - ErrorResponse errorResponse = json.readValue(responseIn, ErrorResponse.class); - throw new CertCannotBePublishedException(errorResponse.getError() + (status)); - } else { - responseIn = connection.getInputStream(); - VerificationResponse response = json.readValue(responseIn, VerificationResponse.class); - return response; + private final List emailAddresses; + + ForEmailAddressesImpl(@Nonnull List emailList) { + this.emailAddresses = emailList; + } + + @Override + public Response execute(String token, List locale) throws IOException { + VerificationRequestDto request = new VerificationRequestDto(token, emailAddresses, locale); + + URL url = api.postRequestVerify(); + HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); + connection.setRequestMethod("POST"); + connection.setDoOutput(true); + connection.setRequestProperty("Content-Type", "application/json"); + + OutputStream out = connection.getOutputStream(); + json.writeValue(out, request); + out.flush(); + out.close(); + + int status = connection.getResponseCode(); + InputStream responseIn; + if (status >= 400) { + responseIn = connection.getErrorStream(); + ErrorResponseDto errorResponse = json.readValue(responseIn, ErrorResponseDto.class); + throw new CertCannotBePublishedException(errorResponse.getError() + " (" + status + ")"); + } else { + responseIn = connection.getInputStream(); + VerificationResponseDto response = json.readValue(responseIn, VerificationResponseDto.class); + return response.toEntity(); + } } } } diff --git a/vks-java/src/main/java/pgp/vks/client/v1/impl/UploadImpl.java b/vks-java/src/main/java/pgp/vks/client/v1/impl/UploadImpl.java index 887901b..67ddf68 100644 --- a/vks-java/src/main/java/pgp/vks/client/v1/impl/UploadImpl.java +++ b/vks-java/src/main/java/pgp/vks/client/v1/impl/UploadImpl.java @@ -4,19 +4,15 @@ package pgp.vks.client.v1.impl; -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; -import org.bouncycastle.util.io.Streams; import pgp.vks.client.Upload; import pgp.vks.client.exception.CertCannotBePublishedException; -import pgp.vks.client.v1.dto.UploadRequest; -import pgp.vks.client.v1.dto.ErrorResponse; -import pgp.vks.client.v1.dto.UploadResponse; +import pgp.vks.client.v1.dto.ErrorResponseDto; +import pgp.vks.client.v1.dto.UploadRequestDto; +import pgp.vks.client.v1.dto.UploadResponseDto; import javax.annotation.Nonnull; import javax.net.ssl.HttpsURLConnection; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -32,12 +28,8 @@ public class UploadImpl implements Upload { } @Override - public UploadResponse cert(@Nonnull InputStream certInStream) throws IOException { - ByteArrayOutputStream certBuf = new ByteArrayOutputStream(); - Streams.pipeAll(certInStream, certBuf); - certInStream.close(); - - UploadRequest request = UploadRequest.fromBytes(certBuf.toByteArray()); + public Response cert(@Nonnull InputStream certInStream) throws IOException { + UploadRequestDto request = UploadRequestDto.fromInputStream(certInStream); URL url = api.postUpload(); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); @@ -46,22 +38,20 @@ public class UploadImpl implements Upload { connection.setDoOutput(true); OutputStream out = connection.getOutputStream(); - byte[] requestBody = json.writeValueAsBytes(request); - out.write(requestBody); + json.writeValue(out, request); out.flush(); out.close(); int status = connection.getResponseCode(); - System.out.println(status); InputStream responseIn; if (status >= 400) { responseIn = connection.getErrorStream(); - ErrorResponse errorResponse = json.readValue(responseIn, ErrorResponse.class); + ErrorResponseDto errorResponse = json.readValue(responseIn, ErrorResponseDto.class); throw new CertCannotBePublishedException(errorResponse.getError() + (status)); } else { responseIn = connection.getInputStream(); - UploadResponse response = json.readValue(responseIn, UploadResponse.class); - return response; + UploadResponseDto dto = json.readValue(responseIn, UploadResponseDto.class); + return dto.toEntity(); } } } diff --git a/vks-java/src/main/java/pgp/vks/client/v1/impl/package-info.java b/vks-java/src/main/java/pgp/vks/client/v1/impl/package-info.java new file mode 100644 index 0000000..6f5369c --- /dev/null +++ b/vks-java/src/main/java/pgp/vks/client/v1/impl/package-info.java @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: 2022 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +/** + * Implementations of interfaces for API v1. + */ +package pgp.vks.client.v1.impl; diff --git a/vks-java/src/main/java/pgp/vks/client/v1/package-info.java b/vks-java/src/main/java/pgp/vks/client/v1/package-info.java new file mode 100644 index 0000000..afcbd84 --- /dev/null +++ b/vks-java/src/main/java/pgp/vks/client/v1/package-info.java @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: 2022 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +/** + * Implementation of API v1. + */ +package pgp.vks.client.v1; diff --git a/vks-java/src/test/java/pgp/vks/client/v1/dto/ErrorResponseDtoTest.java b/vks-java/src/test/java/pgp/vks/client/v1/dto/ErrorResponseDtoTest.java new file mode 100644 index 0000000..d30a983 --- /dev/null +++ b/vks-java/src/test/java/pgp/vks/client/v1/dto/ErrorResponseDtoTest.java @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2022 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package pgp.vks.client.v1.dto; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ErrorResponseDtoTest { + + private static final ObjectMapper json = new ObjectMapper(); + + @Test + public void testSerializeDeserialize() throws JsonProcessingException { + String errorMessage = "Certificate cannot be parsed."; + ErrorResponseDto dto = new ErrorResponseDto(errorMessage); + assertEquals(errorMessage, dto.getError()); + + String val = json.writeValueAsString(dto); + dto = json.readValue(val, ErrorResponseDto.class); + + assertEquals(errorMessage, dto.getError()); + } +} diff --git a/vks-java/src/test/java/pgp/vks/client/impl/v1/dto/UploadRequestTest.java b/vks-java/src/test/java/pgp/vks/client/v1/dto/UploadRequestDtoTest.java similarity index 93% rename from vks-java/src/test/java/pgp/vks/client/impl/v1/dto/UploadRequestTest.java rename to vks-java/src/test/java/pgp/vks/client/v1/dto/UploadRequestDtoTest.java index e9a3998..88d48fa 100644 --- a/vks-java/src/test/java/pgp/vks/client/impl/v1/dto/UploadRequestTest.java +++ b/vks-java/src/test/java/pgp/vks/client/v1/dto/UploadRequestDtoTest.java @@ -2,17 +2,16 @@ // // SPDX-License-Identifier: Apache-2.0 -package pgp.vks.client.impl.v1.dto; +package pgp.vks.client.v1.dto; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.bouncycastle.util.encoders.Base64; import org.junit.jupiter.api.Test; -import pgp.vks.client.v1.dto.UploadRequest; import static org.junit.jupiter.api.Assertions.assertEquals; -public class UploadRequestTest { +public class UploadRequestDtoTest { private static final String TEST_CERT_ARMORED = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + "Comment: 9DF2 C3FE 6F69 A3EE DBD5 FB81 69E8 A788 A36E 7BFD\n" + @@ -71,21 +70,23 @@ public class UploadRequestTest { @Test public void testSerializeDeserializeArmoredCert() throws JsonProcessingException { - UploadRequest request = new UploadRequest(TEST_CERT_ARMORED); + UploadRequestDto request = new UploadRequestDto(TEST_CERT_ARMORED); String val = json.writeValueAsString(request); - request = json.readValue(val, UploadRequest.class); + request = json.readValue(val, UploadRequestDto.class); assertEquals(TEST_CERT_ARMORED, request.getKeyText()); } @Test public void testSerializeDeserializeBase64() throws JsonProcessingException { + // raw bytes byte[] rawCert = Base64.decode(TEST_CERT_BASE64); - UploadRequest request = UploadRequest.fromBytes(rawCert); + + UploadRequestDto request = UploadRequestDto.fromBytes(rawCert); String val = json.writeValueAsString(request); - request = json.readValue(val, UploadRequest.class); + request = json.readValue(val, UploadRequestDto.class); assertEquals(TEST_CERT_BASE64, request.getKeyText()); } diff --git a/vks-java/src/test/java/pgp/vks/client/impl/v1/dto/UploadResponseTest.java b/vks-java/src/test/java/pgp/vks/client/v1/dto/UploadResponseTest.java similarity index 80% rename from vks-java/src/test/java/pgp/vks/client/impl/v1/dto/UploadResponseTest.java rename to vks-java/src/test/java/pgp/vks/client/v1/dto/UploadResponseTest.java index fa0c5cd..55b21d9 100644 --- a/vks-java/src/test/java/pgp/vks/client/impl/v1/dto/UploadResponseTest.java +++ b/vks-java/src/test/java/pgp/vks/client/v1/dto/UploadResponseTest.java @@ -2,13 +2,12 @@ // // SPDX-License-Identifier: Apache-2.0 -package pgp.vks.client.impl.v1.dto; +package pgp.vks.client.v1.dto; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; -import pgp.vks.client.v1.dto.Status; -import pgp.vks.client.v1.dto.UploadResponse; +import pgp.vks.client.Status; import java.util.HashMap; import java.util.Map; @@ -27,10 +26,10 @@ public class UploadResponseTest { statusMap.put("hello@mail.world", Status.unpublished); String token = "t0k3n5tr1n9"; - UploadResponse response = new UploadResponse(fingerprint, statusMap, token); + UploadResponseDto response = new UploadResponseDto(fingerprint, statusMap, token); String val = json.writeValueAsString(response); - response = json.readValue(val, UploadResponse.class); + response = json.readValue(val, UploadResponseDto.class); assertEquals(fingerprint, response.getKeyFingerprint()); assertEquals(statusMap, response.getStatus()); diff --git a/vks-java/src/test/java/pgp/vks/client/v1/dto/VerificationRequestDtoTest.java b/vks-java/src/test/java/pgp/vks/client/v1/dto/VerificationRequestDtoTest.java new file mode 100644 index 0000000..9a9fbc0 --- /dev/null +++ b/vks-java/src/test/java/pgp/vks/client/v1/dto/VerificationRequestDtoTest.java @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: 2022 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package pgp.vks.client.v1.dto; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class VerificationRequestDtoTest { + + private static final ObjectMapper json = new ObjectMapper(); + + @Test + public void testSerializationDeserialization() throws JsonProcessingException { + String token = "thisisalongtokenstring"; + List addresses = Arrays.asList("alice@pgpainless.org", "vanitasvitae@fsfe.org", "Ed_Snowden@lavabit.com"); + List locale = Arrays.asList("de_DE", "de_CH"); + + VerificationRequestDto dto = new VerificationRequestDto(token, addresses, locale); + assertEquals(token, dto.getToken()); + assertEquals(addresses, dto.getAddresses()); + assertEquals(locale, dto.getLocale()); + + String val = json.writeValueAsString(dto); + + dto = json.readValue(val, VerificationRequestDto.class); + assertEquals(token, dto.getToken()); + assertEquals(addresses, dto.getAddresses()); + assertEquals(locale, dto.getLocale()); + } + +} diff --git a/vks-java/src/test/java/pgp/vks/client/v1/dto/VerificationResponseDtoTest.java b/vks-java/src/test/java/pgp/vks/client/v1/dto/VerificationResponseDtoTest.java new file mode 100644 index 0000000..c42dd88 --- /dev/null +++ b/vks-java/src/test/java/pgp/vks/client/v1/dto/VerificationResponseDtoTest.java @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: 2022 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package pgp.vks.client.v1.dto; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import pgp.vks.client.Status; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class VerificationResponseDtoTest { + + private static final ObjectMapper json = new ObjectMapper(); + + @Test + public void testSerializationDeserialization() throws JsonProcessingException { + String fingerprint = "1EBF5F15850C540B3142F1584BDD496D4C6C5F25"; + Map status = new HashMap<>(); + status.put("XXX@riseup.net", Status.published); + status.put("l.p@riseup.net", Status.unpublished); + status.put("laura@riseup.net", Status.pending); + status.put("poitras@gmail.com", Status.revoked); + String token = "incrediblylongandoverlytideoustotypetokenstring"; + + VerificationResponseDto dto = new VerificationResponseDto(fingerprint, status, token); + assertEquals(fingerprint, dto.getKeyFingerprint()); + assertEquals(status, dto.getStatus()); + assertEquals(token, dto.getToken()); + + String val = json.writeValueAsString(dto); + dto = json.readValue(val, VerificationResponseDto.class); + assertEquals(fingerprint, dto.getKeyFingerprint()); + assertEquals(status, dto.getStatus()); + assertEquals(token, dto.getToken()); + } +} diff --git a/vks-java/src/test/java/pgp/vks/client/impl/v1/V1APITest.java b/vks-java/src/test/java/pgp/vks/client/v1/impl/URLMapperTest.java similarity index 94% rename from vks-java/src/test/java/pgp/vks/client/impl/v1/V1APITest.java rename to vks-java/src/test/java/pgp/vks/client/v1/impl/URLMapperTest.java index 8b5c6d1..74c5ab7 100644 --- a/vks-java/src/test/java/pgp/vks/client/impl/v1/V1APITest.java +++ b/vks-java/src/test/java/pgp/vks/client/v1/impl/URLMapperTest.java @@ -2,18 +2,17 @@ // // SPDX-License-Identifier: Apache-2.0 -package pgp.vks.client.impl.v1; +package pgp.vks.client.v1.impl; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import pgp.vks.client.v1.impl.URLMapper; import java.net.MalformedURLException; import java.net.URL; import static org.junit.jupiter.api.Assertions.assertEquals; -public class V1APITest { +public class URLMapperTest { private static URLMapper api; diff --git a/vks-java/src/test/java/pgp/vks/client/impl/v1/VKSTest.java b/vks-java/src/test/java/pgp/vks/client/v1/impl/VKSTest.java similarity index 56% rename from vks-java/src/test/java/pgp/vks/client/impl/v1/VKSTest.java rename to vks-java/src/test/java/pgp/vks/client/v1/impl/VKSTest.java index 80ba414..a3237a4 100644 --- a/vks-java/src/test/java/pgp/vks/client/impl/v1/VKSTest.java +++ b/vks-java/src/test/java/pgp/vks/client/v1/impl/VKSTest.java @@ -2,45 +2,37 @@ // // SPDX-License-Identifier: Apache-2.0 -package pgp.vks.client.impl.v1; +package pgp.vks.client.v1.impl; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.io.Streams; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import pgp.vks.client.RequestVerify; +import pgp.vks.client.Upload; import pgp.vks.client.VKS; +import pgp.vks.client.exception.CertCannotBePublishedException; import pgp.vks.client.exception.CertNotFoundException; -import pgp.vks.client.v1.dto.VerificationResponse; -import pgp.vks.client.v1.impl.VKSImpl; -import pgp.vks.client.v1.dto.UploadResponse; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.net.MalformedURLException; import java.nio.charset.StandardCharsets; -import java.util.Collections; +import java.util.Random; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; public class VKSTest { private static VKS vks; @BeforeAll - static void prepare() { - vks = VKSImpl.keysDotOpenPgpDotOrg(); - } - - @Test - public void testGetByFingerprint() throws IOException { - InputStream inputStream = vks.get().byFingerprint("7F9116FEA90A5983936C7CFAA027DB2F3E1E118A"); - Streams.pipeAll(inputStream, System.out); - } - - @Test - public void testGetByFingerprint_inexistent() { - assertThrows(CertNotFoundException.class, () -> - vks.get().byFingerprint("0000000000000000000000000000000000000000")); + static void prepare() throws MalformedURLException { + vks = new VKSImpl("https://testing2.keys.openpgp.org"); } @Test @@ -77,16 +69,75 @@ public class VKSTest { "YA3TLiYiZbEM\n" + "=QRwY\n" + "-----END PGP PUBLIC KEY BLOCK-----\n"; + System.out.println(keyArmored); String keyFingerprint = "57417147D0C8B548220A36A60BAAB05A087768D3"; - UploadResponse uploadResponse = vks.upload().cert(new ByteArrayInputStream(keyArmored.getBytes(StandardCharsets.UTF_8))); + Upload.Response uploadResponse = vks.upload().cert(new ByteArrayInputStream(keyArmored.getBytes(StandardCharsets.UTF_8))); assertEquals(keyFingerprint, uploadResponse.getKeyFingerprint()); - VerificationResponse verifyResponse = vks.requestVerification().forEmailAddresses( - Collections.singletonList("test123asdasd@byom.de"), - uploadResponse.getToken(), - Collections.singletonList("de_DE")); + RequestVerify.Response verifyResponse = vks.requestVerification() + .forEmailAddress("test123asdasd@byom.de") + .execute(uploadResponse.getToken()); assertEquals(keyFingerprint, verifyResponse.getKeyFingerprint()); } + + @Test + public void testGetByKeyId() throws IOException { + long keyId = 1620429777827217382L; // subkey id of the cert + byte[] expectedHeader = ("-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + + "Comment: 5741 7147 D0C8 B548 220A 36A6 0BAA B05A 0877 68D3") + .getBytes(StandardCharsets.UTF_8); + InputStream in = vks.get().byKeyId(keyId); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Streams.pipeAll(in, out); + in.close(); + + assertTrue(Arrays.areEqual( + expectedHeader, 0, expectedHeader.length, out.toByteArray(), 0, expectedHeader.length)); + } + + @Test + public void testGetByFingerprint() throws IOException { + byte[] expectedHeader = ("-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + + "Comment: 5741 7147 D0C8 B548 220A 36A6 0BAA B05A 0877 68D3") + .getBytes(StandardCharsets.UTF_8); + InputStream in = vks.get().byFingerprint("57417147D0C8B548220A36A60BAAB05A087768D3"); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Streams.pipeAll(in, out); + in.close(); + + assertTrue(Arrays.areEqual( + expectedHeader, 0, expectedHeader.length, out.toByteArray(), 0, expectedHeader.length)); + } + + @Test + public void testGetByEmail() throws IOException { + byte[] expectedHeader = ("-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + + "Comment: 7F91 16FE A90A 5983 936C 7CFA A027 DB2F 3E1E 118A") + .getBytes(StandardCharsets.UTF_8); + + InputStream in = vks.get().byEmail("vanitasvitae@fsfe.org"); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Streams.pipeAll(in, out); + in.close(); + + assertTrue(Arrays.areEqual( + expectedHeader, 0, expectedHeader.length, out.toByteArray(), 0, expectedHeader.length)); + } + + @Test + public void testGetByFingerprint_inexistent() { + assertThrows(CertNotFoundException.class, () -> + vks.get().byFingerprint("0000000000000000000000000000000000000000")); + } + + @Test + public void testUploadBrokenKey() { + byte[] buf = new byte[4096]; + new Random().nextBytes(buf); + + assertThrows(CertCannotBePublishedException.class, () -> + vks.upload().cert(new ByteArrayInputStream(buf))); + } }