New User Panel with TOTP & change password. Admin Panel added Global OTPAuth URL
This commit is contained in:
22
README.md
22
README.md
@@ -137,20 +137,26 @@ FileRise is a lightweight, secure, self-hosted web application for uploading, sy
|
|||||||
- **Seamless Interaction:**
|
- **Seamless Interaction:**
|
||||||
- Both drop zones support smooth drag and drop interactions with animations and pointer event adjustments to prevent interference, ensuring that cards can be dropped reliably regardless of screen position.
|
- Both drop zones support smooth drag and drop interactions with animations and pointer event adjustments to prevent interference, ensuring that cards can be dropped reliably regardless of screen position.
|
||||||
|
|
||||||
### 🔒 Admin Panel & OpenID Connect (OIDC) Integration
|
# 🔒 Admin Panel, TOTP & OpenID Connect (OIDC) Integration
|
||||||
|
|
||||||
- **Flexible Authentication:**
|
- **Flexible Authentication:**
|
||||||
- Supports multiple authentication methods including Form-based Login, Basic Auth, and OpenID Connect (OIDC). Allow disable of only two login options.
|
- Supports multiple authentication methods including Form-based Login, Basic Auth, OpenID Connect (OIDC), and TOTP-based Two-Factor Authentication.
|
||||||
|
- Ensures continuous secure access by allowing administrators to disable only two of the available login options at any time.
|
||||||
|
|
||||||
- **Secure OIDC Authentication:**
|
- **Secure OIDC Authentication:**
|
||||||
- Integrates seamlessly with OIDC providers (e.g., Keycloak, Okta).
|
- Seamlessly integrates with OIDC providers (e.g., Keycloak, Okta).
|
||||||
- Admin-configurable OIDC settings, including Provider URL, Client ID, Client Secret, and Redirect URI.
|
- Provides admin-configurable OIDC settings—including Provider URL, Client ID, Client Secret, and Redirect URI.
|
||||||
- All sensitive configurations are securely stored in an encrypted JSON file.
|
- Stores all sensitive configurations in an encrypted JSON file.
|
||||||
|
|
||||||
|
- **TOTP Two-Factor Authentication:**
|
||||||
|
- Enhances security by integrating Time-based One-Time Password (TOTP) functionality.
|
||||||
|
- The new User Panel automatically displays the TOTP setup modal when users enable TOTP, presenting a QR code for easy configuration in authenticator apps.
|
||||||
|
- Administrators can customize a global OTPAuth URL template for consistent TOTP provisioning across accounts.
|
||||||
|
|
||||||
- **Dynamic Admin Panel:**
|
- **Dynamic Admin Panel:**
|
||||||
- Intuitive Admin Panel with Material Icons for quick recognition and access.
|
- Features an intuitive interface with Material Icons for quick recognition and access.
|
||||||
- Allows administrators to easily manage authentication settings, user management, and login methods.
|
- Allows administrators to manage authentication settings, user management, and login methods in real time.
|
||||||
- Real-time validation prevents disabling all authentication methods simultaneously, ensuring continuous secure access.
|
- Includes real-time validation that prevents the accidental disabling of all authentication methods simultaneously.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
50
auth.php
50
auth.php
@@ -104,16 +104,31 @@ if (isset($failedAttempts[$ip])) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Updated authenticate() function:
|
||||||
|
* It reads each line from users.txt.
|
||||||
|
* It expects records in the format:
|
||||||
|
* username:hashed_password:role[:encrypted_totp_secret]
|
||||||
|
* If a fourth field is present and non-empty, it decrypts it to obtain the TOTP secret.
|
||||||
|
*/
|
||||||
function authenticate($username, $password) {
|
function authenticate($username, $password) {
|
||||||
global $usersFile;
|
global $usersFile, $encryptionKey;
|
||||||
if (!file_exists($usersFile)) {
|
if (!file_exists($usersFile)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$lines = file($usersFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
$lines = file($usersFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||||
foreach ($lines as $line) {
|
foreach ($lines as $line) {
|
||||||
list($storedUser, $storedPass, $storedRole) = explode(':', trim($line), 3);
|
$parts = explode(':', trim($line));
|
||||||
if ($username === $storedUser && password_verify($password, $storedPass)) {
|
if (count($parts) < 3) continue; // Skip invalid lines.
|
||||||
return $storedRole;
|
if ($username === $parts[0] && password_verify($password, $parts[1])) {
|
||||||
|
$result = ['role' => $parts[2]];
|
||||||
|
// If there's a fourth field, decrypt it to get the TOTP secret.
|
||||||
|
if (isset($parts[3]) && !empty($parts[3])) {
|
||||||
|
$result['totp_secret'] = decryptData($parts[3], $encryptionKey);
|
||||||
|
} else {
|
||||||
|
$result['totp_secret'] = null;
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -134,8 +149,27 @@ if (!preg_match('/^[A-Za-z0-9_\- ]+$/', $username)) {
|
|||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
$userRole = authenticate($username, $password);
|
$user = authenticate($username, $password);
|
||||||
if ($userRole !== false) {
|
if ($user !== false) {
|
||||||
|
// Only require TOTP if the user's TOTP secret is set.
|
||||||
|
if (!empty($user['totp_secret'])) {
|
||||||
|
if (empty($data['totp_code'])) {
|
||||||
|
echo json_encode([
|
||||||
|
"totp_required" => true,
|
||||||
|
"message" => "TOTP code required"
|
||||||
|
]);
|
||||||
|
exit();
|
||||||
|
} else {
|
||||||
|
$tfa = new \RobThree\Auth\TwoFactorAuth('FileRise');
|
||||||
|
$providedCode = trim($data['totp_code']);
|
||||||
|
if (!$tfa->verifyCode($user['totp_secret'], $providedCode)) {
|
||||||
|
echo json_encode(["error" => "Invalid TOTP code"]);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// --- End TOTP Integration ---
|
||||||
|
|
||||||
if (isset($failedAttempts[$ip])) {
|
if (isset($failedAttempts[$ip])) {
|
||||||
unset($failedAttempts[$ip]);
|
unset($failedAttempts[$ip]);
|
||||||
saveFailedAttempts($attemptsFile, $failedAttempts);
|
saveFailedAttempts($attemptsFile, $failedAttempts);
|
||||||
@@ -143,7 +177,7 @@ if ($userRole !== false) {
|
|||||||
session_regenerate_id(true);
|
session_regenerate_id(true);
|
||||||
$_SESSION["authenticated"] = true;
|
$_SESSION["authenticated"] = true;
|
||||||
$_SESSION["username"] = $username;
|
$_SESSION["username"] = $username;
|
||||||
$_SESSION["isAdmin"] = ($userRole === "1");
|
$_SESSION["isAdmin"] = ($user['role'] === "1");
|
||||||
|
|
||||||
if ($rememberMe) {
|
if ($rememberMe) {
|
||||||
$token = bin2hex(random_bytes(32));
|
$token = bin2hex(random_bytes(32));
|
||||||
@@ -160,7 +194,7 @@ if ($userRole !== false) {
|
|||||||
$persistentTokens[$token] = [
|
$persistentTokens[$token] = [
|
||||||
"username" => $username,
|
"username" => $username,
|
||||||
"expiry" => $expiry,
|
"expiry" => $expiry,
|
||||||
"isAdmin" => ($userRole === "1")
|
"isAdmin" => ($user['role'] === "1")
|
||||||
];
|
];
|
||||||
$encryptedContent = encryptData(json_encode($persistentTokens, JSON_PRETTY_PRINT), $encryptionKey);
|
$encryptedContent = encryptData(json_encode($persistentTokens, JSON_PRETTY_PRINT), $encryptionKey);
|
||||||
file_put_contents($persistentTokensFile, $encryptedContent, LOCK_EX);
|
file_put_contents($persistentTokensFile, $encryptedContent, LOCK_EX);
|
||||||
|
|||||||
@@ -54,7 +54,19 @@ $userFound = false;
|
|||||||
$newLines = [];
|
$newLines = [];
|
||||||
|
|
||||||
foreach ($lines as $line) {
|
foreach ($lines as $line) {
|
||||||
list($storedUser, $storedHash, $storedRole) = explode(':', trim($line));
|
$parts = explode(':', trim($line));
|
||||||
|
// Expect at least 3 parts: username, hashed password, and role.
|
||||||
|
if (count($parts) < 3) {
|
||||||
|
// Skip invalid lines.
|
||||||
|
$newLines[] = $line;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$storedUser = $parts[0];
|
||||||
|
$storedHash = $parts[1];
|
||||||
|
$storedRole = $parts[2];
|
||||||
|
// Preserve TOTP secret if it exists.
|
||||||
|
$totpSecret = (count($parts) >= 4) ? $parts[3] : "";
|
||||||
|
|
||||||
if ($storedUser === $username) {
|
if ($storedUser === $username) {
|
||||||
$userFound = true;
|
$userFound = true;
|
||||||
// Verify the old password.
|
// Verify the old password.
|
||||||
@@ -64,8 +76,12 @@ foreach ($lines as $line) {
|
|||||||
}
|
}
|
||||||
// Hash the new password.
|
// Hash the new password.
|
||||||
$newHashedPassword = password_hash($newPassword, PASSWORD_BCRYPT);
|
$newHashedPassword = password_hash($newPassword, PASSWORD_BCRYPT);
|
||||||
// Rebuild the line with the new hash.
|
// Rebuild the line with the new hash and preserve TOTP secret if present.
|
||||||
$newLines[] = $username . ":" . $newHashedPassword . ":" . $storedRole;
|
if ($totpSecret !== "") {
|
||||||
|
$newLines[] = $username . ":" . $newHashedPassword . ":" . $storedRole . ":" . $totpSecret;
|
||||||
|
} else {
|
||||||
|
$newLines[] = $username . ":" . $newHashedPassword . ":" . $storedRole;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$newLines[] = $line;
|
$newLines[] = $line;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,25 @@ if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$totp_enabled = false;
|
||||||
|
$username = $_SESSION['username'] ?? '';
|
||||||
|
if ($username) {
|
||||||
|
$lines = file($usersFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
$parts = explode(":", trim($line));
|
||||||
|
// Assuming first field is username and fourth (if exists) is the TOTP secret.
|
||||||
|
if ($parts[0] === $username) {
|
||||||
|
if (isset($parts[3]) && trim($parts[3]) !== "") {
|
||||||
|
$totp_enabled = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
echo json_encode([
|
echo json_encode([
|
||||||
"authenticated" => true,
|
"authenticated" => true,
|
||||||
"isAdmin" => isset($_SESSION["isAdmin"]) ? $_SESSION["isAdmin"] : false
|
"isAdmin" => isset($_SESSION["isAdmin"]) ? $_SESSION["isAdmin"] : false,
|
||||||
|
"totp_enabled" => $totp_enabled
|
||||||
]);
|
]);
|
||||||
?>
|
?>
|
||||||
@@ -258,7 +258,7 @@ function previewFile(fileUrl, fileName) {
|
|||||||
embed.style.height = "80vh";
|
embed.style.height = "80vh";
|
||||||
embed.style.border = "none";
|
embed.style.border = "none";
|
||||||
container.appendChild(embed);
|
container.appendChild(embed);
|
||||||
} else if (/\.(mp4|webm|mov|ogg)$/i.test(fileName)) {
|
} else if (/\.(mp4|webm|mov)$/i.test(fileName)) {
|
||||||
const video = document.createElement("video");
|
const video = document.createElement("video");
|
||||||
video.src = fileUrl;
|
video.src = fileUrl;
|
||||||
video.controls = true;
|
video.controls = true;
|
||||||
|
|||||||
@@ -11,7 +11,12 @@ if (file_exists($configFile)) {
|
|||||||
echo json_encode(['error' => 'Failed to decrypt configuration.']);
|
echo json_encode(['error' => 'Failed to decrypt configuration.']);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
echo $decryptedContent;
|
// Decode the configuration and ensure globalOtpauthUrl is set
|
||||||
|
$config = json_decode($decryptedContent, true);
|
||||||
|
if (!isset($config['globalOtpauthUrl'])) {
|
||||||
|
$config['globalOtpauthUrl'] = "";
|
||||||
|
}
|
||||||
|
echo json_encode($config);
|
||||||
} else {
|
} else {
|
||||||
echo json_encode([
|
echo json_encode([
|
||||||
'oidc' => [
|
'oidc' => [
|
||||||
@@ -24,7 +29,8 @@ if (file_exists($configFile)) {
|
|||||||
'disableFormLogin' => false,
|
'disableFormLogin' => false,
|
||||||
'disableBasicAuth' => false,
|
'disableBasicAuth' => false,
|
||||||
'disableOIDCLogin' => false
|
'disableOIDCLogin' => false
|
||||||
]
|
],
|
||||||
|
'globalOtpauthUrl' => ""
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
<button id="logoutBtn" title="Logout">
|
<button id="logoutBtn" title="Logout">
|
||||||
<i class="material-icons">exit_to_app</i>
|
<i class="material-icons">exit_to_app</i>
|
||||||
</button>
|
</button>
|
||||||
<button id="changePasswordBtn" title="Change Password">
|
<button id="changePasswordBtn" title="Change Password" style="display: none;">
|
||||||
<i class="material-icons">vpn_key</i>
|
<i class="material-icons">vpn_key</i>
|
||||||
</button>
|
</button>
|
||||||
<div id="restoreFilesModal" class="modal centered-modal" style="display: none;">
|
<div id="restoreFilesModal" class="modal centered-modal" style="display: none;">
|
||||||
@@ -391,7 +391,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- JavaScript Files -->
|
|
||||||
<script type="module" src="main.js"></script>
|
<script type="module" src="main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
11
styles.css
11
styles.css
@@ -2048,3 +2048,14 @@ body.dark-mode .admin-panel-content textarea {
|
|||||||
body.dark-mode .admin-panel-content label {
|
body.dark-mode .admin-panel-content label {
|
||||||
color: #e0e0e0;
|
color: #e0e0e0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#openChangePasswordModalBtn {
|
||||||
|
width: auto;
|
||||||
|
padding: 5px 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-right: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changePasswordModal {
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
152
totp_setup.php
Normal file
152
totp_setup.php
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
<?php
|
||||||
|
// totp_setup.php
|
||||||
|
|
||||||
|
require_once 'vendor/autoload.php';
|
||||||
|
require 'config.php';
|
||||||
|
|
||||||
|
use Endroid\QrCode\Builder\Builder;
|
||||||
|
use Endroid\QrCode\Writer\PngWriter;
|
||||||
|
use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelHigh;
|
||||||
|
|
||||||
|
// For debugging purposes, you might enable error reporting temporarily:
|
||||||
|
// ini_set('display_errors', 1);
|
||||||
|
// error_reporting(E_ALL);
|
||||||
|
|
||||||
|
// Start the session and ensure the user is authenticated.
|
||||||
|
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) {
|
||||||
|
http_response_code(403);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify CSRF token provided as a GET parameter.
|
||||||
|
if (!isset($_GET['csrf']) || $_GET['csrf'] !== $_SESSION['csrf_token']) {
|
||||||
|
http_response_code(403);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$username = $_SESSION['username'] ?? '';
|
||||||
|
if (!$username) {
|
||||||
|
http_response_code(400);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set header to output a PNG image.
|
||||||
|
header("Content-Type: image/png");
|
||||||
|
|
||||||
|
// Define the path to your users.txt file.
|
||||||
|
$usersFile = USERS_DIR . USERS_FILE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the TOTP secret for the given user in users.txt.
|
||||||
|
*
|
||||||
|
* @param string $username
|
||||||
|
* @param string $encryptedSecret The encrypted TOTP secret.
|
||||||
|
*/
|
||||||
|
function updateUserTOTPSecret($username, $encryptedSecret) {
|
||||||
|
global $usersFile;
|
||||||
|
$lines = file($usersFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||||
|
$newLines = [];
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
$parts = explode(':', trim($line));
|
||||||
|
if (count($parts) < 3) {
|
||||||
|
$newLines[] = $line;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($parts[0] === $username) {
|
||||||
|
// If a fourth field exists, update it; otherwise, append it.
|
||||||
|
if (count($parts) >= 4) {
|
||||||
|
$parts[3] = $encryptedSecret;
|
||||||
|
} else {
|
||||||
|
$parts[] = $encryptedSecret;
|
||||||
|
}
|
||||||
|
$newLines[] = implode(':', $parts);
|
||||||
|
} else {
|
||||||
|
$newLines[] = $line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_put_contents($usersFile, implode(PHP_EOL, $newLines) . PHP_EOL, LOCK_EX);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the current user's TOTP secret from users.txt (if present).
|
||||||
|
*
|
||||||
|
* @param string $username
|
||||||
|
* @return string|null The decrypted TOTP secret or null if not found.
|
||||||
|
*/
|
||||||
|
function getUserTOTPSecret($username) {
|
||||||
|
global $usersFile, $encryptionKey;
|
||||||
|
if (!file_exists($usersFile)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$lines = file($usersFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
$parts = explode(':', trim($line));
|
||||||
|
if (count($parts) >= 4 && $parts[0] === $username && !empty($parts[3])) {
|
||||||
|
return decryptData($parts[3], $encryptionKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the global OTPAuth URL from admin configuration.
|
||||||
|
*
|
||||||
|
* @return string Global OTPAuth URL template or an empty string if not set.
|
||||||
|
*/
|
||||||
|
function getGlobalOtpauthUrl() {
|
||||||
|
global $encryptionKey;
|
||||||
|
$adminConfigFile = USERS_DIR . 'adminConfig.json';
|
||||||
|
if (file_exists($adminConfigFile)) {
|
||||||
|
$encryptedContent = file_get_contents($adminConfigFile);
|
||||||
|
$decryptedContent = decryptData($encryptedContent, $encryptionKey);
|
||||||
|
if ($decryptedContent !== false) {
|
||||||
|
$config = json_decode($decryptedContent, true);
|
||||||
|
if (isset($config['globalOtpauthUrl']) && !empty($config['globalOtpauthUrl'])) {
|
||||||
|
return $config['globalOtpauthUrl'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
$tfa = new \RobThree\Auth\TwoFactorAuth('FileRise');
|
||||||
|
|
||||||
|
// Retrieve the current TOTP secret for the user.
|
||||||
|
$totpSecret = getUserTOTPSecret($username);
|
||||||
|
if (!$totpSecret) {
|
||||||
|
// If no TOTP secret exists, generate a new one.
|
||||||
|
$totpSecret = $tfa->createSecret();
|
||||||
|
$encryptedSecret = encryptData($totpSecret, $encryptionKey);
|
||||||
|
updateUserTOTPSecret($username, $encryptedSecret);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the otpauth URL to use.
|
||||||
|
// If a global OTPAuth URL template is defined, replace placeholders {label} and {secret}.
|
||||||
|
// Otherwise, use the default method.
|
||||||
|
$globalOtpauthUrl = getGlobalOtpauthUrl();
|
||||||
|
if (!empty($globalOtpauthUrl)) {
|
||||||
|
$label = "FileRise:" . $username;
|
||||||
|
$otpauthUrl = str_replace(
|
||||||
|
["{label}", "{secret}"],
|
||||||
|
[urlencode($label), $totpSecret],
|
||||||
|
$globalOtpauthUrl
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$label = urlencode("FileRise:" . $username);
|
||||||
|
$issuer = urlencode("FileRise");
|
||||||
|
$otpauthUrl = "otpauth://totp/{$label}?secret={$totpSecret}&issuer={$issuer}";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the QR code using Endroid QR Code.
|
||||||
|
$result = Builder::create()
|
||||||
|
->writer(new PngWriter())
|
||||||
|
->data($otpauthUrl)
|
||||||
|
->errorCorrectionLevel(new ErrorCorrectionLevelHigh())
|
||||||
|
->build();
|
||||||
|
|
||||||
|
header('Content-Type: ' . $result->getMimeType());
|
||||||
|
echo $result->getString();
|
||||||
|
?>
|
||||||
@@ -51,6 +51,9 @@ $disableFormLogin = isset($data['disableFormLogin']) ? filter_var($data['disable
|
|||||||
$disableBasicAuth = isset($data['disableBasicAuth']) ? filter_var($data['disableBasicAuth'], FILTER_VALIDATE_BOOLEAN) : false;
|
$disableBasicAuth = isset($data['disableBasicAuth']) ? filter_var($data['disableBasicAuth'], FILTER_VALIDATE_BOOLEAN) : false;
|
||||||
$disableOIDCLogin = isset($data['disableOIDCLogin']) ? filter_var($data['disableOIDCLogin'], FILTER_VALIDATE_BOOLEAN) : false;
|
$disableOIDCLogin = isset($data['disableOIDCLogin']) ? filter_var($data['disableOIDCLogin'], FILTER_VALIDATE_BOOLEAN) : false;
|
||||||
|
|
||||||
|
// Retrieve the global OTPAuth URL (new field). If not provided, default to an empty string.
|
||||||
|
$globalOtpauthUrl = isset($data['globalOtpauthUrl']) ? trim($data['globalOtpauthUrl']) : "";
|
||||||
|
|
||||||
// Prepare configuration array.
|
// Prepare configuration array.
|
||||||
$configUpdate = [
|
$configUpdate = [
|
||||||
'oidc' => [
|
'oidc' => [
|
||||||
@@ -63,7 +66,8 @@ $configUpdate = [
|
|||||||
'disableFormLogin' => $disableFormLogin,
|
'disableFormLogin' => $disableFormLogin,
|
||||||
'disableBasicAuth' => $disableBasicAuth,
|
'disableBasicAuth' => $disableBasicAuth,
|
||||||
'disableOIDCLogin' => $disableOIDCLogin,
|
'disableOIDCLogin' => $disableOIDCLogin,
|
||||||
]
|
],
|
||||||
|
'globalOtpauthUrl' => $globalOtpauthUrl
|
||||||
];
|
];
|
||||||
|
|
||||||
// Define the configuration file path.
|
// Define the configuration file path.
|
||||||
|
|||||||
82
updateUserPanel.php
Normal file
82
updateUserPanel.php
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
<?php
|
||||||
|
// updateUserPanel.php
|
||||||
|
require 'config.php';
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Ensure the user is authenticated.
|
||||||
|
if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) {
|
||||||
|
http_response_code(403);
|
||||||
|
echo json_encode(["error" => "Unauthorized"]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the CSRF token from headers.
|
||||||
|
$headers = array_change_key_case(getallheaders(), CASE_LOWER);
|
||||||
|
$csrfToken = isset($headers['x-csrf-token']) ? trim($headers['x-csrf-token']) : '';
|
||||||
|
if (!isset($_SESSION['csrf_token']) || $csrfToken !== $_SESSION['csrf_token']) {
|
||||||
|
http_response_code(403);
|
||||||
|
echo json_encode(["error" => "Invalid CSRF token"]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = json_decode(file_get_contents("php://input"), true);
|
||||||
|
if (!is_array($data)) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(["error" => "Invalid input"]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$username = $_SESSION['username'] ?? '';
|
||||||
|
if (!$username) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(["error" => "No username in session"]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$totp_enabled = isset($data['totp_enabled']) ? filter_var($data['totp_enabled'], FILTER_VALIDATE_BOOLEAN) : false;
|
||||||
|
$usersFile = USERS_DIR . USERS_FILE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the TOTP secret for a given user by removing or emptying the fourth field.
|
||||||
|
*
|
||||||
|
* @param string $username
|
||||||
|
*/
|
||||||
|
function disableUserTOTP($username) {
|
||||||
|
global $usersFile;
|
||||||
|
$lines = file($usersFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||||
|
$newLines = [];
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
$parts = explode(':', trim($line));
|
||||||
|
// If the line doesn't have at least three parts, leave it alone.
|
||||||
|
if (count($parts) < 3) {
|
||||||
|
$newLines[] = $line;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($parts[0] === $username) {
|
||||||
|
// If a fourth field exists, clear it; otherwise, append an empty field.
|
||||||
|
if (count($parts) >= 4) {
|
||||||
|
$parts[3] = "";
|
||||||
|
} else {
|
||||||
|
$parts[] = "";
|
||||||
|
}
|
||||||
|
$newLines[] = implode(':', $parts);
|
||||||
|
} else {
|
||||||
|
$newLines[] = $line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_put_contents($usersFile, implode(PHP_EOL, $newLines) . PHP_EOL, LOCK_EX);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If TOTP is disabled, clear the user's TOTP secret.
|
||||||
|
if (!$totp_enabled) {
|
||||||
|
disableUserTOTP($username);
|
||||||
|
echo json_encode(["success" => "User panel updated: TOTP disabled"]);
|
||||||
|
exit;
|
||||||
|
} else {
|
||||||
|
// If TOTP is enabled, do not change the stored secret.
|
||||||
|
echo json_encode(["success" => "User panel updated: TOTP remains enabled"]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
?>
|
||||||
@@ -407,7 +407,7 @@ function initResumableUpload() {
|
|||||||
resumableInstance = new Resumable({
|
resumableInstance = new Resumable({
|
||||||
target: "upload.php",
|
target: "upload.php",
|
||||||
query: { folder: window.currentFolder || "root", upload_token: window.csrfToken },
|
query: { folder: window.currentFolder || "root", upload_token: window.csrfToken },
|
||||||
chunkSize: 3 * 1024 * 1024, // 3 MB chunks
|
chunkSize: 1.5 * 1024 * 1024, // 1.5 MB chunks
|
||||||
simultaneousUploads: 3,
|
simultaneousUploads: 3,
|
||||||
testChunks: false,
|
testChunks: false,
|
||||||
throttleProgressCallbacks: 1,
|
throttleProgressCallbacks: 1,
|
||||||
|
|||||||
Reference in New Issue
Block a user