dropped sqlite
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
@@ -1,15 +1,49 @@
|
|||||||
# LightOIDC
|
# LightOIDC
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Deutsch</th>
|
||||||
|
<th>English</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
LightOIDC ist ein [Spezifikations][specification]-treuer OpenID-Connect-Provider mit minimalem Fußabdruck.
|
||||||
|
|
||||||
|
Dieses Ziel wird durch Minimierung der externen Abhängigkeiten auf ein absolutes Minimum erreicht:
|
||||||
|
|
||||||
|
Im Moment baut das Projekt nur auf die folgenden Laufzeit-Bibliotheken auf:
|
||||||
|
|
||||||
|
* [org.json:json](https://github.com/douglascrockford/JSON-java)
|
||||||
|
* [org.bitbucket.b_c:jose4j](https://bitbucket.org/b_c/jose4j)
|
||||||
|
* [com.sun.mail:jakarta.mail](https://projects.eclipse.org/projects/ee4j.mail)
|
||||||
|
* [com.sun.activation:jakarta.activation](https://projects.eclipse.org/projects/ee4j.jaf)
|
||||||
|
|
||||||
|
Im Moment haben diese Bibliotheken keine weiteren (transitiven) Anhängigkeiten, so dass das Projekt nicht durch eine Kaskade von Libraries aufgeblasen wird.
|
||||||
|
|
||||||
|
Das Ermöglicht es, dass die compilierte JAR-Datei weniger als 1,5 MB groß ist!
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
This aims to be a [specification] compliant OpenID connect provider with minimal footprint.
|
This aims to be a [specification] compliant OpenID connect provider with minimal footprint.
|
||||||
|
|
||||||
This goal is achieved by reducing external library dependiencies to an absolute minimum.
|
This goal is achieved by reducing external library dependiencies to an absolute minimum.
|
||||||
|
|
||||||
Currently, this project only depends on the following libraries:
|
Currently, this project only depends on the following runtime libraries:
|
||||||
|
|
||||||
* [org.json:json](https://github.com/douglascrockford/JSON-java)
|
* [org.json:json](https://github.com/douglascrockford/JSON-java)
|
||||||
* [org.bitbucket.b_c:jose4j](https://bitbucket.org/b_c/jose4j)
|
* [org.bitbucket.b_c:jose4j](https://bitbucket.org/b_c/jose4j)
|
||||||
|
* [com.sun.mail:jakarta.mail](https://projects.eclipse.org/projects/ee4j.mail)
|
||||||
|
* [com.sun.activation:jakarta.activation](https://projects.eclipse.org/projects/ee4j.jaf)
|
||||||
|
|
||||||
At the time of writing, these libraries have no further transitive dependencies, this mitigating any bloat from the project.
|
At the time of writing, these libraries have no further transitive dependencies, thus mitigating any bloat from the project.
|
||||||
|
|
||||||
|
As a result, the compiled jar has a size of less than 1.5 MB!
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
## build
|
## build
|
||||||
|
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ 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 project(':de.srsoftware.utils')
|
||||||
implementation 'org.json:json:20240303'
|
implementation 'org.json:json:20240303'
|
||||||
implementation 'org.bitbucket.b_c:jose4j:0.9.6'
|
implementation 'org.bitbucket.b_c:jose4j:0.9.6'
|
||||||
implementation 'com.sun.mail:jakarta.mail:2.0.1'
|
implementation 'com.sun.mail:jakarta.mail:2.0.1'
|
||||||
implementation project(':de.srsoftware.utils')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
|
|||||||
@@ -12,16 +12,14 @@ 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.utils')
|
|
||||||
implementation project(':de.srsoftware.oidc.datastore.encrypted')
|
implementation project(':de.srsoftware.oidc.datastore.encrypted')
|
||||||
implementation project(':de.srsoftware.oidc.datastore.file')
|
implementation project(':de.srsoftware.oidc.datastore.file')
|
||||||
implementation project(':de.srsoftware.oidc.datastore.sqlite')
|
implementation project(':de.srsoftware.oidc.web')
|
||||||
|
implementation project(':de.srsoftware.utils')
|
||||||
implementation 'org.json:json:20240303'
|
implementation 'org.json:json:20240303'
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import static de.srsoftware.oidc.api.Constants.*;
|
|||||||
import static de.srsoftware.oidc.api.data.Permission.*;
|
import static de.srsoftware.oidc.api.data.Permission.*;
|
||||||
import static de.srsoftware.utils.Optionals.emptyIfBlank;
|
import static de.srsoftware.utils.Optionals.emptyIfBlank;
|
||||||
import static de.srsoftware.utils.Paths.configDir;
|
import static de.srsoftware.utils.Paths.configDir;
|
||||||
import static de.srsoftware.utils.Paths.extension;
|
|
||||||
import static de.srsoftware.utils.Strings.uuid;
|
import static de.srsoftware.utils.Strings.uuid;
|
||||||
import static java.lang.System.Logger.Level.DEBUG;
|
import static java.lang.System.Logger.Level.DEBUG;
|
||||||
import static java.lang.System.Logger.Level.ERROR;
|
import static java.lang.System.Logger.Level.ERROR;
|
||||||
@@ -24,7 +23,6 @@ import de.srsoftware.oidc.datastore.encrypted.EncryptedMailConfig;
|
|||||||
import de.srsoftware.oidc.datastore.encrypted.EncryptedUserService;
|
import de.srsoftware.oidc.datastore.encrypted.EncryptedUserService;
|
||||||
import de.srsoftware.oidc.datastore.file.FileStoreProvider;
|
import de.srsoftware.oidc.datastore.file.FileStoreProvider;
|
||||||
import de.srsoftware.oidc.datastore.file.PlaintextKeyStore;
|
import de.srsoftware.oidc.datastore.file.PlaintextKeyStore;
|
||||||
import de.srsoftware.oidc.datastore.sqlite.*;
|
|
||||||
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 de.srsoftware.utils.UuidHasher;
|
import de.srsoftware.utils.UuidHasher;
|
||||||
@@ -52,7 +50,6 @@ public class Application {
|
|||||||
private static final String INDEX = STATIC_PATH + "/index.html";
|
private static final String INDEX = STATIC_PATH + "/index.html";
|
||||||
private static final String WELL_KNOWN = "/.well-known";
|
private static final String WELL_KNOWN = "/.well-known";
|
||||||
private static System.Logger LOG = new ColorLogger("Application").setLogLevel(DEBUG);
|
private static System.Logger LOG = new ColorLogger("Application").setLogLevel(DEBUG);
|
||||||
private static ConnectionProvider connectionProvider = new ConnectionProvider();
|
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
var argMap = map(args);
|
var argMap = map(args);
|
||||||
@@ -90,83 +87,62 @@ public class Application {
|
|||||||
|
|
||||||
private static ClientService setupClientService(Configuration config, Path defaultFile, FileStoreProvider fileStoreProvider) throws SQLException {
|
private static ClientService setupClientService(Configuration config, Path defaultFile, FileStoreProvider fileStoreProvider) throws SQLException {
|
||||||
var clientStore = new File(config.getOrDefault("client_store", defaultFile));
|
var clientStore = new File(config.getOrDefault("client_store", defaultFile));
|
||||||
var clientService = switch (extension(clientStore)) {
|
ClientService clientService = fileStoreProvider.get(clientStore);
|
||||||
case "db", "sqlite", "sqlite3" -> new SqliteClientService(connectionProvider.get(clientStore));
|
|
||||||
default -> fileStoreProvider.get(clientStore);
|
|
||||||
};
|
|
||||||
|
|
||||||
Optional<String> encryptionKey = config.get(ENCRYPTION_KEY);
|
Optional<String> encryptionKey = config.get(ENCRYPTION_KEY);
|
||||||
|
|
||||||
if (encryptionKey.isPresent()){
|
if (encryptionKey.isPresent()) {
|
||||||
var salt = config.getOrDefault(SALT,uuid());
|
var salt = config.getOrDefault(SALT, uuid());
|
||||||
clientService = new EncryptedClientService(encryptionKey.get(),salt,clientService);
|
clientService = new EncryptedClientService(encryptionKey.get(), salt, clientService);
|
||||||
}
|
}
|
||||||
return clientService;
|
return clientService;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AuthorizationService setupAuthService(Configuration config, Path defaultFile, FileStoreProvider fileStoreProvider) throws SQLException {
|
private static AuthorizationService setupAuthService(Configuration config, Path defaultFile, FileStoreProvider fileStoreProvider) throws SQLException {
|
||||||
var authServiceLocation = new File(config.getOrDefault("auth_store",defaultFile));
|
var authServiceLocation = new File(config.getOrDefault("auth_store", defaultFile));
|
||||||
return switch (extension(authServiceLocation)){
|
return fileStoreProvider.get(authServiceLocation);
|
||||||
case "db", "sqlite", "sqlite3" -> new SqliteAuthService(connectionProvider.get(authServiceLocation));
|
|
||||||
default -> fileStoreProvider.get(authServiceLocation);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SessionService setupSessionService(Configuration config, Path defaultFile, FileStoreProvider fileStoreProvider) throws SQLException {
|
private static SessionService setupSessionService(Configuration config, Path defaultFile, FileStoreProvider fileStoreProvider) {
|
||||||
var sessionStore = new File(config.getOrDefault("session_storage",defaultFile));
|
var sessionStore = new File(config.getOrDefault("session_storage", defaultFile));
|
||||||
return switch (extension(sessionStore)){
|
return fileStoreProvider.get(sessionStore);
|
||||||
case "db", "sqlite", "sqlite3" -> new SqliteSessionService(connectionProvider.get(sessionStore));
|
|
||||||
default -> fileStoreProvider.get(sessionStore);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MailConfig setupMailConfig(Configuration config, Path defaultFile, FileStoreProvider fileStoreProvider) throws SQLException {
|
private static MailConfig setupMailConfig(Configuration config, Path defaultFile, FileStoreProvider fileStoreProvider) throws SQLException {
|
||||||
var mailConfigLocation = new File(config.getOrDefault("mail_config_storage",defaultFile));
|
var mailConfigLocation = new File(config.getOrDefault("mail_config_storage", defaultFile));
|
||||||
var mailConfig = switch (extension(mailConfigLocation)){
|
MailConfig mailConfig = fileStoreProvider.get(mailConfigLocation);
|
||||||
case "db", "sqlite", "sqlite3" -> new SqliteMailConfig(connectionProvider.get(mailConfigLocation));
|
|
||||||
default -> fileStoreProvider.get(mailConfigLocation);
|
|
||||||
};
|
|
||||||
|
|
||||||
Optional<String> encryptionKey = config.get(ENCRYPTION_KEY);
|
Optional<String> encryptionKey = config.get(ENCRYPTION_KEY);
|
||||||
|
|
||||||
if (encryptionKey.isPresent()){
|
if (encryptionKey.isPresent()) {
|
||||||
var salt = config.getOrDefault(SALT,uuid());
|
var salt = config.getOrDefault(SALT, uuid());
|
||||||
mailConfig = new EncryptedMailConfig(mailConfig,encryptionKey.get(),salt);
|
mailConfig = new EncryptedMailConfig(mailConfig, encryptionKey.get(), salt);
|
||||||
}
|
}
|
||||||
return mailConfig;
|
return mailConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UserService setupUserService(Configuration config, Path defaultFile, FileStoreProvider fileStoreProvider, UuidHasher passHasher) throws SQLException {
|
private static UserService setupUserService(Configuration config, Path defaultFile, FileStoreProvider fileStoreProvider, UuidHasher passHasher) throws SQLException {
|
||||||
var userStorageLocation = new File(config.getOrDefault("user_storage",defaultFile));
|
var userStorageLocation = new File(config.getOrDefault("user_storage", defaultFile));
|
||||||
var userService = switch (extension(userStorageLocation).toLowerCase()){
|
UserService userService = fileStoreProvider.get(userStorageLocation);
|
||||||
case "db", "sqlite", "sqlite3" -> new SqliteUserService(connectionProvider.get(userStorageLocation),passHasher);
|
|
||||||
default -> fileStoreProvider.get(userStorageLocation);
|
|
||||||
};
|
|
||||||
|
|
||||||
Optional<String> encryptionKey = config.get(ENCRYPTION_KEY);
|
Optional<String> encryptionKey = config.get(ENCRYPTION_KEY);
|
||||||
|
|
||||||
if (encryptionKey.isPresent()){
|
if (encryptionKey.isPresent()) {
|
||||||
var salt = config.getOrDefault(SALT,uuid());
|
var salt = config.getOrDefault(SALT, uuid());
|
||||||
userService = new EncryptedUserService(userService,encryptionKey.get(),salt,passHasher);
|
userService = new EncryptedUserService(userService, encryptionKey.get(), salt, passHasher);
|
||||||
}
|
}
|
||||||
return userService;
|
return userService;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static KeyStorage setupKeyStore(Configuration config, Path defaultConfigDir) throws SQLException {
|
private static KeyStorage setupKeyStore(Configuration config, Path defaultConfigDir) throws SQLException {
|
||||||
var keyStorageLocation = new File(config.getOrDefault("key_storage", defaultConfigDir.resolve("keys")));
|
var keyStorageLocation = new File(config.getOrDefault("key_storage", defaultConfigDir.resolve("keys")));
|
||||||
KeyStorage keyStore = null;
|
KeyStorage keyStore = new PlaintextKeyStore(keyStorageLocation.toPath());
|
||||||
if ((keyStorageLocation.exists() && keyStorageLocation.isDirectory()) || !keyStorageLocation.getName().contains(".")) {
|
|
||||||
keyStore = new PlaintextKeyStore(keyStorageLocation.toPath());
|
|
||||||
} else { // SQLite
|
|
||||||
var conn = connectionProvider.get(keyStorageLocation);
|
|
||||||
keyStore = new SqliteKeyStore(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<String> encryptionKey = config.get(ENCRYPTION_KEY);
|
Optional<String> encryptionKey = config.get(ENCRYPTION_KEY);
|
||||||
|
|
||||||
if (encryptionKey.isPresent()){
|
if (encryptionKey.isPresent()) {
|
||||||
var salt = config.getOrDefault(SALT,uuid());
|
var salt = config.getOrDefault(SALT, uuid());
|
||||||
keyStore = new EncryptedKeyStore(encryptionKey.get(),salt,keyStore);
|
keyStore = new EncryptedKeyStore(encryptionKey.get(), salt, keyStore);
|
||||||
}
|
}
|
||||||
return keyStore;
|
return keyStore;
|
||||||
}
|
}
|
||||||
@@ -196,5 +172,5 @@ public class Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,12 +12,11 @@ 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 project(':de.srsoftware.oidc.api')
|
|
||||||
implementation 'com.sun.mail:jakarta.mail:2.0.1'
|
|
||||||
implementation project(':de.srsoftware.utils')
|
|
||||||
testImplementation project(path: ':de.srsoftware.oidc.api', configuration: "testBundle")
|
testImplementation project(path: ':de.srsoftware.oidc.api', configuration: "testBundle")
|
||||||
|
implementation project(':de.srsoftware.oidc.api')
|
||||||
|
implementation project(':de.srsoftware.utils')
|
||||||
|
implementation 'com.sun.mail:jakarta.mail:2.0.1'
|
||||||
implementation 'org.bitbucket.b_c:jose4j:0.9.6'
|
implementation 'org.bitbucket.b_c:jose4j:0.9.6'
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
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'
|
|
||||||
testImplementation project(path: ':de.srsoftware.oidc.api', configuration: "testBundle")
|
|
||||||
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'
|
|
||||||
implementation 'com.sun.mail:jakarta.mail:2.0.1'
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
test {
|
|
||||||
useJUnitPlatform()
|
|
||||||
}
|
|
||||||
-27
@@ -1,27 +0,0 @@
|
|||||||
/* © SRSoftware 2024 */
|
|
||||||
package de.srsoftware.oidc.datastore.sqlite;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import org.sqlite.SQLiteDataSource;
|
|
||||||
|
|
||||||
public class ConnectionProvider extends HashMap<File, Connection> {
|
|
||||||
public Connection get(Object o) {
|
|
||||||
if (o instanceof File dbFile) try {
|
|
||||||
var conn = super.get(dbFile);
|
|
||||||
if (conn == null) put(dbFile, conn = open(dbFile));
|
|
||||||
return conn;
|
|
||||||
} catch (SQLException sqle) {
|
|
||||||
throw new RuntimeException(sqle);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Connection open(File dbFile) throws SQLException {
|
|
||||||
SQLiteDataSource dataSource = new SQLiteDataSource();
|
|
||||||
dataSource.setUrl("jdbc:sqlite:%s".formatted(dbFile));
|
|
||||||
return dataSource.getConnection();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-170
@@ -1,170 +0,0 @@
|
|||||||
/* © SRSoftware 2024 */
|
|
||||||
package de.srsoftware.oidc.datastore.sqlite;
|
|
||||||
|
|
||||||
import static de.srsoftware.utils.Optionals.nullable;
|
|
||||||
import static de.srsoftware.utils.Strings.uuid;
|
|
||||||
|
|
||||||
import de.srsoftware.oidc.api.AuthorizationService;
|
|
||||||
import de.srsoftware.oidc.api.data.AuthResult;
|
|
||||||
import de.srsoftware.oidc.api.data.Authorization;
|
|
||||||
import de.srsoftware.oidc.api.data.AuthorizedScopes;
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.time.temporal.ChronoUnit;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class SqliteAuthService extends SqliteStore implements AuthorizationService {
|
|
||||||
private static final String STORE_VERSION = "auth_store_version";
|
|
||||||
private static final String CREATE_STORE_VERSION = "INSERT INTO metainfo (key,value) VALUES ('" + STORE_VERSION + "','0')";
|
|
||||||
private static final String SELECT_STORE_VERSION = "SELECT * FROM metainfo WHERE key = '" + STORE_VERSION + "'";
|
|
||||||
private static final String SET_STORE_VERSION = "UPDATE metainfo SET value = ? WHERE key = '" + STORE_VERSION + "'";
|
|
||||||
|
|
||||||
private static final String CREATE_AUTHSTORE_TABLE = "CREATE TABLE IF NOT EXISTS authorizations(userId VARCHAR(255), clientId VARCHAR(255), scope VARCHAR(255), expiration LONG, PRIMARY KEY(userId, clientId, scope));";
|
|
||||||
private static final String SAVE_AUTHORIZATION = "INSERT INTO authorizations(userId, clientId, scope, expiration) VALUES (?,?,?,?) ON CONFLICT DO UPDATE SET expiration = ?";
|
|
||||||
private static final String SELECT_AUTH = "SELECT * FROM authorizations WHERE userId = ? AND clientId = ? AND scope IN";
|
|
||||||
private static final String SELECT_USER_CLIENTS = "SELECT DISTINCT clientId FROM authorizations WHERE userId = ?";
|
|
||||||
private Map<String, Authorization> authCodes = new HashMap<>();
|
|
||||||
|
|
||||||
private Map<String, String> nonceMap = new HashMap<>();
|
|
||||||
|
|
||||||
public SqliteAuthService(Connection connection) throws SQLException {
|
|
||||||
super(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createStoreTables() throws SQLException {
|
|
||||||
conn.prepareStatement(CREATE_AUTHSTORE_TABLE).execute();
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
protected void initTables() throws SQLException {
|
|
||||||
var rs = conn.prepareStatement(SELECT_STORE_VERSION).executeQuery();
|
|
||||||
int availableVersion = 1;
|
|
||||||
int currentVersion;
|
|
||||||
if (rs.next()) {
|
|
||||||
currentVersion = rs.getInt("value");
|
|
||||||
rs.close();
|
|
||||||
} else {
|
|
||||||
rs.close();
|
|
||||||
conn.prepareStatement(CREATE_STORE_VERSION).execute();
|
|
||||||
currentVersion = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
conn.setAutoCommit(false);
|
|
||||||
var stmt = conn.prepareStatement(SET_STORE_VERSION);
|
|
||||||
while (currentVersion < availableVersion) {
|
|
||||||
try {
|
|
||||||
switch (currentVersion) {
|
|
||||||
case 0:
|
|
||||||
createStoreTables();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
stmt.setInt(1, ++currentVersion);
|
|
||||||
stmt.execute();
|
|
||||||
conn.commit();
|
|
||||||
} catch (Exception e) {
|
|
||||||
conn.rollback();
|
|
||||||
LOG.log(System.Logger.Level.ERROR, "Failed to update at {} = {}", STORE_VERSION, currentVersion);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
conn.setAutoCommit(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String authCode(Authorization authorization) {
|
|
||||||
var code = uuid();
|
|
||||||
authCodes.put(code, authorization);
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AuthorizationService authorize(String userId, String clientId, Collection<String> scopes, Instant expiration) {
|
|
||||||
try {
|
|
||||||
conn.setAutoCommit(false);
|
|
||||||
var stmt = conn.prepareStatement(SAVE_AUTHORIZATION);
|
|
||||||
stmt.setString(1, userId);
|
|
||||||
stmt.setString(2, clientId);
|
|
||||||
stmt.setLong(4, expiration.getEpochSecond());
|
|
||||||
stmt.setLong(5, expiration.getEpochSecond());
|
|
||||||
for (var scope : scopes) {
|
|
||||||
stmt.setString(3, scope);
|
|
||||||
stmt.execute();
|
|
||||||
}
|
|
||||||
conn.commit();
|
|
||||||
conn.setAutoCommit(true);
|
|
||||||
return this;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> authorizedClients(String userId) {
|
|
||||||
try {
|
|
||||||
var stmt = conn.prepareStatement(SELECT_USER_CLIENTS);
|
|
||||||
stmt.setString(1, userId);
|
|
||||||
var rs = stmt.executeQuery();
|
|
||||||
var result = new ArrayList<String>();
|
|
||||||
while (rs.next()) result.add(rs.getString(1));
|
|
||||||
rs.close();
|
|
||||||
return result;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<Authorization> consumeAuthorization(String authCode) {
|
|
||||||
return nullable(authCodes.remove(authCode));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<String> consumeNonce(String userId, String clientId) {
|
|
||||||
var nonceKey = String.join("@", userId, clientId);
|
|
||||||
return nullable(nonceMap.get(nonceKey));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AuthResult getAuthorization(String userId, String clientId, Collection<String> scopes) {
|
|
||||||
try {
|
|
||||||
var scopeList = "(" + scopes.stream().map(s -> "?").collect(Collectors.joining(", ")) + ")";
|
|
||||||
var sql = SELECT_AUTH + scopeList;
|
|
||||||
var stmt = conn.prepareStatement(sql);
|
|
||||||
stmt.setString(1, userId);
|
|
||||||
stmt.setString(2, clientId);
|
|
||||||
int i = 3;
|
|
||||||
for (var scope : scopes) stmt.setString(i++, scope);
|
|
||||||
var rs = stmt.executeQuery();
|
|
||||||
var unauthorized = new HashSet<String>(scopes);
|
|
||||||
var authorized = new HashSet<String>();
|
|
||||||
var now = Instant.now();
|
|
||||||
Instant earliestExp = null;
|
|
||||||
while (rs.next()) {
|
|
||||||
long expiration = rs.getLong("expiration");
|
|
||||||
String scope = rs.getString("scope");
|
|
||||||
Instant ex = Instant.ofEpochSecond(expiration).truncatedTo(ChronoUnit.SECONDS);
|
|
||||||
if (ex.isAfter(now)) {
|
|
||||||
unauthorized.remove(scope);
|
|
||||||
authorized.add(scope);
|
|
||||||
if (earliestExp == null || ex.isBefore(earliestExp)) earliestExp = ex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rs.close();
|
|
||||||
if (authorized.isEmpty()) return new AuthResult(null, unauthorized, null);
|
|
||||||
var authorizedScopes = new AuthorizedScopes(authorized, earliestExp);
|
|
||||||
var authorization = new Authorization(clientId, userId, authorizedScopes);
|
|
||||||
return new AuthResult(authorizedScopes, unauthorized, authCode(authorization));
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nonce(String userId, String clientId, String nonce) {
|
|
||||||
var nonceKey = String.join("@", userId, clientId);
|
|
||||||
if (nonce != null) {
|
|
||||||
nonceMap.put(nonceKey, nonce);
|
|
||||||
} else
|
|
||||||
nonceMap.remove(nonceKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-176
@@ -1,176 +0,0 @@
|
|||||||
/* © SRSoftware 2024 */
|
|
||||||
package de.srsoftware.oidc.datastore.sqlite;
|
|
||||||
|
|
||||||
import static de.srsoftware.oidc.api.Constants.*;
|
|
||||||
|
|
||||||
import de.srsoftware.oidc.api.ClientService;
|
|
||||||
import de.srsoftware.oidc.api.data.Client;
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class SqliteClientService extends SqliteStore implements ClientService {
|
|
||||||
private static final String STORE_VERSION = "client_store_version";
|
|
||||||
private static final String CREATE_STORE_VERSION = "INSERT INTO metainfo (key,value) VALUES ('" + STORE_VERSION + "','0')";
|
|
||||||
private static final String SELECT_STORE_VERSION = "SELECT * FROM metainfo WHERE key = '" + STORE_VERSION + "'";
|
|
||||||
private static final String SET_STORE_VERSION = "UPDATE metainfo SET value = ? WHERE key = '" + STORE_VERSION + "'";
|
|
||||||
|
|
||||||
private static final String CREATE_CLIENT_TABLE = "CREATE TABLE IF NOT EXISTS clients(id VARCHAR(255) NOT NULL PRIMARY KEY, name VARCHAR(255), secret VARCHAR(255), landing_page VARCHAR(255));";
|
|
||||||
private static final String CREATE_REDIRECT_TABLE = "CREATE TABLE IF NOT EXISTS client_redirects(clientId VARCHAR(255), uri VARCHAR(255), PRIMARY KEY(clientId, uri));";
|
|
||||||
private static final String SAVE_CLIENT = "INSERT INTO clients (id, name, secret, landing_page) VALUES (?,?,?,?) ON CONFLICT DO UPDATE SET name = ?, secret = ?, landing_page = ?;";
|
|
||||||
private static final String SAVE_REDIRECT = "INSERT OR IGNORE INTO client_redirects(clientId, uri) VALUES (?, ?)";
|
|
||||||
private static final String DROP_OTHER_REDIRECTS = "DELETE FROM client_redirects WHERE clientId = ? AND uri NOT IN";
|
|
||||||
private static final String SELECT_CLIENT = "SELECT * FROM clients WHERE id = ?";
|
|
||||||
private static final String SELECT_CLIENT_REDIRECTS = "SELECT uri FROM client_redirects WHERE clientId = ?";
|
|
||||||
private static final String LIST_CLIENT_REDIRECTS = "SELECT * FROM client_redirects";
|
|
||||||
private static final String LIST_CLIENTS = "SELECT * FROM clients";
|
|
||||||
private static final String DELETE_CLIENT = "DELETE FROM clients WHERE id = ?";
|
|
||||||
private static final String DELETE_CLIENT_REDIRECTS = "DELETE FROM client_redirects WHERE clientId = ?";
|
|
||||||
|
|
||||||
public SqliteClientService(Connection connection) throws SQLException {
|
|
||||||
super(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createStoreTables() throws SQLException {
|
|
||||||
conn.prepareStatement(CREATE_CLIENT_TABLE).execute();
|
|
||||||
conn.prepareStatement(CREATE_REDIRECT_TABLE).execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void initTables() throws SQLException {
|
|
||||||
var rs = conn.prepareStatement(SELECT_STORE_VERSION).executeQuery();
|
|
||||||
int availableVersion = 1;
|
|
||||||
int currentVersion;
|
|
||||||
if (rs.next()) {
|
|
||||||
currentVersion = rs.getInt("value");
|
|
||||||
rs.close();
|
|
||||||
} else {
|
|
||||||
rs.close();
|
|
||||||
conn.prepareStatement(CREATE_STORE_VERSION).execute();
|
|
||||||
currentVersion = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
conn.setAutoCommit(false);
|
|
||||||
var stmt = conn.prepareStatement(SET_STORE_VERSION);
|
|
||||||
while (currentVersion < availableVersion) {
|
|
||||||
try {
|
|
||||||
switch (currentVersion) {
|
|
||||||
case 0:
|
|
||||||
createStoreTables();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
stmt.setInt(1, ++currentVersion);
|
|
||||||
stmt.execute();
|
|
||||||
conn.commit();
|
|
||||||
} catch (Exception e) {
|
|
||||||
conn.rollback();
|
|
||||||
LOG.log(System.Logger.Level.ERROR, "Failed to update at {} = {}", STORE_VERSION, currentVersion);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
conn.setAutoCommit(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<Client> getClient(String clientId) {
|
|
||||||
Optional<Client> result = Optional.empty();
|
|
||||||
try {
|
|
||||||
var stmt = conn.prepareStatement(SELECT_CLIENT_REDIRECTS);
|
|
||||||
stmt.setString(1, clientId);
|
|
||||||
var rs = stmt.executeQuery();
|
|
||||||
var uris = new HashSet<String>();
|
|
||||||
while (rs.next()) uris.add(rs.getString("uri"));
|
|
||||||
rs.close();
|
|
||||||
stmt = conn.prepareStatement(SELECT_CLIENT);
|
|
||||||
stmt.setString(1, clientId);
|
|
||||||
rs = stmt.executeQuery();
|
|
||||||
if (rs.next()) {
|
|
||||||
var name = rs.getString(NAME);
|
|
||||||
var secret = rs.getString(SECRET);
|
|
||||||
var landing = rs.getString(LANDING_PAGE);
|
|
||||||
result = Optional.of(new Client(clientId, name, secret, uris).landingPage(landing));
|
|
||||||
}
|
|
||||||
rs.close();
|
|
||||||
return result;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Client> listClients() {
|
|
||||||
try {
|
|
||||||
var stmt = conn.prepareStatement(LIST_CLIENT_REDIRECTS);
|
|
||||||
var rs = stmt.executeQuery();
|
|
||||||
var redirects = new HashMap<String, Set<String>>();
|
|
||||||
while (rs.next()) {
|
|
||||||
var clientId = rs.getString("clientId");
|
|
||||||
var uri = rs.getString("uri");
|
|
||||||
var set = redirects.computeIfAbsent(clientId, k -> new HashSet<>());
|
|
||||||
set.add(uri);
|
|
||||||
}
|
|
||||||
rs.close();
|
|
||||||
stmt = conn.prepareStatement(LIST_CLIENTS);
|
|
||||||
rs = stmt.executeQuery();
|
|
||||||
var result = new ArrayList<Client>();
|
|
||||||
while (rs.next()) {
|
|
||||||
var id = rs.getString("id");
|
|
||||||
var name = rs.getString(NAME);
|
|
||||||
var secret = rs.getString(SECRET);
|
|
||||||
var landing = rs.getString(LANDING_PAGE);
|
|
||||||
result.add(new Client(id, name, secret, redirects.get(id)).landingPage(landing));
|
|
||||||
}
|
|
||||||
rs.close();
|
|
||||||
return result;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClientService remove(String clientId) {
|
|
||||||
try {
|
|
||||||
var stmt = conn.prepareStatement(DELETE_CLIENT);
|
|
||||||
stmt.setString(1, clientId);
|
|
||||||
stmt.execute();
|
|
||||||
stmt = conn.prepareStatement(DELETE_CLIENT_REDIRECTS);
|
|
||||||
stmt.setString(1, clientId);
|
|
||||||
stmt.execute();
|
|
||||||
return this;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClientService save(Client client) {
|
|
||||||
try {
|
|
||||||
var stmt = conn.prepareStatement(SAVE_CLIENT);
|
|
||||||
stmt.setString(1, client.id());
|
|
||||||
stmt.setString(2, client.name());
|
|
||||||
stmt.setString(3, client.secret());
|
|
||||||
stmt.setString(4, client.landingPage());
|
|
||||||
stmt.setString(5, client.name());
|
|
||||||
stmt.setString(6, client.secret());
|
|
||||||
stmt.setString(7, client.landingPage());
|
|
||||||
stmt.execute();
|
|
||||||
stmt = conn.prepareStatement(SAVE_REDIRECT);
|
|
||||||
stmt.setString(1, client.id());
|
|
||||||
for (var redirect : client.redirectUris()) {
|
|
||||||
stmt.setString(2, redirect);
|
|
||||||
stmt.execute();
|
|
||||||
}
|
|
||||||
var where = "(" + client.redirectUris().stream().map(u -> "?").collect(Collectors.joining(", ")) + ")";
|
|
||||||
var sql = DROP_OTHER_REDIRECTS + where;
|
|
||||||
stmt = conn.prepareStatement(sql);
|
|
||||||
stmt.setString(1, client.id());
|
|
||||||
int i = 2;
|
|
||||||
for (var redirect : client.redirectUris()) stmt.setString(i++, redirect);
|
|
||||||
stmt.execute();
|
|
||||||
return this;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-125
@@ -1,125 +0,0 @@
|
|||||||
/* © SRSoftware 2024 */
|
|
||||||
package de.srsoftware.oidc.datastore.sqlite;
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
public class SqliteKeyStore extends SqliteStore implements KeyStorage {
|
|
||||||
private static final String STORE_VERSION = "key_store_version";
|
|
||||||
private static final String CREATE_STORE_VERSION = "INSERT INTO metainfo (key,value) VALUES ('" + STORE_VERSION + "','0')";
|
|
||||||
private static final String SELECT_STORE_VERSION = "SELECT * FROM metainfo WHERE key = '" + STORE_VERSION + "'";
|
|
||||||
private static final String SET_STORE_VERSION = "UPDATE metainfo SET value = ? WHERE 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 = ?";
|
|
||||||
|
|
||||||
private HashMap<String, PublicJsonWebKey> loaded = new HashMap<>();
|
|
||||||
|
|
||||||
public SqliteKeyStore(Connection connection) throws SQLException {
|
|
||||||
super(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createStoreTables() 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
|
|
||||||
protected void initTables() throws SQLException {
|
|
||||||
var rs = conn.prepareStatement(SELECT_STORE_VERSION).executeQuery();
|
|
||||||
int availableVersion = 1;
|
|
||||||
int currentVersion;
|
|
||||||
if (rs.next()) {
|
|
||||||
currentVersion = rs.getInt("value");
|
|
||||||
rs.close();
|
|
||||||
} else {
|
|
||||||
rs.close();
|
|
||||||
conn.prepareStatement(CREATE_STORE_VERSION).execute();
|
|
||||||
currentVersion = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
conn.setAutoCommit(false);
|
|
||||||
var stmt = conn.prepareStatement(SET_STORE_VERSION);
|
|
||||||
while (currentVersion < availableVersion) {
|
|
||||||
try {
|
|
||||||
switch (currentVersion) {
|
|
||||||
case 0:
|
|
||||||
createStoreTables();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
stmt.setInt(1, ++currentVersion);
|
|
||||||
stmt.execute();
|
|
||||||
conn.commit();
|
|
||||||
} catch (Exception e) {
|
|
||||||
conn.rollback();
|
|
||||||
LOG.log(System.Logger.Level.ERROR, "Failed to update at {} = {}", STORE_VERSION, currentVersion);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
conn.setAutoCommit(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@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 String loadJson(String keyId) throws IOException {
|
|
||||||
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 json;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public KeyStorage store(String keyId, String json) throws IOException {
|
|
||||||
try {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-183
@@ -1,183 +0,0 @@
|
|||||||
/* © SRSoftware 2024 */
|
|
||||||
package de.srsoftware.oidc.datastore.sqlite;
|
|
||||||
|
|
||||||
import static de.srsoftware.oidc.api.Constants.*;
|
|
||||||
|
|
||||||
import de.srsoftware.oidc.api.MailConfig;
|
|
||||||
import jakarta.mail.Authenticator;
|
|
||||||
import jakarta.mail.PasswordAuthentication;
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
public class SqliteMailConfig extends SqliteStore implements MailConfig {
|
|
||||||
private static final String STORE_VERSION = "mail_config_store_version";
|
|
||||||
private static final String CREATE_STORE_VERSION = "INSERT INTO metainfo (key,value) VALUES ('" + STORE_VERSION + "','0')";
|
|
||||||
private static final String SELECT_STORE_VERSION = "SELECT * FROM metainfo WHERE key = '" + STORE_VERSION + "'";
|
|
||||||
private static final String SET_STORE_VERSION = "UPDATE metainfo SET value = ? WHERE key = '" + STORE_VERSION + "'";
|
|
||||||
private static final String CREATE_MAIL_CONFIG_TABLE = "CREATE TABLE mail_config (key VARCHAR(64) PRIMARY KEY, value VARCHAR(255));";
|
|
||||||
private static final String SAVE_MAILCONFIG = "INSERT INTO mail_config (key, value) VALUES (?, ?) ON CONFLICT DO UPDATE SET value = ?";
|
|
||||||
private static final String SELECT_MAILCONFIG = "SELECT * FROM mail_config";
|
|
||||||
private String smtpHost, senderAddress, password;
|
|
||||||
|
|
||||||
private int smtpPort;
|
|
||||||
private boolean smtpAuth, startTls;
|
|
||||||
private Authenticator auth;
|
|
||||||
|
|
||||||
public SqliteMailConfig(Connection connection) throws SQLException {
|
|
||||||
super(connection);
|
|
||||||
smtpHost = "";
|
|
||||||
smtpPort = 0;
|
|
||||||
senderAddress = "";
|
|
||||||
password = "";
|
|
||||||
smtpAuth = true;
|
|
||||||
startTls = true;
|
|
||||||
var rs = conn.prepareStatement(SELECT_MAILCONFIG).executeQuery();
|
|
||||||
while (rs.next()) {
|
|
||||||
var key = rs.getString(1);
|
|
||||||
switch (key) {
|
|
||||||
case SMTP_PORT -> smtpPort = rs.getInt(2);
|
|
||||||
case SMTP_HOST -> smtpHost = rs.getString(2);
|
|
||||||
case START_TLS -> startTls = rs.getBoolean(2);
|
|
||||||
case SMTP_AUTH -> smtpAuth = rs.getBoolean(2);
|
|
||||||
case SMTP_PASSWORD -> password = rs.getString(2);
|
|
||||||
case SMTP_USER -> senderAddress = rs.getString(2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createStoreTables() throws SQLException {
|
|
||||||
conn.prepareStatement(CREATE_MAIL_CONFIG_TABLE).execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void initTables() throws SQLException {
|
|
||||||
var rs = conn.prepareStatement(SELECT_STORE_VERSION).executeQuery();
|
|
||||||
int availableVersion = 1;
|
|
||||||
int currentVersion;
|
|
||||||
if (rs.next()) {
|
|
||||||
currentVersion = rs.getInt("value");
|
|
||||||
rs.close();
|
|
||||||
} else {
|
|
||||||
rs.close();
|
|
||||||
conn.prepareStatement(CREATE_STORE_VERSION).execute();
|
|
||||||
currentVersion = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
conn.setAutoCommit(false);
|
|
||||||
var stmt = conn.prepareStatement(SET_STORE_VERSION);
|
|
||||||
while (currentVersion < availableVersion) {
|
|
||||||
try {
|
|
||||||
switch (currentVersion) {
|
|
||||||
case 0:
|
|
||||||
createStoreTables();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
stmt.setInt(1, ++currentVersion);
|
|
||||||
stmt.execute();
|
|
||||||
conn.commit();
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
conn.rollback();
|
|
||||||
LOG.log(System.Logger.Level.ERROR, "Failed to update at {} = {}", STORE_VERSION, currentVersion);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
conn.setAutoCommit(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String smtpHost() {
|
|
||||||
return smtpHost;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MailConfig smtpHost(String newValue) {
|
|
||||||
smtpHost = newValue;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int smtpPort() {
|
|
||||||
return smtpPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MailConfig smtpPort(int newValue) {
|
|
||||||
smtpPort = newValue;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String senderAddress() {
|
|
||||||
return senderAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MailConfig senderAddress(String newValue) {
|
|
||||||
senderAddress = newValue;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String senderPassword() {
|
|
||||||
return password;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MailConfig senderPassword(String newValue) {
|
|
||||||
password = newValue;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean startTls() {
|
|
||||||
return startTls;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean smtpAuth() {
|
|
||||||
return smtpAuth;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MailConfig startTls(boolean newValue) {
|
|
||||||
startTls = newValue;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MailConfig smtpAuth(boolean newValue) {
|
|
||||||
smtpAuth = newValue;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Authenticator authenticator() {
|
|
||||||
if (auth == null) {
|
|
||||||
auth = new Authenticator() {
|
|
||||||
// override the getPasswordAuthentication method
|
|
||||||
protected PasswordAuthentication getPasswordAuthentication() {
|
|
||||||
return new PasswordAuthentication(senderAddress(), senderPassword());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MailConfig save() {
|
|
||||||
try {
|
|
||||||
var stmt = conn.prepareStatement(SAVE_MAILCONFIG);
|
|
||||||
for (var entry : map().entrySet()) {
|
|
||||||
stmt.setString(1, entry.getKey());
|
|
||||||
stmt.setObject(2, entry.getValue());
|
|
||||||
stmt.setObject(3, entry.getValue());
|
|
||||||
stmt.execute();
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-130
@@ -1,130 +0,0 @@
|
|||||||
/* © SRSoftware 2024 */
|
|
||||||
package de.srsoftware.oidc.datastore.sqlite;
|
|
||||||
|
|
||||||
import static de.srsoftware.utils.Strings.uuid;
|
|
||||||
import static java.time.temporal.ChronoUnit.SECONDS;
|
|
||||||
|
|
||||||
import de.srsoftware.oidc.api.SessionService;
|
|
||||||
import de.srsoftware.oidc.api.data.Session;
|
|
||||||
import de.srsoftware.oidc.api.data.User;
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
public class SqliteSessionService extends SqliteStore implements SessionService {
|
|
||||||
private static final String STORE_VERSION = "session_store_version";
|
|
||||||
private static final String CREATE_STORE_VERSION = "INSERT INTO metainfo (key,value) VALUES ('" + STORE_VERSION + "','0')";
|
|
||||||
private static final String SELECT_STORE_VERSION = "SELECT * FROM metainfo WHERE key = '" + STORE_VERSION + "'";
|
|
||||||
private static final String SET_STORE_VERSION = "UPDATE metainfo SET value = ? WHERE key = '" + STORE_VERSION + "'";
|
|
||||||
|
|
||||||
private static final String CREATE_SESSION_TABLE = "CREATE TABLE sessions (id VARCHAR(64) PRIMARY KEY, userId VARCHAR(64) NOT NULL, expiration LONG NOT NULL, trust_browser BOOLEAN DEFAULT false)";
|
|
||||||
private static final String SAVE_SESSION = "INSERT INTO sessions (id, userId, expiration, trust_browser) VALUES (?,?,?, ?) ON CONFLICT DO UPDATE SET expiration = ?, trust_browser = ?;";
|
|
||||||
private static final String DROP_SESSION = "DELETE FROM sessions WHERE id = ?";
|
|
||||||
private static final String SELECT_SESSION = "SELECT * FROM sessions WHERE id = ?";
|
|
||||||
|
|
||||||
public SqliteSessionService(Connection connection) throws SQLException {
|
|
||||||
super(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Session createSession(User user, boolean trustBrowser) {
|
|
||||||
var now = Instant.now();
|
|
||||||
var endOfSession = now.plus(user.sessionDuration()).truncatedTo(SECONDS);
|
|
||||||
return save(new Session(user.uuid(), endOfSession, uuid(), trustBrowser));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createStoreTables() throws SQLException {
|
|
||||||
conn.prepareStatement(CREATE_SESSION_TABLE).execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SessionService dropSession(String sessionId) {
|
|
||||||
try {
|
|
||||||
var stmt = conn.prepareStatement(DROP_SESSION);
|
|
||||||
stmt.setString(1, sessionId);
|
|
||||||
stmt.execute();
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Session extend(Session session, User user) {
|
|
||||||
var endOfSession = Instant.now().plus(user.sessionDuration());
|
|
||||||
return save(new Session(user.uuid(), endOfSession, session.id(), session.trustBrowser()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void initTables() throws SQLException {
|
|
||||||
var rs = conn.prepareStatement(SELECT_STORE_VERSION).executeQuery();
|
|
||||||
int availableVersion = 1;
|
|
||||||
int currentVersion;
|
|
||||||
if (rs.next()) {
|
|
||||||
currentVersion = rs.getInt("value");
|
|
||||||
rs.close();
|
|
||||||
} else {
|
|
||||||
rs.close();
|
|
||||||
conn.prepareStatement(CREATE_STORE_VERSION).execute();
|
|
||||||
currentVersion = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
conn.setAutoCommit(false);
|
|
||||||
var stmt = conn.prepareStatement(SET_STORE_VERSION);
|
|
||||||
while (currentVersion < availableVersion) {
|
|
||||||
try {
|
|
||||||
switch (currentVersion) {
|
|
||||||
case 0:
|
|
||||||
createStoreTables();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
stmt.setInt(1, ++currentVersion);
|
|
||||||
stmt.execute();
|
|
||||||
conn.commit();
|
|
||||||
} catch (Exception e) {
|
|
||||||
conn.rollback();
|
|
||||||
LOG.log(System.Logger.Level.ERROR, "Failed to update at {} = {}", STORE_VERSION, currentVersion);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
conn.setAutoCommit(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<Session> retrieve(String sessionId) {
|
|
||||||
try {
|
|
||||||
var stmt = conn.prepareStatement(SELECT_SESSION);
|
|
||||||
stmt.setString(1, sessionId);
|
|
||||||
var rs = stmt.executeQuery();
|
|
||||||
Optional<Session> result = Optional.empty();
|
|
||||||
if (rs.next()) {
|
|
||||||
var userID = rs.getString("userId");
|
|
||||||
var expiration = Instant.ofEpochSecond(rs.getLong("expiration"));
|
|
||||||
var trustBrowser = rs.getBoolean("trust_browser");
|
|
||||||
if (expiration.isAfter(Instant.now())) result = Optional.of(new Session(userID, expiration, sessionId, trustBrowser));
|
|
||||||
}
|
|
||||||
rs.close();
|
|
||||||
return result;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Session save(Session session) {
|
|
||||||
try {
|
|
||||||
var stmt = conn.prepareStatement(SAVE_SESSION);
|
|
||||||
var expiration = session.expiration().getEpochSecond();
|
|
||||||
stmt.setString(1, session.id());
|
|
||||||
stmt.setString(2, session.userId());
|
|
||||||
stmt.setLong(3, expiration);
|
|
||||||
stmt.setBoolean(4, session.trustBrowser());
|
|
||||||
stmt.setLong(5, expiration);
|
|
||||||
stmt.setBoolean(6, session.trustBrowser());
|
|
||||||
stmt.execute();
|
|
||||||
return session;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-20
@@ -1,20 +0,0 @@
|
|||||||
/* © SRSoftware 2024 */
|
|
||||||
package de.srsoftware.oidc.datastore.sqlite;
|
|
||||||
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
public abstract class SqliteStore {
|
|
||||||
public static System.Logger LOG = System.getLogger(SqliteStore.class.getSimpleName());
|
|
||||||
private static final String CREATE_MIGRATION_TABLE = "CREATE TABLE IF NOT EXISTS metainfo(key VARCHAR(255) PRIMARY KEY, value TEXT);";
|
|
||||||
|
|
||||||
protected final Connection conn;
|
|
||||||
|
|
||||||
public SqliteStore(Connection connection) throws SQLException {
|
|
||||||
conn = connection;
|
|
||||||
conn.prepareStatement(CREATE_MIGRATION_TABLE).execute();
|
|
||||||
initTables();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void initTables() throws SQLException;
|
|
||||||
}
|
|
||||||
-273
@@ -1,273 +0,0 @@
|
|||||||
/* © SRSoftware 2024 */
|
|
||||||
package de.srsoftware.oidc.datastore.sqlite;
|
|
||||||
|
|
||||||
import static de.srsoftware.utils.Optionals.nullable;
|
|
||||||
import static de.srsoftware.utils.Strings.uuid;
|
|
||||||
import static java.util.Optional.empty;
|
|
||||||
|
|
||||||
import de.srsoftware.oidc.api.UserService;
|
|
||||||
import de.srsoftware.oidc.api.data.AccessToken;
|
|
||||||
import de.srsoftware.oidc.api.data.Permission;
|
|
||||||
import de.srsoftware.oidc.api.data.User;
|
|
||||||
import de.srsoftware.utils.PasswordHasher;
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.time.temporal.ChronoUnit;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public class SqliteUserService extends SqliteStore implements UserService {
|
|
||||||
private static final String STORE_VERSION = "user_store_version";
|
|
||||||
private static final String CREATE_STORE_VERSION = "INSERT INTO metainfo (key,value) VALUES ('" + STORE_VERSION + "','0')";
|
|
||||||
private static final String SELECT_STORE_VERSION = "SELECT * FROM metainfo WHERE key = '" + STORE_VERSION + "'";
|
|
||||||
private static final String SET_STORE_VERSION = "UPDATE metainfo SET value = ? WHERE key = '" + STORE_VERSION + "'";
|
|
||||||
|
|
||||||
private static final String CREATE_USER_TABLE = "CREATE TABLE IF NOT EXISTS users(uuid VARCHAR(255) NOT NULL PRIMARY KEY, password VARCHAR(255), email VARCHAR(255), session_duration INT NOT NULL DEFAULT 10, username VARCHAR(255), realname VARCHAR(255));";
|
|
||||||
private static final String CREATE_USER_PERMISSION_TABLE = "CREATE TABLE IF NOT EXISTS user_permissions(uuid VARCHAR(255), permission VARCHAR(50), PRIMARY KEY(uuid,permission));";
|
|
||||||
private static final String COUNT_USERS = "SELECT count(*) FROM users";
|
|
||||||
private static final String LOAD_USER = "SELECT * FROM users WHERE uuid = ?";
|
|
||||||
private static final String LOAD_PERMISSIONS = "SELECT permission FROM user_permissions WHERE uuid = ?";
|
|
||||||
private static final String FIND_USER = "SELECT * FROM users WHERE uuid = ? OR username LIKE ? OR realname LIKE ? OR email = ? ORDER BY COALESCE(uuid, ?), username";
|
|
||||||
private static final String LIST_USERS = "SELECT * FROM users";
|
|
||||||
private static final String SAVE_USER = "INSERT INTO users (uuid,password,email,session_duration,username,realname) VALUES (?,?,?,?,?,?) ON CONFLICT DO UPDATE SET password = ?, email = ?, session_duration = ?, username = ?, realname = ?;";
|
|
||||||
private static final String INSERT_PERMISSIONS = "INSERT INTO user_permissions (uuid, permission) VALUES (?,?)";
|
|
||||||
private static final String DROP_PERMISSIONS = "DELETE FROM user_permissions WHERE uuid = ?";
|
|
||||||
private static final String DROP_USER = "DELETE FROM users WHERE uuid = ?";
|
|
||||||
private static final String UPDATE_PASSWORD = "UPDATE users SET password = ? WHERE uuid = ?";
|
|
||||||
private final PasswordHasher<String> hasher;
|
|
||||||
|
|
||||||
private Map<String, AccessToken> accessTokens = new HashMap<>();
|
|
||||||
|
|
||||||
|
|
||||||
public SqliteUserService(Connection connection, PasswordHasher<String> passHasher) throws SQLException {
|
|
||||||
super(connection);
|
|
||||||
hasher = passHasher;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AccessToken accessToken(User user) {
|
|
||||||
var token = new AccessToken(uuid(), Objects.requireNonNull(user), Instant.now().plus(1, ChronoUnit.HOURS));
|
|
||||||
accessTokens.put(token.id(), token);
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
private User addPermissions(User user) {
|
|
||||||
try {
|
|
||||||
var stmt = conn.prepareStatement(LOAD_PERMISSIONS);
|
|
||||||
stmt.setString(1, user.uuid());
|
|
||||||
var rs = stmt.executeQuery();
|
|
||||||
while (rs.next()) try {
|
|
||||||
user.add(Permission.valueOf(rs.getString("permission")));
|
|
||||||
} catch (IllegalArgumentException ignored) {
|
|
||||||
}
|
|
||||||
rs.close();
|
|
||||||
return user;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<User> consumeToken(String id) {
|
|
||||||
var user = forToken(id);
|
|
||||||
accessTokens.remove(id);
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createStoreTables() throws SQLException {
|
|
||||||
conn.prepareStatement(CREATE_USER_TABLE).execute();
|
|
||||||
conn.prepareStatement(CREATE_USER_PERMISSION_TABLE).execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UserService delete(User user) {
|
|
||||||
try {
|
|
||||||
conn.setAutoCommit(false);
|
|
||||||
dropPermissionsOf(user.uuid());
|
|
||||||
var stmt = conn.prepareStatement(DROP_USER);
|
|
||||||
stmt.setString(1, user.uuid());
|
|
||||||
stmt.execute();
|
|
||||||
conn.commit();
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void dropPermissionsOf(String uuid) throws SQLException {
|
|
||||||
var stmt = conn.prepareStatement(DROP_PERMISSIONS);
|
|
||||||
stmt.setString(1, uuid);
|
|
||||||
stmt.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<User> find(String idOrEmail) {
|
|
||||||
try {
|
|
||||||
var result = new HashSet<User>();
|
|
||||||
var stmt = conn.prepareStatement(FIND_USER); // TODO: implement test for this query
|
|
||||||
stmt.setString(1, idOrEmail);
|
|
||||||
stmt.setString(2, "%" + idOrEmail + "%");
|
|
||||||
stmt.setString(3, "%" + idOrEmail + "%");
|
|
||||||
stmt.setString(4, idOrEmail);
|
|
||||||
stmt.setString(5, idOrEmail);
|
|
||||||
var rs = stmt.executeQuery();
|
|
||||||
while (rs.next()) result.add(userFrom(rs));
|
|
||||||
rs.close();
|
|
||||||
return result;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<User> forToken(String id) {
|
|
||||||
AccessToken token = accessTokens.get(id);
|
|
||||||
if (token == null) return empty();
|
|
||||||
if (token.valid()) return Optional.of(token.user());
|
|
||||||
accessTokens.remove(id);
|
|
||||||
return empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UserService init(User defaultUser) {
|
|
||||||
try {
|
|
||||||
var rs = conn.prepareStatement(COUNT_USERS).executeQuery();
|
|
||||||
var count = rs.next() ? rs.getInt(1) : 0;
|
|
||||||
rs.close();
|
|
||||||
if (count < 1) save(defaultUser);
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void initTables() throws SQLException {
|
|
||||||
var rs = conn.prepareStatement(SELECT_STORE_VERSION).executeQuery();
|
|
||||||
int availableVersion = 1;
|
|
||||||
int currentVersion;
|
|
||||||
if (rs.next()) {
|
|
||||||
currentVersion = rs.getInt("value");
|
|
||||||
rs.close();
|
|
||||||
} else {
|
|
||||||
rs.close();
|
|
||||||
conn.prepareStatement(CREATE_STORE_VERSION).execute();
|
|
||||||
currentVersion = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
conn.setAutoCommit(false);
|
|
||||||
var stmt = conn.prepareStatement(SET_STORE_VERSION);
|
|
||||||
while (currentVersion < availableVersion) {
|
|
||||||
try {
|
|
||||||
switch (currentVersion) {
|
|
||||||
case 0:
|
|
||||||
createStoreTables();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
stmt.setInt(1, ++currentVersion);
|
|
||||||
stmt.execute();
|
|
||||||
conn.commit();
|
|
||||||
} catch (Exception e) {
|
|
||||||
conn.rollback();
|
|
||||||
LOG.log(System.Logger.Level.ERROR, "Failed to update at {} = {}", STORE_VERSION, currentVersion);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
conn.setAutoCommit(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<User> list() {
|
|
||||||
try {
|
|
||||||
List<User> result = new ArrayList<>();
|
|
||||||
var rs = conn.prepareStatement(LIST_USERS).executeQuery();
|
|
||||||
while (rs.next()) result.add(userFrom(rs));
|
|
||||||
rs.close();
|
|
||||||
for (User user : result) addPermissions(user);
|
|
||||||
return result;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<User> load(String id) {
|
|
||||||
try {
|
|
||||||
User user = null;
|
|
||||||
var stmt = conn.prepareStatement(LOAD_USER);
|
|
||||||
stmt.setString(1, id);
|
|
||||||
var rs = stmt.executeQuery();
|
|
||||||
if (rs.next()) user = userFrom(rs);
|
|
||||||
rs.close();
|
|
||||||
return nullable(user).map(this::addPermissions);
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<User> load(String username, String password) {
|
|
||||||
var candidates = find(username);
|
|
||||||
for (var user : candidates) {
|
|
||||||
if (passwordMatches(password, user)) return Optional.of(user);
|
|
||||||
}
|
|
||||||
return empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean passwordMatches(String password, User user) {
|
|
||||||
return hasher.matches(password, user.hashedPassword());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SqliteUserService save(User user) {
|
|
||||||
try {
|
|
||||||
conn.setAutoCommit(false);
|
|
||||||
var stmt = conn.prepareStatement(SAVE_USER);
|
|
||||||
stmt.setString(1, user.uuid());
|
|
||||||
stmt.setString(2, user.hashedPassword());
|
|
||||||
stmt.setString(3, user.email());
|
|
||||||
stmt.setLong(4, user.sessionDuration().toMinutes());
|
|
||||||
stmt.setString(5, user.username());
|
|
||||||
stmt.setString(6, user.realName());
|
|
||||||
stmt.setString(7, user.hashedPassword());
|
|
||||||
stmt.setString(8, user.email());
|
|
||||||
stmt.setLong(9, user.sessionDuration().toMinutes());
|
|
||||||
stmt.setString(10, user.username());
|
|
||||||
stmt.setString(11, user.realName());
|
|
||||||
stmt.execute();
|
|
||||||
dropPermissionsOf(user.uuid());
|
|
||||||
|
|
||||||
stmt = conn.prepareStatement(INSERT_PERMISSIONS);
|
|
||||||
stmt.setString(1, user.uuid());
|
|
||||||
for (Permission perm : Permission.values()) {
|
|
||||||
if (user.hasPermission(perm)) {
|
|
||||||
stmt.setString(2, perm.toString());
|
|
||||||
stmt.execute();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
conn.commit();
|
|
||||||
return this;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SqliteUserService updatePassword(User user, String plaintextPassword) {
|
|
||||||
return save(user.hashedPassword(hasher.hash(plaintextPassword, uuid())));
|
|
||||||
}
|
|
||||||
|
|
||||||
private User userFrom(ResultSet rs) throws SQLException {
|
|
||||||
var uuid = rs.getString("uuid");
|
|
||||||
var pass = rs.getString("password");
|
|
||||||
var user = rs.getString("username");
|
|
||||||
var name = rs.getString("realname");
|
|
||||||
var mail = rs.getString("email");
|
|
||||||
var mins = rs.getLong("session_duration");
|
|
||||||
return new User(user, pass, name, mail, uuid).sessionDuration(Duration.ofMinutes(mins));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-26
@@ -1,26 +0,0 @@
|
|||||||
/* © SRSoftware 2024 */
|
|
||||||
package de.srsoftware.oidc.datastore.sqlite;
|
|
||||||
|
|
||||||
import static de.srsoftware.utils.Strings.uuid;
|
|
||||||
|
|
||||||
import de.srsoftware.oidc.api.AuthServiceTest;
|
|
||||||
import de.srsoftware.oidc.api.AuthorizationService;
|
|
||||||
import java.io.File;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
|
|
||||||
public class SqliteAuthServiceTest extends AuthServiceTest {
|
|
||||||
private AuthorizationService authorizationService;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected AuthorizationService authorizationService() {
|
|
||||||
return authorizationService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
public void setup() throws SQLException {
|
|
||||||
var dbFile = new File("/tmp/" + uuid() + ".sqlite");
|
|
||||||
var conn = new ConnectionProvider().get(dbFile);
|
|
||||||
authorizationService = new SqliteAuthService(conn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-26
@@ -1,26 +0,0 @@
|
|||||||
/* © SRSoftware 2024 */
|
|
||||||
package de.srsoftware.oidc.datastore.sqlite;
|
|
||||||
|
|
||||||
import static de.srsoftware.utils.Strings.uuid;
|
|
||||||
|
|
||||||
import de.srsoftware.oidc.api.ClientService;
|
|
||||||
import de.srsoftware.oidc.api.ClientServiceTest;
|
|
||||||
import java.io.File;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
|
|
||||||
public class SqliteClientServiceTest extends ClientServiceTest {
|
|
||||||
private ClientService clientService;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ClientService clientService() {
|
|
||||||
return clientService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
public void setup() throws SQLException {
|
|
||||||
var dbFile = new File("/tmp/" + uuid() + ".sqlite");
|
|
||||||
var conn = new ConnectionProvider().get(dbFile);
|
|
||||||
clientService = new SqliteClientService(conn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-26
@@ -1,26 +0,0 @@
|
|||||||
/* © SRSoftware 2024 */
|
|
||||||
package de.srsoftware.oidc.datastore.sqlite;
|
|
||||||
|
|
||||||
import static de.srsoftware.utils.Strings.uuid;
|
|
||||||
|
|
||||||
import de.srsoftware.oidc.api.KeyStorage;
|
|
||||||
import de.srsoftware.oidc.api.KeyStoreTest;
|
|
||||||
import java.io.File;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
|
|
||||||
public class SqliteKeyStoreTest extends KeyStoreTest {
|
|
||||||
private KeyStorage keyStore;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected KeyStorage keyStore() {
|
|
||||||
return keyStore;
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
public void setup() throws SQLException {
|
|
||||||
var dbFile = new File("/tmp/" + uuid() + ".sqlite");
|
|
||||||
var conn = new ConnectionProvider().get(dbFile);
|
|
||||||
keyStore = new SqliteKeyStore(conn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-37
@@ -1,37 +0,0 @@
|
|||||||
/* © SRSoftware 2024 */
|
|
||||||
package de.srsoftware.oidc.datastore.sqlite;
|
|
||||||
|
|
||||||
import static de.srsoftware.utils.Strings.uuid;
|
|
||||||
|
|
||||||
import de.srsoftware.oidc.api.MailConfig;
|
|
||||||
import de.srsoftware.oidc.api.MailConfigTest;
|
|
||||||
import java.io.File;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
|
|
||||||
public class SqliteMailConfigTest extends MailConfigTest {
|
|
||||||
private SqliteMailConfig mailConfig;
|
|
||||||
private File dbFile;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected MailConfig mailConfig() {
|
|
||||||
return mailConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void reOpen() {
|
|
||||||
try {
|
|
||||||
var conn = new ConnectionProvider().get(dbFile);
|
|
||||||
mailConfig = new SqliteMailConfig(conn);
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
public void setup() throws SQLException {
|
|
||||||
dbFile = new File("/tmp/" + uuid() + ".sqlite");
|
|
||||||
var conn = new ConnectionProvider().get(dbFile);
|
|
||||||
mailConfig = new SqliteMailConfig(conn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-32
@@ -1,32 +0,0 @@
|
|||||||
/* © SRSoftware 2024 */
|
|
||||||
package de.srsoftware.oidc.datastore.sqlite;
|
|
||||||
|
|
||||||
|
|
||||||
import de.srsoftware.oidc.api.SessionService;
|
|
||||||
import de.srsoftware.oidc.api.SessionServiceTest;
|
|
||||||
import java.io.File;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.UUID;
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
|
|
||||||
public class SqliteSessionServiceTest extends SessionServiceTest {
|
|
||||||
private File storage = new File("/tmp/" + UUID.randomUUID());
|
|
||||||
private SessionService sessionService = null;
|
|
||||||
|
|
||||||
@AfterEach
|
|
||||||
public void tearDown() {
|
|
||||||
if (storage.exists()) storage.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
public void setup() throws SQLException {
|
|
||||||
tearDown();
|
|
||||||
sessionService = new SqliteSessionService(new ConnectionProvider().get(storage));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected SessionService sessionService() {
|
|
||||||
return sessionService;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-32
@@ -1,32 +0,0 @@
|
|||||||
/* © SRSoftware 2024 */
|
|
||||||
package de.srsoftware.oidc.datastore.sqlite;
|
|
||||||
|
|
||||||
import de.srsoftware.oidc.api.UserService;
|
|
||||||
import de.srsoftware.oidc.api.UserServiceTest;
|
|
||||||
import java.io.File;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.UUID;
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
|
|
||||||
public class SqliteUserServiceTest extends UserServiceTest {
|
|
||||||
private File storage = new File("/tmp/" + UUID.randomUUID());
|
|
||||||
private UserService userService;
|
|
||||||
|
|
||||||
|
|
||||||
@AfterEach
|
|
||||||
public void tearDown() {
|
|
||||||
if (storage.exists()) storage.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
public void setup() throws SQLException {
|
|
||||||
tearDown();
|
|
||||||
userService = new SqliteUserService(new ConnectionProvider().get(storage), hasher());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected UserService userService() {
|
|
||||||
return userService;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+1
-2
@@ -4,9 +4,8 @@ include 'de.srsoftware.logging'
|
|||||||
include 'de.srsoftware.oidc.api'
|
include 'de.srsoftware.oidc.api'
|
||||||
include 'de.srsoftware.oidc.app'
|
include 'de.srsoftware.oidc.app'
|
||||||
include 'de.srsoftware.oidc.backend'
|
include 'de.srsoftware.oidc.backend'
|
||||||
|
include 'de.srsoftware.oidc.datastore.encrypted'
|
||||||
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'
|
|
||||||
include 'de.srsoftware.oidc.datastore.encrypted'
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user