Add missing permissions for TOTP login

This commit is contained in:
Ryan
2025-04-23 21:14:59 -04:00
committed by GitHub
parent f9c60951c9
commit 6bf6206e1c

View File

@@ -867,126 +867,123 @@ class UserController
* ) * )
*/ */
public function verifyTOTP() public function verifyTOTP()
{ {
header('Content-Type: application/json'); header('Content-Type: application/json');
header("Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self';"); header("Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self';");
// Ratelimit // Rate-limit
if (!isset($_SESSION['totp_failures'])) { if (!isset($_SESSION['totp_failures'])) {
$_SESSION['totp_failures'] = 0; $_SESSION['totp_failures'] = 0;
} }
if ($_SESSION['totp_failures'] >= 5) { if ($_SESSION['totp_failures'] >= 5) {
http_response_code(429); http_response_code(429);
echo json_encode(['status' => 'error', 'message' => 'Too many TOTP attempts. Please try again later.']); echo json_encode(['status' => 'error', 'message' => 'Too many TOTP attempts. Please try again later.']);
exit; exit;
} }
// Must be authenticated OR pending login // Must be authenticated OR pending login
if (!((!empty($_SESSION['authenticated'])) || isset($_SESSION['pending_login_user']))) { if (empty($_SESSION['authenticated']) && !isset($_SESSION['pending_login_user'])) {
http_response_code(403); http_response_code(403);
echo json_encode(['status' => 'error', 'message' => 'Not authenticated']); echo json_encode(['status' => 'error', 'message' => 'Not authenticated']);
exit; exit;
} }
// CSRF check // CSRF check
$headersArr = array_change_key_case(getallheaders(), CASE_LOWER); $headersArr = array_change_key_case(getallheaders(), CASE_LOWER);
$csrfHeader = $headersArr['x-csrf-token'] ?? ''; $csrfHeader = $headersArr['x-csrf-token'] ?? '';
if (empty($_SESSION['csrf_token']) || $csrfHeader !== $_SESSION['csrf_token']) { if (empty($_SESSION['csrf_token']) || $csrfHeader !== $_SESSION['csrf_token']) {
http_response_code(403); http_response_code(403);
echo json_encode(['status' => 'error', 'message' => 'Invalid CSRF token']); echo json_encode(['status' => 'error', 'message' => 'Invalid CSRF token']);
exit; exit;
} }
// Parse and validate input // Parse & validate input
$inputData = json_decode(file_get_contents("php://input"), true); $inputData = json_decode(file_get_contents("php://input"), true);
$code = trim($inputData['totp_code'] ?? ''); $code = trim($inputData['totp_code'] ?? '');
if (!preg_match('/^\d{6}$/', $code)) { if (!preg_match('/^\d{6}$/', $code)) {
http_response_code(400); http_response_code(400);
echo json_encode(['status' => 'error', 'message' => 'A valid 6-digit TOTP code is required']); echo json_encode(['status' => 'error', 'message' => 'A valid 6-digit TOTP code is required']);
exit; exit;
} }
// TFA helper // TFA helper
$tfa = new \RobThree\Auth\TwoFactorAuth( $tfa = new \RobThree\Auth\TwoFactorAuth(
new \RobThree\Auth\Providers\Qr\GoogleChartsQrCodeProvider(), new \RobThree\Auth\Providers\Qr\GoogleChartsQrCodeProvider(),
'FileRise', 'FileRise', 6, 30, \RobThree\Auth\Algorithm::Sha1
6, );
30,
\RobThree\Auth\Algorithm::Sha1 // === Pending-login flow (we just came from auth and need to finish login) ===
); if (isset($_SESSION['pending_login_user'])) {
$username = $_SESSION['pending_login_user'];
// Pendinglogin flow (first password step passed) $pendingSecret = $_SESSION['pending_login_secret'] ?? null;
if (isset($_SESSION['pending_login_user'])) { $rememberMe = $_SESSION['pending_login_remember_me'] ?? false;
$username = $_SESSION['pending_login_user'];
$pendingSecret = $_SESSION['pending_login_secret'] ?? null; if (!$pendingSecret || !$tfa->verifyCode($pendingSecret, $code)) {
$rememberMe = $_SESSION['pending_login_remember_me'] ?? false; $_SESSION['totp_failures']++;
http_response_code(400);
if (!$pendingSecret || !$tfa->verifyCode($pendingSecret, $code)) { echo json_encode(['status' => 'error', 'message' => 'Invalid TOTP code']);
$_SESSION['totp_failures']++; exit;
http_response_code(400); }
echo json_encode(['status' => 'error', 'message' => 'Invalid TOTP code']);
exit; // Issue “remember me” token if requested
} if ($rememberMe) {
$tokFile = USERS_DIR . 'persistent_tokens.json';
// === Issue “remember me” token if requested === $token = bin2hex(random_bytes(32));
if ($rememberMe) { $expiry = time() + 30 * 24 * 60 * 60;
$tokFile = USERS_DIR . 'persistent_tokens.json'; $all = [];
$token = bin2hex(random_bytes(32)); if (file_exists($tokFile)) {
$expiry = time() + 30 * 24 * 60 * 60; $dec = decryptData(file_get_contents($tokFile), $GLOBALS['encryptionKey']);
$all = []; $all = json_decode($dec, true) ?: [];
}
if (file_exists($tokFile)) { $all[$token] = [
$dec = decryptData(file_get_contents($tokFile), $GLOBALS['encryptionKey']); 'username' => $username,
$all = json_decode($dec, true) ?: []; 'expiry' => $expiry,
} 'isAdmin' => ((int)userModel::getUserRole($username) === 1),
$isAdmin = ((int)userModel::getUserRole($username) === 1); 'folderOnly' => loadUserPermissions($username)['folderOnly'] ?? false,
$all[$token] = [ 'readOnly' => loadUserPermissions($username)['readOnly'] ?? false,
'username' => $username, 'disableUpload'=> loadUserPermissions($username)['disableUpload']?? false
'expiry' => $expiry, ];
'isAdmin' => $isAdmin file_put_contents(
]; $tokFile,
file_put_contents( encryptData(json_encode($all, JSON_PRETTY_PRINT), $GLOBALS['encryptionKey']),
$tokFile, LOCK_EX
encryptData(json_encode($all, JSON_PRETTY_PRINT), $GLOBALS['encryptionKey']), );
LOCK_EX $secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
); setcookie('remember_me_token', $token, $expiry, '/', '', $secure, true);
setcookie(session_name(), session_id(), $expiry, '/', '', $secure, true);
$secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'); }
// Persistent cookie // === Finalize login into session exactly as finalizeLogin() would ===
setcookie('remember_me_token', $token, $expiry, '/', '', $secure, true); session_regenerate_id(true);
$_SESSION['authenticated'] = true;
// Reissue PHP session cookie $_SESSION['username'] = $username;
setcookie( $_SESSION['isAdmin'] = ((int)userModel::getUserRole($username) === 1);
session_name(), $perms = loadUserPermissions($username);
session_id(), $_SESSION['folderOnly'] = $perms['folderOnly'] ?? false;
$expiry, $_SESSION['readOnly'] = $perms['readOnly'] ?? false;
'/', $_SESSION['disableUpload'] = $perms['disableUpload'] ?? false;
'',
$secure, // Clean up pending markers
true unset(
); $_SESSION['pending_login_user'],
} $_SESSION['pending_login_secret'],
$_SESSION['pending_login_remember_me'],
// Finalize login $_SESSION['totp_failures']
session_regenerate_id(true); );
$_SESSION['authenticated'] = true;
$_SESSION['username'] = $username; // Send back full login payload
$_SESSION['isAdmin'] = $isAdmin; echo json_encode([
$_SESSION['folderOnly'] = loadUserPermissions($username); 'status' => 'ok',
'success' => 'Login successful',
// Clean up 'isAdmin' => $_SESSION['isAdmin'],
unset( 'folderOnly' => $_SESSION['folderOnly'],
$_SESSION['pending_login_user'], 'readOnly' => $_SESSION['readOnly'],
$_SESSION['pending_login_secret'], 'disableUpload' => $_SESSION['disableUpload'],
$_SESSION['pending_login_remember_me'], 'username' => $_SESSION['username']
$_SESSION['totp_failures'] ]);
); exit;
}
echo json_encode(['status' => 'ok', 'message' => 'Login successful']);
exit;
}
// Setup/verification flow (not pending) // Setup/verification flow (not pending)
$username = $_SESSION['username'] ?? ''; $username = $_SESSION['username'] ?? '';