diff --git a/endpoints/user/delete_avatar.php b/endpoints/user/delete_avatar.php index 8217670..ae9ec47 100644 --- a/endpoints/user/delete_avatar.php +++ b/endpoints/user/delete_avatar.php @@ -1,13 +1,7 @@ false, - "message" => translate('session_expired', $i18n) - ])); -} +require_once '../../includes/validate_endpoint.php'; $input = json_decode(file_get_contents('php://input'), true); if (isset($input['avatar'])) { diff --git a/endpoints/user/disable_totp.php b/endpoints/user/disable_totp.php index ecdaa55..25a4ddc 100644 --- a/endpoints/user/disable_totp.php +++ b/endpoints/user/disable_totp.php @@ -2,6 +2,7 @@ require_once '../../includes/connect_endpoint.php'; require_once '../../includes/inputvalidation.php'; +require_once '../../includes/validate_endpoint.php'; if (!function_exists('trigger_deprecation')) { function trigger_deprecation($package, $version, $message, ...$args) @@ -12,15 +13,6 @@ if (!function_exists('trigger_deprecation')) { } } -if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) { - die(json_encode([ - "success" => false, - "message" => translate('session_expired', $i18n), - "reload" => false - ])); -} - - $statement = $db->prepare('SELECT totp_enabled FROM user WHERE id = :id'); $statement->bindValue(':id', $userId, SQLITE3_INTEGER); $result = $statement->execute(); @@ -34,43 +26,69 @@ if ($row['totp_enabled'] == 0) { ])); } -if ($_SERVER["REQUEST_METHOD"] === "POST") { - $postData = file_get_contents("php://input"); - $data = json_decode($postData, true); +$postData = file_get_contents("php://input"); +$data = json_decode($postData, true); - if (isset($data['totpCode']) && $data['totpCode'] != "") { - require_once __DIR__ . '/../../libs/OTPHP/FactoryInterface.php'; - require_once __DIR__ . '/../../libs/OTPHP/Factory.php'; - require_once __DIR__ . '/../../libs/OTPHP/ParameterTrait.php'; - require_once __DIR__ . '/../../libs/OTPHP/OTPInterface.php'; - require_once __DIR__ . '/../../libs/OTPHP/OTP.php'; - require_once __DIR__ . '/../../libs/OTPHP/TOTPInterface.php'; - require_once __DIR__ . '/../../libs/OTPHP/TOTP.php'; - require_once __DIR__ . '/../../libs/Psr/Clock/ClockInterface.php'; - require_once __DIR__ . '/../../libs/OTPHP/InternalClock.php'; - require_once __DIR__ . '/../../libs/constant_time_encoding/Binary.php'; - require_once __DIR__ . '/../../libs/constant_time_encoding/EncoderInterface.php'; - require_once __DIR__ . '/../../libs/constant_time_encoding/Base32.php'; +if (isset($data['totpCode']) && $data['totpCode'] != "") { + require_once __DIR__ . '/../../libs/OTPHP/FactoryInterface.php'; + require_once __DIR__ . '/../../libs/OTPHP/Factory.php'; + require_once __DIR__ . '/../../libs/OTPHP/ParameterTrait.php'; + require_once __DIR__ . '/../../libs/OTPHP/OTPInterface.php'; + require_once __DIR__ . '/../../libs/OTPHP/OTP.php'; + require_once __DIR__ . '/../../libs/OTPHP/TOTPInterface.php'; + require_once __DIR__ . '/../../libs/OTPHP/TOTP.php'; + require_once __DIR__ . '/../../libs/Psr/Clock/ClockInterface.php'; + require_once __DIR__ . '/../../libs/OTPHP/InternalClock.php'; + require_once __DIR__ . '/../../libs/constant_time_encoding/Binary.php'; + require_once __DIR__ . '/../../libs/constant_time_encoding/EncoderInterface.php'; + require_once __DIR__ . '/../../libs/constant_time_encoding/Base32.php'; - $totp_code = $data['totpCode']; + $totp_code = $data['totpCode']; - $statement = $db->prepare('SELECT totp_secret FROM totp WHERE user_id = :id'); + $statement = $db->prepare('SELECT totp_secret FROM totp WHERE user_id = :id'); + $statement->bindValue(':id', $userId, SQLITE3_INTEGER); + $result = $statement->execute(); + $row = $result->fetchArray(SQLITE3_ASSOC); + $secret = $row['totp_secret']; + + $statement = $db->prepare('SELECT backup_codes FROM totp WHERE user_id = :id'); + $statement->bindValue(':id', $userId, SQLITE3_INTEGER); + $result = $statement->execute(); + $row = $result->fetchArray(SQLITE3_ASSOC); + $backupCodes = $row['backup_codes']; + + $clock = new OTPHP\InternalClock(); + $totp = OTPHP\TOTP::createFromSecret($secret, $clock); + $totp->setPeriod(30); + + if ($totp->verify($totp_code, null, 15)) { + $statement = $db->prepare('UPDATE user SET totp_enabled = 0 WHERE id = :id'); $statement->bindValue(':id', $userId, SQLITE3_INTEGER); - $result = $statement->execute(); - $row = $result->fetchArray(SQLITE3_ASSOC); - $secret = $row['totp_secret']; + $statement->execute(); - $statement = $db->prepare('SELECT backup_codes FROM totp WHERE user_id = :id'); + $statement = $db->prepare('DELETE FROM totp WHERE user_id = :id'); $statement->bindValue(':id', $userId, SQLITE3_INTEGER); - $result = $statement->execute(); - $row = $result->fetchArray(SQLITE3_ASSOC); - $backupCodes = $row['backup_codes']; + $statement->execute(); - $clock = new OTPHP\InternalClock(); - $totp = OTPHP\TOTP::createFromSecret($secret, $clock); - $totp->setPeriod(30); + die(json_encode([ + "success" => true, + "message" => translate('success', $i18n), + "reload" => true + ])); + } else { + // Compare the TOTP code agains the backup codes + // Normalize TOTP input + $totp_code = strtolower(trim((string) $totp_code)); - if ($totp->verify($totp_code, null, 15)) { + // Decode and normalize backup codes + $backupCodes = json_decode($backupCodes, true); + $normalizedBackupCodes = array_map(function ($code) { + return strtolower(trim((string) $code)); + }, $backupCodes); + + // Search for the normalized code + if (($key = array_search($totp_code, $normalizedBackupCodes)) !== false) { + // Match found, disable TOTP $statement = $db->prepare('UPDATE user SET totp_enabled = 0 WHERE id = :id'); $statement->bindValue(':id', $userId, SQLITE3_INTEGER); $statement->execute(); @@ -85,53 +103,19 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") { "reload" => true ])); } else { - // Compare the TOTP code agains the backup codes - // Normalize TOTP input - $totp_code = strtolower(trim((string) $totp_code)); - - // Decode and normalize backup codes - $backupCodes = json_decode($backupCodes, true); - $normalizedBackupCodes = array_map(function ($code) { - return strtolower(trim((string) $code)); - }, $backupCodes); - - // Search for the normalized code - if (($key = array_search($totp_code, $normalizedBackupCodes)) !== false) { - // Match found, disable TOTP - $statement = $db->prepare('UPDATE user SET totp_enabled = 0 WHERE id = :id'); - $statement->bindValue(':id', $userId, SQLITE3_INTEGER); - $statement->execute(); - - $statement = $db->prepare('DELETE FROM totp WHERE user_id = :id'); - $statement->bindValue(':id', $userId, SQLITE3_INTEGER); - $statement->execute(); - - die(json_encode([ - "success" => true, - "message" => translate('success', $i18n), - "reload" => true - ])); - } else { - die(json_encode([ - "success" => false, - "message" => translate('totp_code_incorrect', $i18n), - "reload" => false - ])); - } - + die(json_encode([ + "success" => false, + "message" => translate('totp_code_incorrect', $i18n), + "reload" => false + ])); } - } else { - die(json_encode([ - "success" => false, - "message" => translate('fields_missing', $i18n), - "reload" => false - ])); } + } else { die(json_encode([ "success" => false, - "message" => translate('invalid_request_method', $i18n), + "message" => translate('fields_missing', $i18n), "reload" => false ])); } \ No newline at end of file diff --git a/endpoints/user/enable_totp.php b/endpoints/user/enable_totp.php index 0e77307..c306003 100644 --- a/endpoints/user/enable_totp.php +++ b/endpoints/user/enable_totp.php @@ -2,6 +2,7 @@ require_once '../../includes/connect_endpoint.php'; require_once '../../includes/inputvalidation.php'; +require_once '../../includes/validate_endpoint.php'; if (!function_exists('trigger_deprecation')) { function trigger_deprecation($package, $version, $message, ...$args) @@ -12,14 +13,13 @@ if (!function_exists('trigger_deprecation')) { } } -if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) { - die(json_encode([ - "success" => false, - "message" => translate('session_expired', $i18n) - ])); -} +$postData = file_get_contents("php://input"); +$data = json_decode($postData, true) ?? []; + +$action = $data['action'] ?? ''; + +if ($action === 'generate') { -if ($_SERVER["REQUEST_METHOD"] === "GET") { function base32_encode($hex) { $alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; @@ -39,23 +39,19 @@ if ($_SERVER["REQUEST_METHOD"] === "GET") { return $base32; } - $data = $_GET; - if (isset($data['generate']) && $data['generate'] == true) { - $secret = base32_encode(bin2hex(random_bytes(20))); - $qrCodeUrl = "otpauth://totp/Wallos:" . $_SESSION['username'] . "?secret=" . $secret . "&issuer=Wallos"; - $response = [ - "success" => true, - "secret" => $secret, - "qrCodeUrl" => $qrCodeUrl - ]; - echo json_encode($response); - } + + $secret = base32_encode(bin2hex(random_bytes(20))); + $qrCodeUrl = "otpauth://totp/Wallos:" . $_SESSION['username'] . "?secret=" . $secret . "&issuer=Wallos"; + + echo json_encode([ + "success" => true, + "secret" => $secret, + "qrCodeUrl" => $qrCodeUrl, + ]); + exit; } -if ($_SERVER["REQUEST_METHOD"] === "POST") { - $postData = file_get_contents("php://input"); - $data = json_decode($postData, true); - +if ($action === 'verify') { if (isset($data['totpSecret']) && $data['totpSecret'] != "" && isset($data['totpCode']) && $data['totpCode'] != "") { require_once __DIR__ . '/../../libs/OTPHP/FactoryInterface.php'; require_once __DIR__ . '/../../libs/OTPHP/Factory.php'; @@ -134,8 +130,4 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") { "message" => translate('totp_code_incorrect', $i18n) ])); } - - - - } \ No newline at end of file diff --git a/endpoints/user/regenerateapikey.php b/endpoints/user/regenerateapikey.php index 3034d12..f4412c3 100644 --- a/endpoints/user/regenerateapikey.php +++ b/endpoints/user/regenerateapikey.php @@ -1,40 +1,29 @@ prepare($sql); +$stmt->bindValue(':apiKey', $apiKey, SQLITE3_TEXT); +$stmt->bindValue(':userId', $userId, SQLITE3_TEXT); +$result = $stmt->execute(); + +if ($result) { + $response = [ + "success" => true, + "message" => translate('user_details_saved', $i18n), + "apiKey" => $apiKey + ]; + echo json_encode($response); +} else { + $response = [ "success" => false, - "message" => translate('session_expired', $i18n) - ])); -} - -if ($_SERVER["REQUEST_METHOD"] === "POST") { - $postData = file_get_contents("php://input"); - $data = json_decode($postData, true); - - $apiKey = bin2hex(random_bytes(32)); - - $sql = "UPDATE user SET api_key = :apiKey WHERE id = :userId"; - $stmt = $db->prepare($sql); - $stmt->bindValue(':apiKey', $apiKey, SQLITE3_TEXT); - $stmt->bindValue(':userId', $userId, SQLITE3_TEXT); - $result = $stmt->execute(); - - if ($result) { - $response = [ - "success" => true, - "message" => translate('user_details_saved', $i18n), - "apiKey" => $apiKey - ]; - echo json_encode($response); - } else { - $response = [ - "success" => false, - "message" => translate('error_updating_user_data', $i18n) - ]; - echo json_encode($response); - } - -} - -?> \ No newline at end of file + "message" => translate('error_updating_user_data', $i18n) + ]; + echo json_encode($response); +} \ No newline at end of file diff --git a/endpoints/user/save_user.php b/endpoints/user/save_user.php index 7cbdb15..49194bd 100644 --- a/endpoints/user/save_user.php +++ b/endpoints/user/save_user.php @@ -1,6 +1,7 @@ diff --git a/scripts/profile.js b/scripts/profile.js index e1f9768..ee103b4 100644 --- a/scripts/profile.js +++ b/scripts/profile.js @@ -2,29 +2,40 @@ document.addEventListener('DOMContentLoaded', function () { document.getElementById("userForm").addEventListener("submit", function (event) { event.preventDefault(); - document.getElementById("userSubmit").disabled = true; + const submitButton = document.getElementById("userSubmit"); + submitButton.disabled = true; + const formData = new FormData(event.target); + formData.append("action", "save"); + fetch("endpoints/user/save_user.php", { method: "POST", - body: formData + headers: { + "X-CSRF-Token": window.csrfToken, + }, + body: formData, }) .then(response => response.json()) .then(data => { if (data.success) { document.getElementById("avatar").src = document.getElementById("avatarImg").src; - var newUsername = document.getElementById("username").value; + const newUsername = document.getElementById("username").value; document.getElementById("user").textContent = newUsername; showSuccessMessage(data.message); + if (data.reload) { location.reload(); } } else { - showErrorMessage(data.errorMessage); + showErrorMessage(data.errorMessage || translate("failed_save_user")); } - document.getElementById("userSubmit").disabled = false; }) .catch(error => { - showErrorMessage(translate('unknown_error')); + console.error(error); + showErrorMessage(translate("unknown_error")); + }) + .finally(() => { + submitButton.disabled = false; }); }); @@ -81,6 +92,7 @@ function deleteAvatar(path) { method: 'POST', headers: { 'Content-Type': 'application/json', + "X-CSRF-Token": window.csrfToken, }, body: JSON.stringify({ avatar: path }), }) @@ -102,31 +114,36 @@ function deleteAvatar(path) { } function enableTotp() { - const totpSecret = document.querySelector('#totp-secret'); - const totpSecretCode = document.querySelector('#totp-secret-code'); - const qrCode = document.getElementById('totp-qr-code'); - totpSecret.value = ''; - totpSecretCode.textContent = ''; - qrCode.innerHTML = ''; + const totpSecret = document.querySelector("#totp-secret"); + const totpSecretCode = document.querySelector("#totp-secret-code"); + const qrCode = document.getElementById("totp-qr-code"); + totpSecret.value = ""; + totpSecretCode.textContent = ""; + qrCode.innerHTML = ""; - fetch('endpoints/user/enable_totp.php?generate=true', { - method: 'GET' + fetch("endpoints/user/enable_totp.php", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-CSRF-Token": window.csrfToken, + }, + body: JSON.stringify({ action: "generate" }), + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + totpSecret.value = data.secret; + totpSecretCode.textContent = data.secret; + new QRCode(qrCode, data.qrCodeUrl); + openTotpPopup(); + } else { + showErrorMessage(data.message); + } }) - .then(response => response.json()) - .then(data => { - if (data.success) { - totpSecret.value = data.secret; - totpSecretCode.textContent = data.secret; - new QRCode(qrCode, data.qrCodeUrl); - - openTotpPopup(); - } else { - showErrorMessage(data.message); - } - }) - .catch(error => { - showErrorMessage(error); - }); + .catch(error => { + console.error(error); + showErrorMessage(translate("unknown_error")); + }); } function openTotpPopup() { @@ -157,8 +174,9 @@ function submitTotp() { method: 'POST', headers: { 'Content-Type': 'application/json', + "X-CSRF-Token": window.csrfToken, }, - body: JSON.stringify({ totpCode: totpCode, totpSecret: totpSecret }), + body: JSON.stringify({ totpCode: totpCode, totpSecret: totpSecret, action: 'verify' }), }) .then(response => response.json()) .then(data => { @@ -233,6 +251,7 @@ function submitDisableTotp() { method: 'POST', headers: { 'Content-Type': 'application/json', + "X-CSRF-Token": window.csrfToken, }, body: JSON.stringify({ totpCode: totpCode }), }) @@ -253,29 +272,34 @@ function submitDisableTotp() { } function regenerateApiKey() { - const regenerateButton = document.getElementById('regenerateApiKey'); - regenerateButton.disabled = true; + const regenerateButton = document.getElementById("regenerateApiKey"); + regenerateButton.disabled = true; - fetch('endpoints/user/regenerateapikey.php', { - method: 'POST', - }) + fetch("endpoints/user/regenerateapikey.php", { + method: "POST", + headers: { + "X-CSRF-Token": window.csrfToken, + }, + }) .then(response => response.json()) .then(data => { - regenerateButton.disabled = false; - if (data.success) { - const newApiKey = data.apiKey; - document.getElementById('apikey').value = newApiKey; - showSuccessMessage(data.message); - } else { - showErrorMessage(data.message); - } + regenerateButton.disabled = false; + if (data.success) { + const newApiKey = data.apiKey; + document.getElementById("apikey").value = newApiKey; + showSuccessMessage(data.message); + } else { + showErrorMessage(data.message || translate("failed_regenerate_api_key")); + } }) .catch(error => { - regenerateButton.disabled = false; - showErrorMessage(error); + console.error(error); + regenerateButton.disabled = false; + showErrorMessage(translate("unknown_error")); }); } + function exportAsJson() { fetch("endpoints/subscriptions/export.php") .then(response => response.json()) @@ -337,6 +361,7 @@ function deleteAccount(userId) { method: 'POST', headers: { 'Content-Type': 'application/json', + "X-CSRF-Token": window.csrfToken, }, body: JSON.stringify({ userId: userId }), })