oz_keystore/hashicorp/
cloud.rs1use reqwest::{Client, Error};
2use serde::Deserialize;
3
4pub struct HashicorpCloudClient {
5 client: Client,
6 client_id: String,
7 client_secret: String,
8 org_id: String,
9 project_id: String,
10 app_name: String,
11 api_url: String,
12 auth_url: String,
13}
14
15#[derive(Debug, Deserialize)]
16struct TokenResponse {
17 access_token: String,
18}
19
20#[derive(Debug, Deserialize)]
21pub struct StaticVersion {
22 pub value: String,
23}
24
25#[derive(Debug, Deserialize)]
26pub struct HashicorpSecret {
27 pub static_version: StaticVersion,
28}
29
30#[derive(Debug, Deserialize)]
31pub struct HashicorpResponse {
32 pub secret: HashicorpSecret,
33}
34
35impl HashicorpCloudClient {
36 pub fn new(
37 client_id: String,
38 client_secret: String,
39 org_id: String,
40 project_id: String,
41 app_name: String,
42 ) -> Self {
43 Self {
44 client: Client::new(),
45 client_id,
46 client_secret,
47 org_id,
48 project_id,
49 app_name,
50 api_url: "https://api.cloud.hashicorp.com".to_string(),
51 auth_url: "https://auth.idp.hashicorp.com".to_string(),
52 }
53 }
54
55 pub fn with_client(&self, client: Client) -> Self {
56 Self {
57 client,
58 client_id: self.client_id.clone(),
59 client_secret: self.client_secret.clone(),
60 org_id: self.org_id.clone(),
61 project_id: self.project_id.clone(),
62 app_name: self.app_name.clone(),
63 api_url: self.api_url.clone(),
64 auth_url: self.auth_url.clone(),
65 }
66 }
67
68 pub fn with_auth_base_url(&self, auth_url: impl Into<String>) -> Self {
69 Self {
70 auth_url: auth_url.into(),
71 api_url: self.api_url.clone(),
72 client: self.client.clone(),
73 client_id: self.client_id.clone(),
74 client_secret: self.client_secret.clone(),
75 org_id: self.org_id.clone(),
76 project_id: self.project_id.clone(),
77 app_name: self.app_name.clone(),
78 }
79 }
80
81 pub fn with_api_base_url(&self, api_url: impl Into<String>) -> Self {
82 Self {
83 api_url: api_url.into(),
84 auth_url: self.auth_url.clone(),
85 client: self.client.clone(),
86 client_id: self.client_id.clone(),
87 client_secret: self.client_secret.clone(),
88 org_id: self.org_id.clone(),
89 project_id: self.project_id.clone(),
90 app_name: self.app_name.clone(),
91 }
92 }
93
94 async fn get_token(&self) -> Result<String, Error> {
95 let token_response = self
96 .client
97 .post(format!("{}/oauth2/token", self.auth_url))
98 .form(&[
99 ("client_id", &self.client_id),
100 ("client_secret", &self.client_secret),
101 ("grant_type", &String::from("client_credentials")),
102 ("audience", &String::from("https://api.hashicorp.cloud")),
103 ])
104 .send()
105 .await?
106 .json::<TokenResponse>()
107 .await?;
108
109 Ok(token_response.access_token)
110 }
111
112 pub async fn get_secret(&self, secret_name: &str) -> Result<HashicorpResponse, Error> {
113 let token = self.get_token().await?;
114
115 let url = format!(
116 "{}/secrets/2023-11-28/organizations/{}/projects/{}/apps/{}/secrets/{}:open",
117 self.api_url, self.org_id, self.project_id, self.app_name, secret_name
118 );
119
120 self.client
121 .get(url)
122 .header("Authorization", format!("Bearer {}", token))
123 .send()
124 .await?
125 .json()
126 .await
127 }
128}