/************************************************************************************************** * Program Name : File IO * Author : Terry Weiss * Date : March 4, 2016 * Course/Section : CSC264 - 001 * Program Description: This program allows the user to input usernames and passwords from a file, * and allows users to login and logout of the system. Also, once a user is logged in, they * are able to change their password. Only 1 user may be logged in at any time. Before * shutting the program down, write to a file all of the usernames that were logged in. The * program menu allows users to login, logout, change the password, or quit the program. The * user must enter their current password before they're able to change it. If the old * password is incorrect, they will receive an error. When the user changes their password, * the file is updated. If the user tries to log in while a user is already logged in, they * also get an error. All logins are logged in a separate file. * * Methods: * ------- * Main - Runs the transcript program * Menu Prompt - Displays menu and prompts option * Prompt Filename - Prompts for a filename * Check File Exists - Checks if a file already exists **************************************************************************************************/ import java.util.ArrayList; import java.util.Date; import java.io.File; import java.io.FileReader; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.BufferedWriter; import java.io.IOException; public class FileIOTTW { /********************************************************************************************** *============================================================================================* *| Class variables |* *============================================================================================* **********************************************************************************************/ /********************************************************************************************** *============================================================================================* *| Class methods |* *============================================================================================* **********************************************************************************************/ /********************************************************************************************** * Method Name : FileIO - Main * Author : Terry Weiss * Date : March 4, 2016 * Course/Section : CSC264 - 001 * Program Description: This method asks the user for the filenames of their id/password file * and log file. They are then given a menu to login, logout, change their password or * exit. * * BEGIN Main * Dispaly title for file setup * Prompt login file * WHILE (login file doesn't exist) * Display error that file doesn't exist * Prompt login file * END WHILE * Load login file * Prompt log file * Init login info to an empty username and password * Clear screen * Get menu option * WHILE (option isn't to exit) * SWITCH (option) * CASE login: If no login, set login info to result of attempted log in * IF (no one is already logged in) THEN * Set login info to result of attempted login * ELSE * Display message that no one's logged in * END IF * CASE password: Run change password method * CASE logout: Set username and password to empty strings and announce logout * DEFAULT: Throw unsupported operation exception * END SWITCH * Get new menu option * IF (new menu option is to exit) THEN * Save all password changes to the logins file and update log file * END IF * END WHILE * END Main **********************************************************************************************/ public static void main(String[] args) throws IOException { //Local constants final int FILENAME_PROMPT_PAD = 21; //Character padding for filename prompts final int PROMPT_PAD = 18; //Character padding for menu prompt final int MENU_OPTION_LOGIN = 1; //Menu option for logging in final int MENU_OPTION_PASSWD = 2; //Menu option for changing password final int MENU_OPTION_LOGOUT = 3; //Menu option for logging out final int MENU_OPTION_EXIT = 4; //Menu option for exiting final int LOGIN_USERNAME = 0; //Username value in login info final int LOGIN_PASSWORD = 1; //Password value in login info //Local variables String loginsFilename; //File name of login info file String logFilename; //File name of log file int menuOption; //Menu option selected String[] loginInfo; //Username and password of current login ArrayList loginPairs; //List of ID and password pairs ArrayList loginLog; //Log of all successful logins Library64 clear = new Library64(); //Library utility that clears the screen /********************************** Start Main Method ***********************************/ //Dispaly title for file setup System.out.println("\n" + Console.center("File Setup") + "\n"); //Prompt login file loginsFilename = promptFilename(FILENAME_PROMPT_PAD, "Enter login info file name: "); //WHILE (login file doesn't exist) while (!fileExists(loginsFilename)) { //Display error that file doesn't exist System.out.println(Console.padLeft(FILENAME_PROMPT_PAD, "!! FILE DOESN'T EXIST !!")); //Prompt login file loginsFilename = promptFilename(FILENAME_PROMPT_PAD, "Enter login info file name: "); }//end while //Load login file loginPairs = readLogins(loginsFilename); System.out.println("\n" + Console.padLeft(FILENAME_PROMPT_PAD, "Loaded login info.\n")); //Prompt log file logFilename = promptFilename(FILENAME_PROMPT_PAD, "Enter log file name: "); //Init login log to empty array list loginLog = new ArrayList(); //Init login info to an empty username and password loginInfo = new String[2]; //Clear screen clear.clrscr(); //Get menu option menuOption = menuPrompt(); //WHILE (option isn't to exit) while (menuOption != MENU_OPTION_EXIT) { //SWITCH (option) switch (menuOption) { //CASE login: If no one's logged in, set login info to result of attempted login case MENU_OPTION_LOGIN: //IF (no one is already logged in) THEN if (loginInfo[LOGIN_USERNAME] == null || loginInfo[LOGIN_USERNAME].isEmpty()) { //Set login info to result of attempted login loginInfo = attemptLogin(loginPairs, loginLog); } //ELSE (someone is logged in) else { //Display someone is already logged in System.out.println(Console.padLeft(PROMPT_PAD, loginInfo[LOGIN_USERNAME] + " is already logged in!")); }//end if anyone's logged in break; //CASE password: Run change password method case MENU_OPTION_PASSWD: changePassword(loginPairs, loginInfo); break; //CASE logout: Set username and password to empty strings and announce logout case MENU_OPTION_LOGOUT: //IF (no one is logged in) THEN if (loginInfo[LOGIN_USERNAME] == null || loginInfo[LOGIN_USERNAME].isEmpty()) { System.out.println(Console.padLeft(PROMPT_PAD, "No one's logged in!")); } //ELSE (someone's logged in) else { //Set all login info to empty strings loginInfo[LOGIN_USERNAME] = ""; loginInfo[LOGIN_PASSWORD] = ""; //Display success message System.out.println("\n" + Console.padLeft(PROMPT_PAD, "You have successfully logged out.\n")); }//end if anyone's logged in break; default: throw new UnsupportedOperationException("Unsupported menu option was selected."); }//END SWITCH //Get new menu option menuOption = menuPrompt(); //IF (new menu option was to exit) THEN if (menuOption == MENU_OPTION_EXIT) { //Save all password changes to the logins file and update log file exit(loginsFilename, loginPairs, logFilename, loginLog); } }//END WHILE }//end class main /********************************************************************************************** * Method Name : FileIO - Read Logins * Author : Terry Weiss * Date : March 4, 2016 * Course/Section : CSC264 - 001 * Program Description: This method loads all the usernames and passwords from a file. This * method does not verify if the file exists or if it's formatted correctly. All login * info should be formatted as `NAME PASSWORD` with one space between. There should only * one ID/password pair per line. * * BEGIN Read Logins * Init logins to empty array list * Open the input stream for reading logins file * WHILE (there are more lines in the file) * Add the line to the list, splitting the username and password * END WHILE * Close file reader * Return logins * END Read Logins **********************************************************************************************/ private static ArrayList readLogins(String filename) throws IOException { //Local constants //Local variables ArrayList logins; //List of username and password pairs BufferedReader bFile; //File reader reading the logins file String loginLine; //Current line being read from the file /******************************* Start Read Logins Method *******************************/ //Init logins to empty array list logins = new ArrayList(); //Open the input stream for reading logins file bFile = new BufferedReader(new FileReader(filename)); //Get first line of logins file loginLine = bFile.readLine(); //WHILE (there are more lines in the file) while (loginLine != null) { //Add the line to the list, splitting the username and password logins.add(loginLine.split(" ")); //Get the next line from the logins file loginLine = bFile.readLine(); }//end while there are more lines //Close file reader bFile.close(); //Return success return logins; }//end method Read Logins /********************************************************************************************** * Method Name : FileIO - Menu Prompt * Author : Terry Weiss * Date : March 4, 2016 * Course/Section : CSC264 - 001 * Program Description: This method displays the menu options and prompts the user for their * menu option. * * BEGIN Menu Prompt * Display menu title * Display menu options * Prompt user for option * WHILE (option is out of range) * Display error message about out of range * Prompt user for option * END WHILE * Return menu option * END Menu Prompt **********************************************************************************************/ private static int menuPrompt() { //Local constants final int OPTION_PAD = 31; //Character padding for menu options final int PROMPT_PAD = 18; //Character padding for menu prompt final int FIRST_OPTION = 1; //Value of first menu option final int LAST_OPTION = 4; //Value of last menu option //Local variables int option; //User's selected menu option Library64 clear = new Library64(); //Library utility that clears the screen /******************************* Start Menu Prompt Method *******************************/ //Display menu title System.out.println("\n" + Console.center("Login System") + "\n"); //Display menu options System.out.println(Console.padLeft(OPTION_PAD, "1. Log in")); System.out.println(Console.padLeft(OPTION_PAD, "2. Change password")); System.out.println(Console.padLeft(OPTION_PAD, "3. Log out")); System.out.println(Console.padLeft(OPTION_PAD, "4. Exit")); //Prompt user for option System.out.print(Console.padLeft(PROMPT_PAD, "Please choose a menu option: ")); option = Keyboard.readInt(); //WHILE (option is out of range) while (option < FIRST_OPTION || option > LAST_OPTION) { //Display error message about out of range System.out.print("Invalid option. "); //Prompt user for option System.out.print("Choose a menu option: "); option = Keyboard.readInt(); }//end while (option is out of range) //return menu option return option; }//end class menu prompt /********************************************************************************************** * Method Name : FileIO - Prompt Filename * Author : Terry Weiss * Date : March 4, 2016 * Course/Section : CSC264 - 001 * Program Description: This method prompts for a filename. If the original filename is empty * or just spaces, they are given an error message and prompted again. After the first * time, the trailing whitespace of the prompt message is removed to avoid overflowing the * line as easily with the error message. * * BEGIN Prompt Filename * Prompt user for a filename * WHILE (filename is empty) * Display error message about invalid filename * Prompt user for a filename * END WHILE * Return filename * END Prompt Filename **********************************************************************************************/ private static String promptFilename(int pad, String prompt) { //Local constants //Local variables String filename; //New filename /***************************** Start Prompt Filename Method *****************************/ //Prompt user for a filename System.out.print(Console.padLeft(pad, prompt)); filename = Keyboard.readString().trim(); //WHILE (filename is empty) while (filename.isEmpty()) { //Display error message about invalid filename System.out.print(Console.padLeft(pad, "Invalid filename. ")); //Prompt user for a filename System.out.print(prompt.trim() + " "); //Replaces original trailing space with 1 space filename = Keyboard.readString(); } //Return filename return filename; }//end method prompt filename /********************************************************************************************** * Method Name : FileIO - File Exists * Author : Terry Weiss * Date : March 4, 2016 * Course/Section : CSC264 - 001 * Program Description: This method confirms if a file exists. * * BEGIN File Exists * Create new file object and check if it exists * END File Exists **********************************************************************************************/ private static boolean fileExists(String filename) { //Local constants //Local variables /******************************* Start File Exists Method *******************************/ //Create new file object and check if it exists return (new File(filename).exists()); }//end method file exists /********************************************************************************************** * Method Name : FileIO - Attempt Login * Author : Terry Weiss * Date : March 4, 2016 * Course/Section : CSC264 - 001 * Program Description: This method attempts to log in a user. The user is prompted for a * username and password. If the username isn't found, the login fails. Otherwise, the * user is prompted for their password. If the password doesn't match the username's * password, the login also fails. If the login fails, a login array with an empty * username and password is returned. Otherwise, a login array with the matching username * and password is returned, and the login is written to the log file. If the login is * successful, the username and time stamp will be added to the given log ArrayList. * * BEGIN Attempt Login * Init index to 0 * Prompt username * WHILE (username is empty) * Display invalid username and reprompt * END WHILE * WHILE (the username doesn't match AND there are more login pairs) //find username's index * Increment index * END WHILE * IF (username was found) THEN * Init attempts to 0 * Prompt password * WHILE (more attempts are left AND password doesn't match) * Increment attempt * Display error message and reprompt * END WHILE * IF (password attempt matches real password) THEN * Set login's password to password * Set login's username to username * Add username to log * Display successful login * END IF * ELSE * Display username doesn't exist * END IF * END Attempt Login **********************************************************************************************/ private static String[] attemptLogin(ArrayList pairs, ArrayList log) { //Local constants final int LOGIN_FIELDS = 2; //Number of fields returned (currently id & passwd) final int LOGIN_USERNAME = 0; //Username index in login info final int LOGIN_PASSWORD = 1; //Password index in login info final int MAX_ATTEMPTS = 2; //Additional attempts after initial password failure final int PROMPT_PAD = 30; //Character pad for prompt //Local variables int index; //Current index of login pair int attempts; //Number of additional attempts for correct password String username; //User's username String passwordAttempt; //User's password attempt String password; //Username's real password String[] login; //User's login info String logLine; //Read username and password /****************************** Start Attempt Login Method ******************************/ //Init index to 0 index = 0; //Init login to an empty username and password login = new String[LOGIN_FIELDS]; login[LOGIN_USERNAME] = ""; login[LOGIN_USERNAME] = ""; //Prompt for a username System.out.print(Console.padLeft(PROMPT_PAD, "Enter your username: ")); username = Keyboard.readString().trim(); //WHILE (username is empty) while (username.isEmpty()) { //Display invalid username and reprompt System.out.print(Console.padLeft(PROMPT_PAD, "Invalid username. Enter your username: ")); username = Keyboard.readString().trim(); } /* * This is to find the username's index if it exists. If it doesn't exist, index will end * up equaling the array list's size. */ //WHILE (there are more login pairs AND the username doesn't match) while (index < pairs.size() && !pairs.get(index)[LOGIN_USERNAME].equalsIgnoreCase(username)) { //Increment index index++; } //IF (username was found) THEN if (index < pairs.size()) { //Init attempts to 0 attempts = 0; //Prompt password System.out.print(Console.padLeft(PROMPT_PAD, "Enter your password: ")); password = Keyboard.readString(); //WHILE (more attempts are left AND password doesn't match) while (!pairs.get(index)[LOGIN_PASSWORD].equals(password) && attempts < MAX_ATTEMPTS) { //Increment attempt attempts++; //Display error message and reprompt System.out.print(Console.padLeft(PROMPT_PAD, "Invalid password. Please enter your password: ")); password = Keyboard.readString(); }//end while //IF (password attempt matches real password) THEN if (pairs.get(index)[LOGIN_PASSWORD].equals(password)) { //Set login's password to password login[LOGIN_USERNAME] = username; //Set login's username to username login[LOGIN_PASSWORD] = password; //Add username to log log.add(String.format("%-20s %s", username, new Date())); //Display successful login System.out.println("\n" + Console.center("Welcome back, " + username + "!")); }//end if password matches }//end if username was found //ELSE (username wasn't found) else { //Display message that username wasn't found. System.out.println(Console.padLeft(PROMPT_PAD, "Username doesn't exist!")); } //Return login return login; }//end method attempt login /********************************************************************************************** * Method Name : FileIO - Change Password * Author : Terry Weiss * Date : March 4, 2016 * Course/Section : CSC264 - 001 * Program Description: This method changes the password of the current login if someone is * currently logged in. A success or failure message is printed to the console based on * if anyone is logged in. Passwords must have at least 5 characters to be valid. A user * must confirm their current password before being able to change their password. * * BEGIN Change Password * IF (no one is logged in) THEN * Display error message * ELSE * Prompt for current password * IF (password doesn't match) THEN * Display error * ELSE * Store index of current login info * Prompt for new password * WHILE (password is too short) * Reprompt for new password * END WHILE * Set login info's password to entered password * Create temporary login info * Replace index with temporary login info * Display success message * END IF * END IF * END Change Password **********************************************************************************************/ public static void changePassword(ArrayList loginPairs, String[] loginInfo) { //Local constants final boolean APPEND = true; //Append to a file when writing final int LOGIN_USERNAME = 0; //Username index in login info final int LOGIN_PASSWORD = 1; //Password index in login info final int LOGIN_FIELDS = 2; //Number of login fields saved final int PAD = 18; //Character padding for output final int MIN_LENGTH = 5; //Minimum password length //Local variables BufferedWriter writer; //Utility object writes to file writer boolean confirmed; //Whether current password was confirmed int index; //Index of login String passwordAttempt; //User's attempt to confirm password String newPassword; //User's new password String[] temp; //Temporary login info /***************************** Start Change Password Method *****************************/ //IF (no one is logged in) THEN if (loginInfo[LOGIN_USERNAME] == null || loginInfo[LOGIN_USERNAME].isEmpty()) { //Display error message System.out.println(Console.padLeft(PAD, "Please log in to change your password.\n")); } //ELSE (someone is logged in) else { //Prompt for current password System.out.print(Console.padLeft(PAD, "Enter current password: ")); passwordAttempt = Keyboard.readString(); //IF (password doesn't match) THEN if (!passwordAttempt.equals(loginInfo[LOGIN_PASSWORD])) { //Display error System.out.println(Console.padLeft(PAD, "Incorrect password.\n")); } //ELSE (password matches) else { //Store index of current login info index = getIndex(loginPairs, loginInfo); //Prompt for new password System.out.print(Console.padLeft(PAD, "Enter new password: ")); newPassword = Keyboard.readString(); //WHILE (password is too short) while (newPassword.length() < MIN_LENGTH) { //Reprompt for new password System.out.print(Console.padLeft(PAD, "Enter new password (min " + MIN_LENGTH + " characters): ")); newPassword = Keyboard.readString(); }//end while password is too short //Set login info's password to entered password loginInfo[LOGIN_PASSWORD] = newPassword; //Create temporary login info temp = new String[LOGIN_FIELDS]; temp[LOGIN_USERNAME] = loginInfo[LOGIN_USERNAME]; temp[LOGIN_PASSWORD] = newPassword; //Replace index with temporary login info loginPairs.set(index, temp); //Display success message System.out.println(Console.padLeft(PAD, "Password changed successfully!\n")); }//end if password matches }//end if someone is logged in }//end method change password /********************************************************************************************** * Method Name : FileIO - Get Index * Author : Terry Weiss * Date : March 4, 2016 * Course/Section : CSC264 - 001 * Program Description: This method gets the index of a given login in the login list. A * login matches with a case insensitive username and case sensitive password. * * BEGIN Get Index * Init found to false * Init index to 0 * WHILE (there are more logins AND login wasn't found) * IF (username/password matches current pair's username/password) * Set found to true * ELSE * Increment index * END IF * END WHILE * IF (login wasn't found) THEN * Set index to not found * END IF * Return index * END Get Index **********************************************************************************************/ private static int getIndex(ArrayList loginPairs, String[] loginInfo) { //Local constants final int LOGIN_USERNAME = 0; //Username index in login info final int LOGIN_PASSWORD = 1; //Password index in login info final int NOT_FOUND = -1; //Index if login wasn't found //Local variables boolean found; //Whether login was found int index; //Index of login /******************************* Start File Exists Method *******************************/ //Init found to false found = false; //Init index to 0 index = 0; //WHILE (there are more logins AND login wasn't found) while (index < loginPairs.size() && !found) { //IF (username/password matches current pair's username/password) if (loginPairs.get(index)[LOGIN_USERNAME].equalsIgnoreCase(loginInfo[LOGIN_USERNAME]) && loginPairs.get(index)[LOGIN_PASSWORD].equals(loginInfo[LOGIN_PASSWORD])) { //Set found to true found = true; } //ELSE username/passwords don't match else { //Increment index index++; }//end if username/password matches }//end while more logins and not found //IF (login wasn't found) THEN if (!found) { //Set index to not found index = NOT_FOUND; } //Return index return index; }//end method file exists /********************************************************************************************** * Method Name : FileIO - Exit * Author : Terry Weiss * Date : March 4, 2016 * Course/Section : CSC264 - 001 * Program Description: This method saves the new login info and log to their files before * the program exits. * * BEGIN Exit * Init empty writer * TRY to write the pairs * Open writer to write logins info back to file * FOR (each login pair) * Write username and password to the file with a space between them * END FOR * CATCH (IO exception) * Display unable to write to logins file * FINALLY * Flush writer * Close writer * END TRY-CATCH-FINALLY * TRY to write the log * IF (log file exists) THEN * Open writer to append log back to file * ELSE * Open writer to write log back to file * END IF * FOR (each log entry) * Write the log entry to log file * END FOR * CATCH (IO Exception) * Display unable to write to log file * FINALLY * Flush writer * Close writer * END TRY-CATCH-FINALLY * END Exit **********************************************************************************************/ private static void exit(String loginsFilename, ArrayList pairs, String logFilename, ArrayList log) throws IOException { //Local constants final boolean APPEND = true; //Append to a file when writing final int LOGIN_USERNAME = 0; //Username index in login info final int LOGIN_PASSWORD = 1; //Password index in login info final int PAD = 18; //Character padding for output //Local variables BufferedWriter writer; //Utility object writes to file writer /********************************** Start Exit Method ***********************************/ //Init empty writer writer = null; //TRY to write the pairs try { //Open writer to write logins info back to file writer = new BufferedWriter(new FileWriter(loginsFilename)); //FOR (each login pair) for (String[] pair : pairs) { //Write username and password to the file with a space between them writer.write(pair[LOGIN_USERNAME] + " " + pair[LOGIN_PASSWORD] + "\n"); } } //CATCH (IO exception) catch (IOException e) { //Display unable to write to logins file System.out.println(Console.center("!! UNABLE TO WRITE TO LOGINS FILE !!")); } //FINALLY finally { //Flush writer writer.flush(); //Close writer writer.close(); }//end try-catch-finally writing to logins info file //TRY to write the log try { //IF (log file exists) THEN if (fileExists(logFilename)) { //Open writer to append log back to file writer = new BufferedWriter(new FileWriter(logFilename, APPEND)); } //ELSE (log file doesn't exist) else { //Open writer to write log back to file writer = new BufferedWriter(new FileWriter(logFilename)); }//end if log file exists //FOR (each log entry) for (String entry : log) { //Write the log entry to log file writer.write(entry + "\n"); }//end for each log entry } //CATCH (IO Exception) catch (IOException e) { //Display unable to write to log file System.out.println(Console.center("!! UNABLE TO WRITE TO LOG FILE !!")); } //FINALLY finally { //Flush writer writer.flush(); //Close writer writer.close(); }//end try-catch-finally writing to log file }//end method exit }//end class File IO