further Davidson CI testing
This commit is contained in:
@@ -5,6 +5,9 @@ start_app_session();
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$pdo = getDbConnection();
|
||||
enforce_session_version($pdo);
|
||||
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['error' => 'Not authenticated']);
|
||||
@@ -28,7 +31,6 @@ function fetchOwnedCritter(PDO $pdo, int $userId, int $critterId): array|false
|
||||
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
$pdo = getDbConnection();
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
$critterId = $method === 'POST'
|
||||
? filter_input(INPUT_POST, 'id', FILTER_VALIDATE_INT)
|
||||
|
||||
@@ -5,11 +5,13 @@ start_app_session();
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$pdo = getDbConnection();
|
||||
enforce_session_version($pdo);
|
||||
|
||||
$userId = $_SESSION['user_id'] ?? null;
|
||||
$result = false;
|
||||
|
||||
if ($userId !== null) {
|
||||
$pdo = getDbConnection();
|
||||
$tableExists = $pdo->query("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'transactions'")->fetch();
|
||||
|
||||
if ($tableExists) {
|
||||
|
||||
+3
-2
@@ -5,6 +5,9 @@ require __DIR__ . '/lib/ticket.php';
|
||||
require __DIR__ . '/lib/mailer.php';
|
||||
start_app_session();
|
||||
|
||||
$pdo = getDbConnection();
|
||||
enforce_session_version($pdo);
|
||||
|
||||
if (!isset($_SESSION['username']) || !isset($_SESSION['user_id'])) {
|
||||
header('Location: /');
|
||||
exit;
|
||||
@@ -16,8 +19,6 @@ const DAILY_CRITTER_LIMIT = 30;
|
||||
const CRITTER_COOLDOWN_SECONDS = 15;
|
||||
const VERIFICATION_RESEND_COOLDOWN_SECONDS = 60;
|
||||
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$userStmt = $pdo->prepare('SELECT email, username, credits, email_verified_at FROM users WHERE id = :id');
|
||||
$userStmt->execute(['id' => $_SESSION['user_id']]);
|
||||
$userRow = $userStmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
@@ -26,6 +26,9 @@ function getDbConnection(): PDO
|
||||
if (!in_array('email_verified_at', $userColumns, true)) {
|
||||
$pdo->exec('ALTER TABLE users ADD COLUMN email_verified_at TEXT');
|
||||
}
|
||||
if (!in_array('session_version', $userColumns, true)) {
|
||||
$pdo->exec('ALTER TABLE users ADD COLUMN session_version INTEGER NOT NULL DEFAULT 0');
|
||||
}
|
||||
}
|
||||
|
||||
return $pdo;
|
||||
|
||||
@@ -4,6 +4,8 @@ require __DIR__ . '/db.php';
|
||||
start_app_session();
|
||||
|
||||
$pdo = getDbConnection();
|
||||
enforce_session_version($pdo);
|
||||
|
||||
$tableExists = $pdo->query("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'adjectives'")->fetch();
|
||||
|
||||
if (!$tableExists) {
|
||||
|
||||
+6
-3
@@ -48,9 +48,12 @@
|
||||
<a href="#" onClick="openCheckout(itemsList)" class="flex items-center gap-2 px-4 py-2 text-gray-700 hover:bg-gray-50 sm:hidden">
|
||||
<i data-lucide="coins" class="h-4 w-4"></i> Buy 50 Credits
|
||||
</a>
|
||||
<a href="logout.php" class="flex items-center gap-2 px-4 py-2 text-gray-700 hover:bg-gray-50">
|
||||
<i data-lucide="log-out" class="h-4 w-4"></i> Log out
|
||||
</a>
|
||||
<form action="logout.php" method="post">
|
||||
<?php echo csrf_field(); ?>
|
||||
<button type="submit" class="w-full flex items-center gap-2 px-4 py-2 text-left text-gray-700 hover:bg-gray-50">
|
||||
<i data-lucide="log-out" class="h-4 w-4"></i> Log out
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -74,6 +74,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
session_regenerate_id(true);
|
||||
$_SESSION['user_id'] = $user['id'];
|
||||
$_SESSION['username'] = $user['username'];
|
||||
$_SESSION['session_version'] = (int) $user['session_version'];
|
||||
|
||||
header('Location: /');
|
||||
exit;
|
||||
|
||||
@@ -3,6 +3,11 @@ require __DIR__ . '/session.php';
|
||||
|
||||
start_app_session();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !csrf_verify($_POST['csrf_token'] ?? null)) {
|
||||
header('Location: /');
|
||||
exit;
|
||||
}
|
||||
|
||||
$_SESSION = [];
|
||||
session_destroy();
|
||||
|
||||
|
||||
@@ -115,8 +115,14 @@ if (($event['event_type'] ?? '') === 'transaction.completed') {
|
||||
$creditsToAdd = 0;
|
||||
foreach ($items as $item) {
|
||||
$priceId = $item['price']['id'] ?? null;
|
||||
$quantity = $item['quantity'] ?? 1;
|
||||
|
||||
if (!is_int($quantity) || $quantity < 1 || $quantity > 100) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_string($priceId) && isset($creditsPerPrice[$priceId])) {
|
||||
$creditsToAdd += $creditsPerPrice[$priceId];
|
||||
$creditsToAdd += $creditsPerPrice[$priceId] * $quantity;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -115,6 +115,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
session_regenerate_id(true);
|
||||
$_SESSION['user_id'] = $newUserId;
|
||||
$_SESSION['username'] = $username;
|
||||
$_SESSION['session_version'] = 0;
|
||||
|
||||
$verifyToken = createTicket($newUserId, 'email_verify', EMAIL_VERIFICATION_LIFETIME_SECONDS);
|
||||
$appUrl = rtrim(getenv('APP_URL') ?: 'https://ourapp.local', '/');
|
||||
|
||||
+3
-1
@@ -36,7 +36,9 @@ if ($tokenIsWellFormed && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$errors['password'] = 'This reset link has already been used or has expired. Please request a new one.';
|
||||
} else {
|
||||
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
|
||||
$stmt = $pdo->prepare('UPDATE users SET password = :password WHERE id = :id');
|
||||
// Bumping session_version invalidates every existing session for this user
|
||||
// (including a stolen one) the next time enforce_session_version() runs.
|
||||
$stmt = $pdo->prepare('UPDATE users SET password = :password, session_version = session_version + 1 WHERE id = :id');
|
||||
$stmt->execute(['password' => $hashedPassword, 'id' => $userId]);
|
||||
|
||||
header('Location: login.php?reset=success');
|
||||
|
||||
+24
@@ -56,3 +56,27 @@ function csrf_verify(?string $token): bool
|
||||
{
|
||||
return is_string($token) && $token !== '' && isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
|
||||
}
|
||||
|
||||
// Logs the session out if the user's session_version has moved on since login (e.g. a
|
||||
// password reset bumped it), so a stolen/old session cookie stops working immediately
|
||||
// instead of staying valid until it naturally expires. Call this right after
|
||||
// start_app_session() + getDbConnection() on any page that trusts $_SESSION['user_id'].
|
||||
function enforce_session_version(PDO $pdo): void
|
||||
{
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare('SELECT session_version FROM users WHERE id = :id');
|
||||
$stmt->execute(['id' => $_SESSION['user_id']]);
|
||||
$currentVersion = $stmt->fetchColumn();
|
||||
|
||||
$sessionVersionMatches = $currentVersion !== false
|
||||
&& isset($_SESSION['session_version'])
|
||||
&& (int) $currentVersion === (int) $_SESSION['session_version'];
|
||||
|
||||
if (!$sessionVersionMatches) {
|
||||
$_SESSION = [];
|
||||
session_destroy();
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -3,13 +3,14 @@ require __DIR__ . '/session.php';
|
||||
require __DIR__ . '/db.php';
|
||||
start_app_session();
|
||||
|
||||
$pdo = getDbConnection();
|
||||
enforce_session_version($pdo);
|
||||
|
||||
if (!isset($_SESSION['username']) || !isset($_SESSION['user_id'])) {
|
||||
header('Location: /');
|
||||
exit;
|
||||
}
|
||||
|
||||
$pdo = getDbConnection();
|
||||
|
||||
$critterId = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
|
||||
|
||||
function fetchOwnedCritter(PDO $pdo, int $userId, int $critterId)
|
||||
|
||||
Reference in New Issue
Block a user