diff --git a/de.srsoftware.oidc.api/build.gradle b/de.srsoftware.oidc.api/build.gradle index c56e75d..8e8e70e 100644 --- a/de.srsoftware.oidc.api/build.gradle +++ b/de.srsoftware.oidc.api/build.gradle @@ -19,4 +19,17 @@ dependencies { test { useJUnitPlatform() +} + +task jarTest (type: Jar) { + from sourceSets.test.output + archiveClassifier = 'test' +} + +configurations { + testOutput +} + +artifacts { + testOutput jarTest } \ No newline at end of file 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 new file mode 100644 index 0000000..85be012 --- /dev/null +++ b/de.srsoftware.oidc.api/src/test/java/de/srsoftware/oidc/api/UserServiceTest.java @@ -0,0 +1,212 @@ +/* © SRSoftware 2024 */ +package de.srsoftware.oidc.api; + +import static de.srsoftware.oidc.api.data.Permission.*; + +import de.srsoftware.oidc.api.data.Permission; +import de.srsoftware.oidc.api.data.User; +import java.util.UUID; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public abstract class UserServiceTest { + private static final String EMAIL = "heinz@ellmann.de"; + private static final String EMAIL2 = "arno@nym.de"; + private static final String NAME = "Heinz Ellmann"; + private static final String NAME2 = "Arno Nym"; + private static final String PASSWORD = "absolutelysafe"; + private static final String PASSWORD2 = "evenbetterpassword"; + private static final String USERNAME = "heinz_ellmann"; + private static final String USERNAME2 = "arno_nym"; + + protected abstract UserService userService(); + + + protected abstract PasswordHasher hasher(); + + + @Test + public void testListEmpty() { + var users = userService().list(); + Assertions.assertEquals(0, users.size()); + } + + @Test + public void testInit() { + var uuid = UUID.randomUUID().toString(); + var hashedPass = hasher().hash(PASSWORD, uuid); + var firstUser = new User(USERNAME, hashedPass, NAME, EMAIL, uuid); + userService().init(firstUser); + var users = userService().list(); + Assertions.assertEquals(1, users.size()); + var saved = users.get(0); + Assertions.assertTrue(hasher().matches(PASSWORD, saved.hashedPassword())); + Assertions.assertEquals(firstUser, saved); + } + + @Test + public void testSave() { + var uuid = UUID.randomUUID().toString(); + var hashedPass = hasher().hash(PASSWORD, uuid); + var newUser = new User(USERNAME, hashedPass, NAME, EMAIL, uuid); + newUser.add(MANAGE_CLIENTS); + newUser.add(MANAGE_PERMISSIONS); + userService().save(newUser); + var users = userService().list(); + Assertions.assertEquals(1, users.size()); + var saved = users.get(0); + Assertions.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)); + } + + @Test + public void testLoad() { + var uuid = UUID.randomUUID().toString(); + var hashedPass = hasher().hash(PASSWORD, uuid); + var newUser = new User(USERNAME, hashedPass, NAME, EMAIL, uuid); + newUser.add(MANAGE_CLIENTS); + newUser.add(MANAGE_PERMISSIONS); + userService().save(newUser); + var saved = userService().load(uuid); + Assertions.assertTrue(saved.isPresent()); + Assertions.assertEquals(newUser, saved.get()); + } + + @Test + public void testFind() { + var uuid1 = UUID.randomUUID().toString(); + var pass1 = hasher().hash(PASSWORD, uuid1); + var user1 = new User("hicke", pass1, "Heiko Icke", "h.icke@example.com", uuid1); + + var uuid2 = UUID.randomUUID().toString(); + var pass2 = hasher().hash(PASSWORD, uuid2); + var user2 = new User("franz", pass2, "hicke", "franz@example.com", uuid2); + + var uuid3 = UUID.randomUUID().toString(); + var pass3 = hasher().hash(PASSWORD, uuid3); + var user3 = new User("jutta", pass3, "Jutta", "hicke", uuid3); + + var uuid4 = UUID.randomUUID().toString(); + var pass4 = hasher().hash(PASSWORD, uuid4); + var user4 = new User("annabolika", pass4, "Anna Bolika", "anna@example.com", uuid4); + + userService().save(user1).save(user2).save(user3).save(user4); + Assertions.assertEquals(4, userService().list().size()); + var found = userService().find("hicke"); + Assertions.assertEquals(3, found.size()); + + Assertions.assertEquals(1, userService().find("Anna Bolika").size()); + Assertions.assertEquals(0, userService().find("nosferatu").size()); + } + + @Test + public void testAlterPassword() { + var uuid = UUID.randomUUID().toString(); + var hashedPass = hasher().hash(PASSWORD, uuid); + var firstUser = new User(USERNAME, hashedPass, NAME, EMAIL, uuid); + userService().init(firstUser); + + var loaded = userService().load(uuid); + Assertions.assertTrue(loaded.isPresent()); + var oldPass = loaded.get().hashedPassword(); + Assertions.assertTrue(hasher().matches(PASSWORD, oldPass)); + + var newPass = hasher().hash(PASSWORD2, uuid); + userService().save(firstUser.hashedPassword(newPass)); + + loaded = userService().load(uuid); + Assertions.assertTrue(loaded.isPresent()); + newPass = loaded.get().hashedPassword(); + Assertions.assertTrue(hasher().matches(PASSWORD2, newPass)); + } + + @Test + public void testAlterUsername() { + var uuid = UUID.randomUUID().toString(); + var hashedPass = hasher().hash(PASSWORD, uuid); + var firstUser = new User(USERNAME, hashedPass, NAME, EMAIL, uuid); + userService().init(firstUser); + + var loaded = userService().load(uuid); + Assertions.assertTrue(loaded.isPresent()); + Assertions.assertEquals(USERNAME, loaded.get().username()); + + userService().save(firstUser.username(USERNAME2)); + + loaded = userService().load(uuid); + Assertions.assertTrue(loaded.isPresent()); + Assertions.assertEquals(USERNAME2, loaded.get().username()); + } + + @Test + public void testAlterRealname() { + var uuid = UUID.randomUUID().toString(); + var hashedPass = hasher().hash(PASSWORD, uuid); + var firstUser = new User(USERNAME, hashedPass, NAME, EMAIL, uuid); + userService().init(firstUser); + + var loaded = userService().load(uuid); + Assertions.assertTrue(loaded.isPresent()); + Assertions.assertEquals(NAME, loaded.get().realName()); + + userService().save(firstUser.realName(NAME2)); + + loaded = userService().load(uuid); + Assertions.assertTrue(loaded.isPresent()); + Assertions.assertEquals(NAME2, loaded.get().realName()); + } + + @Test + public void testAlterEmail() { + var uuid = UUID.randomUUID().toString(); + var hashedPass = hasher().hash(PASSWORD, uuid); + var firstUser = new User(USERNAME, hashedPass, NAME, EMAIL, uuid); + userService().init(firstUser); + + var loaded = userService().load(uuid); + Assertions.assertTrue(loaded.isPresent()); + Assertions.assertEquals(NAME, loaded.get().realName()); + + userService().save(firstUser.email(EMAIL2)); + + loaded = userService().load(uuid); + Assertions.assertTrue(loaded.isPresent()); + Assertions.assertEquals(EMAIL2, loaded.get().email()); + } + + @Test + public void testAlterPermissions() { + var uuid = UUID.randomUUID().toString(); + var hashedPass = hasher().hash(PASSWORD, uuid); + var firstUser = new User(USERNAME, hashedPass, NAME, EMAIL, uuid); + userService().init(firstUser); + + var opt = userService().load(uuid); + Assertions.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()); + loaded = opt.get(); + Assertions.assertTrue(loaded.hasPermission(MANAGE_CLIENTS)); + Assertions.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()); + 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)); + } +} diff --git a/de.srsoftware.oidc.datastore.file/build.gradle b/de.srsoftware.oidc.datastore.file/build.gradle index 6cec6a0..7c00095 100644 --- a/de.srsoftware.oidc.datastore.file/build.gradle +++ b/de.srsoftware.oidc.datastore.file/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: "testOutput") 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 4e863e1..234b772 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 @@ -50,6 +50,11 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe Files.writeString(storageFile, "{}"); } json = new JSONObject(Files.readString(storageFile)); + json.put(AUTHORIZATIONS, new JSONObject()); + json.put(CLIENTS, new JSONObject()); + json.put(MAILCONFIG, new JSONObject()); + json.put(SESSIONS, new JSONObject()); + json.put(USERS, new JSONObject()); auth = null; // lazy init! } @@ -137,11 +142,8 @@ public class FileStore implements AuthorizationService, ClientService, SessionSe @Override public FileStore init(User defaultUser) { - if (!json.has(AUTHORIZATIONS)) json.put(AUTHORIZATIONS, new JSONObject()); - if (!json.has(CLIENTS)) json.put(CLIENTS, new JSONObject()); - if (!json.has(MAILCONFIG)) json.put(MAILCONFIG, new JSONObject()); - if (!json.has(SESSIONS)) json.put(SESSIONS, new JSONObject()); - if (!json.has(USERS)) save(defaultUser); + var users = json.getJSONObject(USERS); + if (users.length() < 1) save(defaultUser); return this; } diff --git a/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/UuidHasher.java b/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/UuidHasher.java index 922d0f2..9b2849d 100644 --- a/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/UuidHasher.java +++ b/de.srsoftware.oidc.datastore.file/src/main/java/de/srsoftware/oidc/datastore/file/UuidHasher.java @@ -28,7 +28,7 @@ public class UuidHasher implements PasswordHasher { @Override public String salt(String hashedPassword) { - return hashedPassword.split("@")[1]; + return hashedPassword.split("@", 2)[1]; } public static String hex(byte[] bytes) { 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 new file mode 100644 index 0000000..de1dad2 --- /dev/null +++ b/de.srsoftware.oidc.datastore.file/src/test/java/de/srsoftware/oidc/datastore/file/FileStoreUserServiceTest.java @@ -0,0 +1,39 @@ +/* © 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 java.io.File; +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; + +public class FileStoreUserServiceTest extends UserServiceTest { + private PasswordHasher hasher = null; + private File storage = new File("/tmp/" + UUID.randomUUID()); + private UserService userService; + + @Override + 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(); + userService = new FileStore(storage, hasher); + } + + @Override + protected UserService userService() { + return userService; + } +}