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 { /** * 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; } /** * 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 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); } }