PROJECT: CorpPro


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 using PBEWithMD5AndDES 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 and PBEKeySpec 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 single password 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]

  • The search is not case sensitive.

  • The order of the keywords does not matter. e.g. Hans Bo will match Bo Hans

  • You can search by name, email, address, key performance index, position, phone number and tags

  • Partial words or words with typos will be matched too eg. 'Hangs' will be matched to 'Hans'

  • Persons matching at least one keyword will be returned (i.e. OR search). e.g. Hans Bo will return Hans Gruber, Bo Yang

  • Only one tag at a time can be searched, eg. find t/helloWorld will find tags cotaining helloWorld and find t/helloWorld t/world will find tags containing world. find will only register the last tag entered.

Examples:

  • find n/John
    Displays john and John Doe

  • find n/Betsy Tim John
    Displays any person having names Betsy, Tim, or John

  • find e/example@domain.com
    Displays any person having the email example@domain.com

  • find a/jurong west ave
    Displays any person who lives in jurong, 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 of 4.0

  • find d/John is forgetful
    Displays any person who has a note containing John is forgetful

  • find t/tester
    Displays any person who is tagged as tester

  • find n/John Betsy r/secretary a/jurong west ave
    Displays any person having names John and Betsy who is a secretary and lives in jurong 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.

find feature1

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 to 9123 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

safe
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.

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.

findFeature seq

Figure 1. Interactions inside the logic component for the find a/Clementi t/owesMoney command.

closestMatchList seq

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
  1. The PBEKeySpec is first specified using the "PBEWithMD5AndDES" specification.

  2. A secret key is generated from SecretKeyFactory using "PBEWithMD5AndDES" cipher.

  3. A Cipher is then used to encrypt or decrypt the file with a given password and key specifications.

  4. 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.

passwordCommand seq

Figure 1. Interactions inside the logic component for the password command.