Overview
CorpPro is an address book for corporate users.
It provides a fast and efficient system for corporate users to store their contacts safely and securely. It is also easy to maneuver and access contacts.
CorpPro is powered by JavaFX and boasts an intuitive GUI (Graphical User Interface) with easy-to-use CLI (Command Line Interface).
This portfolio documents my ability to write java programs, as well as documentation in the form of user and developer guides.
Summary of contributions
-
Feature 1: Added the ability for users to secure their address book by encrypting data.
-
What it does: Upon entering the
password
command followed by a password, it will encrypt the address book data usingPBEWithMD5AndDES
cipher. -
Justification: This feature was implemented so that corporate users can secure sensitive information in accordance to PDPA (Personal Data Protection Act) rules. This reduces the possibilities of data leaks and unwanted access.
-
Highlights: This feature implements a well-known cipher
PBEWithMD5AndDES
andPBEKeySpec
which accepts the user’s password. Once the file is encrypted, it will not be crackable unless done via brute forcing. It also requires file type checking to make sure that the user can compress both encryption and decryption process into one singlepassword
command much like a toggle so that users need not utilise 2 different commands for encrypting and decrypting. -
Credits: Code snippet adapted from Esailija [Stack overflow]
-
-
Feature 2: Implemented smart searching for
find
command.-
What it does: Users can now search for the contacts they want by approximately specifying other traits/ attributes of the contacts. For example, users can search for contacts who live in Jurong West and are tagged as friends.
-
Justification: Corporate users might need to gather rough statistics of their clients or know which worker(s) to contact and assign work.
-
Highlights: This feature uses Levenshtein and Hamming Distance to find the closest match for the keywords entered. It will then sort the matches based on how close it is to the keywords entered and then perform a set intersection on the search results to narrow it down.
-
-
Feature 3: Implemented custom paths for data storage.
-
What it does: Users can now specify the path they wish to save their addressbook.xml to.
-
Justification: Corporate users might want to have separate address books without the need of storing their application in different folders. They can instead edit the preference file to change the name and location of their data storage file path.
-
Highlights: This feature requires implementing the json reader in
UserPrefs
class and storing it as a public variable for other classes to access.
-
-
Code contributed: [Reposense]
-
Other contributions:
-
Project management:
-
Managed releases
v1.1
-v2.0
(5 releases) on GitHub
-
-
Documentation:
-
Did cosmetic tweaks to existing contents of the User Guide
-
-
Community:
-
Tools:
-
Integrated a new Github plugin (Travis ci) to the team repo
-
Integrated a new Github plugin (AppVeyor) to the team repo
-
Integrated a new Github plugin (Coverall) to the team repo
-
-
Contributions to the User Guide
Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users. |
Finding persons by attribute : find
Finds persons whose names contain any of the given keywords.
Format: find PREFIX KEYWORD [MORE_KEYWORDS]
Examples:
-
find n/John
Displaysjohn
andJohn Doe
-
find n/Betsy Tim John
Displays any person having namesBetsy
,Tim
, orJohn
-
find e/example@domain.com
Displays any person having the emailexample@domain.com
-
find a/jurong west ave
Displays any person who lives injurong
,west
,ave
.
To better your experience with search, you should search for specific keywords to get to your results faster. Words such as Jurong would yield closer results compared to Jurong West Ave
|
-
find k/4.0
Displays any person who has a KPI of4.0
-
find d/John is forgetful
Displays any person who has a note containingJohn is forgetful
-
find t/tester
Displays any person who is tagged astester
-
find n/John Betsy r/secretary a/jurong west ave
Displays any person having namesJohn
andBetsy
who is asecretary
and lives injurong west ave
Search guessing
This feature will also display actual Keywords matched and Keywords guessed to give you a good indicator of which results were guessed.
From the above results we can see that Bernicc
yields Keywords guessed: {Bernice}
whereas Alex
yields Keywords matched: {Alex}
Examples:
-
find p/9123
Displays any person having the phone number similar to9123
eg.91231231
,91231234
, and so on.
To narrow down your searches, try to be more specific in the phone number you wish to search for. |
Encrypting data files : password
Encrypts the data file with your password. All commands will be disabled during encryption, your data will be safely protected.
Format: password YOURPASSWORD
You can use the same command to decrypt the file, just hit password and your correct password.
|
Safe analogy
This feature does not actually store passwords. You will have to secure it with a password again after decryption to secure it. Passwords can only be alpha-numeric. |
Treat this feature as if it is a safe, once you have locked it, you have to remember the password in order to unlock it again.
And once you have unlocked it, you are free to set a new password.
Examples:
-
password helloworld
Locks address book with a password string and clears all data.
Entering password helloworld again unlocks the address book and restores all data.
|
Contributions to the Developer Guide
Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project. |
Find and Search
Current Implementation
The find function has been revamped to support search guessing and search by attributes.
FindCommand
is now backed up by the ClosestMatchList
class which uses LevenshteinDistanceUtil
and HammingDistanceUtil
to generate an ordered set of Person
attributes ordered by similarity.
Design Considerations
Aspect: How find command executes
-
Alternative 1: Find using only predicates
-
Pros: Easy to implement.
-
Cons: Search must be exact, cannot have typos or incomplete keywords
-
-
Alternative 2: Store the search results in a
treeMap
ordered by their Levenshtein or Hamming distances from the search keyword-
Pros: Will also consider searches that are similar to what we want and will account for typos or incomplete keywords
-
Cons: Added complexities in finding and searching, can be vague when searching for number attributes
-
-
Alternative 3 (current choice): Same as alternative 2 but we use Hamming distance for phone numbers and KPI attributes instead.
-
Pros: Phone number and KPI searches are now more precise
-
Cons: Added complexities in finding and searching
-
Aspect: Expanded features of find command
-
Alternative 1: Find only by name
-
Pros: Easy to implement.
-
Cons: Can only search by name of addressees
-
-
Alternative 2: Find by attributes
-
Pros: Can search by email, phone, address, etc instead of just the name of addressees
-
Cons: Can only search for one attribute at a time (i.e find by name or find by email)
-
-
Alternative 3 (current choice): Chain-able find attributes
-
Pros: Can search by email and phone and address, etc instead of just one at a time
-
Cons: Added complexities in find command
-
Aspect: Data structure to support the revamped Find command
treeMap
was used to store the search results ordered by their Levenshtein or Hamming distances.
The results are then filtered and
results furthest away from the top few are ignored. The searches will then be passed thru their respective predicates
(NameContainsKeywordsPredicate
, AddressContainsKeywordsPredicate
, EmailContainsKeywordsPredicate
, KpiContainsKeywordPredicate
, NoteContainsKeywordsPredicate
, PhoneContainsKeywordPredicate
, PositionContainsKeywordsPredicate
, TagContainsKeywordsPredicate
)
before filtering the list.
Searching for a contact:
Step 1. The user executes find a/Clementi t/owesMoney
to find all contacts staying in Clementi and bearing the owesMoney
tag.
Step 2. The user input is parsed by AddressBookParser
which creates a new FindCommandParser
object.
Step 3. The arguments a/Clementi t/owesMoney
are then parsed by the FindCommandParser
.
Step 4. FindCommandParser
then checks the validity of the arguments before it creates the FindCommand
object.
Step 5. FindCommand
then proceeds to create ClosestMatchList
objects.
Step 6. It uses the list of keywords obtained from ClosestMatchList
to create AddressContainsKeywordsPredicate
and TagContainsKeywordsPredicate
.
Step 7. These predicates are combined into a combinedPredicate
object using the "AND" operation.
Step 8. The model is then updated by calling model.updateFilteredPersonList(combinedPredicate)
together with the combined predicate obtained in Step 8.
Step 9. A CommandResult
object will be created and an internal method findActualMatches()
will be called to generate a string of keywords that are exact matches and keywords that are guessed.
Figure 1. Interactions inside the logic component for the find a/Clementi t/owesMoney
command.
Figure 2. Interactions inside the ClosestMatchList
class
Data Encryption
Current Implementation
The encrypt/ decrypt mechanism is facilitated by FileEncryptor
.
It extends AddressBook
with a encrypt/decrypt feature, maintained by PasswordCommand
.
Additionally, it implements the following operations:
-
FileEncryptor#process()
— Decrypts or encrypts the data file depending on its current state (encrypted or decrypted). -
VersionedAddressBook#decryptFile()
— encrypts a file given the path and password. -
VersionedAddressBook#encryptFile()
— decrypts a file given the path and password.
Given below is an example usage scenario and how the password mechanism behaves at each step.
Step 1. The user enters the password command with a password.
If the user enters a password which is non alpha-numeric, an error will be thrown at the CommandResult box. Only alpha-numeric passwords are supported by FileEncryptor
|
Step 2. The user closes the address book.
Step 3. The user re-opens the address book. No data will be shown as the XML data file
is technically not present in the data folder.
Step 4. The user enters the password command with the right password. Address book will be refreshed and restored back to its former state (before encryption).
If the user enters the wrong password , an error will be thrown at the CommandResult box. |
Design Considerations
Aspect: How encryption and decryption is done
-
The
PBEKeySpec
is first specified using the "PBEWithMD5AndDES" specification. -
A secret key is generated from
SecretKeyFactory
using "PBEWithMD5AndDES" cipher. -
A
Cipher
is then used to encrypt or decrypt the file with a given password and key specifications. -
Additional salt is used in the password to ensure that the password cannot be easily broken down by dictionary attacks.
Aspect: Pros and cons of tight security
-
Pros: Your data is protected and it will be near impossible to use any third part tool to crack the data file.
-
Cons: Data will be permanently lost if you forget the password.
Encrypting the address book:
Step 1. The user executes password test
to encrypt the address book with test
as the password.
Step 2. PasswordCommandParser
checks for the validity of the input password (if its alpha-numeric)
Step 3. If the password is acceptable, it is parsed to the PasswordCommand
object
Step 4. Within the PasswordCommand
object, a new FileEncryptor
object is created and it will check if the address book is currently in a locked state
Step 5. If it is not currently locked, it will create a cipher and begin encrypting the address book with the input password.
Step 6. Previous addressbook.xml
will be deleted whereas a new addressbook.xml.encrypted
file will be created.
Step 7. A new emptyPredicate
object will be instantiated and model.updateFilteredPersonList(emptyPredicate)
will be called to clear the address book list.
Step 8. A CommandResult
object will be created to notify the user that the encryption was successful
Accessing commands post encryption:
Step 1. The user executes list
to list out all the contacts in the address book.
Step 2. The user input is parsed by AddressBookParser
which creates a new ListCommandParser
object.
Step 3. The arguments are then parsed by the ListCommandParser
.
Step 4. ListCommandParser
then checks the validity of the arguments before it creates the ListCommand
object.
Step 5. ListCommand
creates a FileEncryptor
object to check if the address book is in a locked state by calling the islocked()
method.
Step 6. isLocked() will return true.
Step 7. A CommandException
will be thrown to warn the user that the address book is in a locked state.
Figure 1. Interactions inside the logic component for the password
command.