Wire auth explain to local auth ops
This commit is contained in:
parent
187b99eb8d
commit
f54920bb65
9 changed files with 299 additions and 19 deletions
|
|
@ -1,6 +1,6 @@
|
|||
pub mod service;
|
||||
|
||||
use geth_auth::AuthExplanation;
|
||||
use geth_auth::{AuthExplanation, AuthOp, AuthOpKind};
|
||||
use geth_cas::{LocalCas, hash_path};
|
||||
use geth_config::{GethConfig, GethPaths, RelayMode};
|
||||
use geth_control::{
|
||||
|
|
@ -16,10 +16,12 @@ use geth_ssh_identity::{
|
|||
certificate_id, revocation_id, ssh_public_key_fingerprint,
|
||||
};
|
||||
use geth_store::{
|
||||
Store, StoredResource, StoredSshCertRequest, StoredSshCertificate, StoredSshRevocation,
|
||||
Store, StoredAuthOp, StoredResource, StoredSshCertRequest, StoredSshCertificate,
|
||||
StoredSshRevocation,
|
||||
};
|
||||
use geth_types::{
|
||||
NodeId, ResourceId, ResourceKind, ResourceName, SshCertId, SshCertRequestId, UnixMillis,
|
||||
AuthOpId, Capability, NodeId, PrincipalId, ResourceId, ResourceKind, ResourceName, SshCertId,
|
||||
SshCertRequestId, UnixMillis,
|
||||
};
|
||||
use std::path::Path;
|
||||
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
||||
|
|
@ -249,16 +251,67 @@ pub fn handle_request(
|
|||
resource,
|
||||
capability,
|
||||
} => {
|
||||
if store.get_peer_card(&subject)?.is_some() {
|
||||
Ok(ControlResponse::AuthExplain(
|
||||
AuthExplanation::discovered_candidate(subject, resource, capability),
|
||||
))
|
||||
let ops = load_auth_ops_for_resource(&store, &resource)?;
|
||||
let discovered = store.get_peer_card(&subject)?.is_some();
|
||||
if ops.is_empty() {
|
||||
if discovered {
|
||||
Ok(ControlResponse::AuthExplain(
|
||||
AuthExplanation::discovered_candidate(subject, resource, capability),
|
||||
))
|
||||
} else {
|
||||
Ok(ControlResponse::AuthExplain(AuthExplanation::stub(
|
||||
subject, resource, capability,
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
Ok(ControlResponse::AuthExplain(AuthExplanation::stub(
|
||||
subject, resource, capability,
|
||||
)))
|
||||
let mut explanation = geth_auth::explain_auth_ops(
|
||||
&ops,
|
||||
PrincipalId::new(subject.clone()),
|
||||
ResourceId::new(resource.clone()),
|
||||
Capability::new(capability.clone()),
|
||||
);
|
||||
if discovered && !explanation.allowed {
|
||||
explanation.reason = format!(
|
||||
"subject is a discovered peer candidate only; discovery does not grant trust or authorization; {}",
|
||||
explanation.reason
|
||||
);
|
||||
}
|
||||
Ok(ControlResponse::AuthExplain(explanation))
|
||||
}
|
||||
}
|
||||
ControlRequest::AuthGrant {
|
||||
subject,
|
||||
resource,
|
||||
capability,
|
||||
grant_id,
|
||||
} => {
|
||||
let created_at = UnixMillis(geth_store::now_ms());
|
||||
let grant_id =
|
||||
grant_id.unwrap_or_else(|| generated_grant_id(&subject, &resource, &capability));
|
||||
let op = AuthOp {
|
||||
id: generated_auth_op_id("grant-create", &resource, &grant_id, created_at),
|
||||
resource: ResourceId::new(resource),
|
||||
created_at,
|
||||
kind: AuthOpKind::GrantCreate {
|
||||
grant_id,
|
||||
principal: PrincipalId::new(subject),
|
||||
capabilities: vec![Capability::new(capability)],
|
||||
},
|
||||
};
|
||||
store_auth_op(&store, &op)?;
|
||||
Ok(ControlResponse::AuthOpRecorded { op })
|
||||
}
|
||||
ControlRequest::AuthRevoke { resource, grant_id } => {
|
||||
let created_at = UnixMillis(geth_store::now_ms());
|
||||
let op = AuthOp {
|
||||
id: generated_auth_op_id("grant-revoke", &resource, &grant_id, created_at),
|
||||
resource: ResourceId::new(resource),
|
||||
created_at,
|
||||
kind: AuthOpKind::GrantRevoke { grant_id },
|
||||
};
|
||||
store_auth_op(&store, &op)?;
|
||||
Ok(ControlResponse::AuthOpRecorded { op })
|
||||
}
|
||||
ControlRequest::SshCertRequest {
|
||||
public_key_path,
|
||||
cert_kind,
|
||||
|
|
@ -447,6 +500,45 @@ fn stored_resource_to_descriptor(stored: StoredResource) -> Result<ResourceDescr
|
|||
))
|
||||
}
|
||||
|
||||
fn store_auth_op(store: &Store, op: &AuthOp) -> Result<(), NodeError> {
|
||||
store.insert_auth_op(&StoredAuthOp {
|
||||
op_id: op.id.to_string(),
|
||||
resource_id: op.resource.to_string(),
|
||||
op_json: serde_json::to_string(op)?,
|
||||
created_at_ms: op.created_at.0,
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_auth_ops_for_resource(store: &Store, resource: &str) -> Result<Vec<AuthOp>, NodeError> {
|
||||
store
|
||||
.list_auth_ops_for_resource(resource)?
|
||||
.into_iter()
|
||||
.map(|stored| serde_json::from_str(&stored.op_json).map_err(NodeError::from))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn generated_grant_id(subject: &str, resource: &str, capability: &str) -> String {
|
||||
format!(
|
||||
"grant:{}",
|
||||
geth_crypto::blake3_hex(format!("{subject}\0{resource}\0{capability}").as_bytes())
|
||||
)
|
||||
}
|
||||
|
||||
fn generated_auth_op_id(
|
||||
kind: &str,
|
||||
resource: &str,
|
||||
stable_id: &str,
|
||||
created_at: UnixMillis,
|
||||
) -> AuthOpId {
|
||||
AuthOpId::new(format!(
|
||||
"auth-op:{}",
|
||||
geth_crypto::blake3_hex(
|
||||
format!("{}\0{kind}\0{resource}\0{stable_id}", created_at.0).as_bytes()
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
fn stable_node_id(agent_id: &str) -> String {
|
||||
format!("node:{agent_id}")
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue