From 79431ca6801664adba0fca4f70088ff2e0e8bd31 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Wed, 11 Sep 2024 23:01:21 +0200 Subject: [PATCH] implemented SqliteSessionService Signed-off-by: Stephan Richter --- .../oidc/api/SessionServiceTest.java | 2 +- .../oidc/datastore/sqlite/SqliteKeyStore.java | 78 ++++++++------- .../sqlite/SqliteSessionService.java | 82 +++++++++++----- .../datastore/sqlite/SqliteUserService.java | 94 +++++++++---------- 4 files changed, 151 insertions(+), 105 deletions(-) diff --git a/de.srsoftware.oidc.api/src/test/java/de/srsoftware/oidc/api/SessionServiceTest.java b/de.srsoftware.oidc.api/src/test/java/de/srsoftware/oidc/api/SessionServiceTest.java index 10d83c0..e9ca81d 100644 --- a/de.srsoftware.oidc.api/src/test/java/de/srsoftware/oidc/api/SessionServiceTest.java +++ b/de.srsoftware.oidc.api/src/test/java/de/srsoftware/oidc/api/SessionServiceTest.java @@ -80,7 +80,7 @@ public abstract class SessionServiceTest { sessionService().dropSession(session.id()); var loaded = sessionService().retrieve(session.id()); - assertTrue(sessionService().retrieve(session.id()).isEmpty()); + assertTrue(loaded.isEmpty()); } @Test diff --git a/de.srsoftware.oidc.datastore.sqlite/src/main/java/de/srsoftware/oidc/datastore/sqlite/SqliteKeyStore.java b/de.srsoftware.oidc.datastore.sqlite/src/main/java/de/srsoftware/oidc/datastore/sqlite/SqliteKeyStore.java index 6c44a32..d5ee604 100644 --- a/de.srsoftware.oidc.datastore.sqlite/src/main/java/de/srsoftware/oidc/datastore/sqlite/SqliteKeyStore.java +++ b/de.srsoftware.oidc.datastore.sqlite/src/main/java/de/srsoftware/oidc/datastore/sqlite/SqliteKeyStore.java @@ -16,13 +16,17 @@ import org.jose4j.jwk.PublicJsonWebKey; import org.jose4j.lang.JoseException; public class SqliteKeyStore extends SqliteStore implements KeyStorage { - 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 = ?"; + 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 loaded = new HashMap<>(); @@ -30,51 +34,57 @@ public class SqliteKeyStore extends SqliteStore implements KeyStorage { 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_KEYSTORE_VERSION).executeQuery(); + var rs = conn.prepareStatement(SELECT_STORE_VERSION).executeQuery(); int availableVersion = 1; - int lastVersion = 1; + int currentVersion; if (rs.next()) { - lastVersion = rs.getInt(1); + currentVersion = rs.getInt(1); + rs.close(); + } else { + rs.close(); + conn.prepareStatement(CREATE_STORE_VERSION).execute(); + currentVersion = 0; } - rs.close(); + conn.setAutoCommit(false); - var stmt = conn.prepareStatement(SET_KEYSTORE_VERSION); - for (int version = lastVersion; version <= availableVersion; version++) { + var stmt = conn.prepareStatement(SET_STORE_VERSION); + while (currentVersion < availableVersion) { try { - switch (version) { - case 1: - createKeyStoreTables(); + switch (currentVersion) { + case 0: + createStoreTables(); + break; } - stmt.setInt(1, version); + stmt.setInt(1, ++currentVersion); stmt.execute(); conn.commit(); } catch (Exception e) { conn.rollback(); - LOG.log(System.Logger.Level.ERROR, "Failed to update at keystore version = {0}", version); + LOG.log(System.Logger.Level.ERROR, "Failed to update at {} = {}", STORE_VERSION, currentVersion); 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 listKeys() { var result = new ArrayList(); diff --git a/de.srsoftware.oidc.datastore.sqlite/src/main/java/de/srsoftware/oidc/datastore/sqlite/SqliteSessionService.java b/de.srsoftware.oidc.datastore.sqlite/src/main/java/de/srsoftware/oidc/datastore/sqlite/SqliteSessionService.java index 30fbaf1..b63ae6f 100644 --- a/de.srsoftware.oidc.datastore.sqlite/src/main/java/de/srsoftware/oidc/datastore/sqlite/SqliteSessionService.java +++ b/de.srsoftware.oidc.datastore.sqlite/src/main/java/de/srsoftware/oidc/datastore/sqlite/SqliteSessionService.java @@ -19,11 +19,43 @@ public class SqliteSessionService extends SqliteStore implements SessionService 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)"; + private static final String SAVE_SESSION = "INSERT INTO sessions (id, userId, expiration) VALUES (?,?,?) ON CONFLICT DO UPDATE SET expiration = ?;"; + 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) { + var now = Instant.now(); + var endOfSession = now.plus(user.sessionDuration()).truncatedTo(SECONDS); + return save(new Session(user.uuid(), endOfSession, uuid())); + } + + 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())); + } + @Override protected void initTables() throws SQLException { var rs = conn.prepareStatement(SELECT_STORE_VERSION).executeQuery(); @@ -59,33 +91,37 @@ public class SqliteSessionService extends SqliteStore implements SessionService conn.setAutoCommit(true); } - private void createStoreTables() throws SQLException { - conn.prepareStatement(CREATE_SESSION_TABLE).execute(); - } - - @Override - public Session createSession(User user) { - var now = Instant.now(); - var endOfSession = now.plus(user.sessionDuration()).truncatedTo(SECONDS); - return save(new Session(user.uuid(), endOfSession, uuid())); - } - - @Override - public SessionService dropSession(String sessionId) { - return null; - } - - @Override - public Session extend(Session session, User user) { - return null; - } - @Override public Optional retrieve(String sessionId) { - return Optional.empty(); + try { + var stmt = conn.prepareStatement(SELECT_SESSION); + stmt.setString(1, sessionId); + var rs = stmt.executeQuery(); + Optional result = Optional.empty(); + if (rs.next()) { + var userID = rs.getString("userId"); + var expiration = Instant.ofEpochSecond(rs.getLong("expiration")); + if (expiration.isAfter(Instant.now())) result = Optional.of(new Session(userID, expiration, sessionId)); + } + rs.close(); + return result; + } catch (SQLException e) { + throw new RuntimeException(e); + } } private Session save(Session session) { - return 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.setLong(4, expiration); + stmt.execute(); + return session; + } catch (SQLException e) { + throw new RuntimeException(e); + } } } diff --git a/de.srsoftware.oidc.datastore.sqlite/src/main/java/de/srsoftware/oidc/datastore/sqlite/SqliteUserService.java b/de.srsoftware.oidc.datastore.sqlite/src/main/java/de/srsoftware/oidc/datastore/sqlite/SqliteUserService.java index ab85a8e..ac989ca 100644 --- a/de.srsoftware.oidc.datastore.sqlite/src/main/java/de/srsoftware/oidc/datastore/sqlite/SqliteUserService.java +++ b/de.srsoftware.oidc.datastore.sqlite/src/main/java/de/srsoftware/oidc/datastore/sqlite/SqliteUserService.java @@ -31,7 +31,7 @@ public class SqliteUserService extends SqliteStore implements UserService { 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 INSERT_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 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 = ?"; @@ -53,6 +53,22 @@ public class SqliteUserService extends SqliteStore implements UserService { 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 consumeToken(String id) { var user = forToken(id); @@ -86,6 +102,25 @@ public class SqliteUserService extends SqliteStore implements UserService { stmt.execute(); } + @Override + public Set find(String idOrEmail) { + try { + var result = new HashSet(); + 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 forToken(String id) { AccessToken token = accessTokens.get(id); @@ -158,35 +193,6 @@ public class SqliteUserService extends SqliteStore implements UserService { } } - 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)); - } - - @Override - public Set find(String idOrEmail) { - try { - var result = new HashSet(); - 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 load(String id) { try { @@ -202,22 +208,6 @@ public class SqliteUserService extends SqliteStore implements UserService { } } - 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 load(String username, String password) { var candidates = find(username); @@ -236,7 +226,7 @@ public class SqliteUserService extends SqliteStore implements UserService { public SqliteUserService save(User user) { try { conn.setAutoCommit(false); - var stmt = conn.prepareStatement(INSERT_USER); + var stmt = conn.prepareStatement(SAVE_USER); stmt.setString(1, user.uuid()); stmt.setString(2, user.hashedPassword()); stmt.setString(3, user.email()); @@ -270,4 +260,14 @@ public class SqliteUserService extends SqliteStore implements UserService { 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)); + } }