A panel writes only what its dashboards' inputs target
It still reads every message its dashboards show. A message a tile only draws, or a setting is bound to, is no longer writable from the panel.
This commit is contained in:
+16
-14
@@ -99,14 +99,17 @@ def _locked(defn: DashboardDef, live: dict[str, Any]) -> bool:
|
||||
def _panel_writable(panel_id: str, request: Request) -> set[str]:
|
||||
"""The messages this panel may publish to.
|
||||
|
||||
The same walk as the read allowlist, minus whatever a locked dashboard
|
||||
contributes: ``locked`` is a dashboard saying it is there to be looked at,
|
||||
and until this it stopped only the client drawing the control — the server
|
||||
took the publish from a screen that asked anyway.
|
||||
What its dashboards' inputs target, which is narrower than what it may
|
||||
read: a chart, a reading or a bound setting is something a screen shows,
|
||||
and showing a message does not entitle it to set one.
|
||||
|
||||
Not quite all of it: a querying chart publishes the request it reads by,
|
||||
and a locked dashboard whose charts cannot ask goes blank rather than
|
||||
read-only. That request is the exception ``panels.requests_of`` names.
|
||||
A locked dashboard contributes less still: ``locked`` is a dashboard
|
||||
saying it is there to be looked at, and until this it stopped only the
|
||||
client drawing the control — the server took the publish from a screen
|
||||
that asked anyway. Not nothing, though: a querying chart publishes the
|
||||
request it reads by, and a locked dashboard whose charts cannot ask goes
|
||||
blank rather than read-only. That request is the exception
|
||||
``panels.requests_of`` names.
|
||||
|
||||
Union, exactly as the allowlist itself is: a message one dashboard on this
|
||||
panel displays and another controls stays writable, because the unlocked
|
||||
@@ -131,9 +134,7 @@ def _panel_writable(panel_id: str, request: Request) -> set[str]:
|
||||
live = controller.state.get_present(bound) if bound and controller else {}
|
||||
for defn in defns:
|
||||
writable |= (
|
||||
panels.requests_of(defn)
|
||||
if _locked(defn, live)
|
||||
else panels.messages_of(defn)
|
||||
panels.requests_of(defn) if _locked(defn, live) else panels.targets_of(defn)
|
||||
)
|
||||
return writable
|
||||
|
||||
@@ -174,11 +175,12 @@ def _panel_may(payload: dict[str, Any], request: Request) -> None:
|
||||
Publishing is in the list because a panel cannot be strictly read-only — a
|
||||
control on a panel is the point of putting one there, and a querying chart
|
||||
asks for its window by publishing a request, which is why that request
|
||||
counts as one of its widget's messages. Bounded to those, though: a screen
|
||||
counts as one of its widget's targets. Bounded to those, though: a screen
|
||||
on a wall has no business reaching a message no tile on it draws, and the
|
||||
catalogue behind ``GET /messages/`` is the whole namespace at once. A
|
||||
dashboard that says it is locked is bounded further still — it entitles a
|
||||
panel to read every message it names and to publish to none of them but
|
||||
catalogue behind ``GET /messages/`` is the whole namespace at once. It
|
||||
reads every message its tiles draw and writes only what its inputs target.
|
||||
A dashboard that says it is locked is bounded further still — it entitles
|
||||
a panel to read every message it names and to publish to none of them but
|
||||
the requests its own charts ask by.
|
||||
|
||||
# ponytail: the panel file and the published dashboards are re-read per
|
||||
|
||||
@@ -197,6 +197,16 @@ def requests_of(defn: DashboardDef) -> set[str]:
|
||||
return {w.target for w in defn.widgets if w.type == "chart" and w.target}
|
||||
|
||||
|
||||
def targets_of(defn: DashboardDef) -> set[str]:
|
||||
"""The messages this dashboard's inputs publish to.
|
||||
|
||||
What a screen showing it may write, a narrower set than what it may read.
|
||||
A tile that only draws a message, and a setting bound to one, are there to
|
||||
be looked at, so a panel showing them is not entitled to set them.
|
||||
"""
|
||||
return {w.target for w in defn.widgets if w.target}
|
||||
|
||||
|
||||
def io_in(panel_id: str) -> set[str]:
|
||||
"""The messages this panel's own hardware publishes to."""
|
||||
panel = find(panel_id)
|
||||
|
||||
@@ -373,7 +373,8 @@ def test_a_panel_speaks_only_its_own_widgets_messages(
|
||||
== 404
|
||||
), target
|
||||
|
||||
# And reads the history of everything its widgets bind to.
|
||||
# And reads the history of everything its widgets bind to, but writes none
|
||||
# of what they only draw.
|
||||
for name in (
|
||||
"demo.temperature",
|
||||
"demo.switch_state",
|
||||
@@ -384,6 +385,12 @@ def test_a_panel_speaks_only_its_own_widgets_messages(
|
||||
client.get(f"{messages}/{name}/history", headers=panel_headers).status_code
|
||||
== 200
|
||||
), name
|
||||
assert (
|
||||
client.post(
|
||||
f"{messages}/{name}", headers=panel_headers, json={"value": 1}
|
||||
).status_code
|
||||
== 403
|
||||
), name
|
||||
|
||||
# A message no tile on this panel names is not its business, in either
|
||||
# direction.
|
||||
|
||||
@@ -389,9 +389,11 @@ As a superuser:
|
||||
The screen collects its credential and shows the assigned published dashboards.
|
||||
Several dashboards get a navigation rail.
|
||||
|
||||
A panel credential permits its assigned dashboards, their bound messages and
|
||||
the artifacts they show. It also permits publishing from its controls and query
|
||||
charts. On a locked dashboard only the query charts may still publish.
|
||||
A panel credential reads its assigned dashboards, their bound messages and the
|
||||
artifacts they show. It writes only the targets of their controls and query
|
||||
charts, and the `in` ports of its own [I/O](#the-devices-own-io). A message its
|
||||
tiles only draw, or a dashboard setting is bound to, stays read only. On a
|
||||
locked dashboard only the query charts may still publish.
|
||||
|
||||
**Unpair screen** revokes a locally paired credential and keeps the panel's
|
||||
dashboard assignments. Deleting the panel removes the assignment and its
|
||||
|
||||
Reference in New Issue
Block a user