The PHP app runs on Cafe24 shared web hosting -- calling it 'cloud' was misleading. Renames the directory, the edge client module, and every doc/script/identifier that used the term. - cloud/ -> server/ (incl. server/deploy/build_cafe24_package.sh paths) - edge/cloudclient.py -> edge/serverclient.py, CloudClient -> ServerClient - cloud/docs/cloud-deploy.md -> server/docs/server-deploy.md - CLOUD_* env/config keys -> SERVER_* - tests renamed to match Tests: PHP 109 pass (4 skipped), pytest 167 pass. dist/ regenerated.
40 lines
1.6 KiB
PHP
40 lines
1.6 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
namespace App\Repository\Pdo;
|
|
|
|
use App\Repository\NonceRepository;
|
|
use PDO;
|
|
|
|
/**
|
|
* `nonces.nonce` is the PRIMARY KEY (see schema.sql). IngestController already
|
|
* calls seen() before remember(), so under normal operation remember() never
|
|
* collides. Under a replay race (two requests with the same nonce arriving
|
|
* concurrently), the PK uniqueness constraint is the real guard: the losing
|
|
* remember() throws a PDOException on the duplicate key instead of silently
|
|
* overwriting -- closing the seen()/remember() TOCTOU flagged in the Task 7
|
|
* review. We deliberately do not catch/swallow that exception here.
|
|
*/
|
|
final class PdoNonceRepository implements NonceRepository
|
|
{
|
|
public function __construct(private PDO $pdo) {}
|
|
|
|
public function seen(string $nonce): bool
|
|
{
|
|
$st = $this->pdo->prepare('SELECT 1 FROM move_nonces WHERE nonce = ? LIMIT 1');
|
|
$st->execute([$nonce]);
|
|
return (bool)$st->fetchColumn();
|
|
}
|
|
|
|
public function remember(string $nonce, int $expiresAt): void
|
|
{
|
|
$st = $this->pdo->prepare('INSERT INTO move_nonces (nonce, expires_at) VALUES (?, ?)');
|
|
$st->execute([$nonce, $expiresAt]);
|
|
// Cron-less reap: Cafe24 has no scheduler, so expired nonces accumulate forever until
|
|
// the table bloats and inserts slow/fail. Opportunistically prune a bounded batch of
|
|
// already-expired rows (safe: seen() only cares about unexpired ones).
|
|
if (mt_rand(1, 100) === 1) {
|
|
$del = $this->pdo->prepare('DELETE FROM move_nonces WHERE expires_at < ? LIMIT 5000');
|
|
$del->execute([time()]);
|
|
}
|
|
}
|
|
}
|