Endpoint telemetry, PC Detail rework and a checked backup/restore path
This commit is contained in:
@@ -77,12 +77,36 @@ COMMAND_ALLOWLIST = {
|
||||
'label': 'Collect now',
|
||||
'description': 'Force the endpoint agent to report fresh telemetry off-cadence.',
|
||||
},
|
||||
'network_connections': {
|
||||
'label': 'Show network connections',
|
||||
'description': 'Return the full TCP connection table with owning processes. '
|
||||
'Read-only diagnostic; the standard report carries only '
|
||||
'listening ports because established connections churn.',
|
||||
},
|
||||
'scheduledtask_run': {
|
||||
'label': 'Run scheduled task',
|
||||
'description': 'Start an already-registered Windows scheduled task on '
|
||||
'the endpoint by its task path (for example '
|
||||
'\\ReportIP). The agent does not create, modify, or '
|
||||
'define tasks - it only starts one that already exists, '
|
||||
'and only when the path clears the agent-side approved '
|
||||
'task allowlist. The task runs in whatever context its '
|
||||
'own registered definition specifies, not the agent\'s.',
|
||||
'targetkind': 'scheduledtask',
|
||||
},
|
||||
'run_script': {
|
||||
'label': 'Run script',
|
||||
'description': 'Run a ShopDB-hosted script on the endpoint. The agent '
|
||||
'fetches the script body by digest using its collector '
|
||||
'credential and runs it in the script\'s configured '
|
||||
'context (system or the logged-on user).',
|
||||
'context (system or the logged-on user). CAUTION: a '
|
||||
'script sent this way runs as SYSTEM unless its '
|
||||
'descriptor pins the user context, so this is arbitrary '
|
||||
'code execution on the endpoint by design. Grant '
|
||||
'endpoints.deploy only to the most trusted operators, '
|
||||
'and gate the UI behind an explicit confirm the way '
|
||||
'system_restart is gated (see confirmRestart in '
|
||||
'PCDetail.vue).',
|
||||
'targetkind': 'script',
|
||||
},
|
||||
'update_app': {
|
||||
@@ -96,11 +120,76 @@ COMMAND_ALLOWLIST = {
|
||||
}
|
||||
|
||||
|
||||
# Dell Command | Update: TWO utilities, deliberately two separate actions.
|
||||
# A scan reports what is available and changes nothing; an update installs and
|
||||
# can reboot. Collapsing them into one parameterized action would make the
|
||||
# destructive one reachable by editing a field, so they stay distinct and the
|
||||
# UI can gate them differently.
|
||||
#
|
||||
# Both are driven through scheduledtask_run against ShopDB-owned task
|
||||
# definitions (\ShopDB-DellCommandScan and \ShopDB-DellCommandUpdate, already in
|
||||
# ScheduledTaskPolicy's shipped defaults) rather than a bespoke verb. The task
|
||||
# definition - not the agent - pins the dcu-cli.exe path, its arguments, and its
|
||||
# output path, which is why no new agent code is needed to run them.
|
||||
#
|
||||
# OUTPUT PATH CONVENTION. A ShopDB-initiated scan MUST write its report under
|
||||
# %ProgramData%\ShopDB\EndpointAgent\ (the same SYSTEM-owned root as the agent
|
||||
# logs), NOT to Dell's default location. Corporate-managed DCU tasks write to
|
||||
# the default; keeping ShopDB's output separate is the only thing that makes
|
||||
# "did ShopDB ask for this scan, or did corporate" answerable when the results
|
||||
# are collected. The path lives in the task definition and is generated
|
||||
# server-side, so it can change without touching the agent.
|
||||
#
|
||||
# STILL TO DO - the results collector. Firing DCU is the easy half. schtasks
|
||||
# /run returns as soon as the task STARTS, so a successful scheduledtask_run
|
||||
# means "scan started", never "scan finished" - the command result cannot carry
|
||||
# findings. Reporting available updates back into ShopDB needs a collector that
|
||||
# reads the report from the path above and folds it into the telemetry payload.
|
||||
# That is a separate change from this one; do not infer from a green command
|
||||
# result that the data path exists.
|
||||
|
||||
|
||||
# Reserved seam - one-click mapped-drive remediation. NOT implemented; this is a
|
||||
# placement note so the next person adds it here rather than inventing a new
|
||||
# transport. When it lands it will re-establish a Disconnected mapping surfaced
|
||||
# by the user-context card:
|
||||
#
|
||||
# 'remap_drive': {
|
||||
# 'label': 'Reconnect mapped drive',
|
||||
# 'description': 'Re-establish a disconnected network drive mapping.',
|
||||
# 'targetkind': 'drive',
|
||||
# },
|
||||
#
|
||||
# It MUST execute in the interactive USER session. A SYSTEM-context remap creates
|
||||
# a mapping in SYSTEM's own drive table that the operator cannot see, so the card
|
||||
# would keep reporting the drive as broken while the "fix" silently succeeded
|
||||
# somewhere nobody is looking. An ELEVATED remap fails the same way, because UAC
|
||||
# token separation gives the elevated context a different drive table - the same
|
||||
# reason the probe itself runs unelevated from the tray.
|
||||
#
|
||||
# CommandExecutor.RunScriptInUserSession is the existing path for this.
|
||||
|
||||
|
||||
# Actions that demand a permission stricter than the base endpoints.command.
|
||||
# The enqueue route requires the caller to ALSO hold the mapped permission, so a
|
||||
# destructive action (a remote reboot) is a heavier grant than a routine toggle.
|
||||
# NOTE FOR THE ROLES/PERMISSIONS WORK: scheduledtask_run is deliberately mapped
|
||||
# onto the EXISTING endpoints.deploy grant rather than introducing a new one, so
|
||||
# the wire is pre-built and needs no migration. Be aware this WIDENS what
|
||||
# endpoints.deploy means: it previously covered run_script + update_app, and now
|
||||
# also covers starting approved scheduled tasks. If that bundle is too coarse,
|
||||
# the clean split is a separate endpoints.corptools permission mapped here - no
|
||||
# other code needs to change, because the enqueue route reads this table.
|
||||
#
|
||||
# Suggested operator-facing wording for the combined grant:
|
||||
# "Caution: Operator can run scheduled tasks, update applications, and affect
|
||||
# system-level changes to the endpoint."
|
||||
# That wording is accurate for scheduledtask_run and update_app. It UNDERSTATES
|
||||
# run_script, which is arbitrary SYSTEM code execution - if run_script stays in
|
||||
# this bundle, say so plainly in the role description.
|
||||
ELEVATED_ACTION_PERMISSIONS = {
|
||||
'system_restart': 'endpoints.restart',
|
||||
'scheduledtask_run': 'endpoints.deploy',
|
||||
'run_script': 'endpoints.deploy',
|
||||
'update_app': 'endpoints.deploy',
|
||||
}
|
||||
@@ -215,6 +304,24 @@ def monitor_id_from_action(action):
|
||||
return None
|
||||
|
||||
|
||||
# Actions gated by ROLE ONLY, never by per-endpoint Configuration Policy.
|
||||
#
|
||||
# These are the Corporate Tools actions. They start already-registered,
|
||||
# agent-allowlisted tasks and are not a per-machine configuration concern, so a
|
||||
# policy that never mentions them must not remove them. Policy exists to shape
|
||||
# what a given PC TYPE exposes (which monitors, which service primitives); it is
|
||||
# not the authority on whether an admin may run a corporate tool.
|
||||
#
|
||||
# Without this exemption the intersection at the end of ALLOWED_ACTIONS_FOR
|
||||
# silently drops these actions on any endpoint whose effective policy is
|
||||
# non-empty, producing "Action not permitted by policy for this endpoint" even
|
||||
# for a caller holding endpoints.deploy. The real gate for these stays
|
||||
# ELEVATED_ACTION_PERMISSIONS (role), enforced in the enqueue route.
|
||||
POLICY_EXEMPT_ACTIONS = frozenset({
|
||||
'scheduledtask_run',
|
||||
})
|
||||
|
||||
|
||||
def ALLOWED_ACTIONS_FOR(pctype=None, assetid=None):
|
||||
"""The action names an endpoint may run.
|
||||
|
||||
@@ -223,8 +330,9 @@ def ALLOWED_ACTIONS_FOR(pctype=None, assetid=None):
|
||||
result is narrowed to the actions the endpoint's effective policy permits -
|
||||
this is the allowlist-gating half of the policy design. With the feature off
|
||||
(or no assetid), it returns the full base allowlist, so nothing changes until
|
||||
a site opts in. Policy resolution is imported lazily to keep this module free
|
||||
of a hard dependency on the computers plugin.
|
||||
a site opts in. Actions in POLICY_EXEMPT_ACTIONS are never narrowed away;
|
||||
they are gated by role alone. Policy resolution is imported lazily to keep
|
||||
this module free of a hard dependency on the computers plugin.
|
||||
"""
|
||||
base = set(COMMAND_ALLOWLIST.keys())
|
||||
if assetid is None:
|
||||
@@ -247,6 +355,9 @@ def ALLOWED_ACTIONS_FOR(pctype=None, assetid=None):
|
||||
# "not permitted by policy". Expand each monitor:<id> to its kind's control
|
||||
# actions before intersecting.
|
||||
allowed = allowed | _monitor_control_actions(allowed)
|
||||
# Role-gated actions are added back unconditionally: policy narrows the
|
||||
# per-endpoint surface, but it does not own the corporate tools.
|
||||
allowed = allowed | POLICY_EXEMPT_ACTIONS
|
||||
# Never widen past the code-level allowlist: policy can only gate within it.
|
||||
return base & allowed
|
||||
|
||||
|
||||
Reference in New Issue
Block a user