Four changes that together get a signed-in officer back to working data. The SSO validation in index.php and embed.php called the public hostname from inside the server. The Lands Commission's NAT rule blocks traffic that leaves the server and tries to come back in, so that curl stalled for its full 30-second timeout and then failed validation, bouncing the user back to the login. Both now call http://l4lmainbackend/sso/validate, overridable with LUPMIS_SSO_BASE. Measured from inside the spdrawing container: 25 ms with no redirect, against a 10-second timeout on the public name. API_ROOT is now the relative path /api. The PWA host already proxies it to the ApiEngine container — a deliberate route, not a catch-all: /api answers on pwa. and api. but not on permits., minio. or minioapi. Same-origin removes the mixed-content, CORS and CORP constraints that Cross-Origin-Embedder-Policy: require-corp imposes on a cross-origin call, and no API hostname is compiled into the bundle any more. The dev server proxies /api to the public host, which is right there because a developer's machine is outside the server and only callers inside it are blocked from the public name. A direct request for /index.html bypassed index.php, which is what injects window.LUPMIS_SESSION, so the app started with no session at all and said so in the menu. The LUSPA landing page links to exactly that URL, and the SPA fallback could not help because its RewriteCond !-f fails precisely when the file exists. .htaccess now routes it through index.php; index.php reads index.html from the filesystem, so there is no loop. This one is inert until AllowOverride is relaxed on the PWA container, where it is currently None. Service worker v29, and v28 before it, which carries the GIS Tools rename to devices that already have the app installed. The tile cache stays pinned at v24, so downloaded map areas survive both updates. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
105 lines
5.0 KiB
ApacheConf
105 lines
5.0 KiB
ApacheConf
# ============================================================================
|
|
# LUPMIS2 PWA — Apache config
|
|
# ============================================================================
|
|
|
|
# Apache's default DirectoryIndex order serves index.html before index.php.
|
|
# We need the opposite so the SSO-aware index.php gets a chance to run first,
|
|
# inject session data into the page, and then return the index.html content.
|
|
DirectoryIndex index.php index.html
|
|
|
|
# Make sure .php files are executed (defensive — usually enabled site-wide,
|
|
# but explicit here in case the deployment dropped this association).
|
|
<FilesMatch "\.php$">
|
|
SetHandler application/x-httpd-php
|
|
</FilesMatch>
|
|
|
|
# Second line of defence for hidden FILES (.env, .htpasswd, …), in case
|
|
# mod_rewrite is not loaded. This does not cover hidden directories such as
|
|
# /.git/config — see the mod_rewrite block below, which does.
|
|
# Both Apache 2.4 and 2.2 syntaxes are given so this works either way.
|
|
#
|
|
# Access control runs before mod_rewrite, so a hidden file answers 403 here
|
|
# while a hidden directory answers 404 from the rewrite below. Both refuse to
|
|
# serve the content, which is the point; the differing codes are cosmetic.
|
|
<FilesMatch "^\.">
|
|
<IfModule mod_authz_core.c>
|
|
Require all denied
|
|
</IfModule>
|
|
<IfModule !mod_authz_core.c>
|
|
Order allow,deny
|
|
Deny from all
|
|
</IfModule>
|
|
</FilesMatch>
|
|
|
|
<IfModule mod_rewrite.c>
|
|
RewriteEngine On
|
|
|
|
# ------------------------------------------------------------------
|
|
# Block hidden paths (.git, .env, .svn, …) — MUST stay first
|
|
# ------------------------------------------------------------------
|
|
# Scanners routinely fetch /.env and /.git/config, and if /.git/index
|
|
# plus /.git/objects/… can be read the whole repository — including its
|
|
# history and any credential ever committed — can be reconstructed.
|
|
#
|
|
# Two things make this easy to get wrong:
|
|
#
|
|
# 1. <FilesMatch "^\."> does NOT stop /.git/config. FilesMatch tests
|
|
# the basename only, and the basename there is "config". Blocking a
|
|
# hidden *directory* needs a rule that sees the whole path, which in
|
|
# .htaccess means mod_rewrite (<DirectoryMatch> is server-config only).
|
|
#
|
|
# 2. The SPA fallback further down only rewrites paths that do NOT
|
|
# exist (!-f / !-d). A real .env on disk therefore skips the
|
|
# fallback and gets served as a plain file. So this rule has to come
|
|
# before it, not after.
|
|
#
|
|
# .well-known is deliberately exempt: blocking it breaks Let's Encrypt
|
|
# (ACME) certificate issuance and renewal.
|
|
#
|
|
# 404 rather than 403, so a probe learns nothing about what exists.
|
|
RewriteCond %{REQUEST_URI} !^/\.well-known/
|
|
RewriteRule (^|/)\. - [R=404,L]
|
|
|
|
# Files that are never meant to be fetched over HTTP, if one is ever
|
|
# deployed into the document root by mistake.
|
|
RewriteRule \.(sql|sqlite|sqlite3|db|bak|backup|old|orig|save|swp|log|ini|conf|key|pem|crt|p12|pfx)$ - [R=404,L,NC]
|
|
RewriteRule (^|/)(composer\.(json|lock)|package(-lock)?\.json|yarn\.lock|Dockerfile|docker-compose\.ya?ml|\.dockerignore)$ - [R=404,L,NC]
|
|
|
|
# Clean URL for the iframe embed endpoint: /embed → embed.php
|
|
# Must come BEFORE the SPA fallback so /embed doesn't get routed to
|
|
# index.php. Query strings (?mode=permit&...) pass through automatically.
|
|
RewriteRule ^embed/?$ embed.php [L]
|
|
|
|
# A direct request for /index.html must still go through index.php, which
|
|
# is what injects window.LUPMIS_SESSION. Without this rule the SPA fallback
|
|
# below cannot help: its RewriteCond !-f fails precisely because index.html
|
|
# IS a real file, so Apache serves it raw and the app starts with no
|
|
# session — the "No session injected" warning. index.php reads index.html
|
|
# from the filesystem (__DIR__), not over HTTP, so there is no loop.
|
|
RewriteRule ^index\.html$ index.php [L]
|
|
|
|
# Common single-page-app behaviour: if a route doesn't map to a real file
|
|
# or directory, send the request to index.php so the PWA can handle it
|
|
# client-side. Comment out this block if hash-based routing is preferred.
|
|
RewriteCond %{REQUEST_FILENAME} !-f
|
|
RewriteCond %{REQUEST_FILENAME} !-d
|
|
RewriteRule ^ index.php [L]
|
|
</IfModule>
|
|
|
|
# Iframe-policy override for the embed endpoint. Some Apache deployments set
|
|
# `X-Frame-Options: SAMEORIGIN` as a default security header for every
|
|
# response — that prevents `permits.lupmis4luspa.org` from framing
|
|
# `pwa.lupmis4luspa.org/embed`, even though our Content-Security-Policy
|
|
# `frame-ancestors` directive explicitly allows it. Safari prefers
|
|
# `X-Frame-Options` when both are present, so we have to remove it.
|
|
#
|
|
# We unset it ONLY for embed.php (so index.php still inherits the
|
|
# site-wide SAMEORIGIN protection against clickjacking). embed.php's own
|
|
# `Content-Security-Policy: frame-ancestors` header (set in PHP) is then
|
|
# the sole iframe-policy header and permits the configured embedder.
|
|
<IfModule mod_headers.c>
|
|
<Files "embed.php">
|
|
Header always unset X-Frame-Options
|
|
</Files>
|
|
</IfModule>
|