unifly_api/legacy/
auth.rs1use secrecy::{ExposeSecret, SecretString};
8use serde_json::json;
9use tracing::debug;
10use url::Url;
11
12use crate::auth::ControllerPlatform;
13use crate::error::Error;
14use crate::legacy::client::LegacyClient;
15
16impl LegacyClient {
17 pub async fn login(&self, username: &str, password: &SecretString) -> Result<(), Error> {
25 let login_path = self
26 .platform()
27 .login_path()
28 .ok_or_else(|| Error::Authentication {
29 message: "login not supported on cloud platform".into(),
30 })?;
31
32 let url = self
33 .base_url()
34 .join(login_path)
35 .map_err(Error::InvalidUrl)?;
36
37 debug!("logging in at {}", url);
38
39 let body = json!({
40 "username": username,
41 "password": password.expose_secret(),
42 });
43
44 let resp = self
45 .http()
46 .post(url)
47 .json(&body)
48 .send()
49 .await
50 .map_err(Error::Transport)?;
51
52 let status = resp.status();
53 if !status.is_success() {
54 let body = resp.text().await.unwrap_or_default();
55 return Err(Error::Authentication {
56 message: format!("login failed (HTTP {status}): {body}"),
57 });
58 }
59
60 if let Some(token) = resp
63 .headers()
64 .get("X-CSRF-Token")
65 .or_else(|| resp.headers().get("x-csrf-token"))
66 .and_then(|v| v.to_str().ok())
67 {
68 self.set_csrf_token(token.to_owned());
69 }
70
71 debug!("login successful");
72 Ok(())
73 }
74
75 pub async fn logout(&self) -> Result<(), Error> {
81 let logout_path = self
82 .platform()
83 .logout_path()
84 .ok_or_else(|| Error::Authentication {
85 message: "logout not supported on cloud platform".into(),
86 })?;
87
88 let url = self
89 .base_url()
90 .join(logout_path)
91 .map_err(Error::InvalidUrl)?;
92
93 debug!("logging out at {}", url);
94
95 let _resp = self
96 .http()
97 .post(url)
98 .send()
99 .await
100 .map_err(Error::Transport)?;
101
102 debug!("logout complete");
103 Ok(())
104 }
105
106 pub async fn detect_platform(base_url: &Url) -> Result<ControllerPlatform, Error> {
112 let http = reqwest::Client::builder()
113 .danger_accept_invalid_certs(true)
114 .build()
115 .map_err(Error::Transport)?;
116
117 let unifi_os_url = base_url
119 .join("/api/auth/login")
120 .map_err(Error::InvalidUrl)?;
121
122 debug!("probing UniFi OS at {}", unifi_os_url);
123
124 if let Ok(resp) = http.get(unifi_os_url).send().await {
125 if resp.status() != reqwest::StatusCode::NOT_FOUND {
128 debug!("detected UniFi OS platform");
129 return Ok(ControllerPlatform::UnifiOs);
130 }
131 }
132 let standalone_url = base_url.join("/api/login").map_err(Error::InvalidUrl)?;
136
137 debug!("probing standalone at {}", standalone_url);
138
139 match http.get(standalone_url).send().await {
140 Ok(_) => {
141 debug!("detected standalone (classic) controller");
142 Ok(ControllerPlatform::ClassicController)
143 }
144 Err(e) => Err(Error::Transport(e)),
145 }
146 }
147}