From 12e129f1f637b553420d9bb4e5bf4bab0af4c2ae Mon Sep 17 00:00:00 2001 From: Zoey Date: Thu, 16 Apr 2026 17:20:50 +0200 Subject: [PATCH] aio-interface: improve headers (#7690) Signed-off-by: Zoey Signed-off-by: Simon L. Signed-off-by: Pablo Zmdl Co-authored-by: Simon L. Co-authored-by: Pablo Zmdl --- .github/workflows/playwright-on-push.yml | 4 ++ Containers/apache/Caddyfile | 7 ++- Containers/mastercontainer/acme.Caddyfile | 2 + Containers/mastercontainer/headers.Caddyfile | 27 ++++++++++ Containers/mastercontainer/internal.Caddyfile | 2 + php/public/click-handlers.js | 27 ++++++++++ php/public/forms.js | 9 ---- php/public/img/collabora.svg | 3 ++ php/public/img/office-none.svg | 3 ++ php/public/img/onlyoffice.svg | 3 ++ php/public/logs.css | 49 +++++++++++++++++ php/public/scroll-into-view.js | 9 ++++ php/public/toggle-dark-mode.js | 5 +- php/src/Controller/DockerController.php | 12 +---- php/templates/containers.twig | 22 ++++---- .../includes/community-containers.twig | 2 +- .../includes/optional-containers.twig | 16 ++---- php/templates/layout.twig | 6 ++- php/templates/log.twig | 52 +------------------ php/templates/login.twig | 2 +- 20 files changed, 162 insertions(+), 100 deletions(-) create mode 100644 Containers/mastercontainer/headers.Caddyfile create mode 100644 php/public/click-handlers.js create mode 100644 php/public/img/collabora.svg create mode 100644 php/public/img/office-none.svg create mode 100644 php/public/img/onlyoffice.svg create mode 100644 php/public/logs.css create mode 100644 php/public/scroll-into-view.js diff --git a/.github/workflows/playwright-on-push.yml b/.github/workflows/playwright-on-push.yml index 961c1603..15f0821d 100644 --- a/.github/workflows/playwright-on-push.yml +++ b/.github/workflows/playwright-on-push.yml @@ -68,6 +68,8 @@ jobs: --publish 8080:8080 \ --volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config \ --volume ./php:/var/www/docker-aio/php \ + --volume ./Containers/mastercontainer/internal.Caddyfile:/internal.Caddyfile \ + --volume ./Containers/mastercontainer/headers.Caddyfile:/headers.Caddyfile \ --volume /var/run/docker.sock:/var/run/docker.sock:ro \ --env SKIP_DOMAIN_VALIDATION=true \ --env APACHE_PORT=11000 \ @@ -97,6 +99,8 @@ jobs: --publish 8080:8080 \ --volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config \ --volume ./php:/var/www/docker-aio/php \ + --volume ./Containers/mastercontainer/internal.Caddyfile:/internal.Caddyfile \ + --volume ./Containers/mastercontainer/headers.Caddyfile:/headers.Caddyfile \ --volume /var/run/docker.sock:/var/run/docker.sock:ro \ --env SKIP_DOMAIN_VALIDATION=false \ --env APACHE_PORT=11000 \ diff --git a/Containers/apache/Caddyfile b/Containers/apache/Caddyfile index a15a9c19..9992c78c 100644 --- a/Containers/apache/Caddyfile +++ b/Containers/apache/Caddyfile @@ -17,8 +17,11 @@ https://{$ADDITIONAL_TRUSTED_DOMAIN}:443, http://{$APACHE_HOST}.nextcloud-aio:23973, # For Collabora callback and WOPI requests, see containers.json {$PROTOCOL}://{$NC_DOMAIN}:{$APACHE_PORT} { - header -Server - header -X-Powered-By + header { + -Server + -X-Powered-By + -Via + } # Collabora route /browser/* { diff --git a/Containers/mastercontainer/acme.Caddyfile b/Containers/mastercontainer/acme.Caddyfile index 0d5e84fe..3a6710c1 100644 --- a/Containers/mastercontainer/acme.Caddyfile +++ b/Containers/mastercontainer/acme.Caddyfile @@ -33,6 +33,8 @@ http://:80 { } https://:8443 { + import headers.Caddyfile + @denied { path /api/auth/login /api/auth/getlogin remote_host nextcloud-aio-nextcloud diff --git a/Containers/mastercontainer/headers.Caddyfile b/Containers/mastercontainer/headers.Caddyfile new file mode 100644 index 00000000..0f55bf7b --- /dev/null +++ b/Containers/mastercontainer/headers.Caddyfile @@ -0,0 +1,27 @@ +header { + # CSP limits which features can be used. By default we allow nothing and only allow required options. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy + # default-src 'none'; Allow nothing by default + # script-src-elem/style-src-elem 'self'; Only allow loading css/js files from same origin (AIO itself) while blocking all inline css/js + # img-src 'self'; Only allow loading images from same origin (from AIO itself) + # connect-src 'self'; Allow fetch to only connect same origin (to AIO itself) + # frame-src 'self'; Allow AIO to only embed itself "what can be embedded" + # base-uri 'none'; This does not fallback to default-src, AIO does not use the html base tag + # form-action 'self'; Html forms are only allowed to submit to AIO and not cross origin + # frame-ancestors 'self'; Only allow AIO itself to embed it self "who can embed" + # upgrade-insecure-requests; Upgrade all http embedings to https + # require-trusted-types-for 'script'; trusted-types 'none'; Blocks DOM changes via js + Content-Security-Policy "default-src 'none'; script-src-elem 'self'; style-src-elem 'self'; img-src 'self'; connect-src 'self'; frame-src 'self'; base-uri 'none'; form-action 'self'; frame-ancestors 'self'; upgrade-insecure-requests; require-trusted-types-for 'script'; trusted-types 'none';" + X-Content-Type-Options "nosniff" # This forces the browser to use the MIME type of the Content-Type header. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Content-Type-Options + X-Frame-Options "SAMEORIGIN" # Only allow AIO itself to embed itself, this is also enforced as part of the CSP frame-ancestors. See https://developer.mozilla.org/de/docs/Web/HTTP/Reference/Headers/X-Frame-Options + X-Permitted-Cross-Domain-Policies "none" # We block all cross origin request, including ones from Adobe Acrobat or Microsoft Silverlight and Adobe Flash Player. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Permitted-Cross-Domain-Policies + X-DNS-Prefetch-Control "off" # Tells the browser to not pre-fetch the DNS of linked pages. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-DNS-Prefetch-Control + Referrer-Policy "no-referrer" # Tells the browser to never sent a Referer header. See https://developer.mozilla.org/de/docs/Web/HTTP/Reference/Headers/Referrer-Policy + X-Robots-Tag "noindex, nofollow" # Tells web crawlers to not index this page. See https://developer.mozilla.org/de/docs/Web/HTTP/Reference/Headers/X-Robots-Tag + Cross-Origin-Opener-Policy "same-origin"; # AIO does not use any popup, still we can isolate its BCG if it is opened as a pop up by another page. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Opener-Policy + Cross-Origin-Embedder-Policy "require-corp"; # Harder rules for cross origin embeds. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Embedder-Policy + Cross-Origin-Resource-Policy "same-origin"; # Only allow the same origin to load resources. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Cross-Origin_Resource_Policy + + -Server + -X-Powered-By + -Via +} diff --git a/Containers/mastercontainer/internal.Caddyfile b/Containers/mastercontainer/internal.Caddyfile index e1809f55..9890acc0 100644 --- a/Containers/mastercontainer/internal.Caddyfile +++ b/Containers/mastercontainer/internal.Caddyfile @@ -24,6 +24,8 @@ } https://:8080 { + import headers.Caddyfile + @denied { path /api/auth/login /api/auth/getlogin remote_host nextcloud-aio-nextcloud diff --git a/php/public/click-handlers.js b/php/public/click-handlers.js new file mode 100644 index 00000000..88aa0d4a --- /dev/null +++ b/php/public/click-handlers.js @@ -0,0 +1,27 @@ +document.addEventListener("DOMContentLoaded", () => { + document.querySelectorAll('input[data-confirm]').forEach((element) => { + element.addEventListener('click', (event) => { + if (!confirm(element.dataset.confirm)) { + event.preventDefault(); + } + }); + }); + + + document.querySelectorAll('input[data-input-show-password]').forEach((element) => { + element.addEventListener('input', (element) => { + let passwordField = element + if (passwordField.type === "password" && passwordField.value !== "") { + passwordField.type = "text"; + } else if (passwordField.type === "text" && passwordField.value === "") { + passwordField.type = "password"; + } + }); + }); + + document.querySelectorAll('[data-stop-event-propagation="true"]').forEach((element) => { + element.addEventListener('click', (event) => { + event.stopPropagation(); + }); + }); +}); diff --git a/php/public/forms.js b/php/public/forms.js index 46cde081..b37fdcdb 100644 --- a/php/public/forms.js +++ b/php/public/forms.js @@ -1,14 +1,5 @@ "use strict"; -function showPassword(id) { - let passwordField = document.getElementById(id); - if (passwordField.type === "password" && passwordField.value !== "") { - passwordField.type = "text"; - } else if (passwordField.type === "text" && passwordField.value === "") { - passwordField.type = "password"; - } -} - (function (){ let lastError; diff --git a/php/public/img/collabora.svg b/php/public/img/collabora.svg new file mode 100644 index 00000000..eb032cdd --- /dev/null +++ b/php/public/img/collabora.svg @@ -0,0 +1,3 @@ + + + diff --git a/php/public/img/office-none.svg b/php/public/img/office-none.svg new file mode 100644 index 00000000..1ee12a79 --- /dev/null +++ b/php/public/img/office-none.svg @@ -0,0 +1,3 @@ + + + diff --git a/php/public/img/onlyoffice.svg b/php/public/img/onlyoffice.svg new file mode 100644 index 00000000..eb032cdd --- /dev/null +++ b/php/public/img/onlyoffice.svg @@ -0,0 +1,3 @@ + + + diff --git a/php/public/logs.css b/php/public/logs.css new file mode 100644 index 00000000..04f6be1a --- /dev/null +++ b/php/public/logs.css @@ -0,0 +1,49 @@ +html, body { + height: 100%; + overflow: hidden; + padding: 0; + margin: 0; +} +pre { + height: 100%; + overflow: auto; + margin: 0; + padding: 1rem; + box-sizing: border-box; +} +#floating-box { + position: fixed; + top: 1rem; + right: 1rem; + max-width: calc(100vw - 2rem); + z-index: 10; + display: flex; + justify-content: end; + align-items: center; +} +#autoloading-box { + display: grid; + gap: 0.5rem; + font-size: large; + border: solid thin gray; + background-color: #f9f9f9; + width: 10rem; + padding: 0.5rem 1rem; + margin: 0 0 0 1rem; +} +.loader { + opacity: 1; + width: 40px; + height: 40px; + align-self: inherit; +} +@starting-style { + .loader { + opacity: 0; + } +} +.loader.hidden { + display: none; + opacity: 0; + transition: opacity 1s, display 1s allow-discrete; +} \ No newline at end of file diff --git a/php/public/scroll-into-view.js b/php/public/scroll-into-view.js new file mode 100644 index 00000000..2c676911 --- /dev/null +++ b/php/public/scroll-into-view.js @@ -0,0 +1,9 @@ +const observer = new MutationObserver((records) => { + const node = records[0]?.addedNodes[0]; + // Text nodes also appear here but can't be scrolled to, so we have to check for the + // function being present. + if (node && typeof(node.scrollIntoView) === 'function') { + node.scrollIntoView(); + } +}); +observer.observe(document, {childList: true, subtree: true}); diff --git a/php/public/toggle-dark-mode.js b/php/public/toggle-dark-mode.js index 8eeba013..44eff9ff 100644 --- a/php/public/toggle-dark-mode.js +++ b/php/public/toggle-dark-mode.js @@ -32,4 +32,7 @@ function setThemeIcon(theme) { setThemeToDOM(getSavedTheme()); // Apply theme when the page loads -document.addEventListener('DOMContentLoaded', () => setThemeIcon(getSavedTheme())); +document.addEventListener('DOMContentLoaded', () => { + setThemeIcon(getSavedTheme()) + document.querySelector('button#theme-toggle')?.addEventListener('click', () => toggleTheme()); +}); diff --git a/php/src/Controller/DockerController.php b/php/src/Controller/DockerController.php index e7359c7b..5942e093 100644 --- a/php/src/Controller/DockerController.php +++ b/php/src/Controller/DockerController.php @@ -381,17 +381,7 @@ readonly class DockerController { - + diff --git a/php/templates/containers.twig b/php/templates/containers.twig index fcf87c1a..17405256 100644 --- a/php/templates/containers.twig +++ b/php/templates/containers.twig @@ -153,7 +153,7 @@
- +
{% endif %} @@ -178,7 +178,7 @@ {% endfor %}

- + {% endif %} {% elseif borg_backup_mode == 'restore' %} @@ -366,7 +366,7 @@ {% if bypass_container_update == true %} {% endif %} - + {% endif %} {% endif %} @@ -413,7 +413,7 @@
- +
{% endif %} @@ -478,7 +478,7 @@
- +
{% if has_backup_run_once == true %} @@ -490,7 +490,7 @@
- +

Backup restore

@@ -503,7 +503,7 @@ {% endfor %} - +

Update backup list

@@ -570,7 +570,7 @@ - + {% endif %} {% if has_backup_run_once == true %} @@ -587,8 +587,8 @@ Click here to change your AIO passphrase

You can change your AIO passphrase below:

- - + + @@ -616,7 +616,7 @@ - +

You need to make sure that the timezone that you enter is valid. An example is Europe/Berlin. You can get valid values by looking at the 'TZ identifier' column of this list: click here. The default is Etc/UTC if nothing is entered.

{% else %} diff --git a/php/templates/includes/community-containers.twig b/php/templates/includes/community-containers.twig index 66cceb2b..da1dd26d 100644 --- a/php/templates/includes/community-containers.twig +++ b/php/templates/includes/community-containers.twig @@ -37,6 +37,6 @@

{% endfor %} - + diff --git a/php/templates/includes/optional-containers.twig b/php/templates/includes/optional-containers.twig index 08f98634..b33f2292 100644 --- a/php/templates/includes/optional-containers.twig +++ b/php/templates/includes/optional-containers.twig @@ -41,11 +41,9 @@
  • Best support for legacy files
  • {% if isAnyRunning == false %} - + Learn more - - - + Collabora Logo {% endif %} @@ -76,11 +74,9 @@
  • Limited ODF compatibility
  • {% if isAnyRunning == false %} - + Learn more - - - + Onlyoffice Logo {% endif %} @@ -99,9 +95,7 @@ {% endif %} > diff --git a/php/templates/layout.twig b/php/templates/layout.twig index ab3095c4..55fb6e5e 100644 --- a/php/templates/layout.twig +++ b/php/templates/layout.twig @@ -1,10 +1,12 @@ - + + AIO + @@ -15,7 +17,7 @@
    - diff --git a/php/templates/log.twig b/php/templates/log.twig index 044c6386..297498fc 100644 --- a/php/templates/log.twig +++ b/php/templates/log.twig @@ -2,57 +2,7 @@ - + diff --git a/php/templates/login.twig b/php/templates/login.twig index 1c5420c2..88432e48 100644 --- a/php/templates/login.twig +++ b/php/templates/login.twig @@ -11,7 +11,7 @@ {% if is_login_allowed == true %}

    Log in using your Nextcloud AIO passphrase:

    - +