New Admin Panel settings (enableWebDAV & shareMaxUploadSize)

This commit is contained in:
Ryan
2025-04-22 17:11:19 -04:00
committed by GitHub
parent ca3e2f316c
commit 242661a9c9
7 changed files with 221 additions and 54 deletions

View File

@@ -1,6 +1,6 @@
# Changelog # Changelog
## Changes 4/22/2025 ## Changes 4/22/2025 v1.2.3
- Support for custom PUID/PGID via `PUID`/`PGID` environment variables, replacing the need to run the container with `--user` - Support for custom PUID/PGID via `PUID`/`PGID` environment variables, replacing the need to run the container with `--user`
- New `PUID` and `PGID` config options in the Unraid Community Apps template - New `PUID` and `PGID` config options in the Unraid Community Apps template
@@ -9,7 +9,18 @@
- `wwwdata` user is remapped at buildtime to the supplied `PUID:PGID`, then Apache drops privileges to that user - `wwwdata` user is remapped at buildtime to the supplied `PUID:PGID`, then Apache drops privileges to that user
- Unraid template: removed recommendation to use `--user`; replaced with `PUID`, `PGID`, and `Container Port` variables - Unraid template: removed recommendation to use `--user`; replaced with `PUID`, `PGID`, and `Container Port` variables
- “Permission denied” errors when forcing `--user 99:100` on Unraid by ensuring startup runs as root - “Permission denied” errors when forcing `--user 99:100` on Unraid by ensuring startup runs as root
- Silence group issue - Dockerfile silence group issue
- `enableWebDAV` toggle in Admin Panel (default: disabled)
- **Admin Panel enhancements**
- New `enableWebDAV` boolean setting
- New `sharedMaxUploadSize` numeric setting (bytes)
- **Shared Folder upload size**
- `sharedMaxUploadSize` is now enforced in `FolderModel::uploadToSharedFolder`
- Upload form header on sharedfolder page dynamically shows “(X MB max size)”
- **API updates**
- `getConfig` and `updateConfig` endpoints now include `enableWebDAV` and `sharedMaxUploadSize`
- Updated `AdminModel` & `AdminController` to persist and validate new settings
- Enhanced `shareFolder()` view to pull from admin config and format the maxuploadsize label
--- ---

View File

@@ -3,7 +3,7 @@ import { sendRequest } from './networkUtils.js';
import { t, applyTranslations, setLocale } from './i18n.js'; import { t, applyTranslations, setLocale } from './i18n.js';
import { loadAdminConfigFunc } from './auth.js'; import { loadAdminConfigFunc } from './auth.js';
const version = "v1.2.2"; // Update this version string as needed const version = "v1.2.3"; // Update this version string as needed
const adminTitle = `${t("admin_panel")} <small style="font-size: 12px; color: gray;">${version}</small>`; const adminTitle = `${t("admin_panel")} <small style="font-size: 12px; color: gray;">${version}</small>`;
let lastLoginData = null; let lastLoginData = null;
@@ -597,6 +597,7 @@ export function openAdminPanel() {
} }
if (config.oidc) Object.assign(window.currentOIDCConfig, config.oidc); if (config.oidc) Object.assign(window.currentOIDCConfig, config.oidc);
if (config.globalOtpauthUrl) window.currentOIDCConfig.globalOtpauthUrl = config.globalOtpauthUrl; if (config.globalOtpauthUrl) window.currentOIDCConfig.globalOtpauthUrl = config.globalOtpauthUrl;
const isDarkMode = document.body.classList.contains("dark-mode"); const isDarkMode = document.body.classList.contains("dark-mode");
const overlayBackground = isDarkMode ? "rgba(0,0,0,0.7)" : "rgba(0,0,0,0.3)"; const overlayBackground = isDarkMode ? "rgba(0,0,0,0.7)" : "rgba(0,0,0,0.3)";
const modalContentStyles = ` const modalContentStyles = `
@@ -611,6 +612,7 @@ export function openAdminPanel() {
max-height: 90vh; max-height: 90vh;
border: ${isDarkMode ? "1px solid #444" : "1px solid #ccc"}; border: ${isDarkMode ? "1px solid #444" : "1px solid #ccc"};
`; `;
let adminModal = document.getElementById("adminPanelModal"); let adminModal = document.getElementById("adminPanelModal");
if (!adminModal) { if (!adminModal) {
@@ -663,6 +665,28 @@ export function openAdminPanel() {
<label for="disableOIDCLogin">${t("disable_oidc_login")}</label> <label for="disableOIDCLogin">${t("disable_oidc_login")}</label>
</div> </div>
</fieldset> </fieldset>
<!-- New WebDAV setting -->
<fieldset style="margin-bottom: 15px;">
<legend>WebDAV Access</legend>
<div class="form-group">
<input type="checkbox" id="enableWebDAV" />
<label for="enableWebDAV">Enable WebDAV</label>
</div>
</fieldset>
<!-- End WebDAV setting -->
<!-- New Shared Max Upload Size setting -->
<fieldset style="margin-bottom: 15px;">
<legend>Shared Max Upload Size (bytes)</legend>
<div class="form-group">
<input type="number" id="sharedMaxUploadSize" class="form-control"
placeholder="e.g. 52428800" />
<small>Enter maximum bytes allowed for shared-folder uploads</small>
</div>
</fieldset>
<!-- End Shared Max Upload Size setting -->
<fieldset style="margin-bottom: 15px;"> <fieldset style="margin-bottom: 15px;">
<legend>${t("oidc_configuration")}</legend> <legend>${t("oidc_configuration")}</legend>
<div class="form-group"> <div class="form-group">
@@ -698,33 +722,34 @@ export function openAdminPanel() {
`; `;
document.body.appendChild(adminModal); document.body.appendChild(adminModal);
// Bind closing events that will use our enhanced close function. // Bind closing
document.getElementById("closeAdminPanel").addEventListener("click", closeAdminPanel); document.getElementById("closeAdminPanel").addEventListener("click", closeAdminPanel);
adminModal.addEventListener("click", (e) => { adminModal.addEventListener("click", e => { if (e.target === adminModal) closeAdminPanel(); });
if (e.target === adminModal) closeAdminPanel();
});
document.getElementById("cancelAdminSettings").addEventListener("click", closeAdminPanel); document.getElementById("cancelAdminSettings").addEventListener("click", closeAdminPanel);
// Bind other buttons. // Bind other buttons
document.getElementById("adminOpenAddUser").addEventListener("click", () => { document.getElementById("adminOpenAddUser").addEventListener("click", () => {
toggleVisibility("addUserModal", true); toggleVisibility("addUserModal", true);
document.getElementById("newUsername").focus(); document.getElementById("newUsername").focus();
}); });
document.getElementById("adminOpenRemoveUser").addEventListener("click", () => { document.getElementById("adminOpenRemoveUser").addEventListener("click", () => {
if (typeof window.loadUserList === "function") { if (typeof window.loadUserList === "function") window.loadUserList();
window.loadUserList();
}
toggleVisibility("removeUserModal", true); toggleVisibility("removeUserModal", true);
}); });
document.getElementById("adminOpenUserPermissions").addEventListener("click", () => { document.getElementById("adminOpenUserPermissions").addEventListener("click", () => {
openUserPermissionsModal(); openUserPermissionsModal();
}); });
document.getElementById("saveAdminSettings").addEventListener("click", () => {
// Save handler
document.getElementById("saveAdminSettings").addEventListener("click", () => {
const disableFormLoginCheckbox = document.getElementById("disableFormLogin"); const disableFormLoginCheckbox = document.getElementById("disableFormLogin");
const disableBasicAuthCheckbox = document.getElementById("disableBasicAuth"); const disableBasicAuthCheckbox = document.getElementById("disableBasicAuth");
const disableOIDCLoginCheckbox = document.getElementById("disableOIDCLogin"); const disableOIDCLoginCheckbox = document.getElementById("disableOIDCLogin");
const totalDisabled = [disableFormLoginCheckbox, disableBasicAuthCheckbox, disableOIDCLoginCheckbox].filter(cb => cb.checked).length; const enableWebDAVCheckbox = document.getElementById("enableWebDAV");
const sharedMaxUploadSizeInput = document.getElementById("sharedMaxUploadSize");
const totalDisabled = [disableFormLoginCheckbox, disableBasicAuthCheckbox, disableOIDCLoginCheckbox]
.filter(cb => cb.checked).length;
if (totalDisabled === 3) { if (totalDisabled === 3) {
showToast(t("at_least_one_login_method")); showToast(t("at_least_one_login_method"));
disableOIDCLoginCheckbox.checked = false; disableOIDCLoginCheckbox.checked = false;
@@ -738,8 +763,8 @@ export function openAdminPanel() {
} }
return; return;
} }
const newHeaderTitle = document.getElementById("headerTitle").value.trim();
const newHeaderTitle = document.getElementById("headerTitle").value.trim();
const newOIDCConfig = { const newOIDCConfig = {
providerUrl: document.getElementById("oidcProviderUrl").value.trim(), providerUrl: document.getElementById("oidcProviderUrl").value.trim(),
clientId: document.getElementById("oidcClientId").value.trim(), clientId: document.getElementById("oidcClientId").value.trim(),
@@ -749,13 +774,18 @@ export function openAdminPanel() {
const disableFormLogin = disableFormLoginCheckbox.checked; const disableFormLogin = disableFormLoginCheckbox.checked;
const disableBasicAuth = disableBasicAuthCheckbox.checked; const disableBasicAuth = disableBasicAuthCheckbox.checked;
const disableOIDCLogin = disableOIDCLoginCheckbox.checked; const disableOIDCLogin = disableOIDCLoginCheckbox.checked;
const enableWebDAV = enableWebDAVCheckbox.checked;
const sharedMaxUploadSize = parseInt(sharedMaxUploadSizeInput.value, 10) || 0;
const globalOtpauthUrl = document.getElementById("globalOtpauthUrl").value.trim(); const globalOtpauthUrl = document.getElementById("globalOtpauthUrl").value.trim();
sendRequest("/api/admin/updateConfig.php", "POST", { sendRequest("/api/admin/updateConfig.php", "POST", {
header_title: newHeaderTitle, header_title: newHeaderTitle,
oidc: newOIDCConfig, oidc: newOIDCConfig,
disableFormLogin, disableFormLogin,
disableBasicAuth, disableBasicAuth,
disableOIDCLogin, disableOIDCLogin,
enableWebDAV,
sharedMaxUploadSize,
globalOtpauthUrl globalOtpauthUrl
}, { "X-CSRF-Token": window.csrfToken }) }, { "X-CSRF-Token": window.csrfToken })
.then(response => { .then(response => {
@@ -764,26 +794,32 @@ export function openAdminPanel() {
localStorage.setItem("disableFormLogin", disableFormLogin); localStorage.setItem("disableFormLogin", disableFormLogin);
localStorage.setItem("disableBasicAuth", disableBasicAuth); localStorage.setItem("disableBasicAuth", disableBasicAuth);
localStorage.setItem("disableOIDCLogin", disableOIDCLogin); localStorage.setItem("disableOIDCLogin", disableOIDCLogin);
localStorage.setItem("enableWebDAV", enableWebDAV);
localStorage.setItem("sharedMaxUploadSize", sharedMaxUploadSize);
if (typeof window.updateLoginOptionsUI === "function") { if (typeof window.updateLoginOptionsUI === "function") {
window.updateLoginOptionsUI({ disableFormLogin, disableBasicAuth, disableOIDCLogin }); window.updateLoginOptionsUI({
disableFormLogin,
disableBasicAuth,
disableOIDCLogin
});
} }
// Update the captured initial state since the changes have now been saved.
captureInitialAdminConfig(); captureInitialAdminConfig();
closeAdminPanel(); closeAdminPanel();
loadAdminConfigFunc(); loadAdminConfigFunc();
} else { } else {
showToast(t("error_updating_settings") + ": " + (response.error || t("unknown_error"))); showToast(t("error_updating_settings") + ": " + (response.error || t("unknown_error")));
} }
}) })
.catch(() => { }); .catch(() => { });
}); });
// Enforce login option constraints. // Enforce login option constraints.
const disableFormLoginCheckbox = document.getElementById("disableFormLogin"); const disableFormLoginCheckbox = document.getElementById("disableFormLogin");
const disableBasicAuthCheckbox = document.getElementById("disableBasicAuth"); const disableBasicAuthCheckbox = document.getElementById("disableBasicAuth");
const disableOIDCLoginCheckbox = document.getElementById("disableOIDCLogin"); const disableOIDCLoginCheckbox = document.getElementById("disableOIDCLogin");
function enforceLoginOptionConstraint(changedCheckbox) { function enforceLoginOptionConstraint(changedCheckbox) {
const totalDisabled = [disableFormLoginCheckbox, disableBasicAuthCheckbox, disableOIDCLoginCheckbox].filter(cb => cb.checked).length; const totalDisabled = [disableFormLoginCheckbox, disableBasicAuthCheckbox, disableOIDCLoginCheckbox]
.filter(cb => cb.checked).length;
if (changedCheckbox.checked && totalDisabled === 3) { if (changedCheckbox.checked && totalDisabled === 3) {
showToast(t("at_least_one_login_method")); showToast(t("at_least_one_login_method"));
changedCheckbox.checked = false; changedCheckbox.checked = false;
@@ -793,13 +829,17 @@ export function openAdminPanel() {
disableBasicAuthCheckbox.addEventListener("change", function () { enforceLoginOptionConstraint(this); }); disableBasicAuthCheckbox.addEventListener("change", function () { enforceLoginOptionConstraint(this); });
disableOIDCLoginCheckbox.addEventListener("change", function () { enforceLoginOptionConstraint(this); }); disableOIDCLoginCheckbox.addEventListener("change", function () { enforceLoginOptionConstraint(this); });
// Initial checkbox and input states
document.getElementById("disableFormLogin").checked = config.loginOptions.disableFormLogin === true; document.getElementById("disableFormLogin").checked = config.loginOptions.disableFormLogin === true;
document.getElementById("disableBasicAuth").checked = config.loginOptions.disableBasicAuth === true; document.getElementById("disableBasicAuth").checked = config.loginOptions.disableBasicAuth === true;
document.getElementById("disableOIDCLogin").checked = config.loginOptions.disableOIDCLogin === true; document.getElementById("disableOIDCLogin").checked = config.loginOptions.disableOIDCLogin === true;
document.getElementById("enableWebDAV").checked = config.enableWebDAV === true;
document.getElementById("sharedMaxUploadSize").value = config.sharedMaxUploadSize || "";
// Capture initial state after the modal loads.
captureInitialAdminConfig(); captureInitialAdminConfig();
} else { } else {
// Update existing modal and show
adminModal.style.backgroundColor = overlayBackground; adminModal.style.backgroundColor = overlayBackground;
const modalContent = adminModal.querySelector(".modal-content"); const modalContent = adminModal.querySelector(".modal-content");
if (modalContent) { if (modalContent) {
@@ -815,6 +855,8 @@ export function openAdminPanel() {
document.getElementById("disableFormLogin").checked = config.loginOptions.disableFormLogin === true; document.getElementById("disableFormLogin").checked = config.loginOptions.disableFormLogin === true;
document.getElementById("disableBasicAuth").checked = config.loginOptions.disableBasicAuth === true; document.getElementById("disableBasicAuth").checked = config.loginOptions.disableBasicAuth === true;
document.getElementById("disableOIDCLogin").checked = config.loginOptions.disableOIDCLogin === true; document.getElementById("disableOIDCLogin").checked = config.loginOptions.disableOIDCLogin === true;
document.getElementById("enableWebDAV").checked = config.enableWebDAV === true;
document.getElementById("sharedMaxUploadSize").value = config.sharedMaxUploadSize || "";
adminModal.style.display = "flex"; adminModal.style.display = "flex";
captureInitialAdminConfig(); captureInitialAdminConfig();
} }
@@ -837,6 +879,8 @@ export function openAdminPanel() {
document.getElementById("disableFormLogin").checked = localStorage.getItem("disableFormLogin") === "true"; document.getElementById("disableFormLogin").checked = localStorage.getItem("disableFormLogin") === "true";
document.getElementById("disableBasicAuth").checked = localStorage.getItem("disableBasicAuth") === "true"; document.getElementById("disableBasicAuth").checked = localStorage.getItem("disableBasicAuth") === "true";
document.getElementById("disableOIDCLogin").checked = localStorage.getItem("disableOIDCLogin") === "true"; document.getElementById("disableOIDCLogin").checked = localStorage.getItem("disableOIDCLogin") === "true";
document.getElementById("enableWebDAV").checked = localStorage.getItem("enableWebDAV") === "true";
document.getElementById("sharedMaxUploadSize").value = localStorage.getItem("sharedMaxUploadSize") || "";
adminModal.style.display = "flex"; adminModal.style.display = "flex";
captureInitialAdminConfig(); captureInitialAdminConfig();
} else { } else {

View File

@@ -1,6 +1,7 @@
<?php <?php
// public/webdav.php // public/webdav.php
// ─── 0) Forward Basic auth into PHP_AUTH_* for every HTTP verb ─────────────
if ( if (
empty($_SERVER['PHP_AUTH_USER']) empty($_SERVER['PHP_AUTH_USER'])
&& !empty($_SERVER['HTTP_AUTHORIZATION']) && !empty($_SERVER['HTTP_AUTHORIZATION'])
@@ -11,46 +12,58 @@ if (
$_SERVER['PHP_AUTH_PW'] = $p; $_SERVER['PHP_AUTH_PW'] = $p;
} }
// ─── 1) Bootstrap & load models ─────────────────────────────────────────────
require_once __DIR__ . '/../config/config.php'; // UPLOAD_DIR, META_DIR, DATE_TIME_FORMAT require_once __DIR__ . '/../config/config.php'; // UPLOAD_DIR, META_DIR, DATE_TIME_FORMAT
require_once __DIR__ . '/../vendor/autoload.php'; // Composer & SabreDAV require_once __DIR__ . '/../vendor/autoload.php'; // Composer & SabreDAV
require_once __DIR__ . '/../src/models/AuthModel.php'; // AuthModel::authenticate(), getUserRole(), loadFolderPermission() require_once __DIR__ . '/../src/models/AuthModel.php'; // AuthModel::authenticate(), getUserRole(), loadFolderPermission()
require_once __DIR__ . '/../src/models/AdminModel.php'; // AdminModel::getConfig()
// ─── 3) Load your WebDAV directory implementation ────────────────────────── // ─── 1.1) Global WebDAV feature toggle ──────────────────────────────────────
$adminConfig = AdminModel::getConfig();
$enableWebDAV = isset($adminConfig['enableWebDAV']) && $adminConfig['enableWebDAV'];
if (!$enableWebDAV) {
header('HTTP/1.1 403 Forbidden');
echo 'WebDAV access is currently disabled by administrator.';
exit;
}
// ─── 2) Load WebDAV directory implementation ──────────────────────────
require_once __DIR__ . '/../src/webdav/FileRiseDirectory.php'; require_once __DIR__ . '/../src/webdav/FileRiseDirectory.php';
use Sabre\DAV\Server; use Sabre\DAV\Server;
use Sabre\DAV\Auth\Backend\BasicCallBack; use Sabre\DAV\Auth\Backend\BasicCallBack;
use Sabre\DAV\Auth\Plugin as AuthPlugin; use Sabre\DAV\Auth\Plugin as AuthPlugin;
use Sabre\DAV\Browser\Plugin as BrowserPlugin;
use Sabre\DAV\Locks\Plugin as LocksPlugin; use Sabre\DAV\Locks\Plugin as LocksPlugin;
use Sabre\DAV\Locks\Backend\File as LocksFileBackend; use Sabre\DAV\Locks\Backend\File as LocksFileBackend;
use FileRise\WebDAV\FileRiseDirectory; use FileRise\WebDAV\FileRiseDirectory;
// ─── 3) HTTPBasic backend ─────────────────────────────────────────────────
$authBackend = new BasicCallBack(function(string $user, string $pass) { $authBackend = new BasicCallBack(function(string $user, string $pass) {
return \AuthModel::authenticate($user, $pass) !== false; return \AuthModel::authenticate($user, $pass) !== false;
}); });
$authPlugin = new AuthPlugin($authBackend, 'FileRise'); $authPlugin = new AuthPlugin($authBackend, 'FileRise');
// ─── 4) Determine user scope ────────────────────────────────────────────────
$user = $_SERVER['PHP_AUTH_USER'] ?? ''; $user = $_SERVER['PHP_AUTH_USER'] ?? '';
$isAdmin = (\AuthModel::getUserRole($user) === '1'); $isAdmin = (\AuthModel::getUserRole($user) === '1');
$folderOnly = (bool)\AuthModel::loadFolderPermission($user); $folderOnly = (bool)\AuthModel::loadFolderPermission($user);
if ($isAdmin || !$folderOnly) { if ($isAdmin || !$folderOnly) {
// admins or unrestricted users see the full /uploads // Admins (or users without folder-only restriction) see the full /uploads
$rootPath = rtrim(UPLOAD_DIR, '/\\'); $rootPath = rtrim(UPLOAD_DIR, '/\\');
} else { } else {
// folderonly users see only /uploads/{username} // Folderonly users see only /uploads/{username}
$rootPath = rtrim(UPLOAD_DIR, '/\\') . DIRECTORY_SEPARATOR . $user; $rootPath = rtrim(UPLOAD_DIR, '/\\') . DIRECTORY_SEPARATOR . $user;
if (!is_dir($rootPath)) { if (!is_dir($rootPath)) {
mkdir($rootPath, 0755, true); mkdir($rootPath, 0755, true);
} }
} }
// ─── 5) Spin up SabreDAV ────────────────────────────────────────────────────
$server = new Server([ $server = new Server([
new FileRiseDirectory($rootPath, $user, $folderOnly), new FileRiseDirectory($rootPath, $user, $folderOnly),
]); ]);
$server->addPlugin($authPlugin); $server->addPlugin($authPlugin);
//$server->addPlugin(new BrowserPlugin()); // optional HTML browser UI
$server->addPlugin( $server->addPlugin(
new LocksPlugin( new LocksPlugin(
new LocksFileBackend(sys_get_temp_dir() . '/sabre-locksdb') new LocksFileBackend(sys_get_temp_dir() . '/sabre-locksdb')

View File

@@ -35,7 +35,9 @@ class AdminController
* @OA\Property(property="disableBasicAuth", type="boolean", example=false), * @OA\Property(property="disableBasicAuth", type="boolean", example=false),
* @OA\Property(property="disableOIDCLogin", type="boolean", example=false) * @OA\Property(property="disableOIDCLogin", type="boolean", example=false)
* ), * ),
* @OA\Property(property="globalOtpauthUrl", type="string", example="") * @OA\Property(property="globalOtpauthUrl", type="string", example=""),
* @OA\Property(property="enableWebDAV", type="boolean", example=false),
* @OA\Property(property="sharedMaxUploadSize", type="integer", example=52428800)
* ) * )
* ), * ),
* @OA\Response( * @OA\Response(
@@ -88,7 +90,9 @@ class AdminController
* @OA\Property(property="disableBasicAuth", type="boolean", example=false), * @OA\Property(property="disableBasicAuth", type="boolean", example=false),
* @OA\Property(property="disableOIDCLogin", type="boolean", example=false) * @OA\Property(property="disableOIDCLogin", type="boolean", example=false)
* ), * ),
* @OA\Property(property="globalOtpauthUrl", type="string", example="") * @OA\Property(property="globalOtpauthUrl", type="string", example=""),
* @OA\Property(property="enableWebDAV", type="boolean", example=false),
* @OA\Property(property="sharedMaxUploadSize", type="integer", example=52428800)
* ) * )
* ), * ),
* @OA\Response( * @OA\Response(
@@ -149,7 +153,7 @@ class AdminController
exit; exit;
} }
// Prepare configuration array. // Prepare existing settings
$headerTitle = isset($data['header_title']) ? trim($data['header_title']) : ""; $headerTitle = isset($data['header_title']) ? trim($data['header_title']) : "";
$oidc = isset($data['oidc']) ? $data['oidc'] : []; $oidc = isset($data['oidc']) ? $data['oidc'] : [];
$oidcProviderUrl = isset($oidc['providerUrl']) ? filter_var($oidc['providerUrl'], FILTER_SANITIZE_URL) : ''; $oidcProviderUrl = isset($oidc['providerUrl']) ? filter_var($oidc['providerUrl'], FILTER_SANITIZE_URL) : '';
@@ -183,20 +187,38 @@ class AdminController
} }
$globalOtpauthUrl = isset($data['globalOtpauthUrl']) ? trim($data['globalOtpauthUrl']) : ""; $globalOtpauthUrl = isset($data['globalOtpauthUrl']) ? trim($data['globalOtpauthUrl']) : "";
// ── NEW: enableWebDAV flag ──────────────────────────────────────
$enableWebDAV = false;
if (array_key_exists('enableWebDAV', $data)) {
$enableWebDAV = filter_var($data['enableWebDAV'], FILTER_VALIDATE_BOOLEAN);
} elseif (isset($data['features']['enableWebDAV'])) {
$enableWebDAV = filter_var($data['features']['enableWebDAV'], FILTER_VALIDATE_BOOLEAN);
}
// ── NEW: sharedMaxUploadSize ──────────────────────────────────────
$sharedMaxUploadSize = null;
if (array_key_exists('sharedMaxUploadSize', $data)) {
$sharedMaxUploadSize = filter_var($data['sharedMaxUploadSize'], FILTER_VALIDATE_INT);
} elseif (isset($data['features']['sharedMaxUploadSize'])) {
$sharedMaxUploadSize = filter_var($data['features']['sharedMaxUploadSize'], FILTER_VALIDATE_INT);
}
$configUpdate = [ $configUpdate = [
'header_title' => $headerTitle, 'header_title' => $headerTitle,
'oidc' => [ 'oidc' => [
'providerUrl' => $oidcProviderUrl, 'providerUrl' => $oidcProviderUrl,
'clientId' => $oidcClientId, 'clientId' => $oidcClientId,
'clientSecret' => $oidcClientSecret, 'clientSecret' => $oidcClientSecret,
'redirectUri' => $oidcRedirectUri, 'redirectUri' => $oidcRedirectUri,
], ],
'loginOptions' => [ 'loginOptions' => [
'disableFormLogin' => $disableFormLogin, 'disableFormLogin' => $disableFormLogin,
'disableBasicAuth' => $disableBasicAuth, 'disableBasicAuth' => $disableBasicAuth,
'disableOIDCLogin' => $disableOIDCLogin, 'disableOIDCLogin' => $disableOIDCLogin,
], ],
'globalOtpauthUrl' => $globalOtpauthUrl 'globalOtpauthUrl' => $globalOtpauthUrl,
'enableWebDAV' => $enableWebDAV,
'sharedMaxUploadSize' => $sharedMaxUploadSize // ← NEW
]; ];
// Delegate to the model. // Delegate to the model.
@@ -207,4 +229,4 @@ class AdminController
echo json_encode($result); echo json_encode($result);
exit; exit;
} }
} }

View File

@@ -401,6 +401,20 @@ class FolderController
* *
* @return void Outputs HTML content. * @return void Outputs HTML content.
*/ */
function formatBytes($bytes)
{
if ($bytes < 1024) {
return $bytes . " B";
} elseif ($bytes < 1024 * 1024) {
return round($bytes / 1024, 2) . " KB";
} elseif ($bytes < 1024 * 1024 * 1024) {
return round($bytes / (1024 * 1024), 2) . " MB";
} else {
return round($bytes / (1024 * 1024 * 1024), 2) . " GB";
}
}
public function shareFolder(): void public function shareFolder(): void
{ {
// Retrieve GET parameters. // Retrieve GET parameters.
@@ -495,12 +509,14 @@ class FolderController
exit; exit;
} }
// Extract data for the HTML view. // Load admin config so we can pull the sharedMaxUploadSize
$folderName = $data['folder']; require_once PROJECT_ROOT . '/src/models/AdminModel.php';
$files = $data['files']; $adminConfig = AdminModel::getConfig();
$currentPage = $data['currentPage']; $sharedMaxUploadSize = isset($adminConfig['sharedMaxUploadSize']) && is_numeric($adminConfig['sharedMaxUploadSize'])
$totalPages = $data['totalPages']; ? (int)$adminConfig['sharedMaxUploadSize']
: null;
// For humanreadable formatting
function formatBytes($bytes) function formatBytes($bytes)
{ {
if ($bytes < 1024) { if ($bytes < 1024) {
@@ -514,6 +530,12 @@ class FolderController
} }
} }
// Extract data for the HTML view.
$folderName = $data['folder'];
$files = $data['files'];
$currentPage = $data['currentPage'];
$totalPages = $data['totalPages'];
// Build the HTML view. // Build the HTML view.
header("Content-Type: text/html; charset=utf-8"); header("Content-Type: text/html; charset=utf-8");
?> ?>
@@ -717,7 +739,11 @@ class FolderController
<!-- Upload Container (if uploads are allowed by the share record) --> <!-- Upload Container (if uploads are allowed by the share record) -->
<?php if (isset($data['record']['allowUpload']) && $data['record']['allowUpload'] == 1): ?> <?php if (isset($data['record']['allowUpload']) && $data['record']['allowUpload'] == 1): ?>
<div class="upload-container"> <div class="upload-container">
<h3>Upload File (50mb max size)</h3> <h3>Upload File
<?php if ($sharedMaxUploadSize !== null): ?>
(<?php echo formatBytes($sharedMaxUploadSize); ?> max size)
<?php endif; ?>
</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 --> <!-- 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="hidden" name="token" value="<?php echo htmlspecialchars($token, ENT_QUOTES, 'UTF-8'); ?>">

View File

@@ -5,6 +5,23 @@ require_once PROJECT_ROOT . '/config/config.php';
class AdminModel class AdminModel
{ {
/**
* Parse a shorthand size value (e.g. "5G", "500M", "123K") into bytes.
*
* @param string $val
* @return int
*/
private static function parseSize(string $val): int
{
$unit = strtolower(substr($val, -1));
$num = (int) rtrim($val, 'bkmgtpezyBKMGTPESY');
switch ($unit) {
case 'g': return $num * 1024 ** 3;
case 'm': return $num * 1024 ** 2;
case 'k': return $num * 1024;
default: return $num;
}
}
/** /**
* Updates the admin configuration file. * Updates the admin configuration file.
@@ -24,6 +41,28 @@ class AdminModel
return ["error" => "Incomplete OIDC configuration."]; return ["error" => "Incomplete OIDC configuration."];
} }
// Ensure enableWebDAV flag is boolean (default to false if missing)
$configUpdate['enableWebDAV'] = isset($configUpdate['enableWebDAV'])
? (bool)$configUpdate['enableWebDAV']
: false;
// Validate sharedMaxUploadSize if provided
if (isset($configUpdate['sharedMaxUploadSize'])) {
$sms = filter_var(
$configUpdate['sharedMaxUploadSize'],
FILTER_VALIDATE_INT,
["options" => ["min_range" => 1]]
);
if ($sms === false) {
return ["error" => "Invalid sharedMaxUploadSize."];
}
$totalBytes = self::parseSize(TOTAL_UPLOAD_SIZE);
if ($sms > $totalBytes) {
return ["error" => "sharedMaxUploadSize must be ≤ TOTAL_UPLOAD_SIZE."];
}
$configUpdate['sharedMaxUploadSize'] = $sms;
}
// Convert configuration to JSON. // Convert configuration to JSON.
$plainTextConfig = json_encode($configUpdate, JSON_PRETTY_PRINT); $plainTextConfig = json_encode($configUpdate, JSON_PRETTY_PRINT);
if ($plainTextConfig === false) { if ($plainTextConfig === false) {
@@ -59,7 +98,8 @@ class AdminModel
* *
* @return array The configuration array, or defaults if not found. * @return array The configuration array, or defaults if not found.
*/ */
public static function getConfig(): array { public static function getConfig(): array
{
$configFile = USERS_DIR . 'adminConfig.json'; $configFile = USERS_DIR . 'adminConfig.json';
if (file_exists($configFile)) { if (file_exists($configFile)) {
$encryptedContent = file_get_contents($configFile); $encryptedContent = file_get_contents($configFile);
@@ -72,10 +112,9 @@ class AdminModel
if (!is_array($config)) { if (!is_array($config)) {
$config = []; $config = [];
} }
// Normalize login options. // Normalize login options if missing
if (!isset($config['loginOptions'])) { if (!isset($config['loginOptions'])) {
// Create loginOptions array from top-level keys if missing.
$config['loginOptions'] = [ $config['loginOptions'] = [
'disableFormLogin' => isset($config['disableFormLogin']) ? (bool)$config['disableFormLogin'] : false, 'disableFormLogin' => isset($config['disableFormLogin']) ? (bool)$config['disableFormLogin'] : false,
'disableBasicAuth' => isset($config['disableBasicAuth']) ? (bool)$config['disableBasicAuth'] : false, 'disableBasicAuth' => isset($config['disableBasicAuth']) ? (bool)$config['disableBasicAuth'] : false,
@@ -88,31 +127,43 @@ class AdminModel
$config['loginOptions']['disableBasicAuth'] = (bool)$config['loginOptions']['disableBasicAuth']; $config['loginOptions']['disableBasicAuth'] = (bool)$config['loginOptions']['disableBasicAuth'];
$config['loginOptions']['disableOIDCLogin'] = (bool)$config['loginOptions']['disableOIDCLogin']; $config['loginOptions']['disableOIDCLogin'] = (bool)$config['loginOptions']['disableOIDCLogin'];
} }
// Default values for other keys
if (!isset($config['globalOtpauthUrl'])) { if (!isset($config['globalOtpauthUrl'])) {
$config['globalOtpauthUrl'] = ""; $config['globalOtpauthUrl'] = "";
} }
if (!isset($config['header_title']) || empty($config['header_title'])) { if (!isset($config['header_title']) || empty($config['header_title'])) {
$config['header_title'] = "FileRise"; $config['header_title'] = "FileRise";
} }
if (!isset($config['enableWebDAV'])) {
$config['enableWebDAV'] = false;
}
// Default sharedMaxUploadSize to 50MB or TOTAL_UPLOAD_SIZE if smaller
if (!isset($config['sharedMaxUploadSize'])) {
$defaultSms = min(50 * 1024 * 1024, self::parseSize(TOTAL_UPLOAD_SIZE));
$config['sharedMaxUploadSize'] = $defaultSms;
}
return $config; return $config;
} else { } else {
// Return defaults. // Return defaults.
return [ return [
'header_title' => "FileRise", 'header_title' => "FileRise",
'oidc' => [ 'oidc' => [
'providerUrl' => 'https://your-oidc-provider.com', 'providerUrl' => 'https://your-oidc-provider.com',
'clientId' => 'YOUR_CLIENT_ID', 'clientId' => 'YOUR_CLIENT_ID',
'clientSecret' => 'YOUR_CLIENT_SECRET', 'clientSecret' => 'YOUR_CLIENT_SECRET',
'redirectUri' => 'https://yourdomain.com/api/auth/auth.php?oidc=callback' 'redirectUri' => 'https://yourdomain.com/api/auth/auth.php?oidc=callback'
], ],
'loginOptions' => [ 'loginOptions' => [
'disableFormLogin' => false, 'disableFormLogin' => false,
'disableBasicAuth' => false, 'disableBasicAuth' => false,
'disableOIDCLogin' => false 'disableOIDCLogin' => false
], ],
'globalOtpauthUrl' => "" 'globalOtpauthUrl' => "",
'enableWebDAV' => false,
'sharedMaxUploadSize' => min(50 * 1024 * 1024, self::parseSize(TOTAL_UPLOAD_SIZE))
]; ];
} }
} }
} }

View File

@@ -152,8 +152,8 @@ find /var/www -type f -exec chmod 664 {} \;
find /var/www -type d -exec chmod 775 {} \; find /var/www -type d -exec chmod 775 {} \;
chown -R ${PUID:-99}:${PGID:-100} /var/www chown -R ${PUID:-99}:${PGID:-100} /var/www
echo "🔥 Final PHP configuration (90-custom.ini):" echo "🔥 Final PHP configuration (99-custom.ini):"
cat /etc/php/8.3/apache2/conf.d/90-custom.ini cat /etc/php/8.3/apache2/conf.d/99-custom.ini
echo "🔥 Final Apache configuration (limit_request_body.conf):" echo "🔥 Final Apache configuration (limit_request_body.conf):"
cat /etc/apache2/conf-enabled/limit_request_body.conf cat /etc/apache2/conf-enabled/limit_request_body.conf