Gate GNSS (turning it on and its position) behind login
GET /api/gnss/position now requires login (app.rb) -- it reveals where the receiver physically is, not something to leave open to anyone with the URL. GnssControl's sidebar icon is hidden entirely when logged out (same pattern as the tree UI's write controls) rather than shown and failing to ever get a fix. LocateControl needs no changes: it only ever reaches GNSS through GnssControl#isEnabled(), which can't turn true for a logged-out visitor if the toggle is never on the page to click. Its browser-geolocation fallback stays open to everyone -- a visitor's own device asking for their own location isn't something to gate. Extracted isLoggedIn() out of gpx.js into a shared public/js/auth.js (gnss.js needed the same check) rather than duplicating it. Verified: /api/gnss/position returns 401 logged out; the sidebar icon is actually hidden (computed display: none, not just present-but- unstyled -- checked properly this time after almost repeating the same present-vs-visible test mistake the .gpx-panel__action bug already taught).
This commit is contained in:
1 parent
d9879bad60
commit
67585cd99a
6 files changed
+37
-12
No files matched your search
@@ -367,6 +367,7 @@ module Maptool
|
||||
end
|
||||
|
||||
get "/api/gnss/position" do
|
||||
require_login!
|
||||
GNSS_LATEST_MUTEX.synchronize do
|
||||
received_at = GNSS_LATEST[:received_at]
|
||||
fresh = received_at && (Time.now.to_f - received_at) < GNSS_STALE_AFTER
|
||||
|
||||
@@ -761,6 +761,20 @@ threading a separate flag through, since that's already there and
|
||||
login state can't change without a full page reload anyway. Delete
|
||||
exists on both file and folder rows — both are gated, easy to miss
|
||||
one and only catch it by testing an actually-logged-out session.
|
||||
`isLoggedIn()` itself lives in `public/js/auth.js` — shared with
|
||||
`GnssControl` (below), the only other place gating on login.
|
||||
|
||||
`GET /api/gnss/position` requires login too, same reasoning — it
|
||||
reveals where the receiver physically is, not something to leave open
|
||||
to anyone with the URL. `GnssControl`'s sidebar icon is hidden
|
||||
entirely when logged out (`isLoggedIn()`, same pattern as the tree
|
||||
UI's write controls above) rather than shown and failing to ever get
|
||||
a fix. `LocateControl` needs no gating of its own here: it only ever
|
||||
reaches GNSS through `GnssControl#isEnabled()`, which can never turn
|
||||
true for a logged-out visitor if the toggle that flips it is never on
|
||||
the page to click — its browser-geolocation fallback stays open to
|
||||
everyone, since that's a visitor's own device asking for their own
|
||||
location, nothing to gate.
|
||||
|
||||
Login delegates to the Gitea instance at `git.priesol.net` via OAuth2
|
||||
(`GET /login` redirects to Gitea's authorize endpoint, `GET
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// No server-rendered flag for this exists, and adding one would be more
|
||||
// plumbing than reusing what's already on the page: the topbar's own
|
||||
// logged_in check (views/index.erb) already renders a "Log out" link only
|
||||
// when logged in. Login state never changes without a full page reload
|
||||
// (there's no client-side login/logout), so this only needs checking once
|
||||
// per use, not reactively/on an event.
|
||||
export function isLoggedIn() {
|
||||
return !!document.querySelector('a[href="/logout"]');
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Marker } from "/vendor/maplibre-gl/maplibre-gl.mjs";
|
||||
import { GNSS_ICON } from "/js/icons.js";
|
||||
import { isLoggedIn } from "/js/auth.js";
|
||||
|
||||
const POSITION_URL = "/api/gnss/position";
|
||||
const POLL_INTERVAL_MS = 1500;
|
||||
@@ -41,6 +42,12 @@ export class GnssControl {
|
||||
this._status = this._container.querySelector(".gnss-control__status");
|
||||
this._icon.addEventListener("click", () => this._setEnabled(!this._enabled));
|
||||
|
||||
// GET /api/gnss/position itself requires login too (app.rb) — hidden
|
||||
// here rather than shown and failing on click, same reasoning as
|
||||
// gpx.js's write-action controls: it reveals where the receiver
|
||||
// physically is, not something to leave open to anyone with the URL.
|
||||
if (!isLoggedIn()) this._container.hidden = true;
|
||||
|
||||
sidebar.appendChild(this._container);
|
||||
}
|
||||
|
||||
|
||||
+1
-11
@@ -14,6 +14,7 @@ import {
|
||||
} from "/js/icons.js";
|
||||
import { notifySidePanelOpen, onlyOneSidePanelOpen } from "/js/panels.js";
|
||||
import { formatActivityType } from "/js/track_profile.js";
|
||||
import { isLoggedIn } from "/js/auth.js";
|
||||
|
||||
const TREE_URL = "/api/gpx/tree";
|
||||
const UPLOAD_URL = "/api/gpx";
|
||||
@@ -31,17 +32,6 @@ const BADGE_IMAGE_PREFIX = "badge-"; // one registered image per distinct badge
|
||||
// one — both land on the same drop targets, told apart by this.
|
||||
const MOVE_MIME = "application/x-gpx-path";
|
||||
|
||||
// No server-rendered flag for this exists yet, and adding one would be more
|
||||
// plumbing than reusing what's already on the page: the topbar's own
|
||||
// logged_in check (views/index.erb) already renders a "Log out" link only
|
||||
// when logged in. Write-action controls (delete/download/upload/new
|
||||
// folder) check this to hide themselves entirely rather than being shown
|
||||
// and failing on click — login state never changes without a full page
|
||||
// reload, so this only needs checking once per render, not reactively.
|
||||
function isLoggedIn() {
|
||||
return !!document.querySelector('a[href="/logout"]');
|
||||
}
|
||||
|
||||
// A sidebar icon that opens a side panel for uploading GPX files (via
|
||||
// drag & drop, stored server-side in a folder hierarchy the user can add to
|
||||
// and reorganize by dragging), and toggling their tracks/waypoints on and
|
||||
|
||||
+5
-1
@@ -24,10 +24,14 @@ class AppTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_gnss_position_unavailable_with_no_fix
|
||||
response = request.get("/api/gnss/position")
|
||||
response = request.get("/api/gnss/position", **logged_in_env)
|
||||
assert_equal({ "available" => false }, json_body(response))
|
||||
end
|
||||
|
||||
def test_gnss_position_requires_login
|
||||
assert_equal 401, request.get("/api/gnss/position").status
|
||||
end
|
||||
|
||||
# --- auth guard ---
|
||||
|
||||
def test_write_routes_require_login
|
||||
|
||||
Reference in new issue
Block a user