diff --git a/emhttp/auth-request.php b/emhttp/auth-request.php index 59f663118..4a82843a1 100644 --- a/emhttp/auth-request.php +++ b/emhttp/auth-request.php @@ -14,6 +14,10 @@ if (isset($_COOKIE[session_name()])) { session_write_close(); } +// Include JS caching functions +require_once '/usr/local/emhttp/webGui/include/JSCache.php'; + +// Base whitelist of files $arrWhitelist = [ '/webGui/styles/clear-sans-bold-italic.eot', '/webGui/styles/clear-sans-bold-italic.woff', @@ -39,8 +43,15 @@ $arrWhitelist = [ '/webGui/images/case-model.png', '/webGui/images/green-on.png', '/webGui/images/red-on.png', - '/webGui/images/yellow-on.png' + '/webGui/images/yellow-on.png', + '/webGui/images/UN-logotype-gradient.svg' ]; + +// Add JS files from the unraid-components directory using cache +$webComponentsDirectory = '/usr/local/emhttp/plugins/dynamix.my.servers/unraid-components/'; +$jsFiles = getCachedJSFiles($webComponentsDirectory); +$arrWhitelist = array_merge($arrWhitelist, $jsFiles); + if (in_array(preg_replace(['/\?v=\d+$/','/\?\d+$/'],'',$_SERVER['REQUEST_URI']),$arrWhitelist)) { // authorized http_response_code(200); diff --git a/emhttp/plugins/dynamix.vm.manager/scripts/pcicheck.php b/emhttp/plugins/dynamix.vm.manager/scripts/pcicheck.php index b8e116e45..d0425b8b1 100755 --- a/emhttp/plugins/dynamix.vm.manager/scripts/pcicheck.php +++ b/emhttp/plugins/dynamix.vm.manager/scripts/pcicheck.php @@ -16,7 +16,7 @@ $docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp'); require_once "$docroot/webGui/include/Helpers.php"; -require_once "$docroot/plugins/dynamix.vm.manager/include/libvirt_helpers.php"; + $pci_device_changes = comparePCIData(); $pcierror = false; diff --git a/emhttp/plugins/dynamix/Eth0.page b/emhttp/plugins/dynamix/Eth0.page index 3e89b0740..b85e740ed 100644 --- a/emhttp/plugins/dynamix/Eth0.page +++ b/emhttp/plugins/dynamix/Eth0.page @@ -64,18 +64,13 @@ function index($key) return filter_var($key, FILTER_SANITIZE_NUMBER_INT); } -function metric($eth, $prot, $index) -{ - $system = '/sys/class/net'; - $bridge = str_replace('eth', 'br', $eth); - $bond = str_replace('eth', 'bond', $eth); - $port = file_exists("$system/$bridge") ? $bridge : (file_exists("$system/$bond") ? $bond : $eth); - $metric = exec("ip -$prot route show default dev $port 2>/dev/null | grep -Pom1 ' metric \K\d+'"); - if ($metric) { - return $metric + $index; - } - exec("ip -$prot route show default 2>/dev/null | grep -Po ' metric \K\d+'", $metrics); - return (count($metrics) ? max($metrics) : 0) + $index + 1; +function metric($eth) { + $system = '/sys/class/net'; + $bridge = str_replace('eth', 'br', $eth); + $bond = str_replace('eth', 'bond', $eth); + $port = file_exists("$system/$bridge") ? $bridge : (file_exists("$system/$bond") ? $bond : $eth); + $index = exec("cat $system/$port/ifindex 2>/dev/null"); + return 1000 + ($index ?: exec("cat $system/*/ifindex | sort -n | tail -1") + 1); } // remove non-existing ethernet ports @@ -493,9 +488,9 @@ function addVLAN(port) { } function removeVLAN(element) { - var id = $(element).prop('id').split('-'); + var form = $(element).closest('form'); $(element).remove(); - $('#view-'+id[1]).find('select').first().trigger('change'); + form.find('select').first().trigger('change'); } function showVLAN(port) { @@ -748,8 +743,8 @@ _(IPv4 address)_: :eth_ipv4_address_help: _(IPv4 default gateway)_: -: " class="narrow" pattern="" title="_(IPv4 address A.B.C.D)_"> - " class="slim"> ** +: " class="narrow" pattern="" title="_(IPv4 address A.B.C.D)_"> + " class="slim"> ** :eth_ipv4_default_gateway_help: @@ -771,8 +766,8 @@ _(IPv6 address)_: :eth_ipv6_address_help: _(IPv6 default gateway)_: -: " pattern="" title="_(IPv6 address nnnn:xxxx::yyyy)_"> - " class="slim"> ** +: " pattern="" title="_(IPv6 address nnnn:xxxx::yyyy)_"> + " class="slim"> ** :eth_ipv6_default_gateway_help: @@ -855,8 +850,8 @@ _(IPv4 address)_:
_(IPv4 default gateway)_: -: " class="narrow" pattern="" title="_(IPv4 address A.B.C.D)_"> - ', 4, $i)?>" class="slim"> ** +: " class="narrow" pattern="" title="_(IPv4 address A.B.C.D)_"> + " class="slim"> ** :eth_ipv4_default_gateway_help: @@ -884,8 +879,8 @@ _(IPv6 address)_:
_(IPv6 default gateway)_: -: " pattern="" title="_(IPv6 address nnnn:xxxx::yyyy)_"> - ', 6, $i)?>" class="slim"> ** +: " pattern="" title="_(IPv6 address nnnn:xxxx::yyyy)_"> + " class="slim"> ** :eth_ipv6_default_gateway_help: @@ -954,7 +949,7 @@ _(IPv4 address)_:
_(IPv4 default gateway)_: : - ** + **
@@ -975,7 +970,7 @@ _(IPv6 address)_:
_(IPv6 default gateway)_: : - ** + **
diff --git a/emhttp/plugins/dynamix/EthX.page b/emhttp/plugins/dynamix/EthX.page index aa7ac8408..0c1b33d06 100644 --- a/emhttp/plugins/dynamix/EthX.page +++ b/emhttp/plugins/dynamix/EthX.page @@ -186,7 +186,7 @@ _(IPv4 address)_:
_(IPv4 default gateway)_: : " class="narrow" pattern="" title="_(IPv4 address A.B.C.D)_"> - " class="slim"> ** + " class="slim"> ** :eth_ipv4_default_gateway_help: @@ -213,7 +213,7 @@ _(IPv6 address)_:
_(IPv6 default gateway)_: : " pattern="" title="_(IPv6 address nnnn:xxxx::yyyy)_"> - " class="slim"> ** + " class="slim"> ** :eth_ipv6_default_gateway_help: @@ -293,7 +293,7 @@ _(IPv4 address)_:
_(IPv4 default gateway)_: : " class="narrow" pattern="" title="_(IPv4 address A.B.C.D)_"> - ',4,$i)?>" class="slim"> ** + " class="slim"> ** :eth_ipv4_default_gateway_help: @@ -320,7 +320,7 @@ _(IPv6 address)_:
_(IPv6 default gateway)_: : " pattern="" title="_(IPv6 address nnnn:xxxx::yyyy)_"> - ',6,$i)?>" class="slim"> ** + " class="slim"> ** :eth_ipv6_default_gateway_help: @@ -386,7 +386,7 @@ _(IPv4 address)_:
_(IPv4 default gateway)_: : - ** + **
@@ -407,7 +407,7 @@ _(IPv6 address)_:
_(IPv6 default gateway)_: : - ** + **
diff --git a/emhttp/plugins/dynamix/LogViewer.page b/emhttp/plugins/dynamix/LogViewer.page new file mode 100644 index 000000000..fb9eb150c --- /dev/null +++ b/emhttp/plugins/dynamix/LogViewer.page @@ -0,0 +1,8 @@ +Menu="UNRAID-OS" +Title="Log Viewer (new)" +Icon="icon-log" +Tag="list" +--- + + + \ No newline at end of file diff --git a/emhttp/plugins/dynamix/include/.login.php b/emhttp/plugins/dynamix/include/.login.php index 5f003e7a9..ed0c465d9 100644 --- a/emhttp/plugins/dynamix/include/.login.php +++ b/emhttp/plugins/dynamix/include/.login.php @@ -3,7 +3,7 @@ // Only start a session to check if they have a cookie that looks like our session $server_name = strtok($_SERVER['HTTP_HOST'], ":"); -if (!empty($_COOKIE['unraid_'.md5($server_name)])) { +if (!empty($_COOKIE['unraid_' . md5($server_name)])) { // Start the session so we can check if $_SESSION has data if (session_status() == PHP_SESSION_NONE) { session_start(); @@ -12,7 +12,7 @@ if (!empty($_COOKIE['unraid_'.md5($server_name)])) { // Check if the user is already logged in if ($_SESSION && !empty($_SESSION['unraid_user'])) { // Redirect the user to the start page - header("Location: /".$start_page); + header("Location: /" . $start_page); exit; } } @@ -81,24 +81,64 @@ function cleanupFails(string $failFile, int $time): int // Save fails to disk if ($updateFails) { - $failText = implode("\n", $fails)."\n"; + $failText = implode("\n", $fails) . "\n"; writeToFile($failFile, $failText); } return count($fails); } + function verifyUsernamePassword(string $username, string $password): bool { - if ($username != "root") { - return false; - } + if ($username != "root") return false; $output = exec("/usr/bin/getent shadow $username"); - if ($output === false) { - return false; - } + if ($output === false) return false; $credentials = explode(":", $output); - return password_verify($password, $credentials[1]); + $valid = password_verify($password, $credentials[1]); + if ($valid) { + return true; + } + + $serverState = new ServerState(); + // We may have an SSO token - check if SSO is enabled and then validate the token + if ($serverState->ssoEnabled && strlen($password) > 500) { + if (!preg_match('/^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+$/', $password)) { + my_logger("SSO Login Attempt Failed: Invalid token format"); + return false; + } + $safePassword = escapeshellarg($password); + + $output = array(); + exec("/etc/rc.d/rc.unraid-api sso validate-token $safePassword 2>&1", $output, $code); + my_logger("SSO Login Attempt Code: $code"); + my_logger("SSO Login Attempt Response: " . print_r($output, true)); + + if ($code !== 0) { + return false; + } + + if (empty($output)) { + return false; + } + + try { + // Split on first { and take everything after it + $jsonParts = explode('{', $output[0], 2); + if (count($jsonParts) < 2) { + my_logger("SSO Login Attempt Failed: No JSON found in response"); + return false; + } + $response = json_decode('{' . $jsonParts[1], true); + if (isset($response['valid']) && $response['valid'] === true) { + return true; + } + } catch (Exception $e) { + my_logger("SSO Login Attempt Exception: " . $e->getMessage()); + return false; + } + } + return false; } // Load configs into memory $my_servers = @parse_ini_file('/boot/config/plugins/dynamix.my.servers/myservers.cfg', true); @@ -146,7 +186,7 @@ if (!empty($username) && !empty($password)) { my_logger("Successful login user {$username} from {$remote_addr}"); // Redirect the user to the start page - header("Location: /".$start_page); + header("Location: /" . $start_page); exit; } catch (Exception $exception) { // Set error message @@ -154,7 +194,7 @@ if (!empty($username) && !empty($password)) { // Log error to syslog my_logger("Unsuccessful login user {$username} from {$remote_addr}"); - appendToFile($failFile, $time."\n"); + appendToFile($failFile, $time . "\n"); } } @@ -171,6 +211,7 @@ $isDarkTheme = $themeHelper->isDarkTheme(); + @@ -182,258 +223,366 @@ $isDarkTheme = $themeHelper->isDarkTheme(); - <?=$var['NAME']?>/Login + <?= $var['NAME'] ?>/Login - "> - + "> +

- +

- +

- - - - - - - + + + + + + +

- - + +

- +

@@ -441,24 +590,25 @@ $isDarkTheme = $themeHelper->isDarkTheme();
- +
+ diff --git a/emhttp/plugins/dynamix/include/DefaultPageLayout.php b/emhttp/plugins/dynamix/include/DefaultPageLayout.php index bb3df8a7e..44d422f4b 100644 --- a/emhttp/plugins/dynamix/include/DefaultPageLayout.php +++ b/emhttp/plugins/dynamix/include/DefaultPageLayout.php @@ -28,22 +28,22 @@ $entity = $notify['entity'] & 1 == 1; $alerts = '/tmp/plugins/my_alerts.txt'; $wlan0 = file_exists('/sys/class/net/wlan0'); -$safemode = _var($var,'safeMode')=='yes'; +$safemode = _var($var, 'safeMode') == 'yes'; $banner = "$config/webGui/banner.png"; $notes = '/var/tmp/unRAIDServer.txt'; if (!file_exists($notes)) { - file_put_contents($notes, shell_exec("$docroot/plugins/dynamix.plugin.manager/scripts/plugin changes $docroot/plugins/unRAIDServer/unRAIDServer.plg")); + file_put_contents($notes, shell_exec("$docroot/plugins/dynamix.plugin.manager/scripts/plugin changes $docroot/plugins/unRAIDServer/unRAIDServer.plg")); } $taskPages = find_pages('Tasks'); $buttonPages = find_pages('Buttons'); $pages = []; // finds subpages if (!empty($myPage['text'])) $pages[$myPage['name']] = $myPage; -if (_var($myPage,'Type')=='xmenu') $pages = array_merge($pages, find_pages($myPage['name'])); +if (_var($myPage, 'Type') == 'xmenu') $pages = array_merge($pages, find_pages($myPage['name'])); // nchan related actions -$nchan = ['webGui/nchan/notify_poller','webGui/nchan/session_check']; +$nchan = ['webGui/nchan/notify_poller', 'webGui/nchan/session_check']; if ($wlan0) $nchan[] = 'webGui/nchan/wlan0'; // build nchan scripts from found pages $allPages = array_merge($taskPages, $buttonPages, $pages); @@ -52,96 +52,94 @@ foreach ($allPages as $page) { } // act on nchan scripts if (count($pages)) { - $running = file_exists($nchan_pid) ? file($nchan_pid,FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES) : []; + $running = file_exists($nchan_pid) ? file($nchan_pid, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) : []; $start = array_diff($nchan, $running); // returns any new scripts to be started $stop = array_diff($running, $nchan); // returns any old scripts to be stopped $running = array_merge($start, $running); // update list of current running nchan scripts // start nchan scripts which are new foreach ($start as $row) { - $script = explode(':',$row)[0]; + $script = explode(':', $row)[0]; exec("$docroot/$script &>/dev/null &"); } // stop nchan scripts with the :stop option foreach ($stop as $row) { - [$script,$opt] = my_explode(':',$row); + [$script, $opt] = my_explode(':', $row); if ($opt == 'stop') { exec("pkill -f $docroot/$script &>/dev/null &"); - array_splice($running,array_search($row,$running),1); + array_splice($running, array_search($row, $running), 1); } } - if (count($running)) file_put_contents($nchan_pid,implode("\n",$running)."\n"); else @unlink($nchan_pid); + if (count($running)) file_put_contents($nchan_pid, implode("\n", $running) . "\n"); + else @unlink($nchan_pid); } ?> -lang="" class="getThemeHtmlClass() ?>"> +lang="" class="getThemeHtmlClass() ?>"> + -<?=_var($var, 'NAME')?>/<?=_var($myPage, 'name')?> - - - - - - - - -"> -"> -"> -"> -"> -"> + <?= _var($var, 'NAME') ?>/<?= _var($myPage, 'name') ?> + + + + + + + + + "> + "> + "> + "> + "> + "> -"> -"> -"> -"> + "> + "> + "> + "> - + isSidebarTheme()) { + echo generate_sidebar_icon_css($taskPages, $buttonPages); + } + ?> + - + - - + + - + + ' . parse_text($button['text'])); + } -'.parse_text($button['text'])); -} + foreach ($pages as $page) { + annotate($page['file']); + includePageStylesheets($page); + } + ?> -foreach ($pages as $page) { - annotate($page['file']); - includePageStylesheets($page); -} -?> - - + + + @@ -149,5 +147,7 @@ foreach ($pages as $page) { + - + + \ No newline at end of file diff --git a/emhttp/plugins/dynamix/include/DefaultPageLayout/BodyInlineJS.php b/emhttp/plugins/dynamix/include/DefaultPageLayout/BodyInlineJS.php index ae5f5eb67..be71c6f72 100644 --- a/emhttp/plugins/dynamix/include/DefaultPageLayout/BodyInlineJS.php +++ b/emhttp/plugins/dynamix/include/DefaultPageLayout/BodyInlineJS.php @@ -71,32 +71,7 @@ defaultPage.on('message', function(msg,meta) { $('#statusbar').html(status); break; case 2: - // notifications - var bell1 = 0, bell2 = 0, bell3 = 0; - $.each($.parseJSON(msg), function(i, notify){ - switch (notify.importance) { - case 'alert' : bell1++; break; - case 'warning': bell2++; break; - case 'normal' : bell3++; break; - } - - if (notify.show) { - $.jGrowl(notify.subject+'
'+notify.description,{ - group: notify.importance, - header: notify.event+': '+notify.timestamp, - theme: notify.file, - beforeOpen: function(e,m,o){if ($('div.jGrowl-notification').hasClass(notify.file)) return(false);}, - afterOpen: function(e,m,o){if (notify.link) $(e).css('cursor','pointer');}, - click: function(e,m,o){if (notify.link) location.replace(notify.link);}, - close: function(e,m,o){$.post('/webGui/include/Notify.php',{cmd:'hide',file:""+notify.file,csrf_token:csrf_token},function(){$.post('/webGui/include/Notify.php',{cmd:'archive',file:notify.file,csrf_token:csrf_token});});} - }); - } - - }); - $('#bell').removeClass('red-orb yellow-orb green-orb').prop('title'," ["+bell1+']\n'+" ["+bell2+']\n'+" ["+bell3+']'); - if (bell1) $('#bell').addClass('red-orb'); else - if (bell2) $('#bell').addClass('yellow-orb'); else - if (bell3) $('#bell').addClass('green-orb'); + // notifications - moved to the Unraid API break; } }); diff --git a/emhttp/plugins/dynamix/include/DefaultPageLayout/GUIModeSessionFix.php b/emhttp/plugins/dynamix/include/DefaultPageLayout/GUIModeSessionFix.php new file mode 100644 index 000000000..516284e1e --- /dev/null +++ b/emhttp/plugins/dynamix/include/DefaultPageLayout/GUIModeSessionFix.php @@ -0,0 +1,26 @@ + \ No newline at end of file diff --git a/emhttp/plugins/dynamix/include/DefaultPageLayout/HeadInlineJS.php b/emhttp/plugins/dynamix/include/DefaultPageLayout/HeadInlineJS.php index 798d1fac5..f4f0b117c 100644 --- a/emhttp/plugins/dynamix/include/DefaultPageLayout/HeadInlineJS.php +++ b/emhttp/plugins/dynamix/include/DefaultPageLayout/HeadInlineJS.php @@ -477,32 +477,6 @@ function digits(number) { return 'three'; } -function openNotifier() { - $.post('/webGui/include/Notify.php',{cmd:'get',csrf_token:csrf_token},function(msg) { - $.each($.parseJSON(msg), function(i, notify){ - $.jGrowl(notify.subject+'
'+notify.description,{ - group: notify.importance, - header: notify.event+': '+notify.timestamp, - theme: notify.file, - sticky: true, - beforeOpen: function(e,m,o){if ($('div.jGrowl-notification').hasClass(notify.file)) return(false);}, - afterOpen: function(e,m,o){if (notify.link) $(e).css('cursor','pointer');}, - click: function(e,m,o){if (notify.link) location.replace(notify.link);}, - close: function(e,m,o){$.post('/webGui/include/Notify.php',{cmd:'archive',file:notify.file,csrf_token:csrf_token});} - }); - }); - }); -} - -function closeNotifier() { - $.post('/webGui/include/Notify.php',{cmd:'get',csrf_token:csrf_token},function(msg) { - $.each($.parseJSON(msg), function(i, notify){ - $.post('/webGui/include/Notify.php',{cmd:'archive',file:notify.file,csrf_token:csrf_token}); - }); - $('div.jGrowl').find('div.jGrowl-close').trigger('click'); - }); -} - function viewHistory() { location.replace('/Tools/NotificationsArchive'); } @@ -536,17 +510,7 @@ $(function() { } $('#'+tab).attr('checked', true); updateTime(); - $.jGrowl.defaults.closeTemplate = ''; - $.jGrowl.defaults.closerTemplate = '':'
'?>[ ]
'; - $.jGrowl.defaults.position = ''; - $.jGrowl.defaults.theme = ''; - $.jGrowl.defaults.themeState = ''; - $.jGrowl.defaults.pool = 10; - 0):?> - $.jGrowl.defaults.life = ; - - $.jGrowl.defaults.sticky = true; - + Shadowbox.setup('a.sb-enable', {modal:true}); // add any pre-existing reboot notices $.post('/webGui/include/Report.php',{cmd:'notice'},function(notices){ diff --git a/emhttp/plugins/dynamix/include/DefaultPageLayout/Header.php b/emhttp/plugins/dynamix/include/DefaultPageLayout/Header.php index 4a33aef78..2bd197073 100644 --- a/emhttp/plugins/dynamix/include/DefaultPageLayout/Header.php +++ b/emhttp/plugins/dynamix/include/DefaultPageLayout/Header.php @@ -1,9 +1,5 @@