From f600040c0e2a36b2985109166706bf2738c0f268 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Tue, 10 Sep 2024 20:53:03 +0200 Subject: [PATCH] refining some tests, preparing test for session service Signed-off-by: Stephan Richter --- de.srsoftware.oidc.api/build.gradle | 6 +- .../de/srsoftware/oidc/api/UserService.java | 2 +- .../oidc/api/SessionServiceTest.java | 4 ++ .../srsoftware/oidc/api/UserServiceTest.java | 64 ++++++++++++------- .../de/srsoftware/oidc/app/Application.java | 2 +- .../oidc/backend/UserController.java | 2 +- .../build.gradle | 2 +- .../oidc/datastore/file/FileStore.java | 11 ++-- .../datastore/file/FileStoreProvider.java | 1 + .../file/FileStoreUserServiceTest.java | 5 +- .../datastore/file/SessionServiceTest.java | 33 ++++++++++ .../build.gradle | 1 + .../datastore/sqlite/SqliteUserService.java | 39 +++++------ .../sqlite/SqliteUserServiceTest.java | 32 ++++++++++ .../de/srsoftware/utils}/PasswordHasher.java | 2 +- .../java/de/srsoftware/utils}/UuidHasher.java | 3 +- 16 files changed, 147 insertions(+), 62 deletions(-) create mode 100644 de.srsoftware.oidc.api/src/test/java/de/srsoftware/oidc/api/SessionServiceTest.java create mode 100644 de.srsoftware.oidc.datastore.file/src/test/java/de/srsoftware/oidc/datastore/file/SessionServiceTest.java create mode 100644 de.srsoftware.oidc.datastore.sqlite/src/test/java/de/srsoftware/oidc/datastore/sqlite/SqliteUserServiceTest.java rename {de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api => de.srsoftware.utils/src/main/java/de/srsoftware/utils}/PasswordHasher.java (91%) rename {de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file => de.srsoftware.utils/src/main/java/de/srsoftware/utils}/UuidHasher.java (91%) diff --git a/de.srsoftware.oidc.api/build.gradle b/de.srsoftware.oidc.api/build.gradle index 8e8e70e..be3cae6 100644 --- a/de.srsoftware.oidc.api/build.gradle +++ b/de.srsoftware.oidc.api/build.gradle @@ -21,15 +21,15 @@ test { useJUnitPlatform() } -task jarTest (type: Jar) { +task jarTests (type: Jar) { from sourceSets.test.output archiveClassifier = 'test' } configurations { - testOutput + testBundle } artifacts { - testOutput jarTest + testBundle jarTests } \ No newline at end of file diff --git a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/UserService.java b/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/UserService.java index 22a2f20..a4daccb 100644 --- a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/UserService.java +++ b/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/UserService.java @@ -28,7 +28,7 @@ public interface UserService { public Set find(String idOrEmail); public Optional load(String id); public Optional load(String username, String password); - public boolean passwordMatches(String password, String hashedPassword); + public boolean passwordMatches(String plaintextPassword, User user); public T save(User user); public T updatePassword(User user, String plaintextPassword); } 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 new file mode 100644 index 0000000..fc0f18c --- /dev/null +++ b/de.srsoftware.oidc.api/src/test/java/de/srsoftware/oidc/api/SessionServiceTest.java @@ -0,0 +1,4 @@ +/* © SRSoftware 2024 */ +package de.srsoftware.oidc.api; + +public class SessionServiceTest {} diff --git a/de.srsoftware.oidc.api/src/test/java/de/srsoftware/oidc/api/UserServiceTest.java b/de.srsoftware.oidc.api/src/test/java/de/srsoftware/oidc/api/UserServiceTest.java index 85be012..8b7c2e5 100644 --- a/de.srsoftware.oidc.api/src/test/java/de/srsoftware/oidc/api/UserServiceTest.java +++ b/de.srsoftware.oidc.api/src/test/java/de/srsoftware/oidc/api/UserServiceTest.java @@ -2,9 +2,13 @@ package de.srsoftware.oidc.api; import static de.srsoftware.oidc.api.data.Permission.*; +import static org.junit.jupiter.api.Assertions.assertTrue; import de.srsoftware.oidc.api.data.Permission; import de.srsoftware.oidc.api.data.User; +import de.srsoftware.utils.PasswordHasher; +import de.srsoftware.utils.UuidHasher; +import java.security.NoSuchAlgorithmException; import java.util.UUID; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -21,8 +25,17 @@ public abstract class UserServiceTest { protected abstract UserService userService(); + private PasswordHasher hasher = null; - protected abstract PasswordHasher hasher(); + protected PasswordHasher hasher() { + if (hasher == null) try { + hasher = new UuidHasher(); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + + return hasher; + } @Test @@ -40,7 +53,7 @@ public abstract class UserServiceTest { var users = userService().list(); Assertions.assertEquals(1, users.size()); var saved = users.get(0); - Assertions.assertTrue(hasher().matches(PASSWORD, saved.hashedPassword())); + assertTrue(hasher().matches(PASSWORD, saved.hashedPassword())); Assertions.assertEquals(firstUser, saved); } @@ -55,12 +68,12 @@ public abstract class UserServiceTest { var users = userService().list(); Assertions.assertEquals(1, users.size()); var saved = users.get(0); - Assertions.assertTrue(hasher().matches(PASSWORD, saved.hashedPassword())); + assertTrue(hasher().matches(PASSWORD, saved.hashedPassword())); Assertions.assertEquals(newUser, saved); Assertions.assertFalse(saved.hasPermission(Permission.MANAGE_USERS)); Assertions.assertFalse(saved.hasPermission(Permission.MANAGE_SMTP)); - Assertions.assertTrue(saved.hasPermission(MANAGE_CLIENTS)); - Assertions.assertTrue(saved.hasPermission(MANAGE_PERMISSIONS)); + assertTrue(saved.hasPermission(MANAGE_CLIENTS)); + assertTrue(saved.hasPermission(MANAGE_PERMISSIONS)); } @Test @@ -72,7 +85,7 @@ public abstract class UserServiceTest { newUser.add(MANAGE_PERMISSIONS); userService().save(newUser); var saved = userService().load(uuid); - Assertions.assertTrue(saved.isPresent()); + assertTrue(saved.isPresent()); Assertions.assertEquals(newUser, saved.get()); } @@ -111,17 +124,22 @@ public abstract class UserServiceTest { userService().init(firstUser); var loaded = userService().load(uuid); - Assertions.assertTrue(loaded.isPresent()); + assertTrue(loaded.isPresent()); var oldPass = loaded.get().hashedPassword(); - Assertions.assertTrue(hasher().matches(PASSWORD, oldPass)); + assertTrue(hasher().matches(PASSWORD, oldPass)); var newPass = hasher().hash(PASSWORD2, uuid); userService().save(firstUser.hashedPassword(newPass)); loaded = userService().load(uuid); - Assertions.assertTrue(loaded.isPresent()); + assertTrue(loaded.isPresent()); newPass = loaded.get().hashedPassword(); - Assertions.assertTrue(hasher().matches(PASSWORD2, newPass)); + assertTrue(hasher().matches(PASSWORD2, newPass)); + + userService().updatePassword(firstUser, PASSWORD); + loaded = userService().load(uuid); + assertTrue(loaded.isPresent()); + assertTrue(userService().passwordMatches(PASSWORD, loaded.get())); } @Test @@ -132,13 +150,13 @@ public abstract class UserServiceTest { userService().init(firstUser); var loaded = userService().load(uuid); - Assertions.assertTrue(loaded.isPresent()); + assertTrue(loaded.isPresent()); Assertions.assertEquals(USERNAME, loaded.get().username()); userService().save(firstUser.username(USERNAME2)); loaded = userService().load(uuid); - Assertions.assertTrue(loaded.isPresent()); + assertTrue(loaded.isPresent()); Assertions.assertEquals(USERNAME2, loaded.get().username()); } @@ -150,13 +168,13 @@ public abstract class UserServiceTest { userService().init(firstUser); var loaded = userService().load(uuid); - Assertions.assertTrue(loaded.isPresent()); + assertTrue(loaded.isPresent()); Assertions.assertEquals(NAME, loaded.get().realName()); userService().save(firstUser.realName(NAME2)); loaded = userService().load(uuid); - Assertions.assertTrue(loaded.isPresent()); + assertTrue(loaded.isPresent()); Assertions.assertEquals(NAME2, loaded.get().realName()); } @@ -168,13 +186,13 @@ public abstract class UserServiceTest { userService().init(firstUser); var loaded = userService().load(uuid); - Assertions.assertTrue(loaded.isPresent()); + assertTrue(loaded.isPresent()); Assertions.assertEquals(NAME, loaded.get().realName()); userService().save(firstUser.email(EMAIL2)); loaded = userService().load(uuid); - Assertions.assertTrue(loaded.isPresent()); + assertTrue(loaded.isPresent()); Assertions.assertEquals(EMAIL2, loaded.get().email()); } @@ -186,27 +204,27 @@ public abstract class UserServiceTest { userService().init(firstUser); var opt = userService().load(uuid); - Assertions.assertTrue(opt.isPresent()); + assertTrue(opt.isPresent()); var loaded = opt.get(); for (var permission : Permission.values()) Assertions.assertFalse(loaded.hasPermission(permission)); userService().save(loaded.add(MANAGE_CLIENTS, MANAGE_PERMISSIONS)); opt = userService().load(uuid); - Assertions.assertTrue(opt.isPresent()); + assertTrue(opt.isPresent()); loaded = opt.get(); - Assertions.assertTrue(loaded.hasPermission(MANAGE_CLIENTS)); - Assertions.assertTrue(loaded.hasPermission(MANAGE_PERMISSIONS)); + assertTrue(loaded.hasPermission(MANAGE_CLIENTS)); + assertTrue(loaded.hasPermission(MANAGE_PERMISSIONS)); Assertions.assertFalse(loaded.hasPermission(MANAGE_SMTP)); Assertions.assertFalse(loaded.hasPermission(MANAGE_USERS)); userService().save(loaded.add(MANAGE_SMTP, MANAGE_USERS).drop(MANAGE_CLIENTS, MANAGE_PERMISSIONS)); opt = userService().load(uuid); - Assertions.assertTrue(opt.isPresent()); + assertTrue(opt.isPresent()); loaded = opt.get(); Assertions.assertFalse(loaded.hasPermission(MANAGE_CLIENTS)); Assertions.assertFalse(loaded.hasPermission(MANAGE_PERMISSIONS)); - Assertions.assertTrue(loaded.hasPermission(MANAGE_SMTP)); - Assertions.assertTrue(loaded.hasPermission(MANAGE_USERS)); + assertTrue(loaded.hasPermission(MANAGE_SMTP)); + assertTrue(loaded.hasPermission(MANAGE_USERS)); } } diff --git a/de.srsoftware.oidc.app/src/main/java/de/srsoftware/oidc/app/Application.java b/de.srsoftware.oidc.app/src/main/java/de/srsoftware/oidc/app/Application.java index b6d9dae..4e83693 100644 --- a/de.srsoftware.oidc.app/src/main/java/de/srsoftware/oidc/app/Application.java +++ b/de.srsoftware.oidc.app/src/main/java/de/srsoftware/oidc/app/Application.java @@ -20,10 +20,10 @@ import de.srsoftware.oidc.api.data.User; import de.srsoftware.oidc.backend.*; import de.srsoftware.oidc.datastore.file.FileStoreProvider; import de.srsoftware.oidc.datastore.file.PlaintextKeyStore; -import de.srsoftware.oidc.datastore.file.UuidHasher; import de.srsoftware.oidc.datastore.sqlite.*; import de.srsoftware.oidc.web.Forward; import de.srsoftware.oidc.web.StaticPages; +import de.srsoftware.utils.UuidHasher; import java.io.File; import java.net.InetSocketAddress; import java.nio.file.Path; diff --git a/de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/UserController.java b/de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/UserController.java index 64d8cab..9a3ef1c 100644 --- a/de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/UserController.java +++ b/de.srsoftware.oidc.backend/src/main/java/de/srsoftware/oidc/backend/UserController.java @@ -298,7 +298,7 @@ public class UserController extends Controller { return sendEmptyResponse(HTTP_FORBIDDEN, ex); } var oldPass = json.getString("oldpass"); - if (!users.passwordMatches(oldPass, user.hashedPassword())) return badRequest(ex, "wrong password"); + if (!users.passwordMatches(oldPass, user)) return badRequest(ex, "wrong password"); var passwords = json.getJSONArray("newpass"); var newPass = passwords.getString(0); diff --git a/de.srsoftware.oidc.datastore.file/build.gradle b/de.srsoftware.oidc.datastore.file/build.gradle index 7c00095..b4d682c 100644 --- a/de.srsoftware.oidc.datastore.file/build.gradle +++ b/de.srsoftware.oidc.datastore.file/build.gradle @@ -12,7 +12,7 @@ repositories { dependencies { testImplementation platform('org.junit:junit-bom:5.10.0') testImplementation 'org.junit.jupiter:junit-jupiter' - testImplementation project(path: ':de.srsoftware.oidc.api', configuration: "testOutput") + testImplementation project(path: ':de.srsoftware.oidc.api', configuration: "testBundle") implementation project(':de.srsoftware.oidc.api') implementation project(':de.srsoftware.utils') implementation 'org.json:json:20240303' diff --git a/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStore.java b/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStore.java index 234b772..cac543f 100644 --- a/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStore.java +++ b/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStore.java @@ -9,6 +9,7 @@ import static java.util.Optional.empty; import de.srsoftware.oidc.api.*; import de.srsoftware.oidc.api.data.*; +import de.srsoftware.utils.PasswordHasher; import jakarta.mail.Authenticator; import jakarta.mail.PasswordAuthentication; import java.io.File; @@ -186,8 +187,8 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe var userData = users.getJSONObject(userId); if (KEYS.stream().map(userData::getString).noneMatch(val -> val.equals(user))) continue; - var hashedPass = userData.getString(PASSWORD); - if (passwordMatches(password, hashedPass)) return User.of(userData, userId); + var loadedUser = User.of(userData, userId).filter(u -> passwordMatches(password, u)); + if (loadedUser.isPresent()) return loadedUser; } return empty(); } catch (Exception e) { @@ -196,8 +197,8 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe } @Override - public boolean passwordMatches(String password, String hashedPassword) { - return passwordHasher.matches(password, hashedPassword); + public boolean passwordMatches(String password, User user) { + return passwordHasher.matches(password, user.hashedPassword()); } @Override @@ -227,7 +228,7 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe public Session createSession(User user) { var now = Instant.now(); var endOfSession = now.plus(user.sessionDuration()); - return save(new Session(user, endOfSession, uuid().toString())); + return save(new Session(user, endOfSession, uuid())); } @Override diff --git a/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStoreProvider.java b/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStoreProvider.java index c957bcd..b6bd4d8 100644 --- a/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStoreProvider.java +++ b/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/FileStoreProvider.java @@ -1,6 +1,7 @@ /* © SRSoftware 2024 */ package de.srsoftware.oidc.datastore.file; +import de.srsoftware.utils.UuidHasher; import java.io.File; import java.io.IOException; import java.util.HashMap; diff --git a/de.srsoftware.oidc.datastore.file/src/test/java/de/srsoftware/oidc/datastore/file/FileStoreUserServiceTest.java b/de.srsoftware.oidc.datastore.file/src/test/java/de/srsoftware/oidc/datastore/file/FileStoreUserServiceTest.java index de1dad2..b9adc0d 100644 --- a/de.srsoftware.oidc.datastore.file/src/test/java/de/srsoftware/oidc/datastore/file/FileStoreUserServiceTest.java +++ b/de.srsoftware.oidc.datastore.file/src/test/java/de/srsoftware/oidc/datastore/file/FileStoreUserServiceTest.java @@ -1,9 +1,10 @@ /* © SRSoftware 2024 */ package de.srsoftware.oidc.datastore.file; -import de.srsoftware.oidc.api.PasswordHasher; import de.srsoftware.oidc.api.UserService; import de.srsoftware.oidc.api.UserServiceTest; +import de.srsoftware.utils.PasswordHasher; +import de.srsoftware.utils.UuidHasher; import java.io.File; import java.io.IOException; import java.security.NoSuchAlgorithmException; @@ -29,7 +30,7 @@ public class FileStoreUserServiceTest extends UserServiceTest { @BeforeEach public void setup() throws IOException { if (storage.exists()) storage.delete(); - userService = new FileStore(storage, hasher); + userService = new FileStore(storage, hasher()); } @Override diff --git a/de.srsoftware.oidc.datastore.file/src/test/java/de/srsoftware/oidc/datastore/file/SessionServiceTest.java b/de.srsoftware.oidc.datastore.file/src/test/java/de/srsoftware/oidc/datastore/file/SessionServiceTest.java new file mode 100644 index 0000000..dffdc7e --- /dev/null +++ b/de.srsoftware.oidc.datastore.file/src/test/java/de/srsoftware/oidc/datastore/file/SessionServiceTest.java @@ -0,0 +1,33 @@ +/* © SRSoftware 2024 */ +package de.srsoftware.oidc.datastore.file; + +import de.srsoftware.oidc.api.SessionService; +import de.srsoftware.utils.PasswordHasher; +import de.srsoftware.utils.UuidHasher; +import java.io.File; +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; + +public class SessionServiceTest { + private PasswordHasher hasher = null; + private File storage = new File("/tmp/" + UUID.randomUUID()); + private SessionService sessionService; + + protected PasswordHasher hasher() { + if (hasher == null) try { + hasher = new UuidHasher(); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + + return hasher; + } + + @BeforeEach + public void setup() throws IOException { + if (storage.exists()) storage.delete(); + sessionService = new FileStore(storage, hasher()); + } +} diff --git a/de.srsoftware.oidc.datastore.sqlite/build.gradle b/de.srsoftware.oidc.datastore.sqlite/build.gradle index 8503eb7..f2ffd5c 100644 --- a/de.srsoftware.oidc.datastore.sqlite/build.gradle +++ b/de.srsoftware.oidc.datastore.sqlite/build.gradle @@ -12,6 +12,7 @@ repositories { 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' 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 93a4c32..c418650 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 @@ -5,11 +5,11 @@ 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.PasswordHasher; 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; @@ -24,16 +24,15 @@ public class SqliteUserService extends SqliteStore implements UserService { 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 ? ORDER BY COALESCE(uuid, ?), username"; + 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 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 INSERT_USER = "INSERT INTO users (uuid,password,email,session_duration,username,realname) VALUES (?,?,?,?,?,?)"; + 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 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 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 hasher; private Map accessTokens = new HashMap<>(); @@ -169,6 +168,7 @@ public class SqliteUserService extends SqliteStore implements UserService { 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(); @@ -213,14 +213,14 @@ public class SqliteUserService extends SqliteStore implements UserService { public Optional load(String username, String password) { var candidates = find(username); for (var user : candidates) { - if (passwordMatches(password, user.hashedPassword())) return Optional.of(user); + if (passwordMatches(password, user)) return Optional.of(user); } return empty(); } @Override - public boolean passwordMatches(String password, String hashedPassword) { - return hasher.matches(password, hashedPassword); + public boolean passwordMatches(String password, User user) { + return hasher.matches(password, user.hashedPassword()); } @Override @@ -234,6 +234,11 @@ public class SqliteUserService extends SqliteStore implements UserService { 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()); @@ -246,24 +251,14 @@ public class SqliteUserService extends SqliteStore implements UserService { } } conn.commit(); - + return this; } catch (SQLException e) { throw new RuntimeException(e); } - return this; } @Override public SqliteUserService updatePassword(User user, String plaintextPassword) { - try { - var stmt = conn.prepareStatement(UPDATE_PASSWORD); - stmt.setString(1, user.uuid()); - var hashedPassword = hasher.hash(plaintextPassword, uuid()); - stmt.setString(2, hashedPassword); - stmt.execute(); - return this; - } catch (SQLException e) { - throw new RuntimeException(e); - } + return save(user.hashedPassword(hasher.hash(plaintextPassword, uuid()))); } } diff --git a/de.srsoftware.oidc.datastore.sqlite/src/test/java/de/srsoftware/oidc/datastore/sqlite/SqliteUserServiceTest.java b/de.srsoftware.oidc.datastore.sqlite/src/test/java/de/srsoftware/oidc/datastore/sqlite/SqliteUserServiceTest.java new file mode 100644 index 0000000..8e9a646 --- /dev/null +++ b/de.srsoftware.oidc.datastore.sqlite/src/test/java/de/srsoftware/oidc/datastore/sqlite/SqliteUserServiceTest.java @@ -0,0 +1,32 @@ +/* © 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; + } +} diff --git a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/PasswordHasher.java b/de.srsoftware.utils/src/main/java/de/srsoftware/utils/PasswordHasher.java similarity index 91% rename from de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/PasswordHasher.java rename to de.srsoftware.utils/src/main/java/de/srsoftware/utils/PasswordHasher.java index 2b9ba2d..3bb60b1 100644 --- a/de.srsoftware.oidc.api/src/main/java/de/srsoftware/oidc/api/PasswordHasher.java +++ b/de.srsoftware.utils/src/main/java/de/srsoftware/utils/PasswordHasher.java @@ -1,5 +1,5 @@ /* © SRSoftware 2024 */ -package de.srsoftware.oidc.api; +package de.srsoftware.utils; public interface PasswordHasher { public String hash(String password, String salt); diff --git a/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/UuidHasher.java b/de.srsoftware.utils/src/main/java/de/srsoftware/utils/UuidHasher.java similarity index 91% rename from de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/UuidHasher.java rename to de.srsoftware.utils/src/main/java/de/srsoftware/utils/UuidHasher.java index 9b2849d..ba7e2fa 100644 --- a/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/UuidHasher.java +++ b/de.srsoftware.utils/src/main/java/de/srsoftware/utils/UuidHasher.java @@ -1,9 +1,8 @@ /* © SRSoftware 2024 */ -package de.srsoftware.oidc.datastore.file; +package de.srsoftware.utils; import static java.nio.charset.StandardCharsets.UTF_8; -import de.srsoftware.oidc.api.PasswordHasher; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException;