Some more cleanup

This commit is contained in:
Paul Schaub 2022-04-06 13:38:14 +02:00
parent 73ed816111
commit bcb0716fc2
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
25 changed files with 690 additions and 127 deletions

View file

@ -0,0 +1,218 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
SPDX-FileCopyrightText: 2021 Paul Schaub <info@pgpainless.org>
SPDX-License-Identifier: CC0-1.0
-->
<!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">
<!-- Suppressions -->
<module name="SuppressionFilter">
<property name="file" value="${config_loc}/suppressions.xml"/>
</module>
<module name="NewlineAtEndOfFile">
<property name="lineSeparator" value="lf"/>
</module>
<module name="RegexpSingleline">
<!--
Matches StringBuilder.append(String) calls where the
argument is a String of length one. Those should be replaced
with append(char) for performance reasons.
TODO: This could be more advanced in order to match also
- .append("\u1234")
-->
<property name="format" value="\.append\(&quot;(.|\\.)&quot;\)"/>
<property name="message" value="Don&apos;t use StringBuilder.append(String) when you can use StringBuilder.append(char). Solution: Replace double quotes of append&apos;s argument with single quotes."/>
</module>
<!-- Whitespace only lines -->
<module name="RegexpSingleline">
<property name="format" value="^\s+$"/>
<property name="message" value="Line containing only whitespace character(s)"/>
</module>
<!-- Mixed spaces/tabs -->
<module name="RegexpSingleline">
<!-- We use {2,} instead of + here to address the typical case where a file was written
with tabs but javadoc is causing '\t *' -->
<property name="format" value="^\t+ {2,}"/>
<property name="message" value="Line containing space(s) after tab(s)"/>
</module>
<!-- Trailing whitespaces -->
<module name="RegexpSingleline">
<!--
Explaining the following Regex
\s+ $
| +- End of Line (2)
+- At least one whitespace (1)
Rationale:
Matches trailing whitespace (2) in lines containing at least one (1) non-whitespace character
-->
<property name="format" value="\s+$"/>
<property name="message" value="Line containing trailing whitespace character(s)"/>
</module>
<!-- <module name="RegexpSingleline"> -->
<!-- <property name="format" value="fqdn"/> -->
<!-- </module> -->
<!-- Space after // -->
<module name="RegexpSingleline">
<property name="format" value="^\s*//[^\s]"/>
<property name="message" value="Comment start ('//') followed by non-space character. You would not continue after a punctuation without a space, would you?"/>
</module>
<module name="JavadocPackage">
</module>
<module name="TreeWalker">
<module name="SuppressionCommentFilter"/>
<module name="FinalClass"/>
<module name="UnusedImports">
<property name="processJavadoc" value="true"/>
</module>
<module name="AvoidStarImport"/>
<module name="IllegalImport"/>
<module name="RedundantImport"/>
<module name="RedundantModifier"/>
<module name="ModifierOrder"/>
<module name="UpperEll"/>
<module name="ArrayTypeStyle"/>
<module name="GenericWhitespace"/>
<module name="EmptyStatement"/>
<module name="PackageDeclaration"/>
<module name="LeftCurly"/>
<!-- Spaces instead of Tabs -->
<module name="RegexpSinglelineJava">
<property name="format" value="^\t+"/>
<property name="message" value="Indent must not use tab characters. Use space instead."/>
</module>
<module name="JavadocMethod">
<!-- TODO stricten those checks -->
<property name="scope" value="public"/>
<!--<property name="allowUndeclaredRTE" value="true"/>-->
<property name="allowMissingParamTags" value="true"/>
<property name="allowMissingThrowsTags" value="true"/>
<property name="allowMissingReturnTag" value="true"/>
<property name="allowMissingJavadoc" value="true"/>
<property name="suppressLoadErrors" value="true"/>
</module>
<module name="JavadocStyle">
<property name="scope" value="public"/>
<property name="checkEmptyJavadoc" value="true"/>
<property name="checkHtml" value="false"/>
</module>
<module name="ParenPad">
</module>
<!-- Whitespace after key tokens -->
<module name="NoWhitespaceAfter">
<property name="tokens" value="INC
, DEC
, UNARY_MINUS
, UNARY_PLUS
, BNOT, LNOT
, DOT
, ARRAY_DECLARATOR
, INDEX_OP
"/>
</module>
<!-- Whitespace after key words -->
<module name="WhitespaceAfter">
<property name="tokens" value="TYPECAST
, LITERAL_IF
, LITERAL_ELSE
, LITERAL_WHILE
, LITERAL_DO
, LITERAL_FOR
, DO_WHILE
"/>
</module>
<module name="WhitespaceAround">
<property
name="ignoreEnhancedForColon"
value="false"
/>
<!-- Currently disabled tokens: LCURLY, RCURLY, WILDCARD_TYPE, GENERIC_START, GENERIC_END -->
<property
name="tokens"
value="ASSIGN
, ARRAY_INIT
, BAND
, BAND_ASSIGN
, BOR
, BOR_ASSIGN
, BSR
, BSR_ASSIGN
, BXOR
, BXOR_ASSIGN
, COLON
, DIV
, DIV_ASSIGN
, DO_WHILE
, EQUAL
, GE
, GT
, LAMBDA
, LAND
, LE
, LITERAL_CATCH
, LITERAL_DO
, LITERAL_ELSE
, LITERAL_FINALLY
, LITERAL_FOR
, LITERAL_IF
, LITERAL_RETURN
, LITERAL_SWITCH
, LITERAL_SYNCHRONIZED
, LITERAL_TRY
, LITERAL_WHILE
, LOR
, LT
, MINUS
, MINUS_ASSIGN
, MOD
, MOD_ASSIGN
, NOT_EQUAL
, PLUS
, PLUS_ASSIGN
, QUESTION
, SL
, SLIST
, SL_ASSIGN
, SR
, SR_ASSIGN
, STAR
, STAR_ASSIGN
, LITERAL_ASSERT
, TYPE_EXTENSION_AND
"/>
</module>
<!--
<module name="CustomImportOrder">
<property name="customImportOrderRules"
value="STATIC###STANDARD_JAVA_PACKAGE###SPECIAL_IMPORTS###THIRD_PARTY_PACKAGE"/>
<property name="specialImportsRegExp" value="^org\.org.pgpainless.core\.org.pgpainless.core"/>
<property name="sortImportsInGroupAlphabetically" value="true"/>
<property name="separateLineBetweenGroups" value="true"/>
</module>
-->
</module>
</module>

View file

@ -0,0 +1,19 @@
<?xml version="1.0"?>
<!--
SPDX-FileCopyrightText: 2021 Paul Schaub <info@pgpainless.org>
SPDX-License-Identifier: CC0-1.0
-->
<!DOCTYPE suppressions PUBLIC
"-//Puppy Crawl//DTD Suppressions 1.1//EN"
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
<suppressions>
<!-- GenericWhitespace has some problems with false postive, leave
it disabled until gradle uses a checkstyle version where this is fixed
-->
<suppress checks="GenericWhitespace"
files="Protocol.java" />
<!-- Suppress JavadocPackage in the test packages -->
<suppress checks="JavadocPackage" files="[\\/]test[\\/]"/>
</suppressions>

View file

@ -16,8 +16,10 @@ dependencies {
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion" testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
api("org.pgpainless:pgp-certificate-store:0.1.0") implementation("org.bouncycastle:bcutil-jdk15on:1.70")
api("org.pgpainless:pgpainless-core:1.1.4")
// api("org.pgpainless:pgp-certificate-store:0.1.0")
// api("org.pgpainless:pgpainless-core:1.1.4")
// @Nonnull, @Nullable... // @Nonnull, @Nullable...
implementation "com.google.code.findbugs:jsr305:3.0.2" implementation "com.google.code.findbugs:jsr305:3.0.2"

View file

@ -4,19 +4,53 @@
package pgp.vks.client; package pgp.vks.client;
import pgp.vks.client.v1.dto.VerificationResponse;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
public interface RequestVerify { public interface RequestVerify {
default VerificationResponse forEmailAddresses(List<String> emailAddresses, String uploadToken) default ForEmailAddresses forEmailAddress(String emailAddress) {
throws IOException { return forEmailAddresses(emailAddress);
return forEmailAddresses(emailAddresses, uploadToken, Arrays.asList("en_US", "en_GB"));
} }
VerificationResponse forEmailAddresses(List<String> emailAddresses, String uploadToken, List<String> locale) ForEmailAddresses forEmailAddresses(String... emailAddresses);
throws IOException;
interface ForEmailAddresses {
default Response execute(String token) throws IOException {
return execute(token, Arrays.asList("en_US", "en_GB"));
}
Response execute(String token, List<String> locale) throws IOException;
}
class Response {
private final String keyFingerprint;
private final Map<String, Status> status;
private final String token;
public Response(String keyFingerprint,
Map<String, Status> status,
String token) {
this.keyFingerprint = keyFingerprint;
this.status = status;
this.token = token;
}
public String getKeyFingerprint() {
return keyFingerprint;
}
public Map<String, Status> getStatus() {
return status;
}
public String getToken() {
return token;
}
}
} }

View file

@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
//
// 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
}

View file

@ -4,14 +4,65 @@
package pgp.vks.client; package pgp.vks.client;
import pgp.vks.client.v1.dto.UploadResponse;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
public interface Upload { public interface Upload {
UploadResponse cert(@Nonnull InputStream certInStream) throws IOException; /**
* Upload a certificate to the key server.
* The <pre>certInStream</pre> 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<String, Status> status;
private final String token;
public Response(String keyFingerprint,
Map<String, Status> 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<String, Status> getStatus() {
return new HashMap<>(status);
}
}
} }

View file

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
package pgp.vks.client.impl.v1.dummy_vks; /**
* Exception classes.
public class DummyVks { */
} package pgp.vks.client.exception;

View file

@ -0,0 +1,8 @@
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
/**
* Client API for communicating with Verifying Key Servers.
*/
package pgp.vks.client;

View file

@ -6,11 +6,11 @@ package pgp.vks.client.v1.dto;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
public class ErrorResponse { public class ErrorResponseDto {
private final String error; private final String error;
public ErrorResponse(@JsonProperty("error") String error) { public ErrorResponseDto(@JsonProperty("error") String error) {
this.error = error; this.error = error;
} }

View file

@ -7,22 +7,33 @@ package pgp.vks.client.v1.dto;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Base64; 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; private final String keytext;
public UploadRequest(@JsonProperty("keytext") String keytext) { public UploadRequestDto(@JsonProperty("keytext") String keytext) {
this.keytext = 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)); String armoredOrBase64 = new String(base64IfNecessary(keytext));
return new UploadRequest(armoredOrBase64); return new UploadRequestDto(armoredOrBase64);
} }
private static byte[] base64IfNecessary(byte[] certBytes) { private static byte[] base64IfNecessary(byte[] certBytes) {

View file

@ -4,19 +4,20 @@
package pgp.vks.client.v1.dto; package pgp.vks.client.v1.dto;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import pgp.vks.client.Status;
import pgp.vks.client.Upload;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
public class UploadResponse { public class UploadResponseDto {
private final String key_fpr; private final String key_fpr;
private final Map<String, Status> status; private final Map<String, Status> status;
private final String token; private final String token;
public UploadResponse(@JsonProperty("key_fpr") String key_fpr, public UploadResponseDto(@JsonProperty("key_fpr") String key_fpr,
@JsonProperty("status") Map<String, Status> status, @JsonProperty("status") Map<String, Status> status,
@JsonProperty("token") String token) { @JsonProperty("token") String token) {
this.key_fpr = key_fpr; this.key_fpr = key_fpr;
@ -38,4 +39,8 @@ public class UploadResponse {
public Map<String, Status> getStatus() { public Map<String, Status> getStatus() {
return new HashMap<>(status); return new HashMap<>(status);
} }
public Upload.Response toEntity() {
return new Upload.Response(getKeyFingerprint(), getStatus(), getToken());
}
} }

View file

@ -8,13 +8,13 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List; import java.util.List;
public class VerificationRequest { public class VerificationRequestDto {
private final String token; private final String token;
private final List<String> addresses; private final List<String> addresses;
private final List<String> locale; private final List<String> locale;
public VerificationRequest(@JsonProperty("token") String token, public VerificationRequestDto(@JsonProperty("token") String token,
@JsonProperty("addresses") List<String> addresses, @JsonProperty("addresses") List<String> addresses,
@JsonProperty("locale") List<String> locale) { @JsonProperty("locale") List<String> locale) {
this.token = token; this.token = token;

View file

@ -5,16 +5,18 @@
package pgp.vks.client.v1.dto; package pgp.vks.client.v1.dto;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import pgp.vks.client.RequestVerify;
import pgp.vks.client.Status;
import java.util.Map; import java.util.Map;
public class VerificationResponse { public class VerificationResponseDto {
private final String key_fpr; private final String key_fpr;
private final Map<String, Status> status; private final Map<String, Status> status;
private final String token; private final String token;
public VerificationResponse(@JsonProperty("key_fpr") String key_fpr, public VerificationResponseDto(@JsonProperty("key_fpr") String key_fpr,
@JsonProperty("status") Map<String, Status> status, @JsonProperty("status") Map<String, Status> status,
@JsonProperty("token") String token) { @JsonProperty("token") String token) {
this.key_fpr = key_fpr; this.key_fpr = key_fpr;
@ -36,4 +38,8 @@ public class VerificationResponse {
public Map<String, Status> getStatus() { public Map<String, Status> getStatus() {
return status; return status;
} }
public RequestVerify.Response toEntity() {
return new RequestVerify.Response(getKeyFingerprint(), getStatus(), getToken());
}
} }

View file

@ -2,11 +2,7 @@
// //
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
/**
* JSON DTOs for v1 API.
*/
package pgp.vks.client.v1.dto; package pgp.vks.client.v1.dto;
public enum Status {
unpublished,
published,
revoked,
pending
}

View file

@ -7,15 +7,17 @@ package pgp.vks.client.v1.impl;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import pgp.vks.client.RequestVerify; import pgp.vks.client.RequestVerify;
import pgp.vks.client.exception.CertCannotBePublishedException; import pgp.vks.client.exception.CertCannotBePublishedException;
import pgp.vks.client.v1.dto.ErrorResponse; import pgp.vks.client.v1.dto.ErrorResponseDto;
import pgp.vks.client.v1.dto.VerificationResponse; import pgp.vks.client.v1.dto.VerificationRequestDto;
import pgp.vks.client.v1.dto.VerificationRequest; import pgp.vks.client.v1.dto.VerificationResponseDto;
import javax.annotation.Nonnull;
import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.HttpsURLConnection;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.URL; import java.net.URL;
import java.util.Arrays;
import java.util.List; import java.util.List;
public class RequestVerifyImpl implements RequestVerify { public class RequestVerifyImpl implements RequestVerify {
@ -28,9 +30,26 @@ public class RequestVerifyImpl implements RequestVerify {
} }
@Override @Override
public VerificationResponse forEmailAddresses(List<String> emailAddresses, String token, List<String> locale) public ForEmailAddresses forEmailAddresses(String... emailAddresses) {
throws IOException { if (emailAddresses == null || emailAddresses.length == 0) {
VerificationRequest request = new VerificationRequest(token, emailAddresses, locale); throw new IllegalArgumentException("At least one email address is required.");
}
List<String> emailList = Arrays.asList(emailAddresses);
return new ForEmailAddressesImpl(emailList);
}
private class ForEmailAddressesImpl implements ForEmailAddresses {
private final List<String> emailAddresses;
ForEmailAddressesImpl(@Nonnull List<String> emailList) {
this.emailAddresses = emailList;
}
@Override
public Response execute(String token, List<String> locale) throws IOException {
VerificationRequestDto request = new VerificationRequestDto(token, emailAddresses, locale);
URL url = api.postRequestVerify(); URL url = api.postRequestVerify();
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
@ -47,12 +66,13 @@ public class RequestVerifyImpl implements RequestVerify {
InputStream responseIn; InputStream responseIn;
if (status >= 400) { if (status >= 400) {
responseIn = connection.getErrorStream(); responseIn = connection.getErrorStream();
ErrorResponse errorResponse = json.readValue(responseIn, ErrorResponse.class); ErrorResponseDto errorResponse = json.readValue(responseIn, ErrorResponseDto.class);
throw new CertCannotBePublishedException(errorResponse.getError() + (status)); throw new CertCannotBePublishedException(errorResponse.getError() + " (" + status + ")");
} else { } else {
responseIn = connection.getInputStream(); responseIn = connection.getInputStream();
VerificationResponse response = json.readValue(responseIn, VerificationResponse.class); VerificationResponseDto response = json.readValue(responseIn, VerificationResponseDto.class);
return response; return response.toEntity();
}
} }
} }
} }

View file

@ -4,19 +4,15 @@
package pgp.vks.client.v1.impl; 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 com.fasterxml.jackson.databind.ObjectMapper;
import org.bouncycastle.util.io.Streams;
import pgp.vks.client.Upload; import pgp.vks.client.Upload;
import pgp.vks.client.exception.CertCannotBePublishedException; import pgp.vks.client.exception.CertCannotBePublishedException;
import pgp.vks.client.v1.dto.UploadRequest; import pgp.vks.client.v1.dto.ErrorResponseDto;
import pgp.vks.client.v1.dto.ErrorResponse; import pgp.vks.client.v1.dto.UploadRequestDto;
import pgp.vks.client.v1.dto.UploadResponse; import pgp.vks.client.v1.dto.UploadResponseDto;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.HttpsURLConnection;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -32,12 +28,8 @@ public class UploadImpl implements Upload {
} }
@Override @Override
public UploadResponse cert(@Nonnull InputStream certInStream) throws IOException { public Response cert(@Nonnull InputStream certInStream) throws IOException {
ByteArrayOutputStream certBuf = new ByteArrayOutputStream(); UploadRequestDto request = UploadRequestDto.fromInputStream(certInStream);
Streams.pipeAll(certInStream, certBuf);
certInStream.close();
UploadRequest request = UploadRequest.fromBytes(certBuf.toByteArray());
URL url = api.postUpload(); URL url = api.postUpload();
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
@ -46,22 +38,20 @@ public class UploadImpl implements Upload {
connection.setDoOutput(true); connection.setDoOutput(true);
OutputStream out = connection.getOutputStream(); OutputStream out = connection.getOutputStream();
byte[] requestBody = json.writeValueAsBytes(request); json.writeValue(out, request);
out.write(requestBody);
out.flush(); out.flush();
out.close(); out.close();
int status = connection.getResponseCode(); int status = connection.getResponseCode();
System.out.println(status);
InputStream responseIn; InputStream responseIn;
if (status >= 400) { if (status >= 400) {
responseIn = connection.getErrorStream(); responseIn = connection.getErrorStream();
ErrorResponse errorResponse = json.readValue(responseIn, ErrorResponse.class); ErrorResponseDto errorResponse = json.readValue(responseIn, ErrorResponseDto.class);
throw new CertCannotBePublishedException(errorResponse.getError() + (status)); throw new CertCannotBePublishedException(errorResponse.getError() + (status));
} else { } else {
responseIn = connection.getInputStream(); responseIn = connection.getInputStream();
UploadResponse response = json.readValue(responseIn, UploadResponse.class); UploadResponseDto dto = json.readValue(responseIn, UploadResponseDto.class);
return response; return dto.toEntity();
} }
} }
} }

View file

@ -0,0 +1,8 @@
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
/**
* Implementations of interfaces for API v1.
*/
package pgp.vks.client.v1.impl;

View file

@ -0,0 +1,8 @@
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
/**
* Implementation of API v1.
*/
package pgp.vks.client.v1;

View file

@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
//
// 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());
}
}

View file

@ -2,17 +2,16 @@
// //
// SPDX-License-Identifier: Apache-2.0 // 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.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.util.encoders.Base64;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import pgp.vks.client.v1.dto.UploadRequest;
import static org.junit.jupiter.api.Assertions.assertEquals; 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" + 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" + "Comment: 9DF2 C3FE 6F69 A3EE DBD5 FB81 69E8 A788 A36E 7BFD\n" +
@ -71,21 +70,23 @@ public class UploadRequestTest {
@Test @Test
public void testSerializeDeserializeArmoredCert() throws JsonProcessingException { public void testSerializeDeserializeArmoredCert() throws JsonProcessingException {
UploadRequest request = new UploadRequest(TEST_CERT_ARMORED); UploadRequestDto request = new UploadRequestDto(TEST_CERT_ARMORED);
String val = json.writeValueAsString(request); String val = json.writeValueAsString(request);
request = json.readValue(val, UploadRequest.class); request = json.readValue(val, UploadRequestDto.class);
assertEquals(TEST_CERT_ARMORED, request.getKeyText()); assertEquals(TEST_CERT_ARMORED, request.getKeyText());
} }
@Test @Test
public void testSerializeDeserializeBase64() throws JsonProcessingException { public void testSerializeDeserializeBase64() throws JsonProcessingException {
// raw bytes
byte[] rawCert = Base64.decode(TEST_CERT_BASE64); byte[] rawCert = Base64.decode(TEST_CERT_BASE64);
UploadRequest request = UploadRequest.fromBytes(rawCert);
UploadRequestDto request = UploadRequestDto.fromBytes(rawCert);
String val = json.writeValueAsString(request); String val = json.writeValueAsString(request);
request = json.readValue(val, UploadRequest.class); request = json.readValue(val, UploadRequestDto.class);
assertEquals(TEST_CERT_BASE64, request.getKeyText()); assertEquals(TEST_CERT_BASE64, request.getKeyText());
} }

View file

@ -2,13 +2,12 @@
// //
// SPDX-License-Identifier: Apache-2.0 // 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.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import pgp.vks.client.v1.dto.Status; import pgp.vks.client.Status;
import pgp.vks.client.v1.dto.UploadResponse;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -27,10 +26,10 @@ public class UploadResponseTest {
statusMap.put("hello@mail.world", Status.unpublished); statusMap.put("hello@mail.world", Status.unpublished);
String token = "t0k3n5tr1n9"; String token = "t0k3n5tr1n9";
UploadResponse response = new UploadResponse(fingerprint, statusMap, token); UploadResponseDto response = new UploadResponseDto(fingerprint, statusMap, token);
String val = json.writeValueAsString(response); String val = json.writeValueAsString(response);
response = json.readValue(val, UploadResponse.class); response = json.readValue(val, UploadResponseDto.class);
assertEquals(fingerprint, response.getKeyFingerprint()); assertEquals(fingerprint, response.getKeyFingerprint());
assertEquals(statusMap, response.getStatus()); assertEquals(statusMap, response.getStatus());

View file

@ -0,0 +1,39 @@
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
//
// 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<String> addresses = Arrays.asList("alice@pgpainless.org", "vanitasvitae@fsfe.org", "Ed_Snowden@lavabit.com");
List<String> 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());
}
}

View file

@ -0,0 +1,42 @@
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
//
// 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<String, Status> 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());
}
}

View file

@ -2,18 +2,17 @@
// //
// SPDX-License-Identifier: Apache-2.0 // 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.BeforeAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import pgp.vks.client.v1.impl.URLMapper;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
public class V1APITest { public class URLMapperTest {
private static URLMapper api; private static URLMapper api;

View file

@ -2,45 +2,37 @@
// //
// SPDX-License-Identifier: Apache-2.0 // 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.bouncycastle.util.io.Streams;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test; 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.VKS;
import pgp.vks.client.exception.CertCannotBePublishedException;
import pgp.vks.client.exception.CertNotFoundException; 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.MalformedURLException;
import java.nio.charset.StandardCharsets; 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.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class VKSTest { public class VKSTest {
private static VKS vks; private static VKS vks;
@BeforeAll @BeforeAll
static void prepare() { static void prepare() throws MalformedURLException {
vks = VKSImpl.keysDotOpenPgpDotOrg(); vks = new VKSImpl("https://testing2.keys.openpgp.org");
}
@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"));
} }
@Test @Test
@ -77,16 +69,75 @@ public class VKSTest {
"YA3TLiYiZbEM\n" + "YA3TLiYiZbEM\n" +
"=QRwY\n" + "=QRwY\n" +
"-----END PGP PUBLIC KEY BLOCK-----\n"; "-----END PGP PUBLIC KEY BLOCK-----\n";
System.out.println(keyArmored);
String keyFingerprint = "57417147D0C8B548220A36A60BAAB05A087768D3"; 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()); assertEquals(keyFingerprint, uploadResponse.getKeyFingerprint());
VerificationResponse verifyResponse = vks.requestVerification().forEmailAddresses( RequestVerify.Response verifyResponse = vks.requestVerification()
Collections.singletonList("test123asdasd@byom.de"), .forEmailAddress("test123asdasd@byom.de")
uploadResponse.getToken(), .execute(uploadResponse.getToken());
Collections.singletonList("de_DE"));
assertEquals(keyFingerprint, verifyResponse.getKeyFingerprint()); 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)));
}
} }