Enforce local KV write capabilities

This commit is contained in:
Eric Wendland 2026-05-18 04:06:41 +02:00
commit 5751748458
8 changed files with 118 additions and 10 deletions

View file

@ -83,6 +83,8 @@ pub enum NodeError {
Document(#[from] geth_document::DocumentError),
#[error("resource not found: {0}")]
ResourceNotFound(String),
#[error("unauthorized: {0}")]
Unauthorized(String),
#[error("secrets error: {0}")]
Secrets(#[from] geth_secrets::SecretsError),
#[error("pubsub error: {0}")]
@ -1009,12 +1011,18 @@ pub fn handle_request(
kv: kv_resource_from_stored(&stored),
})
}
ControlRequest::KvSet { name, key, value } => {
ControlRequest::KvSet {
name,
key,
value,
subject,
} => {
geth_kv::validate_kv_name(&name).map_err(|_| NodeError::InvalidKvName(name.clone()))?;
geth_kv::validate_kv_key(&key).map_err(|_| NodeError::InvalidKvKey(key.clone()))?;
let kv = store
.get_kv_store_by_name(&name)?
.ok_or_else(|| NodeError::KvNotFound(name.clone()))?;
ensure_kv_write_authorized(&store, node, subject, &kv.resource_id, &key)?;
let stored = StoredKvEntry {
kv_id: kv.kv_id,
key,
@ -1321,6 +1329,38 @@ fn ensure_resource_exists(store: &Store, resource_id: &str) -> Result<(), NodeEr
}
}
fn ensure_kv_write_authorized(
store: &Store,
node: &LocalNode,
subject: Option<String>,
resource_id: &str,
key: &str,
) -> Result<(), NodeError> {
let Some(subject) = subject else {
return Ok(());
};
if subject == node.node_id || subject == node.agent_id {
return Ok(());
}
let capability = format!("kv.write_key:{key}");
let ops = load_auth_ops_for_resource(store, resource_id)?;
let explanation = geth_auth::explain_auth_ops(
&ops,
PrincipalId::new(subject.clone()),
ResourceId::new(resource_id.to_owned()),
Capability::new(capability.clone()),
);
if explanation.allowed {
Ok(())
} else {
Err(NodeError::Unauthorized(format!(
"{subject} lacks {capability} on {resource_id}: {}",
explanation.reason
)))
}
}
fn create_resource_secret(
store: &Store,
resource_id: &str,