Split the fleet roster from per-endpoint reads, and stop offering controls nobody can use
Twelve endpoints routes shared one permission, which made a reasonable policy impossible to express. A site that wants signed-out visitors to see live status on a PC page had to hand them the entire fleet - including every logged-in username on the floor - because the per-asset panels and the fleet-wide list were gated on the same string. endpoints.fleet now gates the list and the three dashboard routes. endpoints.view keeps the per-asset reads: status, services, processes, interfaces, events, command history. The name is load-bearing. guest_grantable_permissions() opens any '.view' or '.read' permission to the guest role, so calling this one 'endpoints.fleet' rather than 'endpoints.fleetview' is what keeps the roster off the public surface while per-asset telemetry stays grantable. Verified both ways. Also: the services panel emitted Start/Stop row actions to every reader, because row_actions_for() never consulted the caller. Clicking was always refused server-side, so nothing was exploitable, but showing a read-only viewer controls over a shop-floor PC is the wrong signal - and it stops being cosmetic the moment a site grants that panel to guests. That needed a contract addition rather than a local check: caller_holds() on shopdb.api answers whether THIS caller holds a permission without refusing the request, which is the complement to require_permission for a surface that serves everyone but should only offer what the caller can act on. Guests are included, since a signed-out visitor holds whatever the guest role was granted. Reaching into shopdb.utils.authz instead would repeat the ADR-002 violation the contract gate caught earlier in this branch. Contract 0.24.0 -> 0.25.0. DEPLOYMENT: endpoints.fleet is a new permission. Run 'flask seed permissions' and grant it to the roles that should keep seeing the Fleet page. Until then that page is admin-only, since admins bypass every check - a safe direction, but not an automatic one. Full suite 2124 passed, naming and contract gates green, map and spec regenerated.
This commit is contained in:
@@ -81,7 +81,14 @@ from .plugins import plugin_manager
|
||||
# endpoints command claim/result long-poll callbacks) on the same per-plugin
|
||||
# collector key + managed-token check the ingest route uses, without importing
|
||||
# core collector internals. Additive name on the surface, minor bump.
|
||||
__contract_version__ = '0.24.0'
|
||||
# 0.25.0: added caller_holds(permission) to shopdb.api so a route can ask
|
||||
# whether THIS caller holds a permission without refusing the request. It is for
|
||||
# a surface that serves everyone but should only OFFER what the caller can use:
|
||||
# the endpoints services panel emitted Start/Stop to every reader because the
|
||||
# emission never consulted the caller, so a read-only viewer was shown controls
|
||||
# over a shop-floor PC that 403 on click. Guests are included, since a signed-out
|
||||
# visitor holds whatever the guest role was granted. Additive name, minor bump.
|
||||
__contract_version__ = '0.25.0'
|
||||
|
||||
# Product release version (see ADR-007). The product version and the
|
||||
# plugin-contract version above are distinct series with independent
|
||||
|
||||
Reference in New Issue
Block a user