Overview
Concierge™ is a desktop hotel management application for receptionists to handle potential bookings and current guests. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
Summary of contributions
Access my contributed code here. |
Major enhancement: added the ability to login/logout of the system.
-
What it does: Allows users to login to Concierge and access restricted commands which mutate the data.
-
Significance: With this feature, hotel managers can implement some level of access control. Some features more commonly used (
find
,list
) can still be accessed without signing in. By combining this with the ability to export the command history, the auditing process for rogue commands is expedited. -
Highlights:
-
The login/logout feature is dynamic - the users are not prompted to sign in upon starting Concierge, and users can logout and log back in within the same session. This means that there is a very close integration with the existing commands to verify the sign-in status and whether the commands require signing-in.
-
This feature is complete - I worked with the different architectural components of Concierge, from command parsing (
Logic
) to login verification (Model
) and even password storage (Storage
). -
The feature attempts to achieve some level of security with SHA-256 hashing.
-
-
Credits: The password hash algorithm was taken from Baeldung.
Minor enhancements/ code contributed:
-
Renamed the existing classes and methods from
Person
toGuest
-
Removed
Address
as a field inPerson
, removed the edit and delete commands -
Modified the
add
command to take in room and date details. Room and Booking package by others. -
Modified the
clear
command to maintain empty hotel rooms -
Added function for GUI verification of rooms
-
Added most of the Appendices in the Developer Guide
Project Management:
Contributions to the User Guide
My contributions to the User Guide below showcase my ability to write documentation targeting end-users. |
Adding a booking: add
Adds a booking associated with a guest, room and booking period.
Format: add n/NAME p/PHONE_NUMBER e/EMAIL [t/TAG] r/ROOM_NUMBER from/START_DATE to/END_DATE
A valid booking cannot clash with an existing booking. It must also have a start date from today onwards (i.e. not outdated). |
Example: add n/John Smith p/98765432 e/johnsmith@gmail.com t/VIP r/085
from/17/12/18 to/19/12/18
Add a guest "John Smith" to room 085 for the period of stay from 17/12/18 to 19/12/18.
Adding an inactive booking: you can view the booking by selecting the relevant room, under "All other bookings". Only active bookings (i.e. start date is today) can be seen on the left room pane.
Login : login
Logs in to the Concierge™ application.
Format: login user/USERNAME pw/PASSWORD
Note: The username and password are both case-sensitive.
The default account can be accessed with login user/admin pw/passw0rd
|
A login allows the user to access the commands which can affect the
bookings.
Commands which require login: add
, checkin
, checkout
,
reassign
, service
and clear
.
Example: login user/admin pw/passw0rd
clear
"This command requires you to sign in."
Attempting to clear Concierge™ without a login is not allowed.
login user/admin pw/passw0rd
"Successfully signed in as: admin"
Login with a valid account, such as the default one provided.
After signing in, the clear
command can now be executed.
Adding a new account
Currently, Concierge(TM) does not have a feature for users to add an account via the app. Nevertheless, for the adventurous users who want to do so, this sub-section will be useful.
Step 1: Concierge™ uses SHA-256 password hashing. This hash for your password can be generated using this tool.
Concierge™ is designed to work for alphanumeric usernames and passwords in mind. Do not enter symbols (!, @, %…). Do not begin or end your passwords with whitespaces. |
Step 2: Add the entry to the passwords.json
file. This should be in the
same location as concierge.jar
. Note that entries are separated with a
comma.
Format: "username" : "hashedPassword"
In the image below, a new account with username "newUser" and password "mySecurePassw0rd" has been added.
Step 3: Close and reload the Concierge™ application, and your new account will be recognised.
The parsing of the passwords.json file is delicate. Currently, if you
enter a valid json file format but an incorrect password reference list
format, you will end up with no default account. To resolve this, delete the
passwords.json file and re-run Concierge™.
|
Logout : logout
Logs out of the Concierge™ application.
Format: logout
-
The special classes of commands (as documented in login) can no longer be executed.
-
The
logout
command will erase the command history, so users cannot undo/ redo commands executed before the logout.-
Even if you login again, you cannot undo your previous actions.
-
This achieves the same effect as closing and re-opening Concierge™ after a logout.
-
Example: logout
Contributions to the Developer Guide
My contributions to the Developer Guide below showcase my ability to write technical documentation and the technical depth of my contributions to the project. |
Adding a Booking
The add
command is used by the receptionist to add the guest to the hotel,
and assign him a room.
Current Implementation
We currently accept a Guest
, RoomNumber
and BookingPeriod
as parameters for the AddCommand
constructor. An example of its usage:
add n/Madith p/83141592 e/madith@themyth.com r/041 from/29/11/2018 to/ 03/12/2018
The parsing of the AddCommand
is very similar to what was already
implemented in AddressBook4. More parameters were added, namely the
RoomNumber
and BookingPeriod
. These are parsed to create the respective
objects - Guest
, RoomNumber
and BookingPeriod
.
-
In v2.0, users can enter a start date and duration to specify their booking period.
As in AddressBook4, the Logic
component parses the AddCommand
, and the
Model
handles its execution.
-
In the
Model
, theGuest
is no longer added to Concierge. It was previously the case in AddressBook4. -
A new
Booking
object is created with theGuest
andBookingPeriod
as its parameters. -
This
Booking
is then added to theRoom
with theRoomNumber
specified. EveryRoom
maintains aSortedSet<Booking>
which is encapsulated in theBookings
(plural) class.
An Activity Diagram for the execution of AddCommand#execute
is shown below.
The AddCommandParser already checks that ROOM_NUMBER is a valid
string from 001 to 100 , and the initialisation of Concierge checks that
there are 100 rooms. The RoomNotFoundException is not expected to occur
for any user input, but is left there as a defensive measure.
|
Design Considerations
Aspect: Check for outdated bookings
Outdated bookings are those which have a start date before today. Concierge
disallows users to add
outdated bookings.
-
Alternative 1 (current choice): Do the check in
AddCommand#execute
-
Pros: Very easy to implement. A parameter check in the
execute
method will suffice. Will only affect theAddCommandSystemTest
. -
Cons: The actual
Model#addBooking
does not do any check on theBookingPeriod
being outdated, opening the possibly of outdatedBooking
s being added from via other commands.
-
-
Alternative 2: Do the check in
Room#addBooking
-
Pros: Centralises exceptions thrown related to bookings in the
Booking
class. Increases the cohesiveness of this class. -
Cons: All the existing tests and sample data calling the
addBooking
method with outdated bookings have to be changed. It also becomes difficult to do unit tests on checking in bookings which are outdated but not expired, since these bookings can no longer be added to the model.
-
Aspect: Reduce coupling between Room
and Guest
Semantically, we can observe a strong coupling and dependency between Room
and Guest
. A Room
contains a Guest
, and a Guest
also has a Room
.
Maintaining this coupling allows for very quick lookup both ways, either
given a Guest
(which is common at the reception desk) or given a Room
(which is common for housekeeping).
-
Alternative 1 (current choice): Add
Guest
as a field inRoom
-
Pros: An efficient way for managing bookings. Receptionist can quickly determine if the
Room
is free to book. Lookup time forGuest
not expected to increase greatly, sinceRoom
s are not expected to have a large number of advanced bookings made. -
Cons: Difficult to find the
Room
given theGuest
. When aGuest
has made an advanced booking and wishes to cancel it, we have to search through all theRoom
s. Nevertheless, we expect most guests to be aware of their rooms.
-
-
Alternative 2: Add
Room
as a field inGuest
-
Pros: Very customer-centric design. Centralises all the information about the
Guest
, includingBooking
s made andExpense
s incurred. -
Cons: Making a new
Booking
with aGuest
is highly inefficient.Booking
information is now scattered across individualGuest
s.-
[v1.4] On top of the list of rooms, we maintain a separate list of checked-in guests. This list does not retain any booking information, as it is meant to for a quick lookup of the guests' particulars.
-
-
Login and Logout
The login
feature allows hotel managers to control which receptionists
have full access to Concierge. When paired with the CommandArchive feature,
they can also create a blame history to trace rogue commands.
Current Implementation
Currently, login
is implemented as a
dynamic feature,
so users are not prompted to sign in upon starting Concierge. Instead, they
only have to sign in when executing commands which would mutate the data,
such as add
, checkin
, checkout
, reassign
, service
and clear
.
Logic
Given the nature of the login
command being dynamic (can be entered at any
point in time, between any commands), it is then natural to implement it like
a normal command, extending the abstract Command
class. The logout
command
is also implemented in this way.
Model
The model handles the signing-in, using its attribute LogInManager
.
The Class Diagram of the login module is shown below.
LogInManager
uses an optional username
to keep track of whether
the user is currently signed in. The passwordReferenceList
provides an
immutable key-value lookup for usernames and passwords.
LogInManager
implements the following operations.
-
LogInManager#isSignedIn()
- checks if the user is currently signed in. -
LogInManager#signIn(String, String)
- attempts to sign in with the given username and hashed password. This is handled by thePasswordHashList
. A case-insensitive comparison is used on the hash. -
LogInManager#signOut()
- signs out of Concierge.
A new method resetUndoRedoHistory
was added to the
VersionedConcierge
(used for the Undo/ Redo feature). This is used to clear
the command history upon a logout
command, so users cannot undo important
commands or redo accidental bad commands after signing out.
Login
Shown below is the Sequence Diagram for executing a valid login
command.
The diagram also illustrates how the LogicManager
checks for the sign-in
requirement of commands.
Storage
The passwords.json
file is read when Concierge is first opened (i.e. in
MainApp#init
), and is never written to again. The storage function is
managed by the JsonPasswordsStorage
class. Intuitively, passwords are stored as
key-value pairs for quick look-up.
A SHA-256 hash was used in building this feature. In future, this hashing algorithm can be changed to a HMAC hash, which adds a username salt. Then, different users will not know if they have chosen the same passwords.
Check for sign-in requirement of commands
LogicManager
does the
checks for whether a command requires a sign in,
and whether the model is signed in.
The Command
class exposes a new requiresSignIn()
method that returns
false by default. To make new command require signing-in, one only has to
overwrite this method in that command.
Design Considerations
Aspect: Accessing features of Concierge with/ without login
-
Alternative 1 (current choice): Login is needed only for some features
-
Pros: Manager can implement some level of access control within Concierge, especially since some of the more commonly used Concierge features (
list
,find
) are read-only features. This is quicker than mandating a sign-in at the start and creating different user views based on the account privilege (admin vs normal). -
Cons: Not very intuitive to users. They have to enter commands before being told they need to sign in. The
requiresSignIn()
check takes place after the parsing of the command, so a user can is told they cannot execute the command without a sign-in after their command is parsed correctly.
-
-
Alternative 2: Login is needed for all features
-
Pros: Very easy to check login validity. This only occurs when Concierge is first loaded. Subsequent commands can be executed without additional checks on the sign-in requirement.
-
Aspect: Check for sign-in requirement of commands
Given that sign-in is only required for some commands, the priority in designing this aspect is the ability to easily mandate/ disable compulsory the login requirement for current and future commands.
-
Alternative 1 (current choice): Do the check in
LogInManager#execute
-
Pros: Ensures that commands are checked before any execution. Users will not inadvertently change the model before doing the sign-in checks.
-
Cons: Unable to implement commands that can do some actions without sign-in. For example, a future developer may want to make the
add
command such that when the user is not signed-in, the booking is still added but a tag is added to theGuest
, reminding the manager to verify the booking.-
Violates the Single Responsibility Principle. The job of
LogicManager
is to parse and execute commands.
-
-
-
Alternative 2: Do the check in
Command#execute
-
Pros: Increases cohesiveness of
Command
class. The compulsory sign-in is an attribute of aCommand
, so these checks can be done internally.Command
can implement a methodcheckSignIn(Model)
, and commands which require sign-ins can call this method in their respectiveexecute
methods. -
Cons: While increasing cohesion, this implementation makes less semantic sense. The logical misstep comes because one is executing the method, then checking if the method can be executed, then "reversing" the execution.
-
Aspect: Storage of Passwords
The password file is currently read at MainApp#init
, and saved once.
Unlike the Concierge data, this file is no longer referred to when Concierge
is in use.
-
Alternative 1 (current choice): Store passwords in JSON file
-
Pros: JSON is very easy to work with.
-
Able to utilise existing
JsonUtil
methods used by theUserPrefs
andConfig
classes. -
Easily parse data into key-value pairs, which semantically matches our needs.
-
-
Cons:
JsonUtil
file is not completely suitable for a data type that has potentially an unlimited number of entries, since this utility serialises the data to match the class attributes.
-
-
Alternative 2: Store passwords in same XML file as all other Concierge data
-
Pros: Centralises data storage in Concierge. There is only one single source of truth for all data.
-
Cons: The XML file is too complicated for the needs of password storage.
-
Concierge does not need to write the the passwords file when in use.
concierge.xml
is constantly being written to, which is an unnecessary and possibly unsafe feature for the passwords component. -
Creating a new password entry is difficult since once has to add all the layers of XML tags involved. Nevertheless, users are not expected to be adding new accounts on a regular basis.
-
-
End Note
I am grateful to have had the opportunity to work on this project with amazing team members who each contributed with their own strengths. Thank you, @adamwth @teowz46 @JiaqingTan @neilish3re.