mirror of
https://codeberg.org/PGPainless/cert-d-java.git
synced 2025-09-09 11:19:41 +02:00
Initial commit
This commit is contained in:
commit
b142f310be
46 changed files with 2494 additions and 0 deletions
|
@ -0,0 +1,47 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pgp.cert_d.jdbc.sqlite;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import pgp.certificate_store.SubkeyLookup;
|
||||
|
||||
public class DatabaseSubkeyLookup implements SubkeyLookup {
|
||||
|
||||
private final SubkeyLookupDao dao;
|
||||
|
||||
public DatabaseSubkeyLookup(SubkeyLookupDao dao) {
|
||||
this.dao = dao;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getCertificateFingerprintsForSubkeyId(long subkeyId) throws IOException {
|
||||
try {
|
||||
List<Entry> entries = dao.selectValues(subkeyId);
|
||||
Set<String> certificates = new HashSet<>();
|
||||
for (Entry entry : entries) {
|
||||
certificates.add(entry.getCertificate());
|
||||
}
|
||||
|
||||
return Collections.unmodifiableSet(certificates);
|
||||
} catch (SQLException e) {
|
||||
throw new IOException("Cannot query for subkey lookup entries.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeCertificateSubkeyIds(String certificate, List<Long> subkeyIds) throws IOException {
|
||||
try {
|
||||
dao.insertValues(certificate, subkeyIds);
|
||||
} catch (SQLException e) {
|
||||
throw new IOException("Cannot store subkey lookup entries in database.", e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pgp.cert_d.jdbc.sqlite;
|
||||
|
||||
/**
|
||||
* Subkey-ID database entry.
|
||||
*/
|
||||
public class Entry {
|
||||
|
||||
private final int id;
|
||||
private final String certificate;
|
||||
private final long subkeyId;
|
||||
|
||||
public Entry(int id, long subkeyId, String certificate) {
|
||||
this.id = id;
|
||||
this.subkeyId = subkeyId;
|
||||
this.certificate = certificate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internal ID of this entry in the database.
|
||||
*
|
||||
* @return internal id
|
||||
*/
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key-ID of the subkey.
|
||||
*
|
||||
* @return subkey id
|
||||
*/
|
||||
public long getSubkeyId() {
|
||||
return subkeyId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fingerprint of the certificate the subkey belongs to.
|
||||
* @return fingerprint
|
||||
*/
|
||||
public String getCertificate() {
|
||||
return certificate;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pgp.cert_d.jdbc.sqlite;
|
||||
|
||||
import org.sqlite.SQLiteErrorCode;
|
||||
import org.sqlite.SQLiteException;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SqliteSubkeyLookupDaoImpl implements SubkeyLookupDao {
|
||||
|
||||
private final String databaseUrl;
|
||||
|
||||
private static final String CREATE_TABLE_STMT = "" +
|
||||
"CREATE TABLE IF NOT EXISTS subkey_lookup (\n" +
|
||||
" id integer PRIMARY KEY,\n" + // id (internal to the database)
|
||||
" certificate text NOT NULL,\n" + // certificate fingerprint
|
||||
" subkey_id integer NOT NULL,\n" + // subkey id
|
||||
" UNIQUE(certificate, subkey_id)\n" +
|
||||
")";
|
||||
|
||||
private static final String INSERT_STMT = "" +
|
||||
"INSERT INTO subkey_lookup(certificate, subkey_id) " +
|
||||
"VALUES (?,?)";
|
||||
private static final String QUERY_STMT = "" +
|
||||
"SELECT * FROM subkey_lookup " +
|
||||
"WHERE subkey_id=?";
|
||||
|
||||
public SqliteSubkeyLookupDaoImpl(String databaseURL) throws SQLException {
|
||||
this.databaseUrl = databaseURL;
|
||||
try (Connection connection = getConnection(); Statement statement = connection.createStatement()) {
|
||||
statement.execute(CREATE_TABLE_STMT);
|
||||
}
|
||||
}
|
||||
|
||||
public Connection getConnection() throws SQLException {
|
||||
return DriverManager.getConnection(databaseUrl);
|
||||
}
|
||||
|
||||
public static SqliteSubkeyLookupDaoImpl forDatabaseFile(File databaseFile) throws SQLException {
|
||||
return new SqliteSubkeyLookupDaoImpl("jdbc:sqlite:" + databaseFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
public int insertValues(String certificate, List<Long> subkeyIds) throws SQLException {
|
||||
int inserted = 0;
|
||||
try (Connection connection = getConnection(); PreparedStatement statement = connection.prepareStatement(INSERT_STMT)) {
|
||||
for (long subkeyId : subkeyIds) {
|
||||
try {
|
||||
statement.setString(1, certificate);
|
||||
statement.setLong(2, subkeyId);
|
||||
statement.executeUpdate();
|
||||
inserted++;
|
||||
} catch (SQLiteException e) {
|
||||
// throw any exception, except:
|
||||
// ignore unique constraint-related exceptions if we ignoreDuplicates
|
||||
if (e.getResultCode().code == SQLiteErrorCode.SQLITE_CONSTRAINT_UNIQUE.code) {
|
||||
// ignore duplicates
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return inserted;
|
||||
}
|
||||
|
||||
public List<Entry> selectValues(long subkeyId) throws SQLException {
|
||||
List<Entry> results = new ArrayList<>();
|
||||
try (Connection connection = getConnection(); PreparedStatement statement = connection.prepareStatement(QUERY_STMT)) {
|
||||
statement.setLong(1, subkeyId);
|
||||
try (ResultSet resultSet = statement.executeQuery()) {
|
||||
while (resultSet.next()) {
|
||||
Entry entry = new Entry(
|
||||
resultSet.getInt("id"),
|
||||
resultSet.getLong("subkey_id"),
|
||||
resultSet.getString("certificate"));
|
||||
results.add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pgp.cert_d.jdbc.sqlite;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
public interface SubkeyLookupDao {
|
||||
|
||||
int insertValues(String certificate, List<Long> subkeyIds) throws SQLException;
|
||||
|
||||
List<Entry> selectValues(long subkeyId) throws SQLException;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/**
|
||||
* Implementation of a {@link pgp.certificate_store.SubkeyLookup} mechanism using an SQLite Database.
|
||||
*/
|
||||
package pgp.cert_d.jdbc.sqlite;
|
|
@ -0,0 +1,21 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pgp.cert_d.jdbc.sqlite;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class EntryTest {
|
||||
|
||||
@Test
|
||||
public void simpleGetterTest() {
|
||||
Entry entry = new Entry(1, 123L, "eb85bb5fa33a75e15e944e63f231550c4f47e38e");
|
||||
|
||||
assertEquals(1, entry.getId());
|
||||
assertEquals(123L, entry.getSubkeyId());
|
||||
assertEquals("eb85bb5fa33a75e15e944e63f231550c4f47e38e", entry.getCertificate());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pgp.cert_d.jdbc.sqlite;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class SqliteSubkeyLookupTest {
|
||||
|
||||
private File databaseFile;
|
||||
private DatabaseSubkeyLookup lookup;
|
||||
|
||||
@BeforeEach
|
||||
public void setupLookup() throws IOException, SQLException {
|
||||
databaseFile = Files.createTempFile("pgp.cert.d-", "lookup.db").toFile();
|
||||
databaseFile.createNewFile();
|
||||
databaseFile.deleteOnExit();
|
||||
lookup = new DatabaseSubkeyLookup(SqliteSubkeyLookupDaoImpl.forDatabaseFile(databaseFile));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleInsertAndGet() throws IOException {
|
||||
store("eb85bb5fa33a75e15e944e63f231550c4f47e38e", 123L, 234L);
|
||||
store("d1a66e1a23b182c9980f788cfbfcc82a015e7330", 234L);
|
||||
|
||||
assertEquals(Collections.singleton("eb85bb5fa33a75e15e944e63f231550c4f47e38e"), lookup.getCertificateFingerprintsForSubkeyId(123L));
|
||||
assertEquals(
|
||||
new HashSet<>(Arrays.asList("eb85bb5fa33a75e15e944e63f231550c4f47e38e", "d1a66e1a23b182c9980f788cfbfcc82a015e7330")),
|
||||
lookup.getCertificateFingerprintsForSubkeyId(234L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNonExistingSubkeyYieldsNull() throws IOException {
|
||||
assertTrue(lookup.getCertificateFingerprintsForSubkeyId(6666666).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void secondInstanceLookupTest() throws IOException, SQLException {
|
||||
store("eb85bb5fa33a75e15e944e63f231550c4f47e38e", 1337L);
|
||||
assertEquals(Collections.singleton("eb85bb5fa33a75e15e944e63f231550c4f47e38e"), lookup.getCertificateFingerprintsForSubkeyId(1337));
|
||||
|
||||
// do the lookup using a second db instance on the same file
|
||||
DatabaseSubkeyLookup secondInstance = new DatabaseSubkeyLookup(SqliteSubkeyLookupDaoImpl.forDatabaseFile(databaseFile));
|
||||
assertEquals(Collections.singleton("eb85bb5fa33a75e15e944e63f231550c4f47e38e"), secondInstance.getCertificateFingerprintsForSubkeyId(1337));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ignoreInsertDuplicates() throws IOException {
|
||||
store("d1a66e1a23b182c9980f788cfbfcc82a015e7330", 123L, 234L);
|
||||
// per default we ignore duplicates
|
||||
store("d1a66e1a23b182c9980f788cfbfcc82a015e7330", 123L, 512L);
|
||||
}
|
||||
|
||||
private void store(String cert, long... ids) throws IOException {
|
||||
List<Long> idList = new ArrayList<>();
|
||||
for (long id : ids) {
|
||||
idList.add(id);
|
||||
}
|
||||
lookup.storeCertificateSubkeyIds(cert, idList);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue