MyContacts README
Javadoc documentation available at https://awwsmm.github.io/IBAT/.
Motivation
MyContacts is an application created to fulfill the requirements of the term project for IBAT College Dublin's "Advanced Diploma in Computer Programming (Advanced Java)" course during the Fall 2018 semester. The specifications of the project are as follows:
Write a contacts management application that makes use of JavaFX and a JavaDB database or files for persistent storage. Your application should allow users to carry out CRUD like functionality (i.e., Create, Read, Update and Delete). Your application should support different screens for different kinds of functionality, e.g.:
- There should be a login screen
- There should be a mechanism to change your password
- There should be a way to view contacts (all contacts, and then contacts meeting some basic search criteria. You should support searching on 3 different fields. So, for the contact system it would be first name, surname and contact number. Searching must allow combinations of these fields (e.g., surname and contact number). Results should be displayable in different orders, e.g., ascending/descending on surname, ascending/descending on first name).
- There should be a way to create contacts
- There should be a way to edit contacts
- There should be a way to delete one or more contacts based on the same criteria mentioned in the search feature in point 2 above.
- There should be a group feature that enables contacts to be associated with a group (e.g., basketball team group, personal friends’ group, work colleagues’ group, hiking club group).
- As with contacts there should be a mechanism to support CRUD of groups as well as management of membership of such groups.
This app uses an encrypted Apache Derby database for data storage and JavaFX + FXML for the UI.
Project Structure
MyContacts is packaged into a "fat" jar
which can be found at IBAT/target/assignment-1.0-SNAPSHOT-jar-with-dependencies.jar. This project was built with Apache Maven and follows its directory hierarchy guidelines. *.java
source code can be found at IBAT/src/main/java/watson/, while *.fxml
files and a *.java
file which constructs an example database can be found at IBAT/src/main/resources/.
This project has two runtime dependencies, org.apache.derby:derby
v10.14.2.1 and org.controlsfx:controlsfx
v9.0.0, which are packaged into the assignment-1.0-SNAPSHOT-jar-with-dependencies.jar
file (making it a "fat" jar). You should not need to download these dependencies to use this jar
file.
The project also has several build-time Maven dependencies. All project dependencies are listed here.
Getting Started
This jar
can be run in the jshell
(CLI mode) by running the command:
$ jshell --class-path target/assignment-1.0-SNAPSHOT-jar-with-dependencies.jar
...or it can be run in standalone (GUI) mode with the command
$ java -jar target/assignment-1.0-SNAPSHOT-jar-with-dependencies.jar
There is a pre-made example database at IBAT/example/, which was generated by opening the IBAT/src/main/resources/Example.java
script in the jshell
. Note that all of this source code (including the jar
and the example database) can be downloaded by clicking here [4.7MB zip
file].
Fulfillment of Requirements
1. "There should be a login screen"
Running the app in GUI mode opens the app and shows the following login screen:
... this fulfills the first requirement ("There should be a login screen"). The user can log in as the database owner (DBO) or as a regular user. The DBO has the ability to add and delete users, and see all of the tables present in the database. Regular users cannot see anyone's tables but their own and have no power to add or delete users.
The login screen asks for a database name and boot password. If the database name given specifies a database which doesn't exist, that database is created. If the database exists, but the boot password is incorrect, an error is thrown. Presently, the boot password is defined when the database is created and cannot be changed, but there is functionality in Apache Derby to change the boot password. It just hasn't been implemented yet for MyContacts.
As I have the example database set up locally, I'll log in to that one, rather than creating a new database. The database name of the example database is "example", the boot password is "bootpass", the database owner is "owner", and the database owner's password is "ownerpass".
2. "There should be a mechanism to change your password"
Once logged in, all users (including the DBO) have access to an "Account" menu at the top of the app. In it, there is a menu option titled "Change Password":
...clicking on this menu option will open a popup window that prompts the user to change their password, making sure they enter the new password twice. Passwords have minimal restrictions: they cannot be null, they cannot begin or end with any whitespace, and they cannot be empty strings. That's it. Derby trims leading and trailing whitespace and this causes issues with future authentication. Future improvements to this app could include requiring a certain number of characters, etc. in user passwords.
Once the user enters their new password, they will be logged out so they can log back in with the new password:
Additionally, the DBO has the ability to reset the password of any non-DBO user. This option can be found in the "Users" menu, at "Reset Selected Users' Passwords":
This popup doesn't ask for a new password because passwords are reset to the user's username + "pass", which is why multiple users can have their passwords reset at the same time. (For example, "JEFF"'s reset password would be "jeffpass", all lowercase.) This mechanism is in place in case a user forgets his/her password.
Note that the DBO cannot reset their own password -- they must use the "Change Password" function. This is in place as a security measure: "changing" your own password requires you to enter the old password, while "resetting" doesn't. If the DBO were logged in and left their computer unattended, someone walking by could "reset" their password without having to first enter their current password. As user deletion, etc. also requires the DBO to re-enter their password, being able to reset the DBO's password would mean that this person walking by could then have access to these destructive functions, as well. If this would-be hacker doesn't know the DBO's current password, they can't do anything destructive.
3. Viewing and Searching / Filtering Contacts
In MyContacts, the DBO is set up as an administrative account only. The DBO can add and create users, view all tables in the database, and reset users' passwords. The DBO does not have their own list of contacts or groups. Regular users do, however, so I've logged out and logged back in as "susan" (password "susanpass"):
The first thing a regular user sees when they log in to MyContacts is their contacts list. Contacts have first names, surnames, and phone numbers, as well as auto-incrementing ID numbers.
ControlsFX is used for column filtering and sorting, as it's much more powerful than anything I could have written during the allotted time for this assignment. Right-clicking on a column header opens a drop-down list which can be used to filter individual columns:
When a column is being filtered, a small icon appears next to the column name:
Filters can be applied to multiple columns and only those rows which satisfy all of the filters will be displayed. Right-clicking on further columns shows "greyed-out" options, which have been removed by the earlier-applied filters:
Clicking on the column headers also cycles through the display options, which are: default ordering, low-to-high ordering, and high-to-low ordering. The table can only be ordered on one column at a time:
4. "There should be a way to create contacts"
Contacts can be created by clicking on "Contacts" > "Create New Contact":
This opens a pop-up window which prompts the user for the new contact's first name, surname, and phone number. Any (but not all) of these can be left blank, and there is some basic validation done for phone numbers (they can only contain digits and an optional leading '+' sign). All user-entered information is sanitised to prevent injection attacks (usernames can only contain alphanumeric characters and underscores; contact names can only contain alphabetic characters and apostrophes, etc.).
5. "There should be a way to edit contacts"
Contacts can be edited by selecting one from the table and clicking on "Contacts" > "Update Selected Contact" in the menu. Information which is already available for that contact is filled in and the user can change one or more fields, adding, deleting, or updating any information:
6. Deleting Contacts Based on Search Criteria
One or more contacts can be deleted by selecting them from the table and clicking on "Contacts" > "Delete Selected Contacts" in the menu:
The user will be asked to confirm their action, and then the contacts will be deleted:
This can, of course, be combined with the ControlsFX column filters to select and delete only those contacts which meet some specific criteria:
7. Associating Contacts with Groups
One or more contacts can be added to a group by selecting them from the table and clicking on "Contacts" > "Add Selected Contacts to Group" in the menu:
Contacts can be added to an existing group by selecting it from the dropdown menu:
...or added to a new group by entering the new group name:
The user can see an overview of which contacts belong to which group by selecting "Groups" > "Groups Overview" from the menu:
This opens the "Groups" page, which shows which group names are associated with particular contact IDs:
8. CRUD of Groups
Groups can also be created, updated (renamed), and destroyed (deleted). Adding contacts to a new group, as was just shown, creates a new group. Groups can be updated (renamed) by selecting "Groups" > "Rename Group" from the menu:
A popup appears in which the user can select a particular group to rename and assign it a new name:
That name is immediately reflected in the table on the "Groups" page:
One or more groups can be deleted by selecting "Groups" > "Delete Groups" from the menu:
This opens up a window where the user can select one or more groups to delete:
This deletes the associations between particular contacts and particular groups, but does not delete those contacts themselves.
Considerations
As there were limited resources and time available for this project, there are lots of improvements that could be made. However, the important issues having to do with user and information security were mostly handled. I'd like to run through a few things that I think have been done well here and a few things that I think could be improved, if more time were available.
Security
When set up to do so (as it is in MyContacts databases), Derby can require a valid username and password to allow a user to sign in. User login information is then stored internally using SHA-256 encryption. MyContacts also encrypts the database itself using Derby's default DES encryption.
Derby authenticates user login information when opening the database, but provides no mechanism to authenticate it at any later point. For this reason, hashed user passwords and salt are stored in each user's SECURE
table. When performing sensitive operations (changing passwords, etc.), the user is first asked to verify their password and it is checked against the hash and salt in their SECURE
table. Salt strings are generated in PasswordUtils.java
using SecureRandom
, a cryptographically-secure random number generator. Passwords are hashed 65536 (2e16) times using the "PBKDF2WithHmacSHA512" algorithm, resulting in a 512-bit cryptographic key.
There are limited restrictions on user passwords, though: they cannot be null, empty, or start or end with whitespace. That's it. These restrictions are in place because Derby trims leading and trailing whitespace on these fields. Additional restrictions could be implemented by adjusting the addUser()
, changePassword()
, and resetPassword()
methods in Database.java
.
SQL injection attacks are mitigated by use of PreparedStatement
s and some simple input validation. Usernames can only contain alphanumeric characters and underscores, for instance, and cannot be any reserved SQL words. Contact names can only contain letters, spaces, dashes, and apostrophes, and are inserted into the database using PreparedStatement
s. Phone numbers can only contain digits and leading '+' signs. Whenever a user tries to access a particular table, it is checked against the list of existing tables -- if it doesn't exist, no code is run.
There may be other ways to try to crack into this database, but I think I've covered most of the obvious ones.
Phone Numbers
Additional work could be done to expand the available character set for usernames and contact information, if desired. But it was a design choice to not allow any formatting in phone numbers. There is no universally-accepted format for phone numbers, even international ones. The E-123 standard says that spaces can be used for formatting, but the layperson will also use parentheses, hyphens, or full stop characters, as well. A database should contain information only, not formatting. Once the information is pulled out of the database, it can be formatted differently for different purposes.
ControlsFX
You may have noticed that there are gigantic stack traces in the terminal when you run this app. Well...
MyContacts uses the ControlsFX package for table filtering and searching. There is a known bug in the latest release (9.0.0) which causes it to throw, catch, and print the stack trace for a NoSuchMethodException
.
The exception has been fixed and committed to 9.0.1, but this version is not yet available on the Maven repository. So I downloaded the source for 9.0.1 and built it in Gradle. But v9.0.1 has a new bug in the same method, so unfortunately we're stuck with the stack trace output for now.
If you want to try it out, the v9.0.1 jar is available here and it can be installed locally to Maven with
$ mvn install:install-file \
-Dfile=/path/to/controlsfx-9.0.1-SNAPSHOT.jar \
-DgroupId=org.controlsfx \
-DartifactId=controlsfx \
-Dversion=9.0.1 \
-Dpackaging=jar \
-DgeneratePom=true
Then, in the pom.xml
file, change 9.0.0 to 9.0.1 and build this project with Maven.
Logging
Logging should be done with Log4j or SLF4J instead of printing to the terminal.
Testing
No tests exist in the project, but they should be added with JUnit.
Thread Safety
Not considered for this project.
Miscellaneous
Everything that can be accomplished in the UI can also be accomplished on the command line. CLI functionality is (and should be) a superset of GUI functionality.
Contacts
are set up to only have char or varchar fields (see set()
) at the moment. I would have to do some slight retooling of Contact.java
to extend this
The DBO cannot edit user tables in the GUI, but can on command line. This could be implemented if I had more time.
Another feature which could be added would be the ability to export tables to CSV or Excel formats.
In general, the code could be cleaned up a bit: Database.java
is a bit repetitive and could be refactored, and the package uses a mixture of FXML and the Java-language JavaFX API, but should stick to one or the other (it's kind of messy otherwise).
Known Bugs
Hyphens and apostrophes in contact names may not be working correctly.