1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
7#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
8pub enum AuthCredentialType {
9 ApiKey,
11 Http,
13 #[serde(rename = "OAUTH2")]
15 OAuth2,
16 OpenIdConnect,
18 ServiceAccount,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct HttpCredentials {
25 pub username: Option<String>,
27 pub password: Option<String>,
29 pub token: Option<String>,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct HttpAuth {
36 pub scheme: String,
38 pub credentials: HttpCredentials,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct OAuth2Auth {
45 pub client_id: Option<String>,
47 pub client_secret: Option<String>,
49 pub auth_uri: Option<String>,
51 pub token_uri: Option<String>,
53 pub redirect_uri: Option<String>,
55 pub auth_code: Option<String>,
57 pub access_token: Option<String>,
59 pub refresh_token: Option<String>,
61 pub expires_at: Option<u64>,
63 pub scopes: Option<Vec<String>>,
65 pub auth_response_uri: Option<String>,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct ServiceAccountCredential {
72 pub service_account_key_file: Option<String>,
74 pub service_account_key: Option<serde_json::Value>,
76 pub scopes: Option<Vec<String>>,
78 pub use_default_credential: Option<bool>,
80 pub project_id: Option<String>,
82 pub universe_domain: Option<String>,
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize)]
88pub struct AuthCredential {
89 pub auth_type: AuthCredentialType,
91 pub resource_ref: Option<String>,
93 pub api_key: Option<String>,
95 pub http: Option<HttpAuth>,
97 pub oauth2: Option<OAuth2Auth>,
99 pub service_account: Option<ServiceAccountCredential>,
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn credential_type_screaming_snake_case() {
109 assert_eq!(
110 serde_json::to_string(&AuthCredentialType::ApiKey).unwrap(),
111 "\"API_KEY\""
112 );
113 assert_eq!(
114 serde_json::to_string(&AuthCredentialType::Http).unwrap(),
115 "\"HTTP\""
116 );
117 assert_eq!(
118 serde_json::to_string(&AuthCredentialType::OAuth2).unwrap(),
119 "\"OAUTH2\""
120 );
121 assert_eq!(
122 serde_json::to_string(&AuthCredentialType::OpenIdConnect).unwrap(),
123 "\"OPEN_ID_CONNECT\""
124 );
125 assert_eq!(
126 serde_json::to_string(&AuthCredentialType::ServiceAccount).unwrap(),
127 "\"SERVICE_ACCOUNT\""
128 );
129 }
130
131 #[test]
132 fn credential_type_roundtrip() {
133 let types = [
134 AuthCredentialType::ApiKey,
135 AuthCredentialType::Http,
136 AuthCredentialType::OAuth2,
137 AuthCredentialType::OpenIdConnect,
138 AuthCredentialType::ServiceAccount,
139 ];
140 for t in &types {
141 let json = serde_json::to_string(t).unwrap();
142 let parsed: AuthCredentialType = serde_json::from_str(&json).unwrap();
143 assert_eq!(&parsed, t);
144 }
145 }
146
147 #[test]
148 fn api_key_credential_roundtrip() {
149 let cred = AuthCredential {
150 auth_type: AuthCredentialType::ApiKey,
151 resource_ref: Some("my-resource".into()),
152 api_key: Some("sk-secret-123".into()),
153 http: None,
154 oauth2: None,
155 service_account: None,
156 };
157
158 let json = serde_json::to_string_pretty(&cred).unwrap();
159 let parsed: AuthCredential = serde_json::from_str(&json).unwrap();
160
161 assert_eq!(parsed.auth_type, AuthCredentialType::ApiKey);
162 assert_eq!(parsed.api_key.as_deref(), Some("sk-secret-123"));
163 assert_eq!(parsed.resource_ref.as_deref(), Some("my-resource"));
164 assert!(parsed.http.is_none());
165 assert!(parsed.oauth2.is_none());
166 assert!(parsed.service_account.is_none());
167 }
168
169 #[test]
170 fn http_credential_roundtrip() {
171 let cred = AuthCredential {
172 auth_type: AuthCredentialType::Http,
173 resource_ref: None,
174 api_key: None,
175 http: Some(HttpAuth {
176 scheme: "bearer".into(),
177 credentials: HttpCredentials {
178 username: None,
179 password: None,
180 token: Some("eyJhbGciOi...".into()),
181 },
182 }),
183 oauth2: None,
184 service_account: None,
185 };
186
187 let json = serde_json::to_string(&cred).unwrap();
188 let parsed: AuthCredential = serde_json::from_str(&json).unwrap();
189
190 assert_eq!(parsed.auth_type, AuthCredentialType::Http);
191 let http = parsed.http.unwrap();
192 assert_eq!(http.scheme, "bearer");
193 assert_eq!(http.credentials.token.as_deref(), Some("eyJhbGciOi..."));
194 }
195
196 #[test]
197 fn oauth2_credential_roundtrip() {
198 let cred = AuthCredential {
199 auth_type: AuthCredentialType::OAuth2,
200 resource_ref: None,
201 api_key: None,
202 http: None,
203 oauth2: Some(OAuth2Auth {
204 client_id: Some("client-123".into()),
205 client_secret: Some("secret-456".into()),
206 auth_uri: Some("https://accounts.google.com/o/oauth2/auth".into()),
207 token_uri: Some("https://oauth2.googleapis.com/token".into()),
208 redirect_uri: Some("http://localhost:8080/callback".into()),
209 auth_code: None,
210 access_token: Some("ya29.access".into()),
211 refresh_token: Some("1//refresh".into()),
212 expires_at: Some(1700000000),
213 scopes: Some(vec!["openid".into(), "email".into()]),
214 auth_response_uri: None,
215 }),
216 service_account: None,
217 };
218
219 let json = serde_json::to_string(&cred).unwrap();
220 let parsed: AuthCredential = serde_json::from_str(&json).unwrap();
221
222 assert_eq!(parsed.auth_type, AuthCredentialType::OAuth2);
223 let oauth2 = parsed.oauth2.unwrap();
224 assert_eq!(oauth2.client_id.as_deref(), Some("client-123"));
225 assert_eq!(oauth2.scopes.as_ref().unwrap().len(), 2);
226 assert_eq!(oauth2.expires_at, Some(1700000000));
227 }
228
229 #[test]
230 fn service_account_credential_roundtrip() {
231 let cred = AuthCredential {
232 auth_type: AuthCredentialType::ServiceAccount,
233 resource_ref: None,
234 api_key: None,
235 http: None,
236 oauth2: None,
237 service_account: Some(ServiceAccountCredential {
238 service_account_key_file: Some("/path/to/key.json".into()),
239 service_account_key: None,
240 scopes: Some(vec!["https://www.googleapis.com/auth/cloud-platform".into()]),
241 use_default_credential: Some(true),
242 project_id: Some("my-project".into()),
243 universe_domain: Some("googleapis.com".into()),
244 }),
245 };
246
247 let json = serde_json::to_string(&cred).unwrap();
248 let parsed: AuthCredential = serde_json::from_str(&json).unwrap();
249
250 assert_eq!(parsed.auth_type, AuthCredentialType::ServiceAccount);
251 let sa = parsed.service_account.unwrap();
252 assert_eq!(
253 sa.service_account_key_file.as_deref(),
254 Some("/path/to/key.json")
255 );
256 assert_eq!(sa.use_default_credential, Some(true));
257 assert_eq!(sa.project_id.as_deref(), Some("my-project"));
258 }
259}