package com.bib.essensbestellungsverwaltung; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; import java.util.ArrayList; import java.util.Base64; import java.util.List; /** * A collection of functions loosely related to account management * Acts as an abstraction layer to the database * * @author Malte Schulze Hobeling */ public class AccountMgr { static User currentUser = null; /** * creates a user with createUser(...) and adds its id to the 'worker' table * * @param worker the worker to be created * @return userid or -1 * @author Malte Schulze Hobeling */ protected static long createWorker(Worker worker) { long id = createUser(worker); String sId = String.valueOf(id); Database.insert("worker", new String[] { "userid" }, new String[] { sId }); return id; } /** * creates a user with createUser(...) and adds its id to the 'parent' table * * @param parent the parent to be created * @return userid or -1 * @author Malte Schulze Hobeling */ protected static long createParent(Parent parent) { long id = createUser(parent); String sId = String.valueOf(id); Database.insert("parent", new String[] { "userid" }, new String[] { sId }); return id; } /** * adds a user to the database * * @param user the user to be created * @return userid or -1 * @author Malte Schulze Hobeling */ protected static long createUser(User user) { String[] userH = { "name", "firstname", "addressid", "password", "email" }; String name = user.getName(); String firstname = user.getFirstname(); String pw = hashAndSalt(user.getPassword(), getSalt()); String email = user.getEmail(); long addressId = user.getAddress().getId(); if (addressId < 1) { addressId = createAddress(user.getAddress()); } String[] userD = { name, firstname, String.valueOf(addressId), pw, email }; return Database.insert("user", userH, userD); } /** * adds an address to the database * * @param address the address to be created * @return id or -1 * @author Malte Schulze Hobeling */ protected static long createAddress(Address address) { String[] addressH = { "street", "number", "plz", "city" }; String[] addressD = { address.getStreet(), address.getNumber(), address.getPlz(), address.getCity() }; return Database.insert("address", addressH, addressD); } /** * adds a child and allergies to the database * * @param child the child to be created * @return id of child or -1 * @author Malte Schulze Hobeling */ protected static long createChild(Child child) { String[] childH = { "name", "firstname", "addressid" }; String[] childD = { child.getName(), child.getFirstname(), String.valueOf(child.getAddress().getId()) }; long id = Database.insert("child", childH, childD); String[] child_allergyH = { "childid", "allergyid", "severityid" }; for (AllergySeverity allergy : child.getAllergies()) { String sId = String.valueOf(id); String sAllergyId = String.valueOf(allergy.getAllergy().getId()); String sSeverityId = String.valueOf(allergy.getSeverityId()); String[] child_allergyD = { sId, sAllergyId, sSeverityId }; Database.insert("child_allergy", child_allergyH, child_allergyD); } return id; } /** * update Child * * @param child the child to be updated * @return id of child * @author Johannes Kantz */ protected static long updateChild(Child child) { String[] childH = { "id", "name", "firstname", "addressid" }; String[] childD = { String.valueOf(child.getId()), child.getName(), child.getFirstname(), String.valueOf(child.getAddress().getId()) }; long updates = Database.update("child", childH, childD); String[] child_allergyH = { "childid", "allergyid", "severityid" }; Database.select("child_allergy", new String[] { "childid" }, new String[] { String.valueOf(child.getId()) }) .stream().forEach(row -> { String allergyId = row.split(":")[0]; Database.delete("child_allergy", Integer.parseInt(allergyId)); }); for (AllergySeverity allergy : child.getAllergies()) { String sId = String.valueOf(child.getId()); String sAllergyId = String.valueOf(allergy.getAllergy().getId()); String sSeverityId = String.valueOf(allergy.getSeverityId()); String[] child_allergyD = { sId, sAllergyId, sSeverityId }; Database.insert("child_allergy", child_allergyH, child_allergyD); } return updates; } /** * delete Child * * @author Johannes Kantz */ protected static void deleteChildWithId(long id) { Database.delete("child", id); Database.select("child_allergy", new String[] { "childid" }, new String[] { String.valueOf(id) }).stream() .forEach(row -> { String allergyId = row.split(":")[0]; Database.delete("child_allergy", Integer.parseInt(allergyId)); }); } /** * returns a User(Worker | Parent) for a given id or null if no unique id was * found * * @param id id of the User * @return User(Worker | Parent) or null * @author Malte Schulze Hobeling */ protected static User getUserById(long id) { List entry = Database.getEntryById("user", id); if (entry.size() != 1) { return null; } String[] parts = entry.get(0).split(":"); Address address = getAddressById(id); if (isWorker(String.valueOf(id))) { return new Worker(id, parts[1], parts[2], parts[4], parts[5], address); } else { String[] parent_childH = { "parentuserid" }; String[] parent_childD = { String.valueOf(id) }; List children = new ArrayList<>(); List parent_childEntries = Database.select("parent_child", parent_childH, parent_childD); for (String parent_childEntry : parent_childEntries) { String[] parent_childParts = parent_childEntry.split(":"); children.add(getChildById(Long.parseLong(parent_childParts[2]))); } return new Parent(id, parts[1], parts[2], parts[4], parts[5], address, children); } } /** * returns a Child for a given id or null if no unique id was found * * @param id id of child * @return Child or null * @author Malte Schulze Hobeling */ protected static Child getChildById(long id) { List entry = Database.getEntryById("child", id); if (entry.size() != 1) { return null; } String[] parts = entry.get(0).split(":"); String[] child_allergyH = { "childid" }; String[] child_allergyD = { String.valueOf(id) }; List entriesAllergy = Database.select("child_allergy", child_allergyH, child_allergyD); List allergySeverities = new ArrayList<>(); for (String entryAllergy : entriesAllergy) { String[] allergyParts = entryAllergy.split(":"); List severity = Database.getEntryById("severity", Long.parseLong(allergyParts[3])); String sSeverity = severity.get(0).split(":")[1]; long lSeverity = Long.parseLong(severity.get(0).split(":")[0]); allergySeverities.add( new AllergySeverity(FoodMgr.getAllergyById(Long.parseLong(allergyParts[2])), lSeverity, sSeverity)); } return new Child(id, parts[1], parts[2], getAddressById(Long.parseLong(parts[3])), allergySeverities); } /** * returns all Children * * @return {List getAllChildren() { List entry = Database.getTable("child"); if (entry.size() < 1) { return new ArrayList<>(); } List children = new ArrayList<>(); for (String s : entry) { String[] parts = s.split(":"); String[] child_allergyH = { "childid" }; String[] child_allergyD = { String.valueOf(parts[0]) }; List entriesAllergy = Database.select("child_allergy", child_allergyH, child_allergyD); List allergySeverities = new ArrayList<>(); for (String entryAllergy : entriesAllergy) { String[] allergyParts = entryAllergy.split(":"); List severity = Database.getEntryById("severity", Long.parseLong(allergyParts[3])); String sSeverity = severity.get(0).split(":")[1]; long lSeverity = Long.parseLong(severity.get(0).split(":")[0]); allergySeverities.add(new AllergySeverity(FoodMgr.getAllergyById(Long.parseLong(allergyParts[2])), lSeverity, sSeverity)); } children.add(new Child(Long.parseLong(parts[0]), parts[1], parts[2], getAddressById(Long.parseLong(parts[3])), allergySeverities)); } return children; } /** * returns all Children from parent * @param id parentid * @return {List getAllChildrenFromParentWithId(long id) { List entry = Database.select("parent_child", new String[] { "parentuserid" }, new String[] { String.valueOf(id) }); if (entry.size() < 1) { return new ArrayList<>(); } List childIds = new ArrayList<>(); for (String s : entry) { String[] parts = s.split(":"); childIds.add(parts[2]); } List children = new ArrayList<>(); for (String s : childIds) { List child = Database.getEntryById("child", Long.parseLong(s)); String[] parts = child.get(0).split(":"); String[] child_allergyH = { "childid" }; String[] child_allergyD = { String.valueOf(parts[0]) }; List entriesAllergy = Database.select("child_allergy", child_allergyH, child_allergyD); List allergySeverities = new ArrayList<>(); for (String entryAllergy : entriesAllergy) { String[] allergyParts = entryAllergy.split(":"); List severity = Database.getEntryById("severity", Long.parseLong(allergyParts[3])); String sSeverity = severity.get(0).split(":")[1]; long lSeverity = Long.parseLong(severity.get(0).split(":")[0]); allergySeverities.add(new AllergySeverity(FoodMgr.getAllergyById(Long.parseLong(allergyParts[2])), lSeverity, sSeverity)); } children.add(new Child(Long.parseLong(parts[0]), parts[1], parts[2], getAddressById(Long.parseLong(parts[3])), allergySeverities)); } return children; } /** * returns an Address for a given id or null if no unique id was found * * @param id id of the address * @return Address or null * @author Malte Schulze Hobeling */ protected static Address getAddressById(long id) { List entry = Database.getEntryById("address", id); if (entry.size() != 1) { return null; } String[] parts = entry.get(0).split(":"); return new Address(Long.parseLong(parts[0]), parts[1], parts[2], parts[3], parts[4]); } /** * creates entries in the database to match parent to child * * @param parentId id of parent * @param childId id of child * @return id of parent_child or -1 * @author Malte Schulze Hobeling */ protected static long matchParentChild(String parentId, String childId) { String[] parent_childH = { "parentuserid", "childid" }; String[] parent_childD = { parentId, childId }; return Database.insert("parent_child", parent_childH, parent_childD); } /** * a simple login to check if a given email matches a password * * @param email email * @param pw password * @return id or -1 * @author Malte Schulze Hobeling */ protected static long login(String email, String pw) { String[] pwH = { "email" }; String[] pwD = { email }; List foundEmail = Database.select("user", pwH, pwD); String salt; if (foundEmail.size() == 1) { String[] userParts = foundEmail.get(0).split(":"); String[] pwParts = userParts[4].split("\\."); salt = pwParts[1]; } else { // no unique user found; still calculating a hash for security reasons salt = getSalt(); } String[] userH = { "email", "password" }; String[] userD = { email, hashAndSalt(pw, salt) }; return Database.getSingleId("user", userH, userD); } /** * checks if id is in worker table * * @param id userid * @return true if id is in worker table * @author Malte Schulze Hobeling */ protected static boolean isWorker(String id) { String[] workerH = { "userid" }; String[] workerD = { id }; long workerId = Database.getSingleId("worker", workerH, workerD); return workerId > 0; } /** * checks if id is in parent table * * @param id userid * @return true if id is in parent table * @author Malte Schulze Hobeling */ protected static boolean isParent(String id) { String[] parentH = { "userid" }; String[] parentD = { id }; long parentId = Database.getSingleId("parent", parentH, parentD); return parentId > 0; } /** * returns a hashed and salted password * * @param pw the password to hash * @return hashed and salted password * @author Malte Schulze Hobeling */ private static String hashAndSalt(String pw, String salt) { Base64.Decoder dec = Base64.getDecoder(); byte[] bySalt = dec.decode(salt); KeySpec spec = new PBEKeySpec(pw.toCharArray(), bySalt, 310001, 256); String hashedPw; try { SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); byte[] hash = factory.generateSecret(spec).getEncoded(); Base64.Encoder enc = Base64.getEncoder(); hashedPw = enc.encodeToString(hash); } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { throw new RuntimeException(e); } hashedPw += "." + salt; return hashedPw; } /** * generates a secure random salt, Base64 encoded * * @return String Base64 encoded * @author Malte Schulze Hobeling */ private static String getSalt() { SecureRandom sec = new SecureRandom(); byte[] salt = new byte[16]; sec.nextBytes(salt); Base64.Encoder enc = Base64.getEncoder(); return enc.encodeToString(salt); } /** * gives the invoice for one month and one child * * @param date YYYY-MM the month * @param childId id of child * @return the invoice as a List * @author Malte Schulze Hobeling */ protected static List getInvoice(String date, String childId) { List invoice = new ArrayList<>(); List child = Database.getEntryById("child", Long.parseLong(childId)); if (child.size() != 1) { return invoice; } invoice.add("Monatsabrechnung " + date); String[] childParts = child.get(0).split(":"); invoice.add(childParts[1] + ", " + childParts[2]); String[] food_planH = { "date" }; String[] food_planD = { date + "%" }; List food_plan = Database.select("food_plan", food_planH, food_planD); for (String day : food_plan) { String[] food_planParts = day.split(":"); String[] food_selectionH = { "childid", "food_planid" }; String[] food_selectionD = { childId, food_planParts[0] }; List food_selection = Database.select("food_selection", food_selectionH, food_selectionD); for (String food_select : food_selection) { String[] food_selectParts = food_select.split(":"); List food = Database.getEntryById("food", Long.parseLong(food_selectParts[3])); String[] foodParts = food.get(0).split(":"); String line = food_planParts[1] + ": " + foodParts[1]; invoice.add(line); } } double price = getPrice(); invoice.add("Total: " + (invoice.size() - 2) + " X " + price + "€ = " + ((invoice.size() - 2) * price) + "€"); return invoice; } /** * gets the price per meal from the database and converts it to double * * @return double price * @author Malte Schulze Hobeling */ protected static double getPrice() { List priceEntry = Database.getEntryById("price", 1); return Double.parseDouble(priceEntry.get(0).split(":")[1]) / 100.0; } /** * converts the price per meal to integer and updates it in the database * * @param price double * @author Malte Schulze Hobeling */ protected static void setPrice(double price) { String[] priceH = { "id", "price" }; String[] priceD = { "1", String.valueOf((int) (price * 100)) }; Database.update("price", priceH, priceD); } }