Add resource-scoped bearer access metadata

This commit is contained in:
Eric Wendland 2026-05-17 02:58:58 +02:00
commit d072843cac
10 changed files with 344 additions and 11 deletions

View file

@ -14,7 +14,7 @@ use geth_iroh::{EndpointStatus, GethIrohConfig, GethIrohEndpoint, GethRelayMode}
use geth_keychain::{KeychainOp, KeychainOpKind};
use geth_kv::{KvEntry, KvResource};
use geth_resource::ResourceDescriptor;
use geth_secrets::ResourceMasterSecret;
use geth_secrets::{BearerAccess, ResourceMasterSecret};
use geth_ssh_identity::{
SshCertApproval, SshCertKind, SshCertRequest, SshCertRequestStatus, SshCertificateRecord,
SshRevocationEntry, SshRevocationKind, build_ssh_cert_sign_command, cert_request_id,
@ -71,6 +71,8 @@ pub enum NodeError {
DocumentNotFound(String),
#[error("resource not found: {0}")]
ResourceNotFound(String),
#[error("secrets error: {0}")]
Secrets(#[from] geth_secrets::SecretsError),
#[error("invalid ssh certificate kind: {0}")]
InvalidSshCertKind(String),
#[error("invalid ssh certificate request status: {0}")]
@ -360,6 +362,73 @@ pub fn handle_request(
let secret = create_resource_secret(&store, &resource, next_epoch)?;
Ok(ControlResponse::SecretCreated { secret })
}
ControlRequest::SecretBearerCreate {
resource,
capabilities,
expires_at_ms,
} => {
ensure_resource_exists(&store, &resource)?;
let capabilities = capabilities
.into_iter()
.map(Capability::new)
.collect::<Vec<_>>();
geth_secrets::validate_bearer_capabilities(&capabilities)?;
let created_at = UnixMillis(geth_store::now_ms());
let secret = geth_types::SecretId::new(format!(
"bearer:{}",
geth_crypto::blake3_hex(
format!(
"{resource}\0{}\0{}",
capabilities
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(","),
created_at.0
)
.as_bytes()
)
));
let access = BearerAccess::resource_scoped(
secret.clone(),
ResourceId::new(resource.clone()),
capabilities.clone(),
);
let op = AuthOp {
id: generated_auth_op_id("bearer-create", &resource, secret.as_str(), created_at),
resource: ResourceId::new(resource),
created_at,
kind: AuthOpKind::BearerAccessCreate {
secret,
capabilities,
expires_at: expires_at_ms.map(UnixMillis),
},
};
store_auth_op(&store, &op)?;
Ok(ControlResponse::SecretBearerCreated {
access: BearerAccess {
expires_at: expires_at_ms.map(UnixMillis),
..access
},
})
}
ControlRequest::SecretBearerList => Ok(ControlResponse::SecretBearerList {
access: load_bearer_access(&store)?,
}),
ControlRequest::SecretBearerRevoke { resource, secret } => {
ensure_resource_exists(&store, &resource)?;
let created_at = UnixMillis(geth_store::now_ms());
let op = AuthOp {
id: generated_auth_op_id("bearer-revoke", &resource, &secret, created_at),
resource: ResourceId::new(resource.clone()),
created_at,
kind: AuthOpKind::BearerAccessRevoke {
secret: secret.clone().into(),
},
};
store_auth_op(&store, &op)?;
Ok(ControlResponse::SecretBearerRevoked { resource, secret })
}
ControlRequest::AuthExplain {
subject,
resource,
@ -831,6 +900,26 @@ fn resource_secret_from_stored(stored: StoredResourceSecret) -> ResourceMasterSe
}
}
fn load_bearer_access(store: &Store) -> Result<Vec<BearerAccess>, NodeError> {
let ops = store
.list_auth_ops()?
.into_iter()
.map(|stored| serde_json::from_str(&stored.op_json).map_err(NodeError::from))
.collect::<Result<Vec<AuthOp>, NodeError>>()?;
let view = geth_auth::reduce_auth_ops(&ops);
Ok(view
.bearer_access
.into_values()
.map(|record| BearerAccess {
secret: record.secret,
resource: record.resource,
capabilities: record.capabilities,
expires_at: record.expires_at,
may_delegate: false,
})
.collect())
}
fn store_auth_op(store: &Store, op: &AuthOp) -> Result<(), NodeError> {
store.insert_auth_op(&StoredAuthOp {
op_id: op.id.to_string(),