Files
FileRise/src/controllers/UploadController.php

147 lines
5.4 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
// src/controllers/UploadController.php
require_once __DIR__ . '/../../config/config.php';
require_once PROJECT_ROOT . '/src/lib/ACL.php';
require_once PROJECT_ROOT . '/src/models/UploadModel.php';
class UploadController
{
public function handleUpload(): void
{
header('Content-Type: application/json');
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
$requestParams = ($method === 'GET') ? $_GET : $_POST;
// Detect Resumable.js chunk "test" requests (testChunks=true, default GET)
$isResumableTest =
($method === 'GET'
&& isset($requestParams['resumableChunkNumber'])
&& isset($requestParams['resumableIdentifier']));
// ---- 1) CSRF (skip for resumable GET tests Resumable only cares about HTTP status) ----
if (!$isResumableTest) {
$headersArr = array_change_key_case(getallheaders() ?: [], CASE_LOWER);
$received = '';
if (!empty($headersArr['x-csrf-token'])) {
$received = trim($headersArr['x-csrf-token']);
} elseif (!empty($requestParams['csrf_token'])) {
$received = trim((string)$requestParams['csrf_token']);
} elseif (!empty($requestParams['upload_token'])) {
// legacy alias
$received = trim((string)$requestParams['upload_token']);
}
if (!isset($_SESSION['csrf_token']) || $received !== $_SESSION['csrf_token']) {
// Soft-fail so client can retry with refreshed token
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
http_response_code(200);
echo json_encode([
'csrf_expired' => true,
'csrf_token' => $_SESSION['csrf_token'],
]);
return;
}
}
// ---- 2) Auth + account-level flags ----
if (empty($_SESSION['authenticated'])) {
http_response_code(401);
echo json_encode(['error' => 'Unauthorized']);
return;
}
$username = (string)($_SESSION['username'] ?? '');
$userPerms = loadUserPermissions($username) ?: [];
$isAdmin = ACL::isAdmin($userPerms);
// Admins should never be blocked by account-level "disableUpload"
if (!$isAdmin && !empty($userPerms['disableUpload'])) {
http_response_code(403);
echo json_encode(['error' => 'Upload disabled for this user.']);
return;
}
// ---- 3) Folder-level WRITE permission (ACL) ----
// Prefer the unified param array, fall back to GET only if needed.
$folderParam = isset($requestParams['folder'])
? (string)$requestParams['folder']
: (isset($_GET['folder']) ? (string)$_GET['folder'] : 'root');
// Decode %xx (e.g., "test%20folder") then normalize
$folderParam = rawurldecode($folderParam);
$targetFolder = ACL::normalizeFolder($folderParam);
// Admins bypass folder canWrite checks
if (!$isAdmin && !ACL::canUpload($username, $userPerms, $targetFolder)) {
http_response_code(403);
echo json_encode([
'error' => 'Forbidden: no write access to folder "' . $targetFolder . '".',
]);
return;
}
// ---- 4) Delegate to model (force the sanitized folder) ----
$requestParams['folder'] = $targetFolder;
// Keep legacy behavior for anything still reading $_POST directly
$_POST['folder'] = $targetFolder;
$result = UploadModel::handleUpload($requestParams, $_FILES);
// ---- 5) Special handling for Resumable.js GET tests ----
// Resumable only inspects HTTP status:
// 200 => chunk exists (skip)
// 404/other => chunk missing (upload)
if ($isResumableTest && isset($result['status'])) {
if ($result['status'] === 'found') {
http_response_code(200);
} else {
http_response_code(202); // 202 Accepted = chunk not found
}
echo json_encode($result);
return;
}
// ---- 6) Normal response handling ----
if (isset($result['error'])) {
http_response_code(400);
echo json_encode($result);
return;
}
if (isset($result['status'])) {
echo json_encode($result);
return;
}
echo json_encode([
'success' => $result['success'] ?? 'File uploaded successfully',
'newFilename' => $result['newFilename'] ?? null,
]);
}
public function removeChunks(): void
{
header('Content-Type: application/json');
$receivedToken = isset($_POST['csrf_token']) ? trim((string)$_POST['csrf_token']) : '';
if ($receivedToken !== ($_SESSION['csrf_token'] ?? '')) {
http_response_code(403);
echo json_encode(['error' => 'Invalid CSRF token']);
return;
}
if (!isset($_POST['folder'])) {
http_response_code(400);
echo json_encode(['error' => 'No folder specified']);
return;
}
$folderRaw = (string)$_POST['folder'];
$folder = ACL::normalizeFolder(rawurldecode($folderRaw));
echo json_encode(UploadModel::removeChunks($folder));
}
}