mirror of
https://github.com/myronblair/parkerslingshotrentals
synced 2026-07-27 16:42:32 -05:00
CRITICAL: block direct access to customer license/insurance uploads (was fully downloadable with no auth); fix double-booking race condition and unstable deposit-hold idempotency key
This commit is contained in:
@@ -12,3 +12,10 @@ RewriteRule .* - [F,L]
|
||||
# issue found and fixed on sibling sites this session.
|
||||
RewriteCond %{REQUEST_URI} ^/\.git
|
||||
RewriteRule .* - [F,L]
|
||||
|
||||
# uploads/ (customer license/insurance docs) must never be directly downloadable -
|
||||
# same Order/Deny-doesn't-work-on-OpenLiteSpeed gotcha as .git above. Docs are
|
||||
# served only through view-doc.php / admin/view-doc.php, which read by filesystem
|
||||
# path via readfile() - blocking direct URL access here doesn't break that flow.
|
||||
RewriteCond %{REQUEST_URI} ^/uploads/
|
||||
RewriteRule .* - [F,L]
|
||||
|
||||
+33
-16
@@ -39,29 +39,43 @@ $pkg = PACKAGES[$package];
|
||||
$rentalDate = $date;
|
||||
$endDate = date('Y-m-d', strtotime($date . ' +' . $pkg['days'] . ' days'));
|
||||
|
||||
// Check availability
|
||||
$conflict = db()->prepare(
|
||||
// Check availability + create booking atomically. A named advisory lock (scoped to the
|
||||
// requested date range) prevents two concurrent submissions from both passing the
|
||||
// conflict check before either has inserted, which would otherwise double-book the
|
||||
// vehicle for overlapping dates.
|
||||
$lockName = 'psr_booking_' . $rentalDate . '_' . $endDate;
|
||||
$gotLock = (int) db()->query("SELECT GET_LOCK('" . addslashes($lockName) . "', 5)")->fetchColumn();
|
||||
if (!$gotLock) {
|
||||
http_response_code(503);
|
||||
echo json_encode(['success'=>false,'error'=>'Please try again in a moment.']); exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$conflict = db()->prepare(
|
||||
"SELECT id FROM bookings
|
||||
WHERE status IN ('pending','confirmed')
|
||||
AND rental_date <= ? AND end_date >= ?"
|
||||
);
|
||||
$conflict->execute([$endDate, $rentalDate]);
|
||||
if ($conflict->fetch()) {
|
||||
);
|
||||
$conflict->execute([$endDate, $rentalDate]);
|
||||
if ($conflict->fetch()) {
|
||||
echo json_encode(['success'=>false,'error'=>'Sorry, that date is already booked. Please choose another date.']); exit;
|
||||
}
|
||||
$blockedCheck = db()->prepare("SELECT id FROM blocked_dates WHERE block_date BETWEEN ? AND ?");
|
||||
$blockedCheck->execute([$rentalDate, $endDate]);
|
||||
if ($blockedCheck->fetch()) {
|
||||
}
|
||||
$blockedCheck = db()->prepare("SELECT id FROM blocked_dates WHERE block_date BETWEEN ? AND ?");
|
||||
$blockedCheck->execute([$rentalDate, $endDate]);
|
||||
if ($blockedCheck->fetch()) {
|
||||
echo json_encode(['success'=>false,'error'=>'That date is unavailable. Please choose another date.']); exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Create booking
|
||||
$ref = generateRef();
|
||||
$stmt = db()->prepare(
|
||||
// Create booking
|
||||
$ref = generateRef();
|
||||
$stmt = db()->prepare(
|
||||
"INSERT INTO bookings (booking_ref, name, email, phone, package, rental_date, end_date, amount, notes)
|
||||
VALUES (?,?,?,?,?,?,?,?,?)"
|
||||
);
|
||||
$stmt->execute([$ref, $name, $email, $phone, $package, $rentalDate, $endDate, $pkg['amount'], $message]);
|
||||
);
|
||||
$stmt->execute([$ref, $name, $email, $phone, $package, $rentalDate, $endDate, $pkg['amount'], $message]);
|
||||
} finally {
|
||||
db()->query("SELECT RELEASE_LOCK('" . addslashes($lockName) . "')");
|
||||
}
|
||||
|
||||
$dateLabel = date('F j, Y', strtotime($rentalDate));
|
||||
$pkgLabel = $pkg['label'];
|
||||
@@ -127,7 +141,10 @@ $sqId = null;
|
||||
if ($squareToken) {
|
||||
$sqResp = squareApi('POST', '/payments', [
|
||||
'source_id' => $squareToken,
|
||||
'idempotency_key' => $ref . '-dep-' . time(),
|
||||
// Stable per booking ref (not time()-suffixed) so a network retry or
|
||||
// accidental double-submit of this same deposit hold is recognized by
|
||||
// Square as a duplicate instead of placing a second hold on the card.
|
||||
'idempotency_key' => $ref . '-dep',
|
||||
'amount_money' => ['amount' => (int)(DEPOSIT_AMOUNT * 100), 'currency' => 'USD'],
|
||||
'autocomplete' => false, // hold only — capture when confirmed
|
||||
'location_id' => SQUARE_LOCATION_ID,
|
||||
|
||||
+5
-8
@@ -1,8 +1,5 @@
|
||||
# Block direct web access — documents served only through admin/view-doc.php
|
||||
<IfModule mod_authz_core.c>
|
||||
Require all denied
|
||||
</IfModule>
|
||||
<IfModule !mod_authz_core.c>
|
||||
Order deny,allow
|
||||
Deny from all
|
||||
</IfModule>
|
||||
# Block direct web access - documents served only through view-doc.php / admin/view-doc.php.
|
||||
# Require all denied / Order deny,allow do NOT work on this OpenLiteSpeed setup -
|
||||
# only RewriteRule [F,L] actually blocks requests here (confirmed this session).
|
||||
RewriteEngine On
|
||||
RewriteRule .* - [F,L]
|
||||
|
||||
Reference in New Issue
Block a user