enhanced debug
This commit is contained in:
@ -1,7 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* CLI: Print why the encrypted DB cannot be opened (deploy / env / permissions).
|
* CLI: Print why the encrypted DB cannot be opened (deploy / env / permissions).
|
||||||
* Usage: php cli/diagnose_db.php
|
* Usage: php cli/diagnose_db.php [path/to/questionnaire_database]
|
||||||
*/
|
*/
|
||||||
if (php_sapi_name() !== 'cli') {
|
if (php_sapi_name() !== 'cli') {
|
||||||
http_response_code(403);
|
http_response_code(403);
|
||||||
@ -42,7 +42,14 @@ echo "QDB_MASTER_KEY: set (" . strlen($keyEnv) . " chars)\n";
|
|||||||
$b64 = base64_decode($keyEnv, true);
|
$b64 = base64_decode($keyEnv, true);
|
||||||
echo " parses as base64: " . ($b64 !== false && strlen($b64) > 0 ? 'yes (' . strlen($b64) . ' bytes)' : 'no (uses first 32 ASCII chars)') . "\n";
|
echo " parses as base64: " . ($b64 !== false && strlen($b64) > 0 ? 'yes (' . strlen($b64) . ' bytes)' : 'no (uses first 32 ASCII chars)') . "\n";
|
||||||
|
|
||||||
$dbPath = QDB_PATH;
|
$dbPath = $argv[1] ?? QDB_PATH;
|
||||||
|
if ($dbPath !== QDB_PATH && !is_file($dbPath)) {
|
||||||
|
echo "File not found: $dbPath\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if ($dbPath !== QDB_PATH) {
|
||||||
|
echo "Note: probing alternate file (app still uses " . QDB_PATH . " at runtime)\n";
|
||||||
|
}
|
||||||
$uploads = dirname($dbPath);
|
$uploads = dirname($dbPath);
|
||||||
echo "uploads dir: $uploads\n";
|
echo "uploads dir: $uploads\n";
|
||||||
echo " exists: " . (is_dir($uploads) ? 'yes' : 'no') . "\n";
|
echo " exists: " . (is_dir($uploads) ? 'yes' : 'no') . "\n";
|
||||||
@ -83,34 +90,55 @@ if ($b64 !== false && strlen($b64) > 0) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
echo "\nDecrypt probe (which key matches questionnaire_database?):\n";
|
echo "\nDecrypt probe (which key matches questionnaire_database?):\n";
|
||||||
$matched = null;
|
$matchedLabel = null;
|
||||||
|
$matchedKey = null;
|
||||||
|
$appKey = get_master_key_bytes();
|
||||||
foreach ($candidates as $label => $keyBytes) {
|
foreach ($candidates as $label => $keyBytes) {
|
||||||
$probe = qdb_probe_decrypt($dbPath, $keyBytes);
|
$probe = qdb_probe_decrypt($dbPath, $keyBytes);
|
||||||
if ($probe['ok']) {
|
if ($probe['ok']) {
|
||||||
echo " MATCH: $label\n";
|
echo " MATCH: $label\n";
|
||||||
$matched = $label;
|
if ($matchedLabel === null) {
|
||||||
|
$matchedLabel = $label;
|
||||||
|
$matchedKey = $keyBytes;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
echo " no: $label\n";
|
echo " no: $label\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($matched === null) {
|
if ($matchedLabel === null) {
|
||||||
echo "\nNo known key opened the file. Likely causes:\n";
|
echo "\nNo known key opened the file. Likely causes:\n";
|
||||||
echo " - DB was encrypted with a different QDB_MASTER_KEY than in .env now\n";
|
echo " - DB was encrypted with a different QDB_MASTER_KEY than in .env now\n";
|
||||||
echo " - File was corrupted (e.g. interrupted save); try an older uploads/backups copy\n";
|
echo " - File was corrupted (e.g. interrupted save); try an older uploads/backups copy\n";
|
||||||
echo " - Wrong DB path (another install under /var/www/...)\n";
|
echo " - Wrong DB path (another install under /var/www/...)\n";
|
||||||
|
echo "\nProbe other copies, e.g.:\n";
|
||||||
|
echo " php cli/diagnose_db.php /var/www/html/qdb/uploads/questionnaire_database\n";
|
||||||
echo "\nWiping is NOT required for schema updates. Only wipe if you accept data loss\n";
|
echo "\nWiping is NOT required for schema updates. Only wipe if you accept data loss\n";
|
||||||
echo "and run: mv uploads/questionnaire_database uploads/questionnaire_database.bak\n";
|
echo "and run: mv uploads/questionnaire_database uploads/questionnaire_database.bak\n";
|
||||||
echo "then: php seed_admin.php <user> <pass>\n";
|
echo "then: php seed_admin.php <user> <pass>\n";
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($matched !== 'current .env (get_master_key_bytes)') {
|
$dbMatchesAppKey = $matchedKey !== null && hash_equals($matchedKey, $appKey);
|
||||||
echo "\nThe DB does NOT match your current .env key but matches: $matched\n";
|
|
||||||
echo "Fix: put the matching key in .env, open the DB once, then re-save — or restore a backup.\n";
|
if ($dbPath !== QDB_PATH && $dbMatchesAppKey) {
|
||||||
|
echo "\nThis file decrypts with your .env. Restore it into the app uploads dir:\n";
|
||||||
|
echo " cp " . escapeshellarg($dbPath) . ' ' . escapeshellarg(QDB_PATH) . "\n";
|
||||||
|
echo " chown www-data:www-data " . escapeshellarg(QDB_PATH) . "\n";
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$dbMatchesAppKey) {
|
||||||
|
echo "\nThe DB does NOT match get_master_key_bytes() but matches: $matchedLabel\n";
|
||||||
|
echo "Fix: align QDB_MASTER_KEY in .env and php-fpm pool env, then re-run diagnose.\n";
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($dbPath !== QDB_PATH) {
|
||||||
|
echo "\nDecrypt OK for alternate path. Run without arguments after copying into uploads/.\n";
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
[$pdo, $tmpDb, $lockFp] = qdb_open(false);
|
[$pdo, $tmpDb, $lockFp] = qdb_open(false);
|
||||||
$version = (int)$pdo->query('PRAGMA user_version')->fetchColumn();
|
$version = (int)$pdo->query('PRAGMA user_version')->fetchColumn();
|
||||||
|
|||||||
77
cli/diagnose_login.php
Normal file
77
cli/diagnose_login.php
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CLI: Reproduce login DB + token_add steps (same code path as auth/login).
|
||||||
|
* Usage: sudo -u www-data php cli/diagnose_login.php <username> <password>
|
||||||
|
*/
|
||||||
|
if (php_sapi_name() !== 'cli') {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../common.php';
|
||||||
|
require_once __DIR__ . '/../db_init.php';
|
||||||
|
|
||||||
|
$username = $argv[1] ?? '';
|
||||||
|
$password = $argv[2] ?? '';
|
||||||
|
if ($username === '' || $password === '') {
|
||||||
|
fwrite(STDERR, "Usage: php cli/diagnose_login.php <username> <password>\n");
|
||||||
|
fwrite(STDERR, "Run as www-data to match php-fpm: sudo -u www-data php cli/diagnose_login.php ...\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo 'User: ' . (function_exists('posix_getpwuid') ? (posix_getpwuid(posix_geteuid())['name'] ?? '?') : '?') . "\n";
|
||||||
|
echo 'QDB_MASTER_KEY env length: ' . strlen(getenv('QDB_MASTER_KEY') ?: '') . "\n";
|
||||||
|
echo 'Derived key sha256: ' . hash('sha256', get_master_key_bytes()) . "\n\n";
|
||||||
|
|
||||||
|
try {
|
||||||
|
echo "1) qdb_open(read-only)... ";
|
||||||
|
[$pdo, $tmpDb, $lockFp] = qdb_open(false);
|
||||||
|
echo "OK\n";
|
||||||
|
|
||||||
|
echo "2) lookup user... ";
|
||||||
|
$stmt = $pdo->prepare(
|
||||||
|
"SELECT userID, passwordHash, role, entityID, mustChangePassword FROM users WHERE username = :u"
|
||||||
|
);
|
||||||
|
$stmt->execute([':u' => $username]);
|
||||||
|
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
if (!$user) {
|
||||||
|
qdb_discard($tmpDb, $lockFp);
|
||||||
|
echo "FAIL — user not found\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
echo "OK (role={$user['role']}, mustChange={$user['mustChangePassword']})\n";
|
||||||
|
|
||||||
|
echo "3) password_verify... ";
|
||||||
|
if (!password_verify($password, $user['passwordHash'])) {
|
||||||
|
qdb_discard($tmpDb, $lockFp);
|
||||||
|
echo "FAIL — wrong password\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
echo "OK\n";
|
||||||
|
|
||||||
|
qdb_discard($tmpDb, $lockFp);
|
||||||
|
|
||||||
|
echo "4) token_add (writable save, same as login)... ";
|
||||||
|
$token = bin2hex(random_bytes(32));
|
||||||
|
token_add($token, 120, [
|
||||||
|
'role' => $user['role'],
|
||||||
|
'entityID' => $user['entityID'],
|
||||||
|
'userID' => $user['userID'],
|
||||||
|
'temp' => (int)$user['mustChangePassword'] === 1,
|
||||||
|
]);
|
||||||
|
echo "OK\n";
|
||||||
|
|
||||||
|
echo "5) token_get_record... ";
|
||||||
|
$rec = token_get_record($token);
|
||||||
|
if (!$rec) {
|
||||||
|
echo "FAIL — session not readable after save\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
echo "OK\n";
|
||||||
|
|
||||||
|
token_revoke($token);
|
||||||
|
echo "\nAll login steps succeeded. If the website still fails, check nginx routing (curl below).\n";
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
echo "FAIL\n " . $e->getMessage() . "\n";
|
||||||
|
echo " " . $e->getFile() . ':' . $e->getLine() . "\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user