From fb6fc923ac425237467692dfe71ea21ec03f203f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20S=C3=A4ume?= Date: Sun, 19 May 2024 23:15:43 +0200 Subject: [PATCH] #8: Setup Model, DAO & Fixtures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Dominik Säume --- db/nursingHome.db | Bin 28672 -> 57344 bytes src/main/java/de/hitec/nhplus/Main.java | 4 +- .../hitec/nhplus/datastorage/DaoFactory.java | 9 + .../de/hitec/nhplus/fixtures/Fixtures.java | 5 + .../de/hitec/nhplus/fixtures/UserFixture.java | 83 +++++++ src/main/java/de/hitec/nhplus/login/User.java | 98 +++++++++ .../hitec/nhplus/login/database/UserDao.java | 202 ++++++++++++++++++ .../de/hitec/nhplus/login/database/User.sql | 2 +- .../nhplus/login/database/UserPermission.sql | 4 +- .../nhplus/login/database/UserToNurse.sql | 4 +- 10 files changed, 404 insertions(+), 7 deletions(-) create mode 100644 src/main/java/de/hitec/nhplus/fixtures/UserFixture.java create mode 100644 src/main/java/de/hitec/nhplus/login/User.java create mode 100644 src/main/java/de/hitec/nhplus/login/database/UserDao.java diff --git a/db/nursingHome.db b/db/nursingHome.db index 0cdc030c68d5c1da965119ae1d0f20ad97ae6082..87428bb3d43c31c3f5bde0589499552b4549ff9d 100644 GIT binary patch delta 1826 zcmb7F4Qvx-7{0H!UVA_7*M>ExbgZ{b*lIS{jh191#msL?6Ygh(X1h8RP_PmBRvfEYnzKmkD#6%#!&qyyUyP_kEuC z=brDqyY=V6`a|dzyVZ>#h*?=<$N8;wlWW|_6Z|_S^IgQGzm1spn07-ed5B2kuNgna zGlo8+sK0G!HQ*mfqCU5QT4|uGt3f^@a?d4VSs}(pBB?Adl-`=!I$3X+Wx`%xfMrTJ zL@mK6hwAZ18O9$Bv+Zmr6YK~x!LC4n=?ePicCmFDNlU7*?jyPX&YIrQ$@<%a%q(^R zQ#+d1$#k;Q*-kdt#)h;~vWE!e+PtAQ?=+ThdRAk!zZxL%R5Z3iSeA&3u?UwH)0!PA zX*SYSV&I-t4;$845@wcG^%>}f29V2Fmk5@L@yWO##M3EZlw9Hc;wg?_sa(V)C-gGiX`Y$$S8v)rJdDCjZ9M8^I)cnJHoz(ye@|@h3XJY* zP{dCBhuS4=MvCHn`6LZZBroB}ZU zB>5H);mg669@ignWv)KpZGkT3#l6o_d$3 zzOlK{-QspNdRknKK3^4yBLGy&@-%yntT!r2G)9el(*78gF%$_-$-1qgn_P?)i`ZAod_@;!)DsA<@~fp; z@~Mh-s02seBsW!_qY|K)!jY%tO2;M3 zyNDk4kB6_qez+H2hQ|w4t@Gj5E#*0LD_RATv1t5xPK>AV87K|3&k0Jlqjr!{oUxRM z!2?Q1>X?WoKk5J?ALGQN0uU%XS41!(r6e%ULTx}L>QpO*S#U*ogObubv$ReM!!QEV z@j~Hl!b-spg$ewz_WRYEC=%!Jflc_J8-n_>H^c07J$pwX)IlZ^53y z4<`z_oUM=S*MHrD&b)bcsO}g4VE5X~!;K$&+dTDH>gK@r=d4FRSulKYEjPL9#PV=v zV4nM`?dtpa{TnkscdlDt+A%mh@WaBN&K#-ud~;365g0mER{r*q*XfCyXA|mxTGW&F zpHcrcV@sP>4MC7XZR=cRr=)QhIu2L*Qp0rc8UnAuTg6@MuP`dAtNQ4QBsGB!UPs^` v@OqK7l`hkvz^E)#olq6OfxtWPMiJj;(W3z9bf^NW;fSRO?7~Jn9W(y~I5#6J delta 673 zcmY*WO=uHA6rSnE-TB$sO~7^0O|!-pgT>UOwJmxR^&l1eX=+av*Cvr*vWe*ywSw)E zfG2+kPl_P%mYaL=;6V{Q6g*k1Af+C9)1CxR3Qm$0?Z6w}``-6`Z#9pJtAREOBX&NUK)3)KAn|y_SNi>a9 zVd*)S%LT7qaJ;5b3$b8@&OpAZ4{?j#hi8dM* zd}+?{N>%q?CI_6Pg{oZTJN56pt>p|TnU)0`GoB+Kle=&C%g04`bH7~c)l8s-tN}f$ z4YHT)y!u`((jW949Vcs)1&Qcga%6~-6eR$|q-h!k?iiq%{Y6&l6QYib-C&51 zCN@|FhBkJ?L_A?{YA%TA&SCwY?&#n28$ojKIem%dBn#HW_ZD1F)?0wU?zY$k$?jzp z(IBGdiVbL!QWE~R1%a2U`YS-NyHKAOM6o2f2Y%QN(k^pjI~c`a7XT^_$Kz?qgk2*k W_<87s4H18GrqqgDiUyIhzw|$M)2U7X diff --git a/src/main/java/de/hitec/nhplus/Main.java b/src/main/java/de/hitec/nhplus/Main.java index f8a6e87..eb807a1 100644 --- a/src/main/java/de/hitec/nhplus/Main.java +++ b/src/main/java/de/hitec/nhplus/Main.java @@ -34,8 +34,8 @@ public class Main extends Application { @Override public void start(Stage primaryStage) { this.primaryStage = primaryStage; - executePassword(); - //executeMainApplication(); + //executePassword(); + executeMainApplication(); } private void executePassword() { diff --git a/src/main/java/de/hitec/nhplus/datastorage/DaoFactory.java b/src/main/java/de/hitec/nhplus/datastorage/DaoFactory.java index 955c88d..067101a 100644 --- a/src/main/java/de/hitec/nhplus/datastorage/DaoFactory.java +++ b/src/main/java/de/hitec/nhplus/datastorage/DaoFactory.java @@ -1,5 +1,6 @@ package de.hitec.nhplus.datastorage; +import de.hitec.nhplus.login.database.UserDao; import de.hitec.nhplus.medication.database.MedicationDao; import de.hitec.nhplus.nurse.database.NurseDao; import de.hitec.nhplus.patient.database.PatientDao; @@ -63,4 +64,12 @@ public class DaoFactory { public MedicationDao createMedicationDAO() { return new MedicationDao(ConnectionBuilder.getConnection()); } + + /** + * @return A new {@link UserDao} instance with a database connection. + * @see de.hitec.nhplus.login.User User + */ + public UserDao createUserDAO() { + return new UserDao(ConnectionBuilder.getConnection()); + } } diff --git a/src/main/java/de/hitec/nhplus/fixtures/Fixtures.java b/src/main/java/de/hitec/nhplus/fixtures/Fixtures.java index 51719a3..0ea398b 100644 --- a/src/main/java/de/hitec/nhplus/fixtures/Fixtures.java +++ b/src/main/java/de/hitec/nhplus/fixtures/Fixtures.java @@ -46,6 +46,11 @@ public class Fixtures { medicationFixture.setupTable(connection); medicationFixture.load(); + UserFixture userFixture = new UserFixture(); + userFixture.dropTable(connection); + userFixture.setupTable(connection); + userFixture.load(); + } catch (Exception exception) { System.out.println(exception.getMessage()); } diff --git a/src/main/java/de/hitec/nhplus/fixtures/UserFixture.java b/src/main/java/de/hitec/nhplus/fixtures/UserFixture.java new file mode 100644 index 0000000..2fff14f --- /dev/null +++ b/src/main/java/de/hitec/nhplus/fixtures/UserFixture.java @@ -0,0 +1,83 @@ +package de.hitec.nhplus.fixtures; + +import de.hitec.nhplus.Main; +import de.hitec.nhplus.datastorage.DaoFactory; +import de.hitec.nhplus.login.User; +import de.hitec.nhplus.login.database.UserDao; +import de.hitec.nhplus.medication.Medication; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.*; + +public class UserFixture implements Fixture{ + private static final String SCHEMA = "/de/hitec/nhplus/login/database/User.sql"; + private static final String PERMISSION_SCHEMA = "/de/hitec/nhplus/login/database/UserPermission.sql"; + private static final String TO_NURSE_SCHEMA = "/de/hitec/nhplus/login/database/UserToNurse.sql"; + @Override + public void dropTable(Connection connection) throws SQLException { + connection.createStatement().execute("DROP TABLE IF EXISTS user"); + connection.createStatement().execute("DROP TABLE IF EXISTS user__permissions"); + connection.createStatement().execute("DROP TABLE IF EXISTS user__nurse"); + } + + @Override + public void setupTable(Connection connection) throws SQLException { + final InputStream schema = Main.class.getResourceAsStream(SCHEMA); + final InputStream permissionSchema = Main.class.getResourceAsStream(PERMISSION_SCHEMA); + final InputStream toNurseSchema = Main.class.getResourceAsStream(TO_NURSE_SCHEMA); + + assert schema != null; + assert permissionSchema != null; + assert toNurseSchema != null; + + String SQL = new Scanner(schema, StandardCharsets.UTF_8) + .useDelimiter("\\A") + .next(); + String permissionSQL = new Scanner(permissionSchema, StandardCharsets.UTF_8) + .useDelimiter("\\A") + .next(); + String toNurseSQL = new Scanner(toNurseSchema, StandardCharsets.UTF_8) + .useDelimiter("\\A") + .next(); + + connection.createStatement().execute(SQL); + connection.createStatement().execute(permissionSQL); + connection.createStatement().execute(toNurseSQL); + } + + @Override + public Map load() throws SQLException { + List users = new ArrayList<>(); + + User udo = new User( + "udo", + null, + null, + Integer.parseInt("00000001", 2), + null + ); + udo.setPassword("uD0_187!"); + users.add(udo); + + User maria = new User( + "maria", + null, + null, + 0, + null + ); + maria.setPassword("H!mm3lf4hrt"); + users.add(maria); + + UserDao dao = DaoFactory.getInstance().createUserDAO(); + Map usersByUsername = new HashMap<>(); + for (User user : users){ + dao.create(user); + usersByUsername.put(user.getUsername(), user); + } + return usersByUsername; + } +} diff --git a/src/main/java/de/hitec/nhplus/login/User.java b/src/main/java/de/hitec/nhplus/login/User.java new file mode 100644 index 0000000..0621b9d --- /dev/null +++ b/src/main/java/de/hitec/nhplus/login/User.java @@ -0,0 +1,98 @@ +package de.hitec.nhplus.login; + +import de.hitec.nhplus.nurse.Nurse; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +public class User { + + private int id; + private String username; + private byte[] passwordSalt; + private byte[] passwordHash; + private int permissions = 0; + private Nurse nurse; + + public User( + int id, + String username, + byte[] passwordSalt, + byte[] passwordHash, + int permissions, + Nurse nurse + ) { + this.id = id; + this.username = username; + this.passwordSalt = passwordSalt; + this.passwordHash = passwordHash; + this.permissions = permissions; + this.nurse = nurse; + } + + public User( + String username, + byte[] passwordSalt, + byte[] passwordHash, + int permissions, + Nurse nurse + ) { + this.username = username; + this.passwordSalt = passwordSalt; + this.passwordHash = passwordHash; + this.permissions = permissions; + this.nurse = nurse; + } + + public int getId() { + return id; + } + + public byte[] getPasswordSalt() { + return passwordSalt; + } + + public byte[] getPasswordHash() { + return passwordHash; + } + + public void setPassword(String password) { + try { + SecureRandom random = new SecureRandom(); + byte[] salt = new byte[32]; + random.nextBytes(salt); + this.passwordSalt = salt; + MessageDigest md = MessageDigest.getInstance("SHA-512"); + md.update(salt); + this.passwordHash = md.digest(password.getBytes(StandardCharsets.UTF_8)); + }catch (NoSuchAlgorithmException exception){ + exception.printStackTrace(); + } + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public int getPermissions() { + return permissions; + } + + public void setPermissions(int permissions) { + this.permissions = permissions; + } + + public Nurse getNurse() { + return nurse; + } + + public void setNurse(Nurse nurse) { + this.nurse = nurse; + } +} diff --git a/src/main/java/de/hitec/nhplus/login/database/UserDao.java b/src/main/java/de/hitec/nhplus/login/database/UserDao.java new file mode 100644 index 0000000..6ea36ce --- /dev/null +++ b/src/main/java/de/hitec/nhplus/login/database/UserDao.java @@ -0,0 +1,202 @@ +package de.hitec.nhplus.login.database; + +import de.hitec.nhplus.datastorage.Dao; +import de.hitec.nhplus.datastorage.DaoFactory; +import de.hitec.nhplus.login.User; +import de.hitec.nhplus.nurse.Nurse; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +public class UserDao implements Dao { + protected final Connection connection; + + public UserDao(Connection connection) { + this.connection = connection; + } + + public int readUserId(String username) throws SQLException { + final String SQL = "SELECT id FROM user WHERE username = ?"; + PreparedStatement statement = this.connection.prepareStatement(SQL); + statement.setString(1, username); + return statement.executeQuery().getInt(1); + } + + public byte[] readPasswordSalt(int id) throws SQLException { + final String SQL = "SELECT passwordSalt FROM user WHERE id = ?"; + PreparedStatement statement = this.connection.prepareStatement(SQL); + statement.setInt(1, id); + return statement.executeQuery().getBytes(1); + } + + public byte[] readPasswordHash(int id) throws SQLException { + final String SQL = "SELECT passwordHash FROM user WHERE id = ?"; + PreparedStatement statement = this.connection.prepareStatement(SQL); + statement.setInt(1, id); + return statement.executeQuery().getBytes(1); + } + + @Override + public User read(int id) throws SQLException { + final String SQL = """ + SELECT user.username, user.passwordSalt, user.passwordHash, user__permissions.permissions, user__nurse.nurseId + FROM user + LEFT JOIN user__permissions ON user.id = user__permissions.userId + LEFT JOIN user__nurse ON user.id = user__nurse.userId + WHERE user.id = ?; + """; + PreparedStatement statement = this.connection.prepareStatement(SQL); + statement.setInt(1, id); + ResultSet result = statement.executeQuery(); + int nurseId = result.getInt(5); + Nurse nurse = null; + if (!result.wasNull()) { + nurse = DaoFactory.getInstance().createNurseDAO().read(nurseId); + } + return new User( + id, + result.getString(1), + result.getBytes(2), + result.getBytes(3), + result.getInt(4), + nurse + ); + } + + @Override + public void create(User user) throws SQLException { + connection.setAutoCommit(false); //Switch to Manual Commit, to do an SQL Transaction + final String userSQL = """ + INSERT INTO user + (username, passwordSalt, passwordHash) + VALUES (?, ?, ?); + """; + PreparedStatement statement = this.connection.prepareStatement(userSQL); + statement.setString(1, user.getUsername()); + statement.setBytes(2, user.getPasswordSalt()); + statement.setBytes(3, user.getPasswordHash()); + statement.execute(); + + ResultSet generatedKeys = connection.createStatement().executeQuery("SELECT id FROM user"); + connection.commit(); //Finish SQL Transaction + connection.setAutoCommit(true); //Switch back Mode + + if (!generatedKeys.next()) { + return; + } + int newId = generatedKeys.getInt(1); + + final String permissionSQL = """ + INSERT INTO user__permissions + (userId, permissions) + VALUES (?, ?); + """; + PreparedStatement permissionStatement = this.connection.prepareStatement(permissionSQL); + permissionStatement.setInt(1, newId); + permissionStatement.setInt(2, user.getPermissions()); + permissionStatement.execute(); + + if (user.getNurse() == null) { + return; + } + final String nurseSQL = """ + INSERT INTO user__nurse + (userId, nurseId) + VALUES (?, ?); + + + """; + PreparedStatement nurseStatement = this.connection.prepareStatement(nurseSQL); + permissionStatement.setInt(1, newId); + permissionStatement.setInt(2, user.getNurse().getId()); + permissionStatement.execute(); + } + + @Override + public void update(User user) throws SQLException { + final String userSQL = """ + UPDATE user SET + username = ?, + passwordSalt = ?, + passwordHash = ? + WHERE id = ? + """; + PreparedStatement statement = this.connection.prepareStatement(userSQL); + statement.setString(1, user.getUsername()); + statement.setBytes(2, user.getPasswordSalt()); + statement.setBytes(3, user.getPasswordHash()); + statement.setInt(3, user.getId()); + statement.executeUpdate(); + + final String permissionSQL = """ + UPDATE user__permissions SET + permissions = ? + WHERE userId = ? + """; + PreparedStatement permissionStatement = this.connection.prepareStatement(permissionSQL); + permissionStatement.setInt(1, user.getPermissions()); + permissionStatement.setInt(2, user.getId()); + permissionStatement.executeUpdate(); + + if (user.getNurse() == null) { + final String nurseSQL = """ + DELETE FROM user__nurse WHERE userId = ? + """; + this.connection.prepareStatement(nurseSQL).executeUpdate(); + return; + } + + final String nurseSQL = """ + UPDATE user__nurse set + nurseId = ? + WHERE userId = ? + """; + PreparedStatement nurseStatement = this.connection.prepareStatement(nurseSQL); + nurseStatement.setInt(1, user.getNurse().getId()); + nurseStatement.setInt(2, user.getId()); + permissionStatement.executeUpdate(); + } + + @Override + public void delete(int id) throws SQLException { + final String SQL = """ + DELETE FROM user WHERE user.id = ?; + """; + PreparedStatement preparedStatement = this.connection.prepareStatement(SQL); + preparedStatement.setInt(1, id); + preparedStatement.executeUpdate(); + } + + @Override + public List readAll() throws SQLException { + final String SQL = """ + SELECT user.id, user.username, user.passwordSalt, user.passwordHash, user__permissions.permissions, user__nurse.nurseId + FROM user + LEFT JOIN user__permissions ON user.id = user__permissions.userId + LEFT JOIN user__nurse ON user.id = user__nurse.userId + """; + ResultSet result = connection.prepareStatement(SQL).executeQuery(); + + List users = new ArrayList<>(); + while (result.next()) { + int nurseId = result.getInt(6); + Nurse nurse = null; + if (!result.wasNull()) { + nurse = DaoFactory.getInstance().createNurseDAO().read(nurseId); + } + users.add(new User( + result.getInt(1), + result.getString(2), + result.getBytes(3), + result.getBytes(4), + result.getInt(5), + nurse + )); + } + return users; + } +} diff --git a/src/main/resources/de/hitec/nhplus/login/database/User.sql b/src/main/resources/de/hitec/nhplus/login/database/User.sql index b92a04a..8773d00 100644 --- a/src/main/resources/de/hitec/nhplus/login/database/User.sql +++ b/src/main/resources/de/hitec/nhplus/login/database/User.sql @@ -1,7 +1,7 @@ CREATE TABLE user ( id INTEGER PRIMARY KEY AUTOINCREMENT, - username TEXT NOT NULL, + username TEXT NOT NULL UNIQUE, passwordSalt BLOB NOT NULL, passwordHash BLOB NOT NULL ) \ No newline at end of file diff --git a/src/main/resources/de/hitec/nhplus/login/database/UserPermission.sql b/src/main/resources/de/hitec/nhplus/login/database/UserPermission.sql index 536dce0..4e968f2 100644 --- a/src/main/resources/de/hitec/nhplus/login/database/UserPermission.sql +++ b/src/main/resources/de/hitec/nhplus/login/database/UserPermission.sql @@ -1,6 +1,6 @@ CREATE TABLE user__permissions ( - userId INTEGER NOT NULL, - permissions INTEGER, -- Binary Bitmask for Permissions + userId INTEGER NOT NULL UNIQUE, + permissions INTEGER NOT NULL, -- Binary Bitmask for Permissions FOREIGN KEY (userId) REFERENCES user (id) ON DELETE CASCADE ) \ No newline at end of file diff --git a/src/main/resources/de/hitec/nhplus/login/database/UserToNurse.sql b/src/main/resources/de/hitec/nhplus/login/database/UserToNurse.sql index 4507f47..b46be11 100644 --- a/src/main/resources/de/hitec/nhplus/login/database/UserToNurse.sql +++ b/src/main/resources/de/hitec/nhplus/login/database/UserToNurse.sql @@ -1,7 +1,7 @@ CREATE TABLE user__nurse ( - userId INTEGER NOT NULL, - nurseId INTEGER NOT NULL, + userId INTEGER NOT NULL UNIQUE, + nurseId INTEGER NOT NULL UNIQUE, FOREIGN KEY (userId) REFERENCES user (id) ON DELETE CASCADE, FOREIGN KEY (nurseId) REFERENCES nurse (id) ON DELETE CASCADE ) \ No newline at end of file