Close the review findings on the policy routes, installer and import
Applied on top ofaf13337so nothing already on wilm is rewritten. AUTHORIZATION. The six new policy GETs were @jwt_required(optional=True) with no permission at all, so an anonymous caller could list configuration and business policies - gated only by a feature flag that 404s while off. They now require computers.view. The six policy write routes stacked @jwt_required() above @require_permission, the same pattern removed from nine endpoints routes in this very push: the outer decorator answers an anonymous request 401 before require_permission can consult the guest role, so a grant to guests silently does nothing. Removed, and the now-unused import with them. tests/test_core/test_public_read_surface.py failed ataf13337with seven ungated GETs. It passes now. The seventh, endpoints.claim_next_command, is genuinely agent-facing - authenticated in the handler by the endpoints collector key, because a bay has no user session - so it joins PUBLIC_ENDPOINTS with its reason written down, exactly as report_command_result already does in test_authz.py. Note which half is public: claiming a queued command is the agent's business, while ENQUEUEING one is the real authority and stays gated on endpoints.command. INSTALLER. -BaseUrl and -CollectorKey are now mandatory with no defaults, and https is enforced unless -AllowInsecureHttp is passed for a local test box. The defaults were a real hostname (ADR-015) and a live collector key: a key in source is a key in git history, and that key is also the command channel's kill switch. Removing it here does not undo the disclosure - it must be rotated on the server, and purged fromaf13337- but it stops the next clone carrying it. IMPORT. A committed import wrote thousands of rows and left no audit trail; it now writes one AuditLog row inside the same transaction, so it cannot survive a rollback or go missing from a commit. The computer-type dropdown asked for per_page: 200, which the API ignores in favour of perpage and clamps to 100 anyway - it uses a new computersApi.types.listAll(), the pattern already applied to the other selectors. And the comment claiming the template example rows are "emitted as commented rows the engine skips" was the opposite of the truth: they import as live data, the emitted instructions say so, and a test depends on it. Not fixed here, deliberately: rotating the leaked key, purging it from history, and the endpoint agent's own hardening (per-host credentials, a protected process denylist so stop cannot kill security tooling, signing before the PowerShell runner is enabled, registry ACLs, and the 25-second long poll that will pin a worker thread per bay).
This commit is contained in:
@@ -12,7 +12,6 @@ allowlist, so a bad tree or an unknown action never reaches the database.
|
||||
"""
|
||||
|
||||
from flask import request
|
||||
from flask_jwt_extended import jwt_required
|
||||
|
||||
from shopdb.api import (db, AuditLog, success_response, error_response,
|
||||
ErrorCodes, require_permission)
|
||||
@@ -63,7 +62,7 @@ def _businesspolicy_dict(policy):
|
||||
# =============================================================================
|
||||
|
||||
@computers_bp.route('/policies/configuration', methods=['GET'])
|
||||
@jwt_required(optional=True)
|
||||
@require_permission('computers.view')
|
||||
def list_configuration_policies():
|
||||
gate = _feature_gate()
|
||||
if gate:
|
||||
@@ -73,7 +72,7 @@ def list_configuration_policies():
|
||||
|
||||
|
||||
@computers_bp.route('/policies/configuration/<int:policy_id>', methods=['GET'])
|
||||
@jwt_required(optional=True)
|
||||
@require_permission('computers.view')
|
||||
def get_configuration_policy(policy_id: int):
|
||||
gate = _feature_gate()
|
||||
if gate:
|
||||
@@ -113,7 +112,6 @@ def _apply_actions(policy, actions):
|
||||
|
||||
|
||||
@computers_bp.route('/policies/configuration', methods=['POST'])
|
||||
@jwt_required()
|
||||
@require_permission('computers.create')
|
||||
def create_configuration_policy():
|
||||
gate = _feature_gate()
|
||||
@@ -155,7 +153,6 @@ def create_configuration_policy():
|
||||
|
||||
|
||||
@computers_bp.route('/policies/configuration/<int:policy_id>', methods=['PUT'])
|
||||
@jwt_required()
|
||||
@require_permission('computers.edit')
|
||||
def update_configuration_policy(policy_id: int):
|
||||
gate = _feature_gate()
|
||||
@@ -204,7 +201,6 @@ def update_configuration_policy(policy_id: int):
|
||||
|
||||
|
||||
@computers_bp.route('/policies/configuration/<int:policy_id>', methods=['DELETE'])
|
||||
@jwt_required()
|
||||
@require_permission('computers.delete')
|
||||
def delete_configuration_policy(policy_id: int):
|
||||
gate = _feature_gate()
|
||||
@@ -236,7 +232,7 @@ def delete_configuration_policy(policy_id: int):
|
||||
# =============================================================================
|
||||
|
||||
@computers_bp.route('/policies/business', methods=['GET'])
|
||||
@jwt_required(optional=True)
|
||||
@require_permission('computers.view')
|
||||
def list_business_policies():
|
||||
gate = _feature_gate()
|
||||
if gate:
|
||||
@@ -248,7 +244,7 @@ def list_business_policies():
|
||||
|
||||
|
||||
@computers_bp.route('/policies/business/<int:policy_id>', methods=['GET'])
|
||||
@jwt_required(optional=True)
|
||||
@require_permission('computers.view')
|
||||
def get_business_policy(policy_id: int):
|
||||
gate = _feature_gate()
|
||||
if gate:
|
||||
@@ -284,7 +280,6 @@ def _apply_items(policy, items):
|
||||
|
||||
|
||||
@computers_bp.route('/policies/business', methods=['POST'])
|
||||
@jwt_required()
|
||||
@require_permission('computers.create')
|
||||
def create_business_policy():
|
||||
gate = _feature_gate()
|
||||
@@ -315,7 +310,6 @@ def create_business_policy():
|
||||
|
||||
|
||||
@computers_bp.route('/policies/business/<int:policy_id>', methods=['PUT'])
|
||||
@jwt_required()
|
||||
@require_permission('computers.edit')
|
||||
def update_business_policy(policy_id: int):
|
||||
gate = _feature_gate()
|
||||
@@ -355,7 +349,6 @@ def update_business_policy(policy_id: int):
|
||||
|
||||
|
||||
@computers_bp.route('/policies/business/<int:policy_id>', methods=['DELETE'])
|
||||
@jwt_required()
|
||||
@require_permission('computers.delete')
|
||||
def delete_business_policy(policy_id: int):
|
||||
gate = _feature_gate()
|
||||
@@ -380,7 +373,7 @@ def delete_business_policy(policy_id: int):
|
||||
# =============================================================================
|
||||
|
||||
@computers_bp.route('/policies/actions', methods=['GET'])
|
||||
@jwt_required(optional=True)
|
||||
@require_permission('computers.view')
|
||||
def list_policy_actions():
|
||||
"""The allowlisted action names + labels a policy may reference."""
|
||||
gate = _feature_gate()
|
||||
@@ -395,7 +388,7 @@ def list_policy_actions():
|
||||
|
||||
|
||||
@computers_bp.route('/policies/effective/<int:asset_id>', methods=['GET'])
|
||||
@jwt_required(optional=True)
|
||||
@require_permission('computers.view')
|
||||
def get_effective_policy(asset_id: int):
|
||||
"""Preview the resolved effective policy for one endpoint (debug/UI)."""
|
||||
gate = _feature_gate()
|
||||
|
||||
Reference in New Issue
Block a user