1. How This Guide was prepared
This guide is based on a full read of the application's source code, now including the admin-side files: default.php, form_employment.php, employee_dashboard.php, form_bank.php, index.php, logout.php, register.php, setup_admin.php, submit_hr.php, db.php, footer.php, functions.php, header.php and — added in this revision — admin/dashboard.php, admin/invite.php, admin/view_application.php, admin/delete_user.php. With these four files, every admin-facing workflow referenced by index.php's login redirect is now documented directly from working code rather than inferred from the database structure. The previous edition of this guide flagged the admin dashboard as an unknown; that gap is now closed, and reading these files also surfaced two new issues that need attention (Sections 3.6 and 3.7).
2. System Overview
The portal is a PHP + MySQL web application (built for Hostinger hosting) that lets new employees complete their own onboarding paperwork online instead of on paper. There are two roles:
| Employee | Activates their account via an emailed link, fills in the Employment Application Form and Bank & Tax Details, uploads supporting documents, and submits to HR. |
| Admin (HR) | Manages employee accounts and (based on the login redirect) reviews submitted applications through a separate admin dashboard not included in this handover. |
Core files and what they do
| index.php | Login page for both employees and admins. Redirects by role after successful login. |
| register.php | Account activation page reached via a one-time emailed link containing a token. |
| employee_dashboard.php | Employee's home page — shows status and links to the Employment Form and the Submit-to-HR action. |
| form_employment.php | The full, seven-section Employment Application Form. |
| form_bank.php | Separate "Confidential Salaries Section" form for bank/tax details and salary certificate. |
| submit_hr.php | Handles the one-way "Submit to HR" action, changing the employee's status. |
| setup_admin.php | One-time script that creates (or resets) the master HR/admin account. |
| logout.php | Ends the session and returns to the login page. |
| db.php, functions.php, header.php, footer.php | Shared includes: database connection, CSRF/login helpers, and page layout. |
| admin/dashboard.php | Admin home screen: candidate stats, full employee list, status badges, and links to view/delete/invite. |
| admin/invite.php | Generates a one-time registration link for a new candidate's email address. |
| admin/view_application.php | Read-only, printable view of one employee's full submitted application. |
| admin/delete_user.php | Permanently deletes a non-admin user and their related records. |
3. Urgent Security Findings (Action Required)
While reading the code to write this guide, several issues came up that IT/HR leadership should address quickly, since this system stores highly sensitive personal data (CNIC numbers, bank details, scanned ID documents).
3.1 Database password is hardcoded in plain text
db.php contains a live-looking database password in plain text. Because this file was shared as part of a handover, treat that password as compromised and rotate (change) the database password immediately, then update db.php with the new one.
Recommendation: move all credentials out of the PHP source into environment variables or a config file stored outside the public web root.
3.2 Default admin account has a weak, hardcoded password
setup_admin.php always creates admin@lotte.com with the password "admin123". This script has no login check of its own — anyone who can reach its URL can run it, which deletes the existing admin account and recreates it with the same weak password.
Recommendation: after first use, delete setup_admin.php from the live server entirely. Change the admin password to a strong, unique one immediately.
3.3 Uploaded personal documents are stored in a public, unprotected folder
CNIC scans, education certificates, salary certificates, and photos are saved under assets/uploads/ with no access control. There is no .htaccess or authentication check on this folder.
Recommendation: store uploads outside the public web root and serve them only through an authenticated PHP script.
3.4 "Submitted" applications are not actually locked on the server
The read-only state after submission is only a front-end label. form_employment.php does not check the employee's status before accepting new POST data — a submitted employee could still directly reopen and resave the form.
Recommendation: add a server-side check at the top of form_employment.php that blocks edits once status = 'submitted'.
3.5 File upload validation is extension-only
Uploads are validated only by file extension, not actual file content/MIME type, and have no size limit. A file renamed to end in .pdf but containing different content would currently be accepted.
Recommendation: validate MIME type server-side (e.g. with PHP's fileinfo) and add a maximum file size check.
3.6 The Admin application viewer does not escape employee-entered data (stored XSS risk)
view_application.php prints 32 database fields directly with no escaping. An employee entering a <script> tag in, say, the Career Description would have it executed in the admin's browser — a classic stored XSS vulnerability.
Recommendation: wrap every echoed database value in view_application.php with htmlspecialchars().
3.7 Deleting a user is a destructive GET request with no confirmation-token protection, and cleanup is incomplete
delete_user.php deletes permanently via a plain link (GET), and misses two tables: user_children and user_nominations. Orphaned family data remains.
Recommendation: convert deletion to a POST request protected by a CSRF token, and add the two missing DELETE statements.
4. Initial Admin account setup
The very first admin/HR account is created by visiting setup_admin.php once in a browser. As currently written, it creates this account:
- Email: admin@lotte.com
- Password: admin123
- Name: HR Manager
- Visit the setup_admin.php URL once, in a trusted/internal context only.
- Confirm the on-screen "Admin Created Successfully!" message.
- Immediately log in and change the password to something strong (see the recommendation in Section 3.2 — there is currently no in-app "change password" feature described in the files provided, so this may need to be done directly in the database or via a feature that should be added).
- Delete or relocate setup_admin.php from the live server so it cannot be run again by anyone who finds the URL.
5. Logging In & the Admin Dashboard
Admins use the same login page as employees (index.php) with their admin email and password. The system checks the account's role in the database: accounts with role = 'admin' are redirected to admin/dashboard.php after login, while all other accounts go to employee_dashboard.php.
5.1 Stat cards — three cards at the top summarize all candidates (users with role = 'employee'):
- Total Candidates — Count of every employee-role account ever created.
- Applications Submitted — Count of accounts with status = 'submitted'.
- Pending / In Progress — Everyone else, including freshly invited candidates and those actively filling in the form.
5.2 The candidate table — list of every candidate, newest-invited first, showing name (or "Not Registered" if they haven't activated their account yet), email, a status badge, and the date they were invited.
| Invite Sent | Grey badge. Account created, activation link not yet used. |
| In Progress | Amber badge. Account activated; candidate is filling in or has partially saved the Employment Form. |
| Submitted | Green badge. Candidate has clicked "Submit to HR"; their application is ready to review. |
5.3 Row actions — View (eye icon) only active once status is "Submitted." Delete (trash icon) available for every non-admin row. An "Invite Candidate" button in the top-right corner opens the invite screen.
6. Inviting a new employee
From the Admin Dashboard, click "Invite Candidate."
- Enter the candidate's email address and click "Generate Link."
- The system checks that the email isn't already registered. If it's new, it creates a database record for that candidate with role = 'employee' and status = 'registered', along with a random, one-time activation token.
- A registration link is displayed on screen, e.g.
https://<yourdomain>/register.php?token=..., along with a "Copy" button.
- Copy this link and send it to the candidate yourself — by email, WhatsApp, or however your team normally reaches new hires.
- Click "Invite Another Person" to repeat the process for additional candidates.
No email is sent automatically
Generating a link does not send anything to the candidate — HR must copy the link and deliver it manually. Keep this in mind when coordinating onboarding timelines, and treat the link itself as sensitive since anyone who has it can activate that account.
7. Reviewing a submitted application
Once a candidate's status is "Submitted," click the eye icon next to their name on the Dashboard (or open view_application.php?id=<their user ID> directly) to see their full application in a clean, read-only, printable layout. It mirrors the seven sections of the Employment Form, plus a document-links section:
- 1. Personal Information — photo, position, name, CNIC, contact details, addresses.
- 2. Education — qualifications table plus Achievements text.
- 3. Employment History — previous employers table.
- 4. Career Description — free-text career summary.
- 5. Dependants — spouse details and children table.
- 6. References & Nominations — reference table and nomination table.
- 7. General Information — EOBI/SESSI/NTN, prior application, relations, willingness, witnesses.
- 8. Attached Documents — buttons to open uploaded CNIC, spouse CNIC, and education PDFs.
Treat this screen with care
This page displays a candidate's CNIC, their spouse's and children's identifying details, and links to scanned ID documents on a single screen — exactly the kind of data that should never be left open on an unattended, unlocked computer. See Section 3.6 for a code-level issue on this same page that should be fixed soon.
8. Deleting a user
Click the trash icon next to a candidate's row on the Dashboard. A browser confirmation pop-up appears ("Are you sure you want to remove this user completely?"); confirming permanently deletes:
- The user's employment_details, bank_details, education_history, work_history, and user_references records.
- The user account itself, from the users table.
Admin accounts cannot be deleted this way — the script checks the target's role first and refuses ("Cannot delete Admin accounts") if it's an admin.
This action is permanent, and currently incomplete — see Section 3.7
There is no undo and no archive. Once deleted, an employee's information — and any documents referenced by it — cannot be recovered from within the app.
As currently written, the delete process does not remove that user's children or nomination records, so some of their family members' CNIC/B-Form and nominee details remain in the database even after "deleting" the user. This should be fixed at the code level (Section 3.7) — don't rely on the Delete button alone to fully purge a person's data today.
9. The Employee Lifecycle & status flow
| registered | Account created by HR with an invite token; employee has not yet activated it. |
| in_progress | Employee has activated their account (set a password) and can freely edit their Employment Application. |
| submitted | Employee has clicked "Submit to HR." The Dashboard now shows the form as view-only. |
| active | Used for the admin/HR account itself (not part of the employee onboarding flow). |
submit_hr.php will only allow the transition to "submitted" if the employee has at least saved a record in the Employment Application (employment_details table). If they haven't filled anything in yet, the system blocks submission and asks them to complete the form first.
10. Data Collected — Full Field Reference
This is a complete reference of every field the portal collects, grouped by the database table it's saved to.
employment_details (one record per employee)
- Personal: Position applied for, full name, father's/husband's name, date of birth, place of birth (city & country), gender, marital status, religion
- Contact: Present address, permanent address, office/home/cell phone, personal email, blood group, nationality, CNIC
- Text: Achievements, career description (free text)
- Spouse: Spouse's name, spouse DOB, spouse CNIC
- Numbers: EOBI number, SESSI number, NTN number
- Application: Previously applied (position + date), relations working in the company, willingness to serve anywhere
- Witnesses: Witness 1 & 2 — name and CNIC
- Files: Profile photo path, CNIC (self) file path, CNIC (spouse) file path, education documents (list of file paths, stored as JSON)
Related tables (multiple rows per employee)
| education_history | Certificate/level, institution name, grade, year completed. |
| work_history | Employer name, position, start date, end date, reason for leaving. |
| user_children | Name, gender, date of birth, CNIC/B-Form number. |
| user_nominations | Type (Natural Death / Accidental Death / Provident Fund), nominee name, CNIC, address, age, relationship, share %. |
| user_references | Reference name, designation, address, contact. |
| bank_details | Staff number, account number, IBAN, bank name, branch & code, bank address, tax filing location, NTN, salary certificate file path. |
11. Where Uploaded Files are stored
All uploaded files — passport photos, CNIC PDFs, education certificate PDFs, and salary certificates — are saved on the server's disk under the assets/uploads/ folder, with filenames generated automatically:
- Photo: photo_<userID>_<timestamp>.<ext>
- CNIC (self): cnic_self_pdf_<userID>_<timestamp>.pdf
- CNIC (spouse): cnic_spouse_pdf_<userID>_<timestamp>.pdf
- Education doc: edu_<userID>_<timestamp>_<index>.pdf
- Salary certificate: <userID>_<timestamp>.<ext>
The file path returned by the upload is what's stored in the database (e.g. in employment_details.cnic_self_path). See Section 3.3 for the access-control risk this currently creates.
12. Known Functional Gaps
- No password reset / "Forgot Password" flow for employees or admins.
- No visible link from the employee Dashboard to the Bank & Tax Details page (form_bank.php) — employees may need to be given the direct link, or a Dashboard button should be added.
- Invitations require HR to manually copy and send the registration link — there's no built-in email sending, and no way to resend or revoke a link from the Dashboard once it's generated.
- No search, filter, or export option on the candidate table — for a large candidate list, finding a specific person or pulling data out for reporting means scrolling or querying the database directly.
- No audit trail — there's no record of who changed, viewed, or deleted what and when, which matters for a form containing legally sensitive declarations, nominations, and now a Delete function that permanently destroys records.
- Draft saves fully overwrite related tables (children, nominations, education, work history, references) each time — the previous rows are deleted and re-inserted, so there's no history of earlier versions.
13. Recommendations summary
- Rotate the database password now, and move all credentials into environment variables outside the web root.
- Delete or relocate setup_admin.php after first use, and change the default admin password immediately.
- Move uploaded documents outside the public web root and serve them through an authenticated script.
- Add a server-side lock on the Employment Form once an application has been submitted.
- Add real MIME-type and file-size validation to all upload handlers.
- Escape every field echoed in view_application.php with htmlspecialchars() to close the stored-XSS gap.
- Convert delete_user.php to a CSRF-protected POST request, and add the missing DELETE statements for user_children and user_nominations.
- Consider adding: a password-reset flow, an audit log (especially around deletion and application viewing), a visible Dashboard link to the Bank & Tax Details page, and search/export on the candidate list.