Sulaiman Bil Service

No Live URLGitHub Repo
2024 · Fullstack webapp · Student project · USN
Skjermbilde av Sulaiman Bil Service

Booking system for a real auto repair shop, built in a team of four. My part was the entire admin panel.

Sulaiman Bil Service is a small auto repair shop in Hønefoss that took bookings by phone. We built them a website with online booking and an admin panel so they could take orders without picking up the receiver.

Problem

Sulaiman Bil Service is an auto repair shop that took bookings over the phone. That meant no log of who had booked what, no overview for the workshop, and occasionally the same time slot promised to two people. The owner wanted something online.

Solution

Four of us built the system as a bachelor project (BOP3000, USN, 2024). The stack is PHP, MySQL and Bootstrap, with Flatpickr for the calendar. We managed the database through phpMyAdmin. There I created the tables, set up ENUM for service types, and used the export feature to pull available time slots as JSON for the booking calendar to read. Weekends are blocked and opening hours start at 06:00.

The booking flow and contact form were built by the rest of the team. My part was the entire admin panel, the admin/ folder: login, registration, dashboard and full CRUD on bookings.

Admin dashboard logged in as Deqa, with three shortcut cards for bookings

The admin logs in, sees all bookings in a table and can edit or delete them. Every page in the admin folder starts with a check that the session exists, otherwise you get bounced back to login. Fine as far as it goes, but it assumes the session itself is safe. More on that below.

Decisions and reflection

Decisions I made

Login and register in one file with tabs. I did not want two nearly identical pages. The solution was a URL parameter (?mode=login or ?mode=register), and the same file renders the right form based on it. I whitelisted the value at the top, so if anyone tampers with the URL it falls back to login. The admin is one user who knows where the buttons are, and fewer files makes maintenance easier.

Login tab in a tabbed UI with email and password fields

Register tab with username, email, password and confirm password

bcrypt over simpler hashing. The users here are admin accounts, so it is not a big target. But MD5 and SHA1 are broken for passwords, and there is no reason to take shortcuts on storage. PHP has password_hash and password_verify built in, so I do not have to handle salt manually.

CSRF token on deletion. Deletion is the most destructive operation in the panel. If the admin is logged in and opens a hostile page in another tab, that page can in principle send a POST to my delete endpoint and remove a booking without the admin noticing. The fix was a random token stored in the session, embedded as a hidden field in every delete form and compared server-side before the deletion runs. The comparison uses a constant-time function, so no one can guess the token character by character by measuring response time. Strictly more than a bachelor project needs. I wanted to learn the right pattern.

Delete flow with a table of six bookings and a red Delete button on each row

Prepared statements on every query. Parameterised SQL queries, where values are sent separately from the query and therefore cannot be interpreted as code. That removes SQL injection as an attack surface. A bit more code per query, but it becomes routine quickly.

Custom dark stylesheet (admin.css). I started by reusing the customer site's light Bootstrap theme, but the admin tables have many rows and columns with small data cells. After ten minutes it was tiring to look at. So admin got its own dark stylesheet, and the two parts of the site address completely different users anyway.

Booking list with six realistic customers, dark theme and Edit and Delete on each row

What I learned

The CSRF token landed on delete because that was the visible risk I thought of first. Deletion feels dangerous, edit feels safe. But edit changes state too. If someone tricks the admin into sending a POST, they can just as easily overwrite a booking as delete it. Today I would put CSRF on every endpoint that changes data in the database, not just delete.

The session is the other thing. I check that the admin is logged in on every page, but I never regenerate the session ID after login, and there is no timeout. That means if someone manages to plant a session ID in the browser before the admin logs in, that ID is still valid afterwards. There should have been a regenerate right after the password is verified. That is two lines of code I forgot.

The architecture is the biggest one. We split the code into three parallel PHP modules (admin/, kontaktskjema/, hovedside/) each with its own config.php, plus one more at the root. That is four places the same database settings live. Change the DB password and you have to remember all four. One codebase with a router and a PSR-4 autoloader would have solved it. The config should also have been in environment variables, not in a file per folder. Not pretty, but the app runs.

Technology Stack

  • Frontend:HTML, CSS, JavaScript, Bootstrap, Flatpickr
  • Backend:PHP
  • Database:MySQL, phpMyAdmin
  • Security:bcrypt, CSRF token, prepared statements
  • Tools:Git, Scrum

Key Features

  • Online booking

    Customers book service directly online without calling. Weekends are blocked and opening hours start at 06:00.

  • Admin panel with full CRUD

    Login, registration, dashboard and full editing or deletion of bookings.

  • CSRF-protected deletion

    A random token in the session is compared on the server before destructive operations run.

Other projects