Prototype of the University DCS intranet site.
This is a solution to The PHP module final exam of my CS degree.
- module: Web programming using PHP
- date: 5 August 2018
- author: Nicola D'Aniello
The exam consists in creating a prototype of the university's DCS website, with a private intranet region. The prototype implements authentication, authorisation and a custom database implementation (as no RDBMS or other common solutions where authorised) where new users can be added by an admin.
The full list of requirements can be found in the specifications.pdf file.
The final project is available online at this link, you can use username admin and password dcsadmin01 to login as administrator.
Public access files:
- .root/public_www/p1fma/index.php
- .root/public_www/p1fma/admin.php
- .root/public_www/p1fma/login.php
- .root/public_www/p1fma/logout.php
- .root/public_www/p1fma/secured/index.php
- .root/public_www/p1fma/secured/DTresults.php
- .root/public_www/p1fma/secured/P1results.php
- .root/public_www/p1fma/secured/PfPresults.php
Include functions files:
- .root/public_www/p1fma/include/functions.php
- .root/public_www/p1fma/include/validate-functions.php
- .root/public_www/p1fma/include/user-functions.php
- .root/public_www/p1fma/include/db-functions.php
Other include files:
- .root/public_www/p1fma/include/header.php
- .root/public_www/p1fma/include/footer.php
- .root/public_www/p1fma/include/login-form.php
- .root/public_www/p1fma/include/register-form.php
Private files:
- .root/php
- .root/php
- .root/private/users-db.txt
- .index.php
- .admin.php
- .login.php
- .logout.php
- ./secured/index.php
- ./secured/DTresults.php
- ./secured/P1results.php
- ./secured/PfPresults.php
The work has been divided in the following way:
-
Create each page required in the specifications;
-
Create common Header and Footer for each page;
-
Create Login and Registration forms;
-
Implement Sessions and Login;
-
Implement Authentication and restrict access to secured pages;
-
Implement Authorisation and restrict access to admin page;
-
Create Database to store users (user-db.txt);
-
Implement Database functions (db-functions.php);
-
Refractor Login to use database functions;
-
Implement User Registration with database functions;
-
Implement forms and inputs Validation;
-
Refractor User related functions in its own file (user-functions.php);
-
Refractor Validation functions in its own file (validate-functions.php);
-
Test on Server;
-
Implement settings file to easier debugging on localhost or server (settings.php);
-
Refractor common initialisation functions in its own file (bootstrap.php);
-
Refractor folder structure: implement public and private access folders to improve security;
Database Issues
In order to store users without using existing database services I created a simple file structure that would store each user by line, and each user field separated by a comma. The structure used is the following:
'title', 'first_name', 'surname', 'email', 'username', 'password', 'is_admin'
In order for the script to work the user-db file has to exist and have only one line containing the admin user data.
To prevent the database from being corrupted (e.g. having commas or new lines in the inputs from the user) two functions have been implemented to serialise and deserialise data moved in and out of the database.
Another risk was having two users with the same username, as the script would only find the first user in the file with such a name. To solve this issue the script assess that newly created users have a unique username.
One more security issue is having the data in the database not encrypted, especially user passwords. Since the requirements didn't specify, the data is stored in clear text inside the database.
Code Standards
To keep a more clean and consistent code, and to solve the issue of having to send any header (e.g. for redirects) before content, all files are divided in two sections: on the top the logic of the page and on the bottom all the printing functions.
Always to keep a clean code standard, most of the comments in the files are placed on top of the block of code they are describing. This was done while trying to implement an easy-to-read and and self-describing code, as it seemed a better way rather then placing comments on each line.
Exception Handling
Some functions implement exception handling to present errors to the user, rather then using nested if-else blocks. This is to keep a more clean and consistent code.
validate-functions.php
if ($value ==='')
thrownewException('Title is required and cannot be empty.');
login.php
catch(Exception $e) {
$errors[] = $e-\>getMessage();
One problem with this approach is that the catch blocks will get any error including PHP internal errors that use the common Exception class or its subclasses.
To solve this issue would have been enough to create and catch specific subclasses of Exception (e.g. replace Exception with TitleMissingException for the example above). This solution tough has not been implemented as it seemed to me to go behind the scope and requirements for this exam.
File Structure
To improve on security the settings.php, bootstrap.php and the user database user-db.txt files have been placed outside the /public_www/ directory. This required changing the chmod of the files in order to be included in the scripts in the /public_www/ directory.
The script will work both on local (tested on MAMP) and on server side by simply changing two lines in the settings.php file.
The addressor dcs titan is:
http://titan.dcs.bbk.ac.uk/~ndanie03/p1fma/index.php
Final Note
The script will not work with cookies disabled. I tried to implement SID url propagation for users with disabled cookies, by writing each link with the SID constant:
<ahref="<?phpecho__BASEURL__.'?'.SID?>index.php">home</a>
and by implementing the redirect() function with using the SID.
header('location: '.__BASEURL__.'?'.SID.$url);
The issue was that the constant was always empty, both in local or server, even after changing the php.ini settings.
One last try was to change settings right into the bootstrap.php file:
ini_set("session.use_cookies",false);
ini_set("session.use__only_cookies",false);
ini_set("session.use_trans_sid",true);
but the SID constant would keep returning an empty string.
Since I didn't solve the issue, the solution has not been implemented.