Refactor fixes and adjustments
This commit is contained in:
@@ -4,12 +4,14 @@
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
require_once PROJECT_ROOT . '/src/models/AuthModel.php';
|
||||
require_once PROJECT_ROOT . '/vendor/autoload.php';
|
||||
require_once PROJECT_ROOT . '/src/models/AdminModel.php';
|
||||
|
||||
use RobThree\Auth\Algorithm;
|
||||
use RobThree\Auth\Providers\Qr\GoogleChartsQrCodeProvider;
|
||||
use Jumbojett\OpenIDConnectClient;
|
||||
|
||||
class AuthController {
|
||||
class AuthController
|
||||
{
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
@@ -56,7 +58,8 @@ class AuthController {
|
||||
*
|
||||
* @return void Redirects on success or outputs JSON error.
|
||||
*/
|
||||
public function auth(): void {
|
||||
public function auth(): void
|
||||
{
|
||||
// Global exception handler.
|
||||
set_exception_handler(function ($e) {
|
||||
error_log("Unhandled exception: " . $e->getMessage());
|
||||
@@ -64,7 +67,7 @@ class AuthController {
|
||||
echo json_encode(["error" => "Internal Server Error"]);
|
||||
exit();
|
||||
});
|
||||
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// If OIDC parameters are present, initiate OIDC flow.
|
||||
@@ -73,20 +76,15 @@ class AuthController {
|
||||
$oidcAction = 'callback';
|
||||
}
|
||||
if ($oidcAction) {
|
||||
// Load admin configuration for OIDC.
|
||||
$adminConfigFile = USERS_DIR . 'adminConfig.json';
|
||||
if (file_exists($adminConfigFile)) {
|
||||
$enc = file_get_contents($adminConfigFile);
|
||||
$dec = decryptData($enc, $encryptionKey);
|
||||
$cfg = ($dec !== false) ? json_decode($dec, true) : [];
|
||||
} else {
|
||||
$cfg = [];
|
||||
}
|
||||
$oidc_provider_url = $cfg['oidc']['providerUrl'] ?? 'https://your-oidc-provider.com';
|
||||
$oidc_client_id = $cfg['oidc']['clientId'] ?? 'YOUR_CLIENT_ID';
|
||||
$oidc_client_secret = $cfg['oidc']['clientSecret'] ?? 'YOUR_CLIENT_SECRET';
|
||||
$oidc_redirect_uri = $cfg['oidc']['redirectUri'] ?? 'https://yourdomain.com/api/auth/auth.php?oidc=callback';
|
||||
// new: delegate to AdminModel
|
||||
$cfg = AdminModel::getConfig();
|
||||
// Optional: log to confirm you loaded the right values
|
||||
error_log("Loaded OIDC config: " . print_r($cfg['oidc'], true));
|
||||
|
||||
$oidc_provider_url = $cfg['oidc']['providerUrl'];
|
||||
$oidc_client_id = $cfg['oidc']['clientId'];
|
||||
$oidc_client_secret = $cfg['oidc']['clientSecret'];
|
||||
$oidc_redirect_uri = $cfg['oidc']['redirectUri'];
|
||||
$oidc = new OpenIDConnectClient($oidc_provider_url, $oidc_client_id, $oidc_client_secret);
|
||||
$oidc->setRedirectURL($oidc_redirect_uri);
|
||||
|
||||
@@ -94,7 +92,7 @@ class AuthController {
|
||||
try {
|
||||
$oidc->authenticate();
|
||||
$username = $oidc->requestUserInfo('preferred_username');
|
||||
|
||||
|
||||
// Check for TOTP secret.
|
||||
$totp_secret = null;
|
||||
$usersFile = USERS_DIR . USERS_FILE;
|
||||
@@ -110,17 +108,17 @@ class AuthController {
|
||||
if ($totp_secret) {
|
||||
$_SESSION['pending_login_user'] = $username;
|
||||
$_SESSION['pending_login_secret'] = $totp_secret;
|
||||
header("Location: index.html?totp_required=1");
|
||||
header("Location: /index.html?totp_required=1");
|
||||
exit();
|
||||
}
|
||||
|
||||
|
||||
// Finalize login (no TOTP)
|
||||
session_regenerate_id(true);
|
||||
$_SESSION["authenticated"] = true;
|
||||
$_SESSION["username"] = $username;
|
||||
$_SESSION["isAdmin"] = (AuthModel::getUserRole($username) === "1");
|
||||
$_SESSION["folderOnly"] = loadUserPermissions($username);
|
||||
header("Location: index.html");
|
||||
header("Location: /index.html");
|
||||
exit();
|
||||
} catch (Exception $e) {
|
||||
error_log("OIDC authentication error: " . $e->getMessage());
|
||||
@@ -141,32 +139,32 @@ class AuthController {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Fallback: Form-based Authentication.
|
||||
$data = json_decode(file_get_contents("php://input"), true);
|
||||
$username = trim($data["username"] ?? "");
|
||||
$password = trim($data["password"] ?? "");
|
||||
$rememberMe = isset($data["remember_me"]) && $data["remember_me"] === true;
|
||||
|
||||
|
||||
if (!$username || !$password) {
|
||||
http_response_code(400);
|
||||
echo json_encode(["error" => "Username and password are required"]);
|
||||
exit();
|
||||
}
|
||||
|
||||
|
||||
if (!preg_match(REGEX_USER, $username)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(["error" => "Invalid username format. Only letters, numbers, underscores, dashes, and spaces are allowed."]);
|
||||
exit();
|
||||
}
|
||||
|
||||
|
||||
$ip = $_SERVER['REMOTE_ADDR'];
|
||||
$currentTime = time();
|
||||
$attemptsFile = USERS_DIR . 'failed_logins.json';
|
||||
$failedAttempts = AuthModel::loadFailedAttempts($attemptsFile);
|
||||
$maxAttempts = 5;
|
||||
$lockoutTime = 30 * 60; // 30 minutes
|
||||
|
||||
|
||||
if (isset($failedAttempts[$ip])) {
|
||||
$attemptData = $failedAttempts[$ip];
|
||||
if ($attemptData['count'] >= $maxAttempts && ($currentTime - $attemptData['last_attempt']) < $lockoutTime) {
|
||||
@@ -175,7 +173,7 @@ class AuthController {
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$user = AuthModel::authenticate($username, $password);
|
||||
if ($user !== false) {
|
||||
// Handle TOTP if required.
|
||||
@@ -203,19 +201,19 @@ class AuthController {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Clear failed attempts.
|
||||
if (isset($failedAttempts[$ip])) {
|
||||
unset($failedAttempts[$ip]);
|
||||
AuthModel::saveFailedAttempts($attemptsFile, $failedAttempts);
|
||||
}
|
||||
|
||||
|
||||
session_regenerate_id(true);
|
||||
$_SESSION["authenticated"] = true;
|
||||
$_SESSION["username"] = $username;
|
||||
$_SESSION["isAdmin"] = ($user['role'] === "1");
|
||||
$_SESSION["folderOnly"] = loadUserPermissions($username);
|
||||
|
||||
|
||||
// Handle "remember me"
|
||||
if ($rememberMe) {
|
||||
$persistentTokensFile = USERS_DIR . 'persistent_tokens.json';
|
||||
@@ -240,7 +238,7 @@ class AuthController {
|
||||
$secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
|
||||
setcookie('remember_me_token', $tokenPersistent, $expiry, '/', '', $secure, true);
|
||||
}
|
||||
|
||||
|
||||
echo json_encode([
|
||||
"status" => "ok",
|
||||
"success" => "Login successful",
|
||||
@@ -298,7 +296,8 @@ class AuthController {
|
||||
*
|
||||
* @return void Outputs a JSON response with authentication details.
|
||||
*/
|
||||
public function checkAuth(): void {
|
||||
public function checkAuth(): void
|
||||
{
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$usersFile = USERS_DIR . USERS_FILE;
|
||||
@@ -328,7 +327,7 @@ class AuthController {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Determine admin status using AuthModel::getUserRole()
|
||||
$userRole = AuthModel::getUserRole($username);
|
||||
$isAdmin = ((int)$userRole === 1);
|
||||
@@ -344,7 +343,7 @@ class AuthController {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/auth/token.php",
|
||||
* summary="Retrieve CSRF token and share URL",
|
||||
@@ -366,7 +365,8 @@ class AuthController {
|
||||
*
|
||||
* @return void Outputs the JSON response.
|
||||
*/
|
||||
public function getToken(): void {
|
||||
public function getToken(): void
|
||||
{
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode([
|
||||
"csrf_token" => $_SESSION['csrf_token'],
|
||||
@@ -400,7 +400,8 @@ class AuthController {
|
||||
*
|
||||
* @return void Redirects on success or sends a 401 header.
|
||||
*/
|
||||
public function loginBasic(): void {
|
||||
public function loginBasic(): void
|
||||
{
|
||||
// Set header for plain-text or JSON as needed.
|
||||
header('Content-Type: application/json');
|
||||
|
||||
@@ -432,7 +433,7 @@ class AuthController {
|
||||
// If TOTP is required, store pending values and redirect to prompt for TOTP.
|
||||
$_SESSION['pending_login_user'] = $username;
|
||||
$_SESSION['pending_login_secret'] = $secret;
|
||||
header("Location: index.html?totp_required=1");
|
||||
header("Location: /index.html?totp_required=1");
|
||||
exit;
|
||||
}
|
||||
// Finalize login.
|
||||
@@ -442,7 +443,7 @@ class AuthController {
|
||||
$_SESSION["isAdmin"] = (AuthModel::getUserRole($username) === "1");
|
||||
$_SESSION["folderOnly"] = AuthModel::loadFolderPermission($username);
|
||||
|
||||
header("Location: index.html");
|
||||
header("Location: /index.html");
|
||||
exit;
|
||||
}
|
||||
// Invalid credentials; prompt again.
|
||||
@@ -473,16 +474,17 @@ class AuthController {
|
||||
*
|
||||
* @return void Redirects to index.html with a logout flag.
|
||||
*/
|
||||
public function logout(): void {
|
||||
public function logout(): void
|
||||
{
|
||||
// Retrieve headers and check CSRF token.
|
||||
$headersArr = array_change_key_case(getallheaders(), CASE_LOWER);
|
||||
$receivedToken = isset($headersArr['x-csrf-token']) ? trim($headersArr['x-csrf-token']) : '';
|
||||
|
||||
|
||||
// Log mismatch but do not prevent logout.
|
||||
if (isset($_SESSION['csrf_token']) && $receivedToken !== $_SESSION['csrf_token']) {
|
||||
error_log("CSRF token mismatch on logout. Proceeding with logout.");
|
||||
}
|
||||
|
||||
|
||||
// Remove the "remember_me_token" from persistent tokens.
|
||||
if (isset($_COOKIE['remember_me_token'])) {
|
||||
$token = $_COOKIE['remember_me_token'];
|
||||
@@ -501,24 +503,29 @@ class AuthController {
|
||||
$secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
|
||||
setcookie('remember_me_token', '', time() - 3600, '/', '', $secure, true);
|
||||
}
|
||||
|
||||
|
||||
// Clear session data.
|
||||
$_SESSION = [];
|
||||
|
||||
|
||||
// Clear the session cookie.
|
||||
if (ini_get("session.use_cookies")) {
|
||||
$params = session_get_cookie_params();
|
||||
setcookie(session_name(), '', time() - 42000,
|
||||
$params["path"], $params["domain"],
|
||||
$params["secure"], $params["httponly"]
|
||||
setcookie(
|
||||
session_name(),
|
||||
'',
|
||||
time() - 42000,
|
||||
$params["path"],
|
||||
$params["domain"],
|
||||
$params["secure"],
|
||||
$params["httponly"]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Destroy the session.
|
||||
session_destroy();
|
||||
|
||||
|
||||
// Redirect the user to the login page (or index) with a logout flag.
|
||||
header("Location: index.html?logout=1");
|
||||
header("Location: /index.html?logout=1");
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -935,7 +935,7 @@ class FileController {
|
||||
</head>
|
||||
<body>
|
||||
<h2>This file is protected by a password.</h2>
|
||||
<form method="get" action="api/file/share.php">
|
||||
<form method="get" action="/api/file/share.php">
|
||||
<input type="hidden" name="token" value="<?php echo htmlspecialchars($token, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<label for="pass">Password:</label>
|
||||
<input type="password" name="pass" id="pass" required>
|
||||
|
||||
@@ -437,7 +437,7 @@ class FolderController {
|
||||
<div class="container">
|
||||
<h2>Folder Protected</h2>
|
||||
<p>This folder is protected by a password. Please enter the password to view its contents.</p>
|
||||
<form method="get" action="api/folder/shareFolder.php">
|
||||
<form method="get" action="/api/folder/shareFolder.php">
|
||||
<input type="hidden" name="token" value="<?php echo htmlspecialchars($token, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<label for="pass">Password:</label>
|
||||
<input type="password" name="pass" id="pass" required>
|
||||
@@ -534,7 +534,7 @@ class FolderController {
|
||||
foreach ($files as $file):
|
||||
$filePath = $data['realFolderPath'] . DIRECTORY_SEPARATOR . $file;
|
||||
$fileSize = file_exists($filePath) ? formatBytes(filesize($filePath)) : "Unknown";
|
||||
$downloadLink = "api/folder/downloadSharedFile.php?token=" . urlencode($token) . "&file=" . urlencode($file);
|
||||
$downloadLink = "/api/folder/downloadSharedFile.php?token=" . urlencode($token) . "&file=" . urlencode($file);
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
@@ -557,7 +557,7 @@ class FolderController {
|
||||
<!-- Pagination Controls -->
|
||||
<div class="pagination">
|
||||
<?php if ($currentPage > 1): ?>
|
||||
<a href="api/folder/shareFolder.php?token=<?php echo urlencode($token); ?>&page=<?php echo $currentPage - 1; ?><?php echo !empty($providedPass) ? "&pass=" . urlencode($providedPass) : ""; ?>">Prev</a>
|
||||
<a href="/api/folder/shareFolder.php?token=<?php echo urlencode($token); ?>&page=<?php echo $currentPage - 1; ?><?php echo !empty($providedPass) ? "&pass=" . urlencode($providedPass) : ""; ?>">Prev</a>
|
||||
<?php else: ?>
|
||||
<span>Prev</span>
|
||||
<?php endif; ?>
|
||||
@@ -569,12 +569,12 @@ class FolderController {
|
||||
<?php if ($i == $currentPage): ?>
|
||||
<span class="current"><?php echo $i; ?></span>
|
||||
<?php else: ?>
|
||||
<a href="api/folder/shareFolder.php?token=<?php echo urlencode($token); ?>&page=<?php echo $i; ?><?php echo !empty($providedPass) ? "&pass=" . urlencode($providedPass) : ""; ?>"><?php echo $i; ?></a>
|
||||
<a href="/api/folder/shareFolder.php?token=<?php echo urlencode($token); ?>&page=<?php echo $i; ?><?php echo !empty($providedPass) ? "&pass=" . urlencode($providedPass) : ""; ?>"><?php echo $i; ?></a>
|
||||
<?php endif; ?>
|
||||
<?php endfor; ?>
|
||||
|
||||
<?php if ($currentPage < $totalPages): ?>
|
||||
<a href="api/folder/shareFolder.php?token=<?php echo urlencode($token); ?>&page=<?php echo $currentPage + 1; ?><?php echo !empty($providedPass) ? "&pass=" . urlencode($providedPass) : ""; ?>">Next</a>
|
||||
<a href="/api/folder/shareFolder.php?token=<?php echo urlencode($token); ?>&page=<?php echo $currentPage + 1; ?><?php echo !empty($providedPass) ? "&pass=" . urlencode($providedPass) : ""; ?>">Next</a>
|
||||
<?php else: ?>
|
||||
<span>Next</span>
|
||||
<?php endif; ?>
|
||||
@@ -584,7 +584,7 @@ class FolderController {
|
||||
<?php if (isset($data['record']['allowUpload']) && $data['record']['allowUpload'] == 1): ?>
|
||||
<div class="upload-container">
|
||||
<h3>Upload File (50mb max size)</h3>
|
||||
<form action="api/folder/uploadToSharedFolder.php" method="post" enctype="multipart/form-data">
|
||||
<form action="/api/folder/uploadToSharedFolder.php" method="post" enctype="multipart/form-data">
|
||||
<!-- Pass the share token so the upload endpoint can verify -->
|
||||
<input type="hidden" name="token" value="<?php echo htmlspecialchars($token, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<input type="file" name="fileToUpload" required>
|
||||
@@ -613,7 +613,9 @@ class FolderController {
|
||||
var galleryContainer = document.getElementById("galleryViewContainer");
|
||||
var html = '<div class="shared-gallery-container">';
|
||||
filesData.forEach(function(file) {
|
||||
var fileUrl = "uploads/<?php echo htmlspecialchars($folderName, ENT_QUOTES, 'UTF-8'); ?>/" + encodeURIComponent(file);
|
||||
var fileUrl = window.location.origin
|
||||
+ "/uploads/<?php echo rawurlencode($folderName); ?>/"
|
||||
+ encodeURIComponent(file);
|
||||
var ext = file.split('.').pop().toLowerCase();
|
||||
var thumbnail = "";
|
||||
if (['jpg','jpeg','png','gif','bmp','webp','svg','ico'].indexOf(ext) >= 0) {
|
||||
@@ -900,7 +902,7 @@ class FolderController {
|
||||
$_SESSION['upload_message'] = "File uploaded successfully.";
|
||||
|
||||
// Redirect back to the shared folder view.
|
||||
$redirectUrl = "api/folder/shareFolder.php?token=" . urlencode($token);
|
||||
$redirectUrl = "/api/folder/shareFolder.php?token=" . urlencode($token);
|
||||
header("Location: " . $redirectUrl);
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ class AdminModel
|
||||
'providerUrl' => 'https://your-oidc-provider.com',
|
||||
'clientId' => 'YOUR_CLIENT_ID',
|
||||
'clientSecret' => 'YOUR_CLIENT_SECRET',
|
||||
'redirectUri' => 'https://yourdomain.com/auth.php?oidc=callback'
|
||||
'redirectUri' => 'https://yourdomain.com/api/auth/auth.php?oidc=callback'
|
||||
],
|
||||
'loginOptions' => [
|
||||
'disableFormLogin' => false,
|
||||
|
||||
@@ -403,7 +403,7 @@ class FolderModel {
|
||||
$baseUrl = $protocol . "://" . $host;
|
||||
}
|
||||
// The share URL points to the shared folder page.
|
||||
$link = $baseUrl . "api/folder/shareFolder.php?token=" . urlencode($token);
|
||||
$link = $baseUrl . "/api/folder/shareFolder.php?token=" . urlencode($token);
|
||||
|
||||
return ["token" => $token, "expires" => $expires, "link" => $link];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user