diff --git a/Main.java b/Main.java index 789fa8c..441f147 100644 --- a/Main.java +++ b/Main.java @@ -1,11 +1,37 @@ import java.sql.Connection; import java.sql.SQLException; +import java.util.List; -import src.db.Database; +import src.models.UserModel; +import src.models.squirrel.Database; +import src.models.squirrel.ModelManager; public class Main { public static void main(String[] args) throws SQLException { + ModelManager.initializeModels(); + Database.migrate = conn -> src.Migration.run(conn); + Database.getConnection(); + + UserModel userModel = ModelManager.get(UserModel.class); + List users = userModel.where(java.util.Collections.emptyMap()); + for (UserModel user : users) { + user.set("name", user.get("name") + " Updated"); + user.save(); + System.out.println(user); + } + Connection conn = Database.getConnection(); + // Example: Run a simple SQL query + try (var stmt = conn.createStatement(); + var rs = stmt.executeQuery("SELECT COUNT(*) AS user_count FROM users")) { + if (rs.next()) { + int userCount = rs.getInt("user_count"); + System.out.println("Total users: " + userCount); + } + } catch (SQLException e) { + e.printStackTrace(); + } + Database.close(); } } diff --git a/src/Migration.java b/src/Migration.java new file mode 100644 index 0000000..6d39736 --- /dev/null +++ b/src/Migration.java @@ -0,0 +1,59 @@ +package src; + +import java.sql.Connection; +import java.sql.Statement; + +public class Migration { + public static void run(Connection conn) { + try (Statement stmt = conn.createStatement()) { + stmt.execute(""" + CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + email TEXT NOT NULL UNIQUE, + password_hash TEXT NOT NULL + ) + """); + + stmt.execute(""" + CREATE TABLE IF NOT EXISTS accounts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + owner_id INTEGER NOT NULL, + type TEXT CHECK(type IN ('CHECKING','SAVINGS','CREDIT')) NOT NULL, + account_number TEXT NOT NULL UNIQUE, + bank_code TEXT NOT NULL, + balance REAL NOT NULL DEFAULT 0, + FOREIGN KEY(owner_id) REFERENCES users(id) + ) + """); + + stmt.execute(""" + CREATE TABLE IF NOT EXISTS giro_accounts ( + id INTEGER PRIMARY KEY, + overdraft_limit REAL DEFAULT 0, + FOREIGN KEY(id) REFERENCES accounts(id) ON DELETE CASCADE + ) + """); + + stmt.execute(""" + CREATE TABLE IF NOT EXISTS spar_accounts ( + id INTEGER PRIMARY KEY, + interest_rate REAL DEFAULT 0, + FOREIGN KEY(id) REFERENCES accounts(id) ON DELETE CASCADE + ) + """); + + stmt.execute(""" + CREATE TABLE IF NOT EXISTS kredit_accounts ( + id INTEGER PRIMARY KEY, + credit_limit REAL DEFAULT 0, + repayment_plan TEXT, + FOREIGN KEY(id) REFERENCES accounts(id) ON DELETE CASCADE + ) + """); + + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/src/models/UserModel.java b/src/models/UserModel.java new file mode 100644 index 0000000..093ca8f --- /dev/null +++ b/src/models/UserModel.java @@ -0,0 +1,75 @@ +package src.models; + +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; + +import src.models.squirrel.Model; +import src.models.squirrel.ModelManager; + +import java.security.SecureRandom; +import java.util.Base64; +import java.util.Arrays; +import java.util.Map; +import java.util.List; + +public class UserModel extends Model { + + { + tableName = "users"; + columns.add("id"); + columns.add("name"); + columns.add("email"); + columns.add("password_hash"); + } + + private static final int ITERATIONS = 65536; + private static final int KEY_LENGTH = 256; + + public static UserModel register(String name, String email, String password) { + if (password == null || password.trim().isEmpty()) { + throw new IllegalArgumentException("Password cannot be empty"); + } + + UserModel userModel = ModelManager.get(UserModel.class); + List existingUser = userModel.where(Map.of("email", email)); + if (existingUser != null && !existingUser.isEmpty()) { + throw new IllegalArgumentException("Email already exists"); + } + + byte[] salt = getSalt(); + UserModel user = new UserModel(); + + user.set("name", name); + user.set("email", email); + user.set("password_hash", hashPassword(password, salt)); + user.create(); + return user; + } + + public static String hashPassword(String password, byte[] salt) { + try { + PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, KEY_LENGTH); + SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); + byte[] hash = skf.generateSecret(spec).getEncoded(); + return Base64.getEncoder().encodeToString(hash); + } catch (Exception e) { + throw new RuntimeException("Failed to hash password", e); + } + } + + public static byte[] getSalt() { + SecureRandom sr = new SecureRandom(); + byte[] salt = new byte[16]; + sr.nextBytes(salt); + return salt; + } + + public static boolean validatePassword(String password, String storedHash, byte[] salt) { + try { + String newHash = hashPassword(password, salt); + return Arrays.equals(Base64.getDecoder().decode(storedHash), Base64.getDecoder().decode(newHash)); + } catch (Exception e) { + throw new RuntimeException("Failed to validate password", e); + } + } +}