vault_mgmt_lib/
step_down.rs1use http_body_util::{BodyExt, Empty};
2use hyper::body::Bytes;
3use secrecy::Secret;
4
5use crate::{step_down_request, BytesBody, HttpRequest};
6
7#[async_trait::async_trait]
9pub trait StepDown {
10 async fn step_down(&mut self, token: Secret<String>) -> anyhow::Result<()>;
12}
13
14#[async_trait::async_trait]
15impl<T> StepDown for T
16where
17 T: HttpRequest<BytesBody> + Send + Sync + 'static,
18{
19 async fn step_down(&mut self, token: Secret<String>) -> anyhow::Result<()> {
20 let http_req = step_down_request(token, Empty::<Bytes>::new().boxed())?;
21
22 let (parts, body) = self.send_request(http_req).await?.into_parts();
23
24 let body = String::from_utf8(body.to_vec())?;
25
26 if parts.status != hyper::StatusCode::NO_CONTENT {
27 return Err(anyhow::anyhow!("stepping-down: {}", body));
28 }
29
30 Ok(())
31 }
32}
33
34#[cfg(test)]
35mod tests {
36 use std::str::FromStr;
37
38 use http::{Method, StatusCode};
39 use secrecy::Secret;
40 use wiremock::{
41 matchers::{header, method, path},
42 Mock, MockServer, ResponseTemplate,
43 };
44
45 use crate::{HttpForwarderService, StepDown};
46
47 #[tokio::test]
48 async fn stepdown_calls_api() {
49 let mock_server = MockServer::start().await;
50
51 Mock::given(method(Method::PUT))
52 .and(path("/v1/sys/step-down"))
53 .and(header("X-Vault-Request", "true"))
54 .and(header("X-Vault-Token", "abc"))
55 .respond_with(ResponseTemplate::new(StatusCode::NO_CONTENT))
56 .expect(1)
57 .mount(&mock_server)
58 .await;
59
60 let mut client = HttpForwarderService::http(
61 tokio::net::TcpStream::connect(mock_server.uri().strip_prefix("http://").unwrap())
62 .await
63 .unwrap(),
64 )
65 .await
66 .unwrap();
67
68 let outcome = client.step_down(Secret::from_str("abc").unwrap()).await;
69
70 assert!(outcome.is_ok());
71 }
72}