refactor(#1834): derive cancel-loose-end privilege from the socket, not the MANAGER_AGENT name
The cancel-loose-end guards (cancel approval / question / reminder)
inferred manager-level privilege by string-matching the canceller
against the hardcoded `hive_sh4re::MANAGER_AGENT` ("ruth"). That laundered
privilege through a name: a request arrives on the privileged manager
socket, gets stamped with the bootstrap agent's name, and the guards
re-derive privilege from that name. Renaming or replacing the bootstrap
agent would then silently move privilege.
Privilege is a property of the SOCKET the request arrived on (the manager
socket is the trust boundary), so thread an explicit `privileged: bool`
through `dispatch_shared` → `handle_cancel_loose_end` → the three guards:
- `Broker::cancel_reminder_as` and `OperatorQuestions::cancel`: the
`== MANAGER_AGENT` leg becomes `privileged` (owner/asker + operator name
legs unchanged).
- `check_approval_canceller_is_manager(canceller)` →
`check_can_cancel_approval(privileged)` (manager-socket-only); unit tests
updated to assert on the flag.
The manager socket passes `privileged = true`; the agent socket passes
`false`. `MANAGER_AGENT` is still passed as the actor NAME for legitimate
attribution/routing (notifications, schedule ownership, bootstrap
destroy-protection) — those are not privilege checks and are left intact.
Scope is the privilege guards only.
This commit is contained in:
parent
e797b75ca9
commit
53f49615fa
5 changed files with 65 additions and 49 deletions
|
|
@ -942,9 +942,10 @@ impl Broker {
|
|||
|
||||
/// Cancel a pending reminder on behalf of `canceller`. Returns
|
||||
/// the owner agent name on success (handy for logging). Auth
|
||||
/// rules mirror `OperatorQuestions::cancel`: owner, operator, or
|
||||
/// manager.
|
||||
pub fn cancel_reminder_as(&self, id: i64, canceller: &str) -> Result<String> {
|
||||
/// rules mirror `OperatorQuestions::cancel`: the owner, the
|
||||
/// operator, or a `privileged` caller (one that arrived on the
|
||||
/// manager socket — the trust boundary, not a name match).
|
||||
pub fn cancel_reminder_as(&self, id: i64, canceller: &str, privileged: bool) -> Result<String> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let owner: Option<String> = conn
|
||||
.query_row(
|
||||
|
|
@ -956,9 +957,8 @@ impl Broker {
|
|||
let Some(owner) = owner else {
|
||||
anyhow::bail!("reminder {id} not pending (already delivered or unknown)");
|
||||
};
|
||||
let authorised = canceller == owner
|
||||
|| canceller == hive_sh4re::OPERATOR_RECIPIENT
|
||||
|| canceller == hive_sh4re::MANAGER_AGENT;
|
||||
let authorised =
|
||||
privileged || canceller == owner || canceller == hive_sh4re::OPERATOR_RECIPIENT;
|
||||
if !authorised {
|
||||
anyhow::bail!("reminder {id}: '{canceller}' not allowed to cancel (owner = '{owner}')");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue