(&self)
| 71 | |
| 72 | impl Revoke { |
| 73 | async fn execute(&self) -> CliResult<()> { |
| 74 | let mut store = IdentityStore::open_default().map_err(|e| { |
| 75 | CliError::Internal(anyhow::anyhow!("Failed to open identity store: {e}")) |
| 76 | })?; |
| 77 | |
| 78 | let agent = store |
| 79 | .load_by_name(&self.name) |
| 80 | .map_err(|_| CliError::IdentityNotFound(self.name.clone()))?; |
| 81 | |
| 82 | let delegations = load_for_delegate(&store, &agent)?; |
| 83 | if delegations.is_empty() { |
| 84 | return Err(CliError::DelegationError { |
| 85 | message: format!("'{}' has no delegation to revoke", self.name), |
| 86 | }); |
| 87 | } |
| 88 | |
| 89 | // Revoke every live certificate, not just the newest. Leaving an older |
| 90 | // one standing would make revocation look done while the agent kept |
| 91 | // working under a certificate nobody was looking at. |
| 92 | let mut revoked = Vec::new(); |
| 93 | for resolved in delegations.iter().filter(|d| d.is_usable()) { |
| 94 | let delegator = store |
| 95 | .load_by_name(&resolved.delegation.delegator_name) |
| 96 | .map_err(|_| CliError::DelegationError { |
| 97 | message: format!( |
| 98 | "The delegating identity '{}' is not on this machine, so a signed \ |
| 99 | revocation cannot be produced here.", |
| 100 | resolved.delegation.delegator_name |
| 101 | ), |
| 102 | })?; |
| 103 | let keypair = store.load_keypair(&delegator.id, None).map_err(|e| { |
| 104 | CliError::Internal(anyhow::anyhow!("Failed to load signing key: {e}")) |
| 105 | })?; |
| 106 | |
| 107 | let revocation = cert::mint_revocation( |
| 108 | &delegator, |
| 109 | &keypair, |
| 110 | &resolved.delegation.id, |
| 111 | self.reason.as_deref(), |
| 112 | ); |
| 113 | let document = serde_json::to_string_pretty(&revocation).map_err(|e| { |
| 114 | CliError::Internal(anyhow::anyhow!("Failed to encode revocation: {e}")) |
| 115 | })?; |
| 116 | |
| 117 | // Local first: this machine stops using the delegation immediately, |
| 118 | // even if the network call below fails. |
| 119 | store |
| 120 | .save_revocation(&resolved.id(), &document) |
| 121 | .map_err(|e| { |
| 122 | CliError::Internal(anyhow::anyhow!("Failed to record revocation: {e}")) |
| 123 | })?; |
| 124 | |
| 125 | revoked.push((resolved.delegation.id.to_urn(), delegator, revocation)); |
| 126 | } |
| 127 | |
| 128 | if revoked.is_empty() { |
| 129 | print_hint(&format!( |
| 130 | "'{}' has no active delegation — nothing to revoke.", |
no test coverage detected