implemented sqlite keystore
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
@@ -12,13 +12,16 @@ repositories {
|
|||||||
dependencies {
|
dependencies {
|
||||||
testImplementation platform('org.junit:junit-bom:5.10.0')
|
testImplementation platform('org.junit:junit-bom:5.10.0')
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||||
|
implementation 'org.xerial:sqlite-jdbc:3.46.0.0'
|
||||||
implementation project(':de.srsoftware.http')
|
implementation project(':de.srsoftware.http')
|
||||||
implementation project(':de.srsoftware.logging')
|
implementation project(':de.srsoftware.logging')
|
||||||
implementation project(':de.srsoftware.oidc.api')
|
implementation project(':de.srsoftware.oidc.api')
|
||||||
implementation project(':de.srsoftware.oidc.backend')
|
implementation project(':de.srsoftware.oidc.backend')
|
||||||
implementation project(':de.srsoftware.oidc.web')
|
implementation project(':de.srsoftware.oidc.web')
|
||||||
implementation project(':de.srsoftware.utils')
|
implementation project(':de.srsoftware.utils')
|
||||||
implementation project(':de.srsoftware.oidc.datastore.file')}
|
implementation project(':de.srsoftware.oidc.datastore.file')
|
||||||
|
implementation project(':de.srsoftware.oidc.datastore.sqlite')
|
||||||
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
|
|||||||
@@ -21,12 +21,14 @@ import de.srsoftware.oidc.backend.*;
|
|||||||
import de.srsoftware.oidc.datastore.file.FileStore;
|
import de.srsoftware.oidc.datastore.file.FileStore;
|
||||||
import de.srsoftware.oidc.datastore.file.PlaintextKeyStore;
|
import de.srsoftware.oidc.datastore.file.PlaintextKeyStore;
|
||||||
import de.srsoftware.oidc.datastore.file.UuidHasher;
|
import de.srsoftware.oidc.datastore.file.UuidHasher;
|
||||||
|
import de.srsoftware.oidc.datastore.sqlite.SqliteKeyStore;
|
||||||
import de.srsoftware.oidc.web.Forward;
|
import de.srsoftware.oidc.web.Forward;
|
||||||
import de.srsoftware.oidc.web.StaticPages;
|
import de.srsoftware.oidc.web.StaticPages;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
import org.sqlite.SQLiteDataSource;
|
||||||
|
|
||||||
public class Application {
|
public class Application {
|
||||||
public static final String API_CLIENT = "/api/client";
|
public static final String API_CLIENT = "/api/client";
|
||||||
@@ -55,6 +57,13 @@ public class Application {
|
|||||||
var firstHash = passwordHasher.hash(FIRST_USER_PASS, FIRST_UUID);
|
var firstHash = passwordHasher.hash(FIRST_USER_PASS, FIRST_UUID);
|
||||||
var firstUser = new User(FIRST_USER, firstHash, FIRST_USER, "%s@internal".formatted(FIRST_USER), FIRST_UUID).add(MANAGE_CLIENTS, MANAGE_SMTP, MANAGE_USERS);
|
var firstUser = new User(FIRST_USER, firstHash, FIRST_USER, "%s@internal".formatted(FIRST_USER), FIRST_UUID).add(MANAGE_CLIENTS, MANAGE_SMTP, MANAGE_USERS);
|
||||||
KeyStorage keyStore = new PlaintextKeyStore(keyDir);
|
KeyStorage keyStore = new PlaintextKeyStore(keyDir);
|
||||||
|
{ // SQLite
|
||||||
|
SQLiteDataSource dataSource = new SQLiteDataSource();
|
||||||
|
var dbFile = storageFile.getParentFile().toPath().resolve("db.sqlite3").toFile();
|
||||||
|
dataSource.setUrl("jdbc:sqlite:%s".formatted(dbFile));
|
||||||
|
var conn = dataSource.getConnection();
|
||||||
|
keyStore = new SqliteKeyStore(conn);
|
||||||
|
}
|
||||||
KeyManager keyManager = new RotatingKeyManager(keyStore);
|
KeyManager keyManager = new RotatingKeyManager(keyStore);
|
||||||
FileStore fileStore = new FileStore(storageFile, passwordHasher).init(firstUser);
|
FileStore fileStore = new FileStore(storageFile, passwordHasher).init(firstUser);
|
||||||
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
|
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
|
||||||
|
|||||||
23
de.srsoftware.oidc.datastore.sqlite/build.gradle
Normal file
23
de.srsoftware.oidc.datastore.sqlite/build.gradle
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
}
|
||||||
|
|
||||||
|
group = 'de.srsoftware'
|
||||||
|
version = '1.0-SNAPSHOT'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
testImplementation platform('org.junit:junit-bom:5.10.0')
|
||||||
|
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||||
|
implementation project(':de.srsoftware.oidc.api')
|
||||||
|
implementation project(':de.srsoftware.utils')
|
||||||
|
implementation 'org.bitbucket.b_c:jose4j:0.9.6'
|
||||||
|
implementation 'org.xerial:sqlite-jdbc:3.46.0.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
/* © SRSoftware 2024 */
|
||||||
|
package de.srsoftware.oidc.datastore.sqlite;
|
||||||
|
|
||||||
|
|
||||||
|
import static org.jose4j.jwk.JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE;
|
||||||
|
|
||||||
|
import de.srsoftware.oidc.api.KeyManager;
|
||||||
|
import de.srsoftware.oidc.api.KeyStorage;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import org.jose4j.jwk.PublicJsonWebKey;
|
||||||
|
import org.jose4j.lang.JoseException;
|
||||||
|
|
||||||
|
public class SqliteKeyStore implements KeyStorage {
|
||||||
|
private static final String CREATE_MIGRATION_TABLE = "CREATE TABLE IF NOT EXISTS metainfo(key VARCHAR(255) PRIMARY KEY, value TEXT);";
|
||||||
|
private static final String SELECT_KEYSTORE_VERSION = "SELECT * FROM metainfo WHERE key = 'key_store_version'";
|
||||||
|
private static final String SET_KEYSTORE_VERSION = "UPDATE metainfo SET value = ? WHERE key = 'key_store_version'";
|
||||||
|
private static final String CREATE_KEYSTORE_TABLE = "CREATE TABLE IF NOT EXISTS keystore(key_id VARCHAR(255) PRIMARY KEY, json TEXT NOT NULL);";
|
||||||
|
private static final String SAVE_KEY = "INSERT INTO keystore(key_id, json) values (?,?) ON CONFLICT(key_id) DO UPDATE SET json = ?";
|
||||||
|
private static final String SELECT_KEY_IDS = "SELECT key_id FROM keystore";
|
||||||
|
private static final String LOAD_KEY = "SELECT json FROM keystore WHERE key_id = ?";
|
||||||
|
private static final String DROP_KEY = "DELETE FROM keystore WHERE key_id = ?";
|
||||||
|
public static System.Logger LOG = System.getLogger(SqliteKeyStore.class.getSimpleName());
|
||||||
|
|
||||||
|
private HashMap<String, PublicJsonWebKey> loaded = new HashMap<>();
|
||||||
|
private final Connection conn;
|
||||||
|
|
||||||
|
public SqliteKeyStore(Connection connection) throws SQLException {
|
||||||
|
conn = connection;
|
||||||
|
initTables();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initTables() throws SQLException {
|
||||||
|
conn.prepareStatement(CREATE_MIGRATION_TABLE).execute();
|
||||||
|
var rs = conn.prepareStatement(SELECT_KEYSTORE_VERSION).executeQuery();
|
||||||
|
int availableVersion = 1;
|
||||||
|
int lastVersion = 1;
|
||||||
|
if (rs.next()) {
|
||||||
|
lastVersion = rs.getInt(1);
|
||||||
|
}
|
||||||
|
rs.close();
|
||||||
|
conn.setAutoCommit(false);
|
||||||
|
var stmt = conn.prepareStatement(SET_KEYSTORE_VERSION);
|
||||||
|
for (int version = lastVersion; version <= availableVersion; version++) {
|
||||||
|
try {
|
||||||
|
switch (version) {
|
||||||
|
case 1:
|
||||||
|
createKeyStoreTables();
|
||||||
|
}
|
||||||
|
stmt.setInt(1, version);
|
||||||
|
stmt.execute();
|
||||||
|
conn.commit();
|
||||||
|
} catch (Exception e) {
|
||||||
|
conn.rollback();
|
||||||
|
LOG.log(System.Logger.Level.ERROR, "Failed to update at keystore version = {0}", version);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn.setAutoCommit(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createKeyStoreTables() throws SQLException {
|
||||||
|
conn.prepareStatement(CREATE_KEYSTORE_TABLE).execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyStorage drop(String keyId) {
|
||||||
|
try {
|
||||||
|
var stmt = conn.prepareStatement(DROP_KEY);
|
||||||
|
stmt.setString(1, keyId);
|
||||||
|
stmt.execute();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOG.log(System.Logger.Level.WARNING, "Failed to drop key {0} from database:", keyId, e);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> listKeys() {
|
||||||
|
var result = new ArrayList<String>();
|
||||||
|
try {
|
||||||
|
var rs = conn.prepareStatement(SELECT_KEY_IDS).executeQuery();
|
||||||
|
while (rs.next()) result.add(rs.getString(1));
|
||||||
|
rs.close();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
LOG.log(System.Logger.Level.WARNING, "Failed to read key ids from table!");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PublicJsonWebKey load(String keyId) throws IOException, KeyManager.KeyCreationException {
|
||||||
|
try {
|
||||||
|
var stmt = conn.prepareStatement(LOAD_KEY);
|
||||||
|
stmt.setString(1, keyId);
|
||||||
|
var rs = stmt.executeQuery();
|
||||||
|
String json = null;
|
||||||
|
if (rs.next()) {
|
||||||
|
json = rs.getString(1);
|
||||||
|
}
|
||||||
|
rs.close();
|
||||||
|
return PublicJsonWebKey.Factory.newPublicJwk(json);
|
||||||
|
} catch (JoseException e) {
|
||||||
|
throw new KeyManager.KeyCreationException(e);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyStorage store(PublicJsonWebKey jsonWebKey) throws IOException {
|
||||||
|
try {
|
||||||
|
var keyId = jsonWebKey.getKeyId();
|
||||||
|
var json = jsonWebKey.toJson(INCLUDE_PRIVATE);
|
||||||
|
var stmt = conn.prepareStatement(SAVE_KEY);
|
||||||
|
stmt.setString(1, keyId);
|
||||||
|
stmt.setString(2, json);
|
||||||
|
stmt.setString(3, json);
|
||||||
|
stmt.execute();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,4 +7,5 @@ include 'de.srsoftware.oidc.backend'
|
|||||||
include 'de.srsoftware.oidc.datastore.file'
|
include 'de.srsoftware.oidc.datastore.file'
|
||||||
include 'de.srsoftware.oidc.web'
|
include 'de.srsoftware.oidc.web'
|
||||||
include 'de.srsoftware.utils'
|
include 'de.srsoftware.utils'
|
||||||
|
include 'de.srsoftware.oidc.datastore.sqlite'
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user