Add resource-scoped bearer access metadata
This commit is contained in:
parent
a05112e6a2
commit
d072843cac
10 changed files with 344 additions and 11 deletions
|
|
@ -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(),
|
||||
|
|
|
|||
Loading…
Reference in a new issue