feat: patching REST API, patching scope, run IDs from apply-updates and MCP
This commit is contained in:
@@ -191,3 +191,33 @@ func CountPolicyTargets(p models.PatchPolicy) (int, error) {
|
||||
}
|
||||
return len(servers), err
|
||||
}
|
||||
|
||||
var ErrPatchRunActive = errors.New("a run of this policy is already in progress")
|
||||
|
||||
// CheckPolicyScope refuses a tag-restricted token acting on a policy whose
|
||||
// targets reach outside its restriction, with the workflow rule unchanged.
|
||||
func CheckPolicyScope(instanceID string, p models.PatchPolicy, tokenScope map[string]string) error {
|
||||
return validateWorkflowTargetScope(instanceID, p.TargetServerIDs, p.TargetTags, tokenScope)
|
||||
}
|
||||
|
||||
// StartRunNow opens a window of the policy's usual length starting now. It is
|
||||
// how an operator tests a policy on a Tuesday afternoon.
|
||||
func StartRunNow(instanceID, policyID, actor string, tokenScope map[string]string) (*models.PatchRun, error) {
|
||||
p, err := GetPolicy(instanceID, policyID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := CheckPolicyScope(instanceID, *p, tokenScope); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
w, err := GetWindow(instanceID, p.WindowID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx, cancel := patchCtx()
|
||||
defer cancel()
|
||||
if db.Col("patch_runs").FindOne(ctx, bson.M{"instance_id": instanceID, "policy_id": policyID, "status": models.PatchRunRunning}).Err() == nil {
|
||||
return nil, ErrPatchRunActive
|
||||
}
|
||||
return StartPolicyRun(*p, patchsched.WindowEnd(time.Now(), w.DurationMinutes), models.PatchSourceRunNow, actor)
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
// the vocabulary below.
|
||||
var ErrInvalidScope = errors.New("invalid scope")
|
||||
|
||||
// ScopeResources is the whole vocabulary. Ten resources, each with :read and
|
||||
// :write, and write implies read on the same resource.
|
||||
// ScopeResources is the whole vocabulary. Eleven resources, each with :read
|
||||
// and :write, and write implies read on the same resource.
|
||||
//
|
||||
// It is deliberately coarse. A scope per endpoint is a table nobody maintains,
|
||||
// and a route added without an entry either fails closed and breaks, or
|
||||
@@ -27,6 +27,7 @@ var ScopeResources = []string{
|
||||
"workloads",
|
||||
"settings",
|
||||
"status",
|
||||
"patching",
|
||||
// mcp:read is permission to reach the MCP endpoint at all; mcp:write is
|
||||
// permission for its write tools, which are not merely refused without it
|
||||
// but omitted from tools/list entirely.
|
||||
|
||||
Reference in New Issue
Block a user