Commit Graph
566 Commits
Author SHA1 Message Date
Hamza-AyedandClaude Opus 5 c9b4d14da6 Route account lookups through the blind index and keep it fresh on write
These are the paths that must stop depending on deterministic encryption
before storage can move to AES-GCM. Each keeps its original ciphertext
comparison in the same statement, so behaviour is unchanged today and no
account becomes unreachable during the transition.

Lookups:
- auth/login.php — passenger sign-in matched the raw value against the
  encrypted column, which only works because encryptData() is CBC with a
  fixed IV.
- auth/passenger/register.php and auth/driver/register.php — duplicate
  detection. Without the index these would stop detecting existing accounts
  under GCM and allow the same phone to register twice.

Writes now populate the index in the same statement as the value:
- both registration paths write phone/email/name indexes with the row;
  driver indexes are computed before the encryption pass, since the raw
  values are unavailable afterwards.
- passenger profile update and admin driver update refresh the index when
  the underlying field changes. For the composite name index the untouched
  half is read back from the row.

Adds --audit to the backfill script: recomputes every index from its
encrypted value and reports missing or stale entries. Drift here is silent
by nature — it surfaces only when a real search fails.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 16:04:40 +03:00
Hamza-AyedandClaude Opus 5 15f55ff3e4 Add --verify to the backfill script
Confirms a value is findable through the blind index after backfilling,
without opening the console or the database. Prints matched row ids only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 15:59:50 +03:00
Hamza-AyedandClaude Opus 5 761b957c96 Silence REQUEST_METHOD warning when bootstrap runs from the CLI
The migration and backfill scripts load bootstrap.php outside a request, where
$_SERVER['REQUEST_METHOD'] does not exist, so every CLI run printed a warning
before its output.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 15:55:18 +03:00
Hamza-AyedandClaude Opus 5 57e22477fb Add an idempotent CLI migration runner
phpMyAdmin is unreachable on this deployment, so schema changes need a path
that does not depend on it. migrate.php reuses core/Database, meaning no
credentials are passed on the command line, and checks information_schema
before each ALTER so re-running is safe and never fails on a duplicate
column. Supports --status and --dry-run.

Covers the blind-index columns and the missing adminUser status/approved_by/
approved_at columns. Every change is additive and nullable, so applying it to
a running database changes no behaviour on its own.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 15:35:34 +03:00
Hamza-AyedandClaude Opus 5 6802026dbd Add blind-index search layer; fix captain detail 200-with-empty-body
Searching encrypted columns currently works only because encryptData() is
AES-CBC with a fixed IV, i.e. deterministic. That determinism is what leaks
equality and shared prefixes, and it is why moving storage to AES-GCM would
break every lookup. This separates the two concerns.

- core/Security/BlindIndex.php: HMAC-SHA256 over a normalised value, keyed by
  a secret pepper. Phone numbers have a small keyspace, so a bare SHA-256
  would be reversible by enumeration; the pepper lives in the environment, not
  the database. The scope string includes table and field so the same number
  does not produce a matching index across tables.
  Normalisation unifies local/international phone forms, lowercases emails and
  folds Arabic alef/ya/ta-marbuta and diacritics for names.
- migrations/: nullable *_bidx columns plus indexes, and the missing
  adminUser.status/approved_by/approved_at columns that admin approvals need.
- scripts/backfill_blind_index.php: restartable, batched, --dry-run capable,
  touches only index columns.
- Admin lookups by phone/email now match the index, keeping the old ciphertext
  comparison in the same query so search keeps working until the backfill runs.
  bootstrap exposes $blindIndex as null when no pepper is configured.

Also: AdminCaptain/getCaptainDetailsById.php selected driver.education, a
column absent from this schema. The PDOException was uncaught, so the client
received an empty body with HTTP 200 — the "non-JSON response" seen when
opening a captain. It now omits the column, catches the error, reports it as
JSON, and requires an admin role.

Console: opening any sidebar section refetches its data instead of showing
what was loaded when the console started.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 15:16:09 +03:00
Hamza-AyedandClaude Opus 5 81ee2acefd Add removal/blacklist tools, Arabic UI, and a persistent sign-in error
Authorisation: driver/deleteCaptain.php permanently deletes a captain and had
no role check at all — any valid token, including a driver's or passenger's,
could delete captains. It now requires super_admin. The two unblacklist
endpoints had no role check either and now require an admin role.

Console:
- Blacklist & removal screen (super admin only): current blocks, permanent
  removal, and lifting a block. Deletion additionally requires the phone
  number to be retyped, because the row is deleted outright.
- Arabic interface with full RTL layout and a language toggle. Sidebar,
  content offset, input affordances, toasts and directional icons all mirror;
  numbers, identifiers and the diagnostics output stay LTR.
- Sign-in failures now render in the form and stay there. They were only
  shown as a toast, which disappears before it can be read — a locked
  account or a rejected device looked like the page simply returning to
  the login screen with no explanation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 14:53:26 +03:00
Hamza-Ayed 42f6d33efd Update: 2026-07-25 14:44:30 2026-07-25 14:44:30 +03:00
Hamza-AyedandClaude Opus 5 4009af8dd3 Add campaign launcher and app version manager; fix SQL injection in updatePackages
serviceapp/updatePackages.php built its UPDATE by interpolating the request
values straight into the SQL string, so any caller with a valid token could
execute arbitrary SQL through the version field. It now uses bound
parameters, requires an admin role, validates the version format, and writes
an audit entry.

trigger_campaign.php gains dry_run=1: it performs the same Gemini analysis
and target selection but returns before creating the promo code and before
dispatching any notification. Launching without previewing was the only
option before, and a launch writes a seven-day discount and pushes to every
passenger in the country.

Console:
- Campaign launcher with a mandatory preview. Launching stays disabled until
  the current parameters have been previewed, and re-locks if any parameter
  changes afterwards or once a launch completes.
- App version manager with the same version-format check as the server and a
  confirmation naming the old and new values.

Cache busting: assets are served straight off a bind mount with no version,
so browsers kept running the previously cached build after a deploy. Both
asset links now carry ?v=, and the build id is shown in Session & Security
and printed in the diagnostics report.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 14:41:27 +03:00
Hamza-AyedandClaude Opus 5 67f55e5192 Add driver document review and staff onboarding
Driver documents: paged list from auth/driver/drivers_pending_list.php, a
detail panel showing each uploaded document as a thumbnail linking to the
full image, and activation via Admin/driver/updateDriverFromAdmin.php. The
confirmation states how many documents were reviewed and warns explicitly
when a captain has none on file, since approving then activates an
unverified account.

Details are requested as a POST body. The mobile app calls this endpoint as
GET "?id=", which filterRequest() never reads, so its detail lookup cannot
be receiving an id at all.

Staff: pending admin/service accounts with per-account activation via
Staff/activate.php, the employee list, and a creation form posting to
Staff/add.php. Administrator accounts are offered only to super admins,
matching add.php's own check; passwords are rejected below 8 characters and
cleared from the form after submission.

Both screens mask phone numbers for plain admins and never render
token/password/fingerprint fields.

Also stop .btn-primary stretching to full width when used inline in a card
header — it is styled for the login form.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 14:30:49 +03:00
Hamza-AyedandClaude Opus 5 a0ab6c5155 Add broadcast notifications and transit route approvals
Broadcast: ride/firebase/send_fcm.php is an internal service guarded by a
shared secret, so the browser cannot call it — holding that key client-side
would expose it, and the endpoint cannot tell who the sender is. A new
Admin/notifications/broadcast.php sits in front of it: it runs behind
connect.php, requires super_admin, restricts the target to the two topics the
apps actually subscribe to ('drivers'/'passengers') so it cannot be used to
push to an arbitrary topic or a single device token, bounds the title and
body, writes an audit entry before dispatching, and only then forwards the
call internally with the shared secret.

The composer shows a live push preview and an explicit confirmation naming
the audience, since a broadcast cannot be recalled.

Route approvals: draft routes render with their stops, distance and stop
count, and approve/reject posts to transit/route/approve.php behind a
confirmation stating the consequence. Available to admins and super admins,
matching the endpoint's own role check.

Also render user-supplied text with unicode-bidi: plaintext — Arabic names,
addresses and messages were being laid out left-to-right inside the
English UI.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 02:15:02 +03:00
Hamza-AyedandClaude Opus 5 41a06bba0c Add fleet, invoice, promo and lookup-driven admin modules
Brings the console to 18 modules across 26 navigation entries.

- Fleet performance (best captains, per-captain card charges), invoice
  totals and promo codes as read-only panels.
- Ride monitor: monitorRide.php and getDriverGiftPayment.php answer only for
  a given phone number, so they get an input rather than a panel that would
  render an error on load.
- Status-like values in generated tiles use the same label mapping as the
  tables, so raw values such as cancelled_by_passenger no longer leak into
  the UI.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 02:02:08 +03:00
Hamza-AyedandClaude Opus 5 db4ca7dd7a Fix dashbord.php parse error; require super_admin on pricing and crypto tools
Hotfix: a comment added to the dashboard SQL contained double quotes inside
the double-quoted PHP string, terminating it and making dashbord.php fail to
parse. Production was returning a parse error for every dashboard request.

Authorisation gaps closed — connect.php only proves a token is valid, it does
not check what the caller is allowed to do:

- Admin/ggg.php decrypts any database field and was authorised solely by an
  admin phone number sent in the request body. Anyone who knew a listed
  number could decrypt platform data without signing in. It now runs behind
  connect.php, requires super_admin, keeps the phone list as a second factor,
  and records every use.
- ride/kazan/update.php, kazan/add.php and ride/promo/{add,update,delete}.php
  changed live pricing and discount codes with no role check at all, so any
  valid token — including a driver's or passenger's — could rewrite the fare
  table. All now require super_admin.

Staff/pending.php: adminUser has no `status` column in this deployment, so
the query failed with an opaque "unavailable". It now checks for the column
and reports the actual reason.

Console: Kazan tariff editor for super admins — sends only changed fields,
shows an old → new confirmation before saving, and stays read-only with an
explanatory notice for plain admins.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 02:00:14 +03:00
Hamza-AyedandClaude Opus 5 852c6ece5c Fix admin status handling for the current ride pipeline; extend console
The ride table holds two generations of status values: the legacy CamelCase
set ('Finished', 'CancelFromPassenger') and the lowercase set written by
backend/ride/rides/* today ('completed', 'cancelled_by_passenger'). Admin
queries only matched the legacy set, so on live data:

- get_rides_by_status.php returned nothing meaningful for every filter, and
  the "in progress" default masked it.
- dashbord.php reported total_driver_earnings as NULL, completed_rides as a
  fraction of the real count, and cancelled_rides as 0.
- driver_avg_duration averaged in negative durations, yielding "-00h 22m".

All three now match on LOWER(status) across both families.

Staff/pending.php ran with no authentication at all, exposing pending
admins' names and phone numbers to any caller; it now goes through
connect.php with a role check. It also returned HTTP 400 for everything when
the `users` table was absent — each source is queried independently and
reports its own availability.

Console:
- Render rides from either schema generation (price/date/time and
  start_location coordinates, or the older address/created_at columns).
- Null aggregates render as "—" rather than a measured 0.00.
- Add tariff/promo, WhatsApp send and encryption modules, all super-admin
  gated; pricing remains read-only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 01:47:55 +03:00
Hamza-Ayed 915a148ebf Update: 2026-07-25 01:38:11 2026-07-25 01:38:11 +03:00
Hamza-Ayed 0d9095fd3f Update: 2026-07-25 01:16:32 2026-07-25 01:16:33 +03:00
Hamza-Ayed eda7018434 Update: 2026-07-25 01:02:01 2026-07-25 01:02:02 +03:00
Hamza-Ayed 822cb5963a Ensure Live Backend mode is enforced and clear old Demo localStorage session 2026-07-25 00:44:11 +03:00
Hamza-Ayed 8ec7b9aae6 Bind live MySQL database tables (Rides, Passengers, Pending Approvals) into Siro Admin Portal 2026-07-25 00:41:18 +03:00
Hamza-Ayed c9168c1c80 Enhance Siro Admin Dashboard with live backend API database bindings 2026-07-25 00:33:32 +03:00
Hamza-Ayed 10f1154cc7 Update adminUser fingerprint and fingerprint_hash in database upon successful OTP verification 2026-07-25 00:30:14 +03:00
Hamza-Ayed 9d2a665d64 Restore WhatsApp OTP (Nabeh API) flow for Admin authentication 2026-07-25 00:25:53 +03:00
Hamza-Ayed 4c738e8f43 Remove legacy encrypt_decrypt.php - bootstrap.php already provides encryptionHelper 2026-07-25 00:23:27 +03:00
Hamza-Ayed 032e1edda5 Remove broken providers.php require_once - issue JWT directly on valid password 2026-07-24 23:59:27 +03:00
Hamza-Ayed 1ca2c5a5dd Fix require_once path in login.php using realpath 2026-07-24 23:51:28 +03:00
Hamza-Ayed f115292dd2 Refactor login.php with decryption lookup for encrypted fields and restore Nabeh OTP integration 2026-07-24 23:48:57 +03:00
Hamza-Ayed 60e344598b Fix Admin login path issue and issue direct JWT on valid credentials 2026-07-24 23:45:28 +03:00
Hamza-Ayed 8bc9290916 Safely initialize encryptionHelper and handle Throwable in Admin login.php 2026-07-24 23:42:39 +03:00
Hamza-Ayed 9e2964d307 Allow email and ID login in Admin auth login.php and bind web device fingerprint 2026-07-24 23:38:17 +03:00
Hamza-Ayed b3126013f5 Update: 2026-07-24 23:31:37 2026-07-24 23:31:37 +03:00
Hamza-Ayed 5ebff42841 Update: 2026-07-24 23:23:54 2026-07-24 23:23:55 +03:00
Hamza-Ayed 2944f21f53 Add dashboard web applications (siro-admin & siro-service) with docker Nginx routing 2026-07-24 22:59:26 +03:00
Hamza-Ayed 917dfc025f Update: 2026-07-24 22:57:32 2026-07-24 22:57:32 +03:00
Hamza-Ayed 0b24bc21b6 Update: 2026-07-24 22:55:34 2026-07-24 22:55:34 +03:00
Hamza-Ayed 474c212bcb refactor: migrate amount fields to decimal and implement location service restriction for unsupported regions 2026-07-24 15:08:37 +03:00
Hamza-Ayed e7629a9eb9 Update: 2026-07-23 22:14:10 2026-07-23 22:14:10 +03:00
Hamza-Ayed 695d6d7cb2 Update: 2026-07-23 21:18:25 2026-07-23 21:18:25 +03:00
Hamza-Ayed e7aa28fe3d Update: 2026-07-23 21:05:22 2026-07-23 21:05:22 +03:00
Hamza-Ayed 7d646b579b Update: 2026-07-23 21:02:51 2026-07-23 21:02:51 +03:00
Hamza-Ayed b4451a0dde Update: 2026-07-23 20:50:10 2026-07-23 20:50:10 +03:00
Hamza-Ayed 92eb7fe25d Update: 2026-07-23 20:43:21 2026-07-23 20:43:21 +03:00
Hamza-Ayed 1e785061ec Fix decryptData null type error and live analytics overflow 2026-07-23 20:42:48 +03:00
Hamza-Ayed 12e70f3d7e Fix status warning in admin login 2026-07-23 20:34:27 +03:00
Hamza-Ayed 12a1cbc98c Update: 2026-07-23 20:15:22 2026-07-23 20:15:22 +03:00
Hamza-Ayed c35b350b31 Update: 2026-07-23 16:56:57 2026-07-23 16:56:58 +03:00
Hamza-Ayed 1cef598fc5 Update: 2026-07-23 02:19:01 2026-07-23 02:19:01 +03:00
Hamza-Ayed e5bf70fddb Update: 2026-07-23 02:14:58 2026-07-23 02:14:58 +03:00
Hamza-Ayed 9e065e5a83 Update: 2026-07-23 00:16:56 2026-07-23 00:16:56 +03:00
Hamza-Ayed ed8a93a6a3 Update: 2026-07-23 00:13:41 2026-07-23 00:13:41 +03:00
Hamza-Ayed a7fa40dc31 Update: 2026-07-23 00:11:36 2026-07-23 00:11:37 +03:00
Hamza-Ayed b86f95c90d Update: 2026-07-22 23:27:18 2026-07-22 23:27:18 +03:00