started to implement SqliteUserService
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
@@ -128,8 +128,9 @@ public final class User {
|
|||||||
return uuid;
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sessionDuration(Duration newVal) {
|
public User sessionDuration(Duration newVal) {
|
||||||
sessionDuration = newVal;
|
sessionDuration = newVal;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Duration sessionDuration() {
|
public Duration sessionDuration() {
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ public class Application {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UserService setupUserService(Configuration config, Path defaultFile, FileStoreProvider fileStoreProvider) {
|
private static UserService setupUserService(Configuration config, Path defaultFile, FileStoreProvider fileStoreProvider) throws SQLException {
|
||||||
var userStorageLocation = new File(config.getOrDefault("user_storage",defaultFile));
|
var userStorageLocation = new File(config.getOrDefault("user_storage",defaultFile));
|
||||||
return switch (extension(userStorageLocation).toLowerCase()){
|
return switch (extension(userStorageLocation).toLowerCase()){
|
||||||
case "db", "sqlite", "sqlite3" -> new SqliteUserService(connectionProvider.get(userStorageLocation));
|
case "db", "sqlite", "sqlite3" -> new SqliteUserService(connectionProvider.get(userStorageLocation));
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe
|
|||||||
private static final System.Logger LOG = System.getLogger(FileStore.class.getSimpleName());
|
private static final System.Logger LOG = System.getLogger(FileStore.class.getSimpleName());
|
||||||
private static final String AUTHORIZATIONS = "authorizations";
|
private static final String AUTHORIZATIONS = "authorizations";
|
||||||
private static final String CLIENTS = "clients";
|
private static final String CLIENTS = "clients";
|
||||||
private static final String CODES = "codes";
|
|
||||||
private static final String REDIRECT_URIS = "redirect_uris";
|
private static final String REDIRECT_URIS = "redirect_uris";
|
||||||
private static final String SESSIONS = "sessions";
|
private static final String SESSIONS = "sessions";
|
||||||
private static final String USERS = "users";
|
private static final String USERS = "users";
|
||||||
|
|||||||
@@ -15,8 +15,7 @@ import java.util.List;
|
|||||||
import org.jose4j.jwk.PublicJsonWebKey;
|
import org.jose4j.jwk.PublicJsonWebKey;
|
||||||
import org.jose4j.lang.JoseException;
|
import org.jose4j.lang.JoseException;
|
||||||
|
|
||||||
public class SqliteKeyStore implements KeyStorage {
|
public class SqliteKeyStore extends SqliteStore 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 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 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 CREATE_KEYSTORE_TABLE = "CREATE TABLE IF NOT EXISTS keystore(key_id VARCHAR(255) PRIMARY KEY, json TEXT NOT NULL);";
|
||||||
@@ -24,18 +23,15 @@ public class SqliteKeyStore implements KeyStorage {
|
|||||||
private static final String SELECT_KEY_IDS = "SELECT key_id FROM keystore";
|
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 LOAD_KEY = "SELECT json FROM keystore WHERE key_id = ?";
|
||||||
private static final String DROP_KEY = "DELETE 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 HashMap<String, PublicJsonWebKey> loaded = new HashMap<>();
|
||||||
private final Connection conn;
|
|
||||||
|
|
||||||
public SqliteKeyStore(Connection connection) throws SQLException {
|
public SqliteKeyStore(Connection connection) throws SQLException {
|
||||||
conn = connection;
|
super(connection);
|
||||||
initTables();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initTables() throws SQLException {
|
@Override
|
||||||
conn.prepareStatement(CREATE_MIGRATION_TABLE).execute();
|
protected void initTables() throws SQLException {
|
||||||
var rs = conn.prepareStatement(SELECT_KEYSTORE_VERSION).executeQuery();
|
var rs = conn.prepareStatement(SELECT_KEYSTORE_VERSION).executeQuery();
|
||||||
int availableVersion = 1;
|
int availableVersion = 1;
|
||||||
int lastVersion = 1;
|
int lastVersion = 1;
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/* © 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;
|
||||||
|
}
|
||||||
@@ -1,50 +1,190 @@
|
|||||||
/* © SRSoftware 2024 */
|
/* © SRSoftware 2024 */
|
||||||
package de.srsoftware.oidc.datastore.sqlite;
|
package de.srsoftware.oidc.datastore.sqlite;
|
||||||
|
|
||||||
|
import static de.srsoftware.utils.Strings.uuid;
|
||||||
|
import static java.util.Optional.empty;
|
||||||
|
|
||||||
import de.srsoftware.oidc.api.UserService;
|
import de.srsoftware.oidc.api.UserService;
|
||||||
import de.srsoftware.oidc.api.data.AccessToken;
|
import de.srsoftware.oidc.api.data.AccessToken;
|
||||||
|
import de.srsoftware.oidc.api.data.Permission;
|
||||||
import de.srsoftware.oidc.api.data.User;
|
import de.srsoftware.oidc.api.data.User;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.util.List;
|
import java.sql.ResultSet;
|
||||||
import java.util.Optional;
|
import java.sql.SQLException;
|
||||||
import java.util.Set;
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
public class SqliteUserService implements UserService {
|
public class SqliteUserService extends SqliteStore implements UserService {
|
||||||
private final Connection conn;
|
private static final String SELECT_USERSTORE_VERSION = "SELECT * FROM metainfo WHERE key = 'user_store_version'";
|
||||||
|
private static final String SET_USERSTORE_VERSION = "UPDATE metainfo SET value = ? WHERE key = 'user_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), sessionDuration 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 INSERT_USER = "INSERT INTO users (uuid,password,email,sessionDuration,username,realname) VALUES (?,?,?,?,?,?)";
|
||||||
|
private static final String DROP_PERMISSIONS = "DELETE FROM user_permissions WHERE uuid = ?";
|
||||||
|
private static final String INSERT_PERMISSIONS = "INSERT INTO user_permissions (uuid, permission) VALUES (?,?)";
|
||||||
|
private static final String DROP_USER = "DELETE FROM users WHERE uuid = ?";
|
||||||
|
private static final String LIST_USERS = "SELECT * FROM users";
|
||||||
|
private static final String LIST_USER_PERMISSIONS = "SELECT * FROM user_permissions WHERE uuid = ?";
|
||||||
|
|
||||||
public SqliteUserService(Connection connection) {
|
private Map<String, AccessToken> accessTokens = new HashMap<>();
|
||||||
conn = connection;
|
|
||||||
|
|
||||||
|
public SqliteUserService(Connection connection) throws SQLException {
|
||||||
|
super(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AccessToken accessToken(User user) {
|
public AccessToken accessToken(User user) {
|
||||||
return null;
|
var token = new AccessToken(uuid(), Objects.requireNonNull(user), Instant.now().plus(1, ChronoUnit.HOURS));
|
||||||
|
accessTokens.put(token.id(), token);
|
||||||
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<User> consumeToken(String accessToken) {
|
public Optional<User> consumeToken(String id) {
|
||||||
return Optional.empty();
|
var user = forToken(id);
|
||||||
|
accessTokens.remove(id);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dropPermissionsOf(String uuid) throws SQLException {
|
||||||
|
var stmt = conn.prepareStatement(DROP_PERMISSIONS);
|
||||||
|
stmt.setString(1, uuid);
|
||||||
|
stmt.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserService delete(User user) {
|
public UserService delete(User user) {
|
||||||
return null;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<User> forToken(String accessToken) {
|
public Optional<User> forToken(String id) {
|
||||||
return Optional.empty();
|
AccessToken token = accessTokens.get(id);
|
||||||
|
if (token == null) return empty();
|
||||||
|
if (token.valid()) return Optional.of(token.user());
|
||||||
|
accessTokens.remove(id);
|
||||||
|
return empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserService init(User defaultUser) {
|
public UserService init(User defaultUser) {
|
||||||
return null;
|
try {
|
||||||
|
var rs = conn.prepareStatement(COUNT_USERS).executeQuery();
|
||||||
|
var count = rs.next() ? rs.getInt(1) : 0;
|
||||||
|
if (count < 1) {
|
||||||
|
var stmt = conn.prepareStatement(INSERT_USER);
|
||||||
|
stmt.setString(1, defaultUser.uuid());
|
||||||
|
stmt.setString(2, defaultUser.hashedPassword());
|
||||||
|
stmt.setString(3, defaultUser.email());
|
||||||
|
stmt.setLong(4, defaultUser.sessionDuration().toMinutes());
|
||||||
|
stmt.setString(5, defaultUser.username());
|
||||||
|
stmt.setString(6, defaultUser.realName());
|
||||||
|
stmt.execute();
|
||||||
|
}
|
||||||
|
rs.close();
|
||||||
|
conn.setAutoCommit(false);
|
||||||
|
dropPermissionsOf(defaultUser.uuid());
|
||||||
|
|
||||||
|
var stmt = conn.prepareStatement(INSERT_PERMISSIONS);
|
||||||
|
stmt.setString(1, defaultUser.uuid());
|
||||||
|
for (Permission perm : Permission.values()) {
|
||||||
|
if (defaultUser.hasPermission(perm)) {
|
||||||
|
stmt.setString(2, perm.toString());
|
||||||
|
stmt.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn.commit();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initTables() throws SQLException {
|
||||||
|
var rs = conn.prepareStatement(SELECT_USERSTORE_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_USERSTORE_VERSION);
|
||||||
|
for (int version = lastVersion; version <= availableVersion; version++) {
|
||||||
|
try {
|
||||||
|
switch (version) {
|
||||||
|
case 1:
|
||||||
|
createUserStoreTables();
|
||||||
|
}
|
||||||
|
stmt.setInt(1, version);
|
||||||
|
stmt.execute();
|
||||||
|
conn.commit();
|
||||||
|
} catch (Exception e) {
|
||||||
|
conn.rollback();
|
||||||
|
LOG.log(System.Logger.Level.ERROR, "Failed to update at user store version = {0}", version);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn.setAutoCommit(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createUserStoreTables() throws SQLException {
|
||||||
|
conn.prepareStatement(CREATE_USER_TABLE).execute();
|
||||||
|
conn.prepareStatement(CREATE_USER_PERMISSION_TABLE).execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<User> list() {
|
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) listPermissions(user.uuid()).forEach(user::add);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
return List.of();
|
return List.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Permission> listPermissions(String uuid) throws SQLException {
|
||||||
|
var perms = new ArrayList<Permission>();
|
||||||
|
var stmt = conn.prepareStatement(LIST_USER_PERMISSIONS);
|
||||||
|
stmt.setString(1, uuid);
|
||||||
|
var rs = stmt.executeQuery();
|
||||||
|
while (rs.next()) {
|
||||||
|
var perm = rs.getString("permission");
|
||||||
|
perms.add(Permission.valueOf(perm));
|
||||||
|
}
|
||||||
|
rs.close();
|
||||||
|
return perms;
|
||||||
|
}
|
||||||
|
|
||||||
|
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("sessionDuration");
|
||||||
|
return new User(user, pass, name, mail, uuid).sessionDuration(Duration.ofMinutes(mins));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<User> find(String key) {
|
public Set<User> find(String key) {
|
||||||
return Set.of();
|
return Set.of();
|
||||||
|
|||||||
Reference in New Issue
Block a user