Implements the sentryagent-idp Rust SDK crate (sdk-rust/) with: - TokenManager with Arc<Mutex<TokenCache>> for thread-safe token caching - AgentIdPClient with full method coverage: agents, oauth2, credentials, audit, marketplace, delegation - Error hierarchy via thiserror (AgentIdPError enum) - All model types with serde derive - 429 RateLimited handling with Retry-After parsing; zero unwrap() calls - Unit tests (mockito), doc tests, and integration tests (#[ignore]) - quickstart example, full README, cargo doc clean Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
99 lines
2.9 KiB
Rust
99 lines
2.9 KiB
Rust
//! Credential management methods for `AgentIdPClient`.
|
|
//!
|
|
//! Covers `POST /agents/{id}/credentials` (generate),
|
|
//! `POST /agents/{id}/credentials/rotate`, and
|
|
//! `DELETE /agents/{id}/credentials/{cred_id}`.
|
|
|
|
use crate::agents::parse_response;
|
|
use crate::client::AgentIdPClient;
|
|
use crate::error::AgentIdPError;
|
|
use crate::models::Credentials;
|
|
|
|
impl AgentIdPClient {
|
|
/// Generates a new set of credentials (client ID + secret) for an agent.
|
|
///
|
|
/// `POST /agents/{id}/credentials` → `201 Credentials`
|
|
///
|
|
/// The `client_secret` field in the response is the **only time** the
|
|
/// plaintext secret is returned — store it securely.
|
|
pub async fn generate_credentials(
|
|
&self,
|
|
agent_id: &str,
|
|
) -> Result<Credentials, AgentIdPError> {
|
|
let auth = self.get_auth_header().await?;
|
|
let url = format!("{}/agents/{}/credentials", self.base_url, agent_id);
|
|
|
|
let resp = self
|
|
.http
|
|
.post(&url)
|
|
.header("Authorization", auth)
|
|
.header("Content-Length", "0")
|
|
.send()
|
|
.await?;
|
|
|
|
parse_response(resp).await
|
|
}
|
|
|
|
/// Rotates the credentials for an agent, invalidating the previous secret.
|
|
///
|
|
/// `POST /agents/{id}/credentials/rotate` → `200 Credentials`
|
|
///
|
|
/// The new `client_secret` is returned in the response and will not be
|
|
/// retrievable again.
|
|
pub async fn rotate_credentials(
|
|
&self,
|
|
agent_id: &str,
|
|
) -> Result<Credentials, AgentIdPError> {
|
|
let auth = self.get_auth_header().await?;
|
|
let url = format!(
|
|
"{}/agents/{}/credentials/rotate",
|
|
self.base_url, agent_id
|
|
);
|
|
|
|
let resp = self
|
|
.http
|
|
.post(&url)
|
|
.header("Authorization", auth)
|
|
.header("Content-Length", "0")
|
|
.send()
|
|
.await?;
|
|
|
|
parse_response(resp).await
|
|
}
|
|
|
|
/// Revokes a specific credential set for an agent.
|
|
///
|
|
/// `DELETE /agents/{id}/credentials/{cred_id}` → `204 No Content`
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// Returns [`crate::error::AgentIdPError::NotFound`] when the agent or
|
|
/// credential ID does not exist.
|
|
pub async fn revoke_credentials(
|
|
&self,
|
|
agent_id: &str,
|
|
cred_id: &str,
|
|
) -> Result<(), AgentIdPError> {
|
|
let auth = self.get_auth_header().await?;
|
|
let url = format!(
|
|
"{}/agents/{}/credentials/{}",
|
|
self.base_url, agent_id, cred_id
|
|
);
|
|
|
|
let resp = self
|
|
.http
|
|
.delete(&url)
|
|
.header("Authorization", auth)
|
|
.send()
|
|
.await?;
|
|
|
|
if resp.status().as_u16() == 204 {
|
|
return Ok(());
|
|
}
|
|
|
|
// Delegate error handling to parse_response; the Ok branch is unreachable.
|
|
let _: Credentials = parse_response(resp).await?;
|
|
Ok(())
|
|
}
|
|
}
|