fix(#1953): gate approval cancel on submitter ownership + document submitter_of errors
This commit is contained in:
parent
3618399d94
commit
ebaf192476
2 changed files with 27 additions and 6 deletions
|
|
@ -136,6 +136,11 @@ impl Approvals {
|
||||||
/// The agent that submitted approval `id` (the authenticated socket
|
/// The agent that submitted approval `id` (the authenticated socket
|
||||||
/// caller at submit time). `None` for legacy rows predating the
|
/// caller at submit time). `None` for legacy rows predating the
|
||||||
/// `submitter` column — callers route those to the root agent.
|
/// `submitter` column — callers route those to the root agent.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an error if the sqlite prepare/query fails. A missing row
|
||||||
|
/// or a `NULL` submitter is not an error — both yield `Ok(None)`.
|
||||||
pub fn submitter_of(&self, id: i64) -> Result<Option<String>> {
|
pub fn submitter_of(&self, id: i64) -> Result<Option<String>> {
|
||||||
let conn = self.conn.lock().unwrap();
|
let conn = self.conn.lock().unwrap();
|
||||||
let submitter: Option<String> = conn
|
let submitter: Option<String> = conn
|
||||||
|
|
|
||||||
|
|
@ -144,8 +144,10 @@ pub fn handle_answer(
|
||||||
/// Handle `CancelLooseEnd` from a per-agent socket. Dispatches by kind, each
|
/// Handle `CancelLooseEnd` from a per-agent socket. Dispatches by kind, each
|
||||||
/// with its own auth check: question / reminder cancels are ownership-only
|
/// with its own auth check: question / reminder cancels are ownership-only
|
||||||
/// (an agent cancels its own), and approval cancels require the `approvals`
|
/// (an agent cancels its own), and approval cancels require the `approvals`
|
||||||
/// tool-group (the grantable capability) — no positional / hardcoded
|
/// tool-group (the grantable capability) AND ownership — the canceller must
|
||||||
/// privilege. (The operator's cancel-anything path is a separate handler.)
|
/// be the approval's submitter — so no positional / hardcoded privilege and
|
||||||
|
/// no cross-agent cancellation. (The operator's cancel-anything path is a
|
||||||
|
/// separate handler.)
|
||||||
/// On question cancel, fires the `QuestionAnswered` event back to the asker
|
/// On question cancel, fires the `QuestionAnswered` event back to the asker
|
||||||
/// so the harness loop can react (mirrors the operator-cancel dashboard path).
|
/// so the harness loop can react (mirrors the operator-cancel dashboard path).
|
||||||
pub fn handle_cancel_loose_end(
|
pub fn handle_cancel_loose_end(
|
||||||
|
|
@ -195,11 +197,25 @@ pub fn handle_cancel_loose_end(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
hive_sh4re::CancelLooseEndKind::Approval => {
|
hive_sh4re::CancelLooseEndKind::Approval => {
|
||||||
// Withdrawing an approval is a hive-wide orchestration action,
|
// Withdrawing an approval needs the grantable `approvals`
|
||||||
// gated on the grantable `approvals` tool-group (held by the
|
// tool-group (held by any approval-submitting orchestrator)
|
||||||
// orchestrator that submits approvals) — not on a positional /
|
// AND ownership: only the agent that submitted the approval
|
||||||
// hardcoded privilege.
|
// may withdraw it. Without the ownership check, any
|
||||||
|
// approvals-group agent could cancel any other's approval by
|
||||||
|
// id. A NULL submitter (legacy row predating the column) is
|
||||||
|
// owned by the root agent.
|
||||||
check_can_cancel_approval(canceller)?;
|
check_can_cancel_approval(canceller)?;
|
||||||
|
let submitter = coord
|
||||||
|
.approvals
|
||||||
|
.submitter_of(id)
|
||||||
|
.map_err(|e| format!("{e:#}"))?
|
||||||
|
.unwrap_or_else(|| hive_sh4re::MANAGER_AGENT.to_owned());
|
||||||
|
if submitter != canceller {
|
||||||
|
return Err(format!(
|
||||||
|
"cancel_loose_end: approval {id} was submitted by {submitter}, \
|
||||||
|
not {canceller}; only the submitting agent can withdraw it"
|
||||||
|
));
|
||||||
|
}
|
||||||
let approval = coord
|
let approval = coord
|
||||||
.approvals
|
.approvals
|
||||||
.mark_cancelled(id, canceller)
|
.mark_cancelled(id, canceller)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue