1use crate::api_model::*;
8use crate::operations::*;
9
10use headers::{ContentType, HeaderMap, HeaderMapExt, HeaderValue};
11use serde::{Deserialize, Serialize};
12use simple_hyper_client::blocking::Client as HttpClient;
13use simple_hyper_client::hyper::header::AUTHORIZATION;
14use simple_hyper_client::{Bytes, Method, StatusCode};
15use uuid::Uuid;
16
17use std::fmt;
18use std::io::Read;
19use std::marker::PhantomData;
20use std::sync::atomic::{AtomicU64, Ordering};
21use std::time::{SystemTime, UNIX_EPOCH};
22
23pub const DEFAULT_API_ENDPOINT: &'static str = "https://sdkms.fortanix.com";
24
25pub type Result<T> = ::std::result::Result<T, Error>;
26
27enum Auth {
28 Basic(String),
29 Bearer(String),
30}
31
32impl Auth {
33 fn from_api_key(api_key: &str) -> Self {
34 Auth::Basic(api_key.to_owned())
35 }
36
37 fn from_user_pass<T: fmt::Display>(username: T, password: &str) -> Self {
38 Auth::Basic(base64::encode(format!("{}:{}", username, password)))
39 }
40
41 fn format_header(&self) -> HeaderValue {
42 let value = match *self {
43 Auth::Basic(ref basic) => format!("Basic {}", basic),
44 Auth::Bearer(ref bearer) => format!("Bearer {}", bearer),
45 };
46 let bytes = Bytes::from(value);
47 HeaderValue::from_maybe_shared(bytes).expect("invalid characters in auth header")
49 }
50}
51
52pub struct SdkmsClientBuilder {
54 client: Option<HttpClient>,
55 api_endpoint: Option<String>,
56 auth: Option<Auth>,
57}
58
59impl SdkmsClientBuilder {
60 pub fn with_http_client(mut self, client: HttpClient) -> Self {
62 self.client = Some(client);
63 self
64 }
65 pub fn with_api_endpoint(mut self, api_endpoint: &str) -> Self {
67 self.api_endpoint = Some(api_endpoint.to_owned());
68 self
69 }
70 pub fn with_api_key(mut self, api_key: &str) -> Self {
73 self.auth = Some(Auth::from_api_key(api_key));
74 self
75 }
76 pub fn with_access_token(mut self, access_token: &str) -> Self {
78 self.auth = Some(Auth::Bearer(access_token.to_owned()));
79 self
80 }
81 pub fn build(self) -> Result<SdkmsClient> {
83 let client = match self.client {
84 Some(client) => client,
85 None => {
86 #[cfg(feature = "native-tls")]
87 {
88 use simple_hyper_client::HttpsConnector;
89 use tokio_native_tls::native_tls::TlsConnector;
90
91 let ssl = TlsConnector::new()?;
92 let connector = HttpsConnector::new(ssl.into());
93 HttpClient::with_connector(connector)
94 }
95 #[cfg(not(feature = "native-tls"))]
96 panic!("You should either provide an HTTP Client or compile this crate with native-tls feature");
97 }
98 };
99
100 Ok(SdkmsClient {
101 client,
102 api_endpoint: self
103 .api_endpoint
104 .unwrap_or_else(|| DEFAULT_API_ENDPOINT.to_owned()),
105 auth: self.auth,
106 last_used: AtomicU64::new(0),
107 auth_response: None,
108 })
109 }
110}
111
112pub struct SdkmsClient {
150 auth: Option<Auth>,
151 api_endpoint: String,
152 client: HttpClient,
153 last_used: AtomicU64, auth_response: Option<AuthResponse>,
155}
156
157impl SdkmsClient {
158 pub fn builder() -> SdkmsClientBuilder {
159 SdkmsClientBuilder {
160 client: None,
161 api_endpoint: None,
162 auth: None,
163 }
164 }
165
166 fn authenticate(&self, auth: Option<&Auth>) -> Result<Self> {
167 let auth_response: AuthResponse = json_request_with_auth(
168 &self.client,
169 &self.api_endpoint,
170 Method::POST,
171 "/sys/v1/session/auth",
172 auth,
173 None::<&()>,
174 )?;
175 Ok(SdkmsClient {
176 client: self.client.clone(),
177 api_endpoint: self.api_endpoint.clone(),
178 auth: Some(Auth::Bearer(auth_response.access_token.clone())),
179 last_used: AtomicU64::new(now().0),
180 auth_response: Some(auth_response),
181 })
182 }
183
184 pub fn authenticate_with_api_key(&self, api_key: &str) -> Result<Self> {
185 self.authenticate(Some(Auth::from_api_key(api_key)).as_ref())
186 }
187
188 pub fn authenticate_with_cert(&self, app_id: Option<&Uuid>) -> Result<Self> {
189 self.authenticate(app_id.map(|id| Auth::from_user_pass(id, "")).as_ref())
190 }
191
192 pub fn authenticate_app(&self, app_id: &Uuid, app_secret: &str) -> Result<Self> {
193 self.authenticate(Some(Auth::from_user_pass(app_id, app_secret)).as_ref())
194 }
195
196 pub fn authenticate_user(&self, email: &str, password: &str) -> Result<Self> {
197 self.authenticate(Some(Auth::from_user_pass(email, password)).as_ref())
198 }
199
200 pub fn api_endpoint(&self) -> &str {
201 &self.api_endpoint
202 }
203
204 pub fn auth_response(&self) -> Option<&AuthResponse> {
205 self.auth_response.as_ref()
206 }
207
208 pub fn entity_id(&self) -> Option<Uuid> {
209 self.auth_response().map(|ar| ar.entity_id)
210 }
211
212 pub fn has_session(&self) -> bool {
213 match self.auth {
214 Some(Auth::Bearer(_)) => true,
215 _ => false,
216 }
217 }
218
219 fn json_request<E, D>(&self, method: Method, uri: &str, req: Option<&E>) -> Result<D>
220 where
221 E: Serialize,
222 D: for<'de> Deserialize<'de>,
223 {
224 let Self {
225 ref client,
226 ref api_endpoint,
227 ref auth,
228 ..
229 } = *self;
230 let result = json_request_with_auth(client, api_endpoint, method, uri, auth.as_ref(), req)?;
231 self.last_used.store(now().0, Ordering::Relaxed);
232 Ok(result)
233 }
234}
235
236impl Drop for SdkmsClient {
237 fn drop(&mut self) {
238 let _ = self.terminate();
239 }
240}
241
242impl SdkmsClient {
243 pub fn terminate(&mut self) -> Result<()> {
244 if let Some(Auth::Bearer(_)) = self.auth {
245 self.json_request(Method::POST, "/sys/v1/session/terminate", None::<&()>)?;
246 self.auth = None;
247 }
248 Ok(())
249 }
250
251 pub fn invoke_plugin_nice<I, O>(&self, id: &Uuid, req: &I) -> Result<O>
252 where
253 I: Serialize,
254 O: for<'de> Deserialize<'de>,
255 {
256 let req = serde_json::to_value(req)?;
257 let output = self.execute::<OperationInvokePlugin>(&req, (id,), None)?;
258 Ok(serde_json::from_value(output)?)
259 }
260
261 pub fn execute<O: Operation>(
262 &self,
263 body: &O::Body,
264 p: <O::PathParams as TupleRef>::Ref,
265 q: Option<&O::QueryParams>,
266 ) -> Result<O::Output> {
267 self.json_request(O::method(), &O::path(p, q), O::to_body(body).as_ref())
268 }
269
270 pub fn request_approval<O: Operation>(
271 &self,
272 body: &O::Body,
273 p: <O::PathParams as TupleRef>::Ref,
274 q: Option<&O::QueryParams>,
275 description: Option<String>,
276 ) -> Result<PendingApproval<O>> {
277 let request = self.create_approval_request(&ApprovalRequestRequest {
278 operation: Some(O::path(p, q)),
279 method: Some(format!("{}", O::method())),
280 body: O::to_body(body),
281 description,
282 })?;
283 Ok(PendingApproval::from_request_id(request.request_id))
284 }
285
286 pub fn expires_in(&self) -> Option<u64> {
287 let expires_at = self.last_used.load(Ordering::Relaxed)
288 + self.auth_response().map_or(0, |ar| ar.expires_in as u64);
289 expires_at.checked_sub(now().0)
290 }
291}
292
293pub struct PendingApproval<O: Operation>(Uuid, PhantomData<O>);
294
295impl<O: Operation> fmt::Debug for PendingApproval<O> {
296 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
297 fmt::Debug::fmt(&self.0, formatter)
298 }
299}
300
301impl<O: Operation> PendingApproval<O> {
302 pub fn from_request_id(request_id: Uuid) -> Self {
303 PendingApproval(request_id, PhantomData)
304 }
305
306 pub fn request_id(&self) -> Uuid {
307 self.0
308 }
309
310 pub fn get(&self, sdkms: &SdkmsClient) -> Result<ApprovalRequest> {
311 sdkms.get_approval_request(&self.0)
312 }
313
314 pub fn status(&self, sdkms: &SdkmsClient) -> Result<ApprovalStatus> {
315 Ok(self.get(sdkms)?.status)
316 }
317
318 pub fn result(&self, sdkms: &SdkmsClient) -> Result<Result<O::Output>> {
319 let result = sdkms.get_approval_request_result(&self.0)?;
320 Ok(if result.is_ok() {
321 serde_json::from_value::<O::Output>(result.body).map_err(Error::EncoderError)
322 } else {
323 let msg: String = serde_json::from_value(result.body).map_err(Error::EncoderError)?;
324 Err(Error::from_status(
325 StatusCode::from_u16(result.status).unwrap(),
326 msg,
327 ))
328 })
329 }
330}
331
332impl<O: Operation> Clone for PendingApproval<O> {
333 fn clone(&self) -> Self {
334 PendingApproval(self.0, PhantomData)
335 }
336}
337
338fn now() -> Time {
339 Time(
340 SystemTime::now()
341 .duration_since(UNIX_EPOCH)
342 .expect("Invalid system time")
343 .as_secs(),
344 )
345}
346
347fn json_decode_reader<R: Read, T: for<'de> Deserialize<'de>>(rdr: &mut R) -> serde_json::Result<T> {
348 match serde_json::from_reader(rdr) {
349 Err(ref e) if e.is_eof() && e.line() == 1 && e.column() == 0 => {
351 serde_json::from_value(serde_json::Value::Null)
352 }
353 v => v,
354 }
355}
356
357fn json_request_with_auth<E, D>(
358 client: &HttpClient,
359 api_endpoint: &str,
360 method: Method,
361 path: &str,
362 auth: Option<&Auth>,
363 body: Option<&E>,
364) -> Result<D>
365where
366 E: Serialize,
367 D: for<'de> Deserialize<'de>,
368{
369 let url = format!("{}{}", api_endpoint, path);
370 let mut req = client.request(method.clone(), &url)?;
371 let mut headers = HeaderMap::new();
372 if let Some(auth) = auth {
373 headers.insert(AUTHORIZATION, auth.format_header());
374 }
375 if let Some(request_body) = body {
376 headers.typed_insert(ContentType::json());
377 let body = serde_json::to_string(request_body).map_err(Error::EncoderError)?;
378 req = req.body(body);
379 }
380 req = req.headers(headers);
381 match req.send() {
382 Err(e) => {
383 info!("Error {} {}", method, url);
384 Err(Error::NetworkError(e))
385 }
386 Ok(ref mut res) if res.status().is_success() => {
387 info!("{} {} {}", res.status().as_u16(), method, url);
388 json_decode_reader(res.body_mut()).map_err(|err| Error::EncoderError(err))
389 }
390 Ok(ref mut res) => {
391 info!("{} {} {}", res.status().as_u16(), method, url);
392 let mut buffer = String::new();
393 res.body_mut()
394 .read_to_string(&mut buffer)
395 .map_err(|err| Error::IoError(err))?;
396 Err(Error::from_status(res.status(), buffer))
397 }
398 }
399}
400
401#[cfg(test)]
402mod tests {
403 use super::*;
404
405 fn assert_send<T: Send>() {}
406 fn assert_sync<T: Sync>() {}
407
408 #[test]
409 fn client_is_send_and_sync() {
410 assert_send::<SdkmsClient>();
411 assert_sync::<SdkmsClient>();
412
413 assert_send::<SdkmsClientBuilder>();
414 assert_sync::<SdkmsClientBuilder>();
415 }
416}