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.
|
# issue found and fixed on sibling sites this session.
|
||||||
RewriteCond %{REQUEST_URI} ^/\.git
|
RewriteCond %{REQUEST_URI} ^/\.git
|
||||||
RewriteRule .* - [F,L]
|
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]
|
||||||
|
|||||||
+39
-22
@@ -39,29 +39,43 @@ $pkg = PACKAGES[$package];
|
|||||||
$rentalDate = $date;
|
$rentalDate = $date;
|
||||||
$endDate = date('Y-m-d', strtotime($date . ' +' . $pkg['days'] . ' days'));
|
$endDate = date('Y-m-d', strtotime($date . ' +' . $pkg['days'] . ' days'));
|
||||||
|
|
||||||
// Check availability
|
// Check availability + create booking atomically. A named advisory lock (scoped to the
|
||||||
$conflict = db()->prepare(
|
// requested date range) prevents two concurrent submissions from both passing the
|
||||||
"SELECT id FROM bookings
|
// conflict check before either has inserted, which would otherwise double-book the
|
||||||
WHERE status IN ('pending','confirmed')
|
// vehicle for overlapping dates.
|
||||||
AND rental_date <= ? AND end_date >= ?"
|
$lockName = 'psr_booking_' . $rentalDate . '_' . $endDate;
|
||||||
);
|
$gotLock = (int) db()->query("SELECT GET_LOCK('" . addslashes($lockName) . "', 5)")->fetchColumn();
|
||||||
$conflict->execute([$endDate, $rentalDate]);
|
if (!$gotLock) {
|
||||||
if ($conflict->fetch()) {
|
http_response_code(503);
|
||||||
echo json_encode(['success'=>false,'error'=>'Sorry, that date is already booked. Please choose another date.']); exit;
|
echo json_encode(['success'=>false,'error'=>'Please try again in a moment.']); exit;
|
||||||
}
|
|
||||||
$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
|
try {
|
||||||
$ref = generateRef();
|
$conflict = db()->prepare(
|
||||||
$stmt = db()->prepare(
|
"SELECT id FROM bookings
|
||||||
"INSERT INTO bookings (booking_ref, name, email, phone, package, rental_date, end_date, amount, notes)
|
WHERE status IN ('pending','confirmed')
|
||||||
VALUES (?,?,?,?,?,?,?,?,?)"
|
AND rental_date <= ? AND end_date >= ?"
|
||||||
);
|
);
|
||||||
$stmt->execute([$ref, $name, $email, $phone, $package, $rentalDate, $endDate, $pkg['amount'], $message]);
|
$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()) {
|
||||||
|
echo json_encode(['success'=>false,'error'=>'That date is unavailable. Please choose another date.']); exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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]);
|
||||||
|
} finally {
|
||||||
|
db()->query("SELECT RELEASE_LOCK('" . addslashes($lockName) . "')");
|
||||||
|
}
|
||||||
|
|
||||||
$dateLabel = date('F j, Y', strtotime($rentalDate));
|
$dateLabel = date('F j, Y', strtotime($rentalDate));
|
||||||
$pkgLabel = $pkg['label'];
|
$pkgLabel = $pkg['label'];
|
||||||
@@ -127,7 +141,10 @@ $sqId = null;
|
|||||||
if ($squareToken) {
|
if ($squareToken) {
|
||||||
$sqResp = squareApi('POST', '/payments', [
|
$sqResp = squareApi('POST', '/payments', [
|
||||||
'source_id' => $squareToken,
|
'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'],
|
'amount_money' => ['amount' => (int)(DEPOSIT_AMOUNT * 100), 'currency' => 'USD'],
|
||||||
'autocomplete' => false, // hold only — capture when confirmed
|
'autocomplete' => false, // hold only — capture when confirmed
|
||||||
'location_id' => SQUARE_LOCATION_ID,
|
'location_id' => SQUARE_LOCATION_ID,
|
||||||
|
|||||||
+5
-8
@@ -1,8 +1,5 @@
|
|||||||
# Block direct web access — documents served only through admin/view-doc.php
|
# Block direct web access - documents served only through view-doc.php / admin/view-doc.php.
|
||||||
<IfModule mod_authz_core.c>
|
# Require all denied / Order deny,allow do NOT work on this OpenLiteSpeed setup -
|
||||||
Require all denied
|
# only RewriteRule [F,L] actually blocks requests here (confirmed this session).
|
||||||
</IfModule>
|
RewriteEngine On
|
||||||
<IfModule !mod_authz_core.c>
|
RewriteRule .* - [F,L]
|
||||||
Order deny,allow
|
|
||||||
Deny from all
|
|
||||||
</IfModule>
|
|
||||||
|
|||||||
Reference in New Issue
Block a user