Make revoking an agent and locking a dashboard actually revoke and lock
An MCP access token is a stateless JWT good until it expires, so deleting the client row revoked nothing already handed out — on the MCP endpoint or on the REST API, which takes the same token directly. Both doors now look the client up by the `client_id` the token has always carried, so tokens already in circulation are held to it too. A dashboard's `locked` setting stopped the client drawing a control and nothing else; the server took a publish from a panel showing it anyway. It now bounds the panel's write scope, resolved live where a flow drives the flag, exactly as the client resolves it. Reads are untouched — read-only is not blind — and so is a querying chart's request, which is how that tile reads rather than something anyone touched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CL9zvnnvcp1mvA8o7impxk
This commit is contained in:
@@ -18,7 +18,7 @@ from pathlib import Path
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
from fluksio.core.config import settings
|
||||
from fluksio.flow.dashboards import DashboardNotFound, DashboardStore
|
||||
from fluksio.flow.dashboards import DashboardDef, DashboardNotFound, DashboardStore
|
||||
from fluksio.flow.schemas import _validate_name
|
||||
|
||||
|
||||
@@ -101,29 +101,62 @@ def find(panel_id: str) -> PanelDef | None:
|
||||
return None
|
||||
|
||||
|
||||
def messages_for(panel_id: str, store: DashboardStore) -> set[str]:
|
||||
"""Every message this panel's dashboards read or write.
|
||||
def dashboards_for(panel_id: str, store: DashboardStore) -> list[DashboardDef]:
|
||||
"""The published documents this panel shows, in rail order.
|
||||
|
||||
What a screen is entitled to see, as its own dashboards define it. Read
|
||||
from the published documents, since that is what a panel draws, and empty
|
||||
for a panel that is gone — which is the same answer as "nothing".
|
||||
Published, since that is what a panel draws. A name that no longer
|
||||
resolves is a dashboard someone deleted and is skipped, and a panel that
|
||||
is gone shows nothing.
|
||||
|
||||
Handed back whole rather than walked here, because what a panel may do
|
||||
with a message depends on the document it came from — a dashboard that
|
||||
says it is locked entitles a screen to read it and not to touch it.
|
||||
"""
|
||||
panel = find(panel_id)
|
||||
if panel is None:
|
||||
return []
|
||||
found: list[DashboardDef] = []
|
||||
for name in panel.dashboards:
|
||||
try:
|
||||
found.append(store.read(name))
|
||||
except DashboardNotFound:
|
||||
continue
|
||||
return found
|
||||
|
||||
|
||||
def messages_of(defn: DashboardDef) -> set[str]:
|
||||
"""Every message one dashboard reads or writes.
|
||||
|
||||
A dashboard's own bound settings count, not only its widgets': the theme a
|
||||
panel is driven to is a message no tile on it draws, and a wall panel
|
||||
refused its own theme message is the one surface the setting exists for.
|
||||
"""
|
||||
panel = find(panel_id)
|
||||
if panel is None:
|
||||
return set()
|
||||
names: set[str] = set()
|
||||
for dashboard in panel.dashboards:
|
||||
try:
|
||||
defn = store.read(dashboard)
|
||||
except DashboardNotFound:
|
||||
continue
|
||||
names.update(defn.setting_messages)
|
||||
for widget in defn.widgets:
|
||||
names.update(widget.messages)
|
||||
if widget.target:
|
||||
names.add(widget.target)
|
||||
names = set(defn.setting_messages)
|
||||
for widget in defn.widgets:
|
||||
names.update(widget.messages)
|
||||
if widget.target:
|
||||
names.add(widget.target)
|
||||
return names
|
||||
|
||||
|
||||
def requests_of(defn: DashboardDef) -> set[str]:
|
||||
"""The publishes this dashboard makes in order to read.
|
||||
|
||||
A querying chart asks a flow for the series it draws by publishing a
|
||||
request, so that publish is how the tile reads rather than something
|
||||
anyone touched. Every other message a dashboard sends comes from a
|
||||
control, which is what marking it read-only turns off — so this is what a
|
||||
locked dashboard is still entitled to send.
|
||||
"""
|
||||
return {w.target for w in defn.widgets if w.type == "chart" and w.target}
|
||||
|
||||
|
||||
def messages_for(panel_id: str, store: DashboardStore) -> set[str]:
|
||||
"""Every message this panel's dashboards read or write.
|
||||
|
||||
What a screen is entitled to see, as its own dashboards define it, and
|
||||
empty for a panel that is gone — which is the same answer as "nothing".
|
||||
"""
|
||||
return {
|
||||
name for defn in dashboards_for(panel_id, store) for name in messages_of(defn)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user