Throw NoSuchElementException for non-existent certificates/keys

Fixes #2
This commit is contained in:
Paul Schaub 2022-08-27 12:16:53 +02:00
parent eab31b8c12
commit a248e0d717
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
4 changed files with 75 additions and 10 deletions

View file

@ -15,6 +15,8 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -46,13 +48,17 @@ public class PGPCertificateDirectory
if (!openPgpV4FingerprintPattern.matcher(fingerprint).matches()) { if (!openPgpV4FingerprintPattern.matcher(fingerprint).matches()) {
throw new BadNameException(); throw new BadNameException();
} }
return backend.readByFingerprint(fingerprint); Certificate certificate = backend.readByFingerprint(fingerprint);
if (certificate == null) {
throw new NoSuchElementException();
}
return certificate;
} }
@Override @Override
public Certificate getByFingerprintIfChanged(String fingerprint, long tag) public Certificate getByFingerprintIfChanged(String fingerprint, long tag)
throws IOException, BadNameException, BadDataException { throws IOException, BadNameException, BadDataException {
if (tag != backend.getTagForFingerprint(fingerprint)) { if (!Objects.equals(tag, backend.getTagForFingerprint(fingerprint))) {
return getByFingerprint(fingerprint); return getByFingerprint(fingerprint);
} }
return null; return null;
@ -66,13 +72,13 @@ public class PGPCertificateDirectory
if (keyMaterial != null) { if (keyMaterial != null) {
return keyMaterial.asCertificate(); return keyMaterial.asCertificate();
} }
return null; throw new NoSuchElementException();
} }
@Override @Override
public Certificate getBySpecialNameIfChanged(String specialName, long tag) public Certificate getBySpecialNameIfChanged(String specialName, long tag)
throws IOException, BadNameException, BadDataException { throws IOException, BadNameException, BadDataException {
if (tag != backend.getTagForSpecialName(specialName)) { if (!Objects.equals(tag, backend.getTagForSpecialName(specialName))) {
return getBySpecialName(specialName); return getBySpecialName(specialName);
} }
return null; return null;
@ -121,7 +127,11 @@ public class PGPCertificateDirectory
@Override @Override
public KeyMaterial getTrustRoot() throws IOException, BadDataException { public KeyMaterial getTrustRoot() throws IOException, BadDataException {
try { try {
return backend.readBySpecialName(SpecialNames.TRUST_ROOT); KeyMaterial keyMaterial = backend.readBySpecialName(SpecialNames.TRUST_ROOT);
if (keyMaterial == null) {
throw new NoSuchElementException();
}
return keyMaterial;
} catch (BadNameException e) { } catch (BadNameException e) {
throw new AssertionError("'" + SpecialNames.TRUST_ROOT + "' is implementation MUST"); throw new AssertionError("'" + SpecialNames.TRUST_ROOT + "' is implementation MUST");
} }

View file

@ -33,6 +33,7 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.NoSuchElementException;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
@ -348,7 +349,7 @@ public class FileBasedCertificateDirectoryBackend implements PGPCertificateDirec
private Long getTag(File file) throws IOException { private Long getTag(File file) throws IOException {
if (!file.exists()) { if (!file.exists()) {
throw new IllegalArgumentException("File MUST exist."); throw new NoSuchElementException();
} }
Path path = file.toPath(); Path path = file.toPath();
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class); BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);

View file

@ -31,6 +31,7 @@ import java.nio.file.Files;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set; import java.util.Set;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -68,6 +69,55 @@ public class PGPCertificateDirectoryTest {
Arguments.of(Named.of("FileBasedCertificateDirectory", fileBased))); Arguments.of(Named.of("FileBasedCertificateDirectory", fileBased)));
} }
@ParameterizedTest
@MethodSource("provideTestSubjects")
public void getNonExistentCertByFingerprintThrowsNoSuchElementException(PGPCertificateDirectory directory) {
assertThrows(NoSuchElementException.class, () ->
directory.getByFingerprint("0000000000000000000000000000000000000000"));
}
@ParameterizedTest
@MethodSource("provideTestSubjects")
public void getNonExistentCertByFingerprintIfChangedThrowsNoSuchElementException(PGPCertificateDirectory directory) {
assertThrows(NoSuchElementException.class, () ->
directory.getByFingerprintIfChanged("0000000000000000000000000000000000000000", 12));
}
@ParameterizedTest
@MethodSource("provideTestSubjects")
public void getNonExistentCertBySpecialNameThrowsNoSuchElementException(PGPCertificateDirectory directory) {
assertThrows(NoSuchElementException.class, () ->
directory.getBySpecialName(SpecialNames.TRUST_ROOT));
}
@ParameterizedTest
@MethodSource("provideTestSubjects")
public void getNonExistentCertBySpecialNameIfChangedThrowsNoSuchElementException(PGPCertificateDirectory directory) {
assertThrows(NoSuchElementException.class, () ->
directory.getBySpecialNameIfChanged(SpecialNames.TRUST_ROOT, 12));
}
@ParameterizedTest
@MethodSource("provideTestSubjects")
public void getNonExistentTrustRootThrowsNoSuchElementException(PGPCertificateDirectory directory) {
assertThrows(NoSuchElementException.class, () ->
directory.getTrustRoot());
}
@ParameterizedTest
@MethodSource("provideTestSubjects")
public void getNonExistentTrustRootIfChangedThrowsNoSuchElementException(PGPCertificateDirectory directory) {
assertThrows(NoSuchElementException.class, () ->
directory.getTrustRootCertificateIfChanged(12));
}
@ParameterizedTest
@MethodSource("provideTestSubjects")
public void getNonExistentTrustRootCertificateThrowsNoSuchElementException(PGPCertificateDirectory directory) {
assertThrows(NoSuchElementException.class, () ->
directory.getTrustRootCertificate());
}
@ParameterizedTest @ParameterizedTest
@MethodSource("provideTestSubjects") @MethodSource("provideTestSubjects")
public void lockDirectoryAndTryInsertWillFail(PGPCertificateDirectory directory) public void lockDirectoryAndTryInsertWillFail(PGPCertificateDirectory directory)
@ -130,7 +180,7 @@ public class PGPCertificateDirectoryTest {
@MethodSource("provideTestSubjects") @MethodSource("provideTestSubjects")
public void testInsertAndGetSingleCert(PGPCertificateDirectory directory) public void testInsertAndGetSingleCert(PGPCertificateDirectory directory)
throws BadDataException, IOException, InterruptedException, BadNameException { throws BadDataException, IOException, InterruptedException, BadNameException {
assertNull(directory.getByFingerprint(CEDRIC_FP), "Empty directory MUST NOT contain certificate"); assertThrows(NoSuchElementException.class, () -> directory.getByFingerprint(CEDRIC_FP), "Empty directory MUST NOT contain certificate");
Certificate certificate = directory.insert(TestKeys.getCedricCert(), merger); Certificate certificate = directory.insert(TestKeys.getCedricCert(), merger);
assertEquals(CEDRIC_FP, certificate.getFingerprint(), "Fingerprint of inserted cert MUST match"); assertEquals(CEDRIC_FP, certificate.getFingerprint(), "Fingerprint of inserted cert MUST match");
@ -148,7 +198,7 @@ public class PGPCertificateDirectoryTest {
@MethodSource("provideTestSubjects") @MethodSource("provideTestSubjects")
public void testInsertAndGetTrustRootAndCert(PGPCertificateDirectory directory) public void testInsertAndGetTrustRootAndCert(PGPCertificateDirectory directory)
throws BadDataException, IOException, InterruptedException { throws BadDataException, IOException, InterruptedException {
assertNull(directory.getTrustRoot()); assertThrows(NoSuchElementException.class, () -> directory.getTrustRoot());
KeyMaterial trustRootMaterial = directory.insertTrustRoot( KeyMaterial trustRootMaterial = directory.insertTrustRoot(
TestKeys.getHarryKey(), merger); TestKeys.getHarryKey(), merger);
@ -188,6 +238,7 @@ public class PGPCertificateDirectoryTest {
assertNotNull(directory.getTrustRootCertificateIfChanged(tag + 1)); assertNotNull(directory.getTrustRootCertificateIfChanged(tag + 1));
Long oldTag = tag; Long oldTag = tag;
Thread.sleep(10);
// "update" key // "update" key
trustRootMaterial = directory.insertTrustRoot( trustRootMaterial = directory.insertTrustRoot(
TestKeys.getHarryKey(), merger); TestKeys.getHarryKey(), merger);
@ -241,10 +292,12 @@ public class PGPCertificateDirectoryTest {
Long oldTag = tag; Long oldTag = tag;
Thread.sleep(10);
// Change the file on disk directly, this invalidates the tag due to changed modification date // Change the file on disk directly, this invalidates the tag due to changed modification date
File certFile = resolver.getCertFileByFingerprint(certificate.getFingerprint()); File certFile = resolver.getCertFileByFingerprint(certificate.getFingerprint());
FileOutputStream fileOut = new FileOutputStream(certFile); FileOutputStream fileOut = new FileOutputStream(certFile);
Streams.pipeAll(certificate.getInputStream(), fileOut); Streams.pipeAll(certificate.getInputStream(), fileOut);
fileOut.write("\n".getBytes());
fileOut.close(); fileOut.close();
// Old invalidated tag indicates a change, so the modified certificate is returned // Old invalidated tag indicates a change, so the modified certificate is returned

View file

@ -17,6 +17,7 @@ import pgp.certificate_store.exception.BadNameException;
import java.io.IOException; import java.io.IOException;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.NoSuchElementException;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
@ -52,7 +53,7 @@ public class PGPCertificateStoreAdapterTest {
@Test @Test
public void testInsertGetCertificate() public void testInsertGetCertificate()
throws BadDataException, IOException, InterruptedException, BadNameException { throws BadDataException, IOException, InterruptedException, BadNameException {
assertNull(adapter.getCertificate(TestKeys.CEDRIC_FP)); assertThrows(NoSuchElementException.class, () -> adapter.getCertificate(TestKeys.CEDRIC_FP));
assertFalse(adapter.getCertificates().hasNext()); assertFalse(adapter.getCertificates().hasNext());
Certificate certificate = adapter.insertCertificate(TestKeys.getCedricCert(), merger); Certificate certificate = adapter.insertCertificate(TestKeys.getCedricCert(), merger);
@ -70,7 +71,7 @@ public class PGPCertificateStoreAdapterTest {
@Test @Test
public void testInsertGetTrustRoot() public void testInsertGetTrustRoot()
throws BadDataException, BadNameException, IOException, InterruptedException { throws BadDataException, BadNameException, IOException, InterruptedException {
assertNull(adapter.getCertificate(SpecialNames.TRUST_ROOT)); assertThrows(NoSuchElementException.class, () -> adapter.getCertificate(SpecialNames.TRUST_ROOT));
Certificate certificate = adapter.insertCertificateBySpecialName( Certificate certificate = adapter.insertCertificateBySpecialName(
SpecialNames.TRUST_ROOT, TestKeys.getHarryKey(), merger); SpecialNames.TRUST_ROOT, TestKeys.getHarryKey(), merger);