1use reqwest::StatusCode;
2use std::collections::HashMap;
3use std::error::Error;
4use models::{ErrorResponse, LoginRequest, LoginResponse, MeResponse};
5use traits::Authenticator;
6
7pub mod hwid;
8pub mod models;
9pub mod traits;
10
11pub const NOT_LINKED: &str = "not_linked";
12
13pub struct BasicAuthenticator {
14 pub base_url: String,
15 pub client: reqwest::Client,
16}
17
18impl BasicAuthenticator {
19 pub fn new(base_url: impl Into<String>) -> Self {
20 BasicAuthenticator {
21 base_url: base_url.into(),
22 client: reqwest::Client::new(),
23 }
24 }
25}
26
27#[async_trait::async_trait]
28impl Authenticator for BasicAuthenticator {
29 async fn login(
30 &self,
31 creds: &LoginRequest,
32 ) -> Result<LoginResponse, Box<dyn Error + Send + Sync>> {
33 let url = format!("{}/license-api-connector/login", self.base_url.trim_end_matches('/'));
34
35 let resp = self.client.post(&url).form(&creds).send().await?;
36
37 match resp.status() {
38 StatusCode::OK | StatusCode::CREATED => {
39 let lr = resp.json::<LoginResponse>().await?;
40 Ok(lr)
41 }
42 StatusCode::UNAUTHORIZED | StatusCode::BAD_REQUEST => {
43 let err = resp.json::<ErrorResponse>().await?;
44 Err(format!("{}", err.detail).into())
45 }
46 other => Err(format!("unexpected response status: {}", other).into()),
47 }
48 }
49
50 async fn me(&self, access_token: &str) -> Result<MeResponse, Box<dyn Error + Send + Sync>> {
51 let url = format!("{}/users/me", self.base_url.trim_end_matches('/'));
52
53 let resp = self
54 .client
55 .get(&url)
56 .bearer_auth(access_token)
57 .send()
58 .await?;
59
60 match resp.status() {
61 StatusCode::OK => {
62 let mr = resp.json::<MeResponse>().await?;
63 Ok(mr)
64 }
65 StatusCode::UNAUTHORIZED | StatusCode::BAD_REQUEST => {
66 let err = resp.json::<ErrorResponse>().await?;
67 Err(format!("{}", err.detail).into())
68 }
69 other => Err(format!("unexpected response status: {}", other).into()),
70 }
71 }
72
73 async fn link_hwid(
74 &self,
75 hwid: &str,
76 access_token: &str,
77 ) -> Result<String, Box<dyn Error + Send + Sync>> {
78 let url = format!("{}/users/hwid", self.base_url.trim_end_matches('/'));
79
80 let mut map = HashMap::new();
81 map.insert("value", hwid);
82
83 let resp = self
84 .client
85 .patch(&url)
86 .json(&map)
87 .bearer_auth(access_token)
88 .send()
89 .await?;
90
91 match resp.status() {
92 StatusCode::OK => Ok("hwid successfully linked".to_string().into()),
93 StatusCode::UNAUTHORIZED | StatusCode::BAD_REQUEST => {
94 let err = resp.json::<ErrorResponse>().await?;
95 Err(format!("link failed: {}", err.detail).into())
96 }
97 other => Err(format!("unexpected response status: {}", other).into()),
98 }
99 }
100}