import java.io.FileNotFoundException; import java.io.FileReader; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.sql.*; import java.util.Scanner; public class Project3 { private static Connection con; private static Scanner in; private static PrintWriter out; private static int loggedInUserId = -1; public static void main(String[] args) { try { Class.forName("oracle.jdbc.driver.OracleDriver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } try { String u = "mocull"; String p = "lyaN8EVc"; con = DriverManager.getConnection("jdbc:oracle:thin:@claros.cs.purdue.edu:1524:strep", u, p); } catch (SQLException e) { e.printStackTrace(); } try { in = new Scanner(new FileReader(args[0])); in.useDelimiter(System.getProperty("line.separator")); out = new PrintWriter(args[1], "UTF-8"); int commandCounter = 0; String command = ""; String result = ""; while (in.hasNext()) { command = in.next(); commandCounter++; out.println(commandCounter + ": " + command); System.out.println(commandCounter + ": " + command); try { result = process(command); } catch (Exception err) { System.out.println("ERROR ON COMMAND " + commandCounter + ":"); err.printStackTrace(); result = "An error occurred, check the logs (stdout) for more information."; } out.println(result); out.println(""); System.out.println(result); System.out.println(""); } out.close(); } catch (FileNotFoundException err) { System.out.println("Input or output file not found."); } catch (UnsupportedEncodingException err) { System.out.println("Input or output file are not UTF-8 encoded."); } } public static String autokeyEncrypt(String plaintext, String key) { //System.out.println(plaintext + " + " + key); key = key.toUpperCase(); String plaintextUpper = plaintext.toUpperCase(); String kPrime = key + plaintext; kPrime = kPrime.toUpperCase().substring(0, plaintext.length()); char[] ciphertextChars = kPrime.toCharArray(); final int toLowerOffset = 'a' - 'A'; int j = 0; for (int i = 0; i < ciphertextChars.length; i++) { while (kPrime.charAt(j) < 'A' || kPrime.charAt(j) > 'Z') { j++; } //System.out.println("j=" + j + " : " + kPrime.charAt(j)); char plainChar = plaintextUpper.charAt(i); if (plainChar >= 'A' && plainChar <= 'Z' && kPrime.charAt(j) >= 'A' && kPrime.charAt(j) <= 'Z') { ciphertextChars[i] = (char) (((kPrime.charAt(j) - 'A') + (plainChar - 'A')) % 26 + 'A'); j++; } else { // Special character. ciphertextChars[i] = plaintextUpper.charAt(i); } } // Convert to capitals after the shift. for (int i = 0; i < ciphertextChars.length; i++) { char plainChar = plaintext.charAt(i); boolean plainIsUpper = plainChar >= 'A' && plainChar <= 'Z'; boolean plainIsLower = plainChar >= 'a' && plainChar <= 'z'; boolean plainIsAlpha = plainIsLower || plainIsUpper; boolean cipherIsUpper = ciphertextChars[i] >= 'A' && ciphertextChars[i] <= 'Z'; boolean cipherIsLower = ciphertextChars[i] >= 'a' && ciphertextChars[i] <= 'z'; if (plainIsAlpha) { if (plainIsUpper && cipherIsLower) { ciphertextChars[i] -= toLowerOffset; } else if (plainIsLower && cipherIsUpper) { ciphertextChars[i] += toLowerOffset; } } } //System.out.println(" -> " + String.valueOf(ciphertextChars)); return String.valueOf(ciphertextChars); } public static String autokeyDecrypt(String ciphertext, String key) { //System.out.println(ciphertext + " + " + key); key = key.toUpperCase(); String ciphertextUpper = ciphertext.toUpperCase(); String kPrime = key; char[] plaintextChars = ciphertext.toCharArray(); final int toLowerOffset = 'a' - 'A'; int j = 0; for (int i = 0; i < ciphertext.length(); i++) { while (kPrime.charAt(j) < 'A' || kPrime.charAt(j) > 'Z') { j++; } if (ciphertextUpper.charAt(i) >= 'A' && ciphertextUpper.charAt(i) <= 'Z' && kPrime.charAt(j) >= 'A' && kPrime.charAt(j) <= 'Z') { int shift = ((ciphertextUpper.charAt(i) - 'A') - (kPrime.charAt(j) - 'A')); if (shift < 0) { shift += 26; } shift = shift % 26 + 'A'; plaintextChars[i] = (char) (shift); kPrime += (char) (shift); j++; } else { // Special character. plaintextChars[i] = ciphertextUpper.charAt(i); } } // Convert to capitals after the shift. for (int i = 0; i < plaintextChars.length; i++) { char caseChar = ciphertext.charAt(i); boolean caseIsUpper = caseChar >= 'A' && caseChar <= 'Z'; boolean caseIsLower = caseChar >= 'a' && caseChar <= 'z'; boolean caseIsAlpha = caseIsLower || caseIsUpper; boolean cipherIsUpper = plaintextChars[i] >= 'A' && plaintextChars[i] <= 'Z'; boolean cipherIsLower = plaintextChars[i] >= 'a' && plaintextChars[i] <= 'z'; if (caseIsAlpha) { if (caseIsUpper && cipherIsLower) { plaintextChars[i] -= toLowerOffset; } else if (caseIsLower && cipherIsUpper) { plaintextChars[i] += toLowerOffset; } } } //System.out.println(" -> " + String.valueOf(plaintextChars)); return String.valueOf(plaintextChars); } public static String process(String command) throws Exception { String[] items = command.split(" "); if (items[0].equalsIgnoreCase("LOGIN")) { return login(items[1], items[2]); } else if (items[0].equalsIgnoreCase("CREATE")) { if (items[1].equalsIgnoreCase("ROLE")) { return createRole(items[2], items[3]); } else if (items[1].equalsIgnoreCase("USER")) { return createUser(items[2], items[3]); } } else if (items[0].equalsIgnoreCase("GRANT")) { if (items[1].equalsIgnoreCase("ROLE")) { return grantRole(items[2], items[3]); } else if (items[1].equalsIgnoreCase("PRIVILEGE")) { return grantPrivilege(items[2], items[4], items[6]); } } else if (items[0].equalsIgnoreCase("REVOKE")) { return revokePrivilege(items[2], items[4], items[6]); } else if (items[0].equalsIgnoreCase("INSERT")) { String[] leftOfValues = command.split("VALUES")[0].trim().split(" "); String[] rightOfEncrypt = command.split("ENCRYPT")[1].trim().split(" "); String values = command.split("VALUES ")[1].trim().split(" ENCRYPT")[0]; return insert(leftOfValues[2], values, Integer.parseInt(rightOfEncrypt[0]), rightOfEncrypt[1]); } else if (items[0].equalsIgnoreCase("SELECT")) { return select(items[1], items[3]); } else if (items[0].equalsIgnoreCase("QUIT")) { return quit(); } throw new Exception("Unknown command."); } private static boolean isAdmin() { return loggedInUserId == 1; } private static boolean hasPermission(String tableName, String privName) { if (isAdmin()) { return true; } int privid = -1; String stmt = "SELECT privid FROM Privileges WHERE privname=?"; try { PreparedStatement ps = con.prepareStatement(stmt); ps.setString(1, privName); ResultSet rs = ps.executeQuery(); while (rs.next()) { privid = rs.getInt("privid"); } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } // SELECT P.roleid, privid, tablename FROM (SELECT roleid FROM UsersRoles WHERE userid=2) R, RolesPrivileges P WHERE P.roleid=R.roleid AND privid=1 AND tablename='Products'; stmt = "SELECT P.roleid, privid, tablename FROM (SELECT roleid FROM UsersRoles WHERE userid=?) R, RolesPrivileges P WHERE P.roleid=R.roleid AND privid=? AND tablename=?"; boolean success = false; try { PreparedStatement ps = con.prepareStatement(stmt); ps.setInt(1, loggedInUserId); ps.setInt(2, privid); ps.setString(3, tableName); ResultSet rs = ps.executeQuery(); if (rs.next()) { success = true; } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } return success; } private static String login(String username, String password) { String stmt = "SELECT R.userid, R.roleid FROM (SELECT userid FROM Users WHERE username=? AND password=?) U, UsersRoles R WHERE U.userid = R.userid"; String exitResponse = "Invalid login"; try { PreparedStatement ps = con.prepareStatement(stmt); ps.setString(1, username); ps.setString(2, password); ResultSet rs = ps.executeQuery(); while (rs.next()) { loggedInUserId = rs.getInt("userid"); exitResponse = "Login successful"; } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); exitResponse = "Invalid login"; } return exitResponse; } private static String createRole(String roleName, String encryptionKey) { String exitResponse = "Authorization failure"; if (isAdmin()) { try { String stmt = "SELECT MAX(roleid) AS roleid FROM Roles"; int maxId = -1; try { PreparedStatement ps = con.prepareStatement(stmt); ResultSet rs = ps.executeQuery(); while (rs.next()) { maxId = rs.getInt("roleid"); } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } if (maxId > 0) { int nextId = maxId + 1; stmt = "INSERT INTO Roles VALUES(?, ?, ?)"; try { PreparedStatement ps = con.prepareStatement(stmt); ps.setInt(1, nextId); ps.setString(2, roleName); ps.setString(3, encryptionKey); int updates = ps.executeUpdate(); if (updates == 1) { exitResponse = "Role created successfully"; } else { throw new Exception("Incorrect number of roles updated."); } } catch(SQLException e) { e.printStackTrace(); } } else { throw new Exception("Failed to retrieve maximum role ID."); } return exitResponse; } catch (Exception err) { err.printStackTrace(); } } return exitResponse; } private static String createUser(String username, String password) { String exitResponse = "Authorization failure"; if (isAdmin()) { try { String stmt = "SELECT MAX(userid) AS userid FROM Users"; int maxId = -1; try { PreparedStatement ps = con.prepareStatement(stmt); ResultSet rs = ps.executeQuery(); while (rs.next()) { maxId = rs.getInt("userid"); } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } if (maxId > 0) { int nextId = maxId + 1; stmt = "INSERT INTO Users VALUES(?, ?, ?)"; try { PreparedStatement ps = con.prepareStatement(stmt); ps.setInt(1, nextId); ps.setString(2, username); ps.setString(3, password); int updates = ps.executeUpdate(); if (updates == 1) { exitResponse = "User created successfully"; } else { throw new Exception("Incorrect number of users updated."); } } catch(SQLException e) { e.printStackTrace(); } } else { throw new Exception("Failed to retrieve maximum user ID."); } return exitResponse; } catch (Exception err) { err.printStackTrace(); } } return exitResponse; } private static String grantRole(String username, String roleName) { String exitResponse = "Authorization failure"; if (isAdmin()) { try { int userid = -1; int roleid = -1; String stmt = "SELECT userid FROM Users WHERE username=?"; try { PreparedStatement ps = con.prepareStatement(stmt); ps.setString(1, username); ResultSet rs = ps.executeQuery(); while (rs.next()) { userid = rs.getInt("userid"); } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } stmt = "SELECT roleid FROM Roles WHERE rolename=?"; try { PreparedStatement ps = con.prepareStatement(stmt); ps.setString(1, roleName); ResultSet rs = ps.executeQuery(); while (rs.next()) { roleid = rs.getInt("roleid"); } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } if (userid > 0 && roleid > 0) { stmt = "INSERT INTO UsersRoles VALUES(?, ?)"; PreparedStatement ps = con.prepareStatement(stmt); ps.setInt(1, userid); ps.setInt(2, roleid); int updates = ps.executeUpdate(); if (updates == 1) { exitResponse = "Role assigned successfully"; } else { throw new Exception("Incorrect number of user roles updated."); } } else { throw new Exception("Failed to retrieve user or role ID."); } return exitResponse; } catch (Exception err) { err.printStackTrace(); } } return exitResponse; } private static String grantPrivilege(String privName, String roleName, String tableName) { String exitResponse = "Authorization failure"; if (isAdmin()) { try { int privid = -1; int roleid = -1; String stmt = "SELECT privid FROM Privileges WHERE privname=?"; try { PreparedStatement ps = con.prepareStatement(stmt); ps.setString(1, privName); ResultSet rs = ps.executeQuery(); while (rs.next()) { privid = rs.getInt("privid"); } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } stmt = "SELECT roleid FROM Roles WHERE rolename=?"; try { PreparedStatement ps = con.prepareStatement(stmt); ps.setString(1, roleName); ResultSet rs = ps.executeQuery(); while (rs.next()) { roleid = rs.getInt("roleid"); } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } if (privid > 0 && roleid > 0) { stmt = "INSERT INTO RolesPrivileges VALUES(?, ?, ?)"; PreparedStatement ps = con.prepareStatement(stmt); ps.setInt(1, roleid); ps.setInt(2, privid); ps.setString(3, tableName); int updates = ps.executeUpdate(); if (updates == 1) { exitResponse = "Privilege granted successfully"; } else { throw new Exception("Incorrect number of role privileges updated."); } } else { throw new Exception("Failed to retrieve privilege or role ID."); } return exitResponse; } catch (Exception err) { err.printStackTrace(); } } return exitResponse; } private static String revokePrivilege(String privName, String roleName, String tableName) { String exitResponse = "Authorization failure"; if (isAdmin()) { try { int privid = -1; int roleid = -1; String stmt = "SELECT privid FROM Privileges WHERE privname=?"; try { PreparedStatement ps = con.prepareStatement(stmt); ps.setString(1, privName); ResultSet rs = ps.executeQuery(); while (rs.next()) { privid = rs.getInt("privid"); } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } stmt = "SELECT roleid FROM Roles WHERE rolename=?"; try { PreparedStatement ps = con.prepareStatement(stmt); ps.setString(1, roleName); ResultSet rs = ps.executeQuery(); while (rs.next()) { roleid = rs.getInt("roleid"); } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } if (privid > 0 && roleid > 0) { stmt = "DELETE FROM RolesPrivileges WHERE roleid=? AND privid=? AND tablename=?"; PreparedStatement ps = con.prepareStatement(stmt); ps.setInt(1, roleid); ps.setInt(2, privid); ps.setString(3, tableName); int updates = ps.executeUpdate(); if (updates == 1) { exitResponse = "Privilege revoked successfully"; } else { throw new Exception("Incorrect number of role privileges updated."); } } else { throw new Exception("Failed to retrieve privilege or role ID."); } return exitResponse; } catch (Exception err) { err.printStackTrace(); } } return exitResponse; } private static String insert(String tableName, String valueList, int columnNo, String ownerRole) { String exitResponse = "Authorization failure"; // https://regex101.com/r/C370lO/1 String valueStrings[] = valueList.substring(2, valueList.length() - 2).split("([\\(\\),\\']+\\s[\\(\\),\\']+)|([\\(\\),\\']+)"); try { if (hasPermission(tableName, "INSERT")) { // We need the roleId regardless. String encryptionKey = ""; int roleId = -1; String stmt = "SELECT encryptionKey, roleid FROM Roles WHERE rolename=?"; try { PreparedStatement ps = con.prepareStatement(stmt); ps.setString(1, ownerRole); ResultSet rs = ps.executeQuery(); while (rs.next()) { encryptionKey = rs.getString("encryptionKey"); roleId = rs.getInt("roleid"); } rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } if (columnNo > 0) { valueStrings[columnNo - 1] = autokeyEncrypt(valueStrings[columnNo - 1], encryptionKey); } // Not as safe, but we will use it for now. String sqlValues = "'" + String.join("', '", valueStrings) + "', '" + columnNo + "', '" + roleId + "'"; stmt = "INSERT INTO " + tableName + " VALUES(" + sqlValues + ")"; try { PreparedStatement ps = con.prepareStatement(stmt); //ps.setString(1, tableName); int updates = ps.executeUpdate(); if (updates == 1) { exitResponse = "Row inserted successfully"; } else { throw new Exception("Incorrect number of rows updated."); } } catch (SQLException e) { e.printStackTrace(); } return exitResponse; } else { return exitResponse; } } catch (Exception e) { e.printStackTrace(); return exitResponse; } } private static String select(String criteria, String tableName) { String exitResponse = "Authorization failure"; try { if (hasPermission(tableName, "SELECT")) { String stmt = "SELECT " + criteria + " FROM " + tableName; try { PreparedStatement ps = con.prepareStatement(stmt); ResultSet rs = ps.executeQuery(); ResultSetMetaData rsmd = rs.getMetaData(); int cols = rsmd.getColumnCount() - 1; // Truncate secret columns. exitResponse = ""; for (int i = 1; i < cols; i++) { exitResponse += rsmd.getColumnLabel(i); if (i != cols - 1) { exitResponse += ", "; } } exitResponse += "\n"; while (rs.next()) { int encryptedColumn = rs.getInt("encryptedColumn"); int ownerRole = rs.getInt("ownerRole"); String encrpytionKey = null; stmt = "SELECT encryptionKey FROM (SELECT roleid FROM UsersRoles WHERE userid=? AND roleid=?) U, Roles R WHERE R.roleid=U.roleid"; try { PreparedStatement psRole = con.prepareStatement(stmt); psRole.setInt(1, loggedInUserId); psRole.setInt(2, ownerRole); ResultSet rsRole = psRole.executeQuery(); while (rsRole.next()) { encrpytionKey = rsRole.getString("encryptionKey"); } rsRole.close(); psRole.close(); } catch (SQLException e) { e.printStackTrace(); } for (int i = 1; i < cols; i++) { String colValue = rs.getString(i); if (i == encryptedColumn && encrpytionKey != null) { colValue = autokeyDecrypt(colValue, encrpytionKey); } exitResponse += colValue; if (i != cols - 1) { exitResponse += ", "; } } exitResponse += "\n"; } exitResponse = exitResponse.substring(0, exitResponse.length() - 1); // Cut off the last \n. rs.close(); ps.close(); } catch (SQLException e) { e.printStackTrace(); } return exitResponse; } else { return exitResponse; } } catch (Exception e) { e.printStackTrace(); return exitResponse; } } private static String quit() { out.close(); System.exit(0); return ""; } }