account oidc init rs
Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
parent
d89c1d3093
commit
3454454bd5
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -4419,6 +4419,7 @@ dependencies = [
|
|||||||
"system_shutdown",
|
"system_shutdown",
|
||||||
"tray-item",
|
"tray-item",
|
||||||
"trayicon",
|
"trayicon",
|
||||||
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
"virtual_display",
|
"virtual_display",
|
||||||
"whoami",
|
"whoami",
|
||||||
@ -5492,6 +5493,7 @@ dependencies = [
|
|||||||
"idna",
|
"idna",
|
||||||
"matches",
|
"matches",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
|
"serde 1.0.144",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -64,6 +64,7 @@ wol-rs = "0.9.1"
|
|||||||
flutter_rust_bridge = { git = "https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge", optional = true }
|
flutter_rust_bridge = { git = "https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge", optional = true }
|
||||||
errno = "0.2.8"
|
errno = "0.2.8"
|
||||||
rdev = { git = "https://github.com/asur4s/rdev" }
|
rdev = { git = "https://github.com/asur4s/rdev" }
|
||||||
|
url = { version = "2.1", features = ["serde"] }
|
||||||
|
|
||||||
[target.'cfg(not(target_os = "linux"))'.dependencies]
|
[target.'cfg(not(target_os = "linux"))'.dependencies]
|
||||||
reqwest = { version = "0.11", features = ["json", "rustls-tls"], default-features=false }
|
reqwest = { version = "0.11", features = ["json", "rustls-tls"], default-features=false }
|
||||||
|
42
src/hbbs_http.rs
Normal file
42
src/hbbs_http.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
use hbb_common::{
|
||||||
|
anyhow::{self, bail},
|
||||||
|
tokio, ResultType,
|
||||||
|
};
|
||||||
|
use reqwest::Response;
|
||||||
|
use serde_derive::Deserialize;
|
||||||
|
use serde_json::{Map, Value};
|
||||||
|
use serde::de::DeserializeOwned;
|
||||||
|
|
||||||
|
pub mod account;
|
||||||
|
|
||||||
|
pub enum HbbHttpResponse<T> {
|
||||||
|
ErrorFormat,
|
||||||
|
Error(String),
|
||||||
|
DataTypeFormat,
|
||||||
|
Data(T),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main(flavor = "current_thread")]
|
||||||
|
async fn resp_to_serde_map(resp: Response) -> reqwest::Result<Map<String, Value>> {
|
||||||
|
resp.json().await
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: DeserializeOwned> TryFrom<Response> for HbbHttpResponse<T> {
|
||||||
|
type Error = reqwest::Error;
|
||||||
|
|
||||||
|
fn try_from(resp: Response) -> Result<Self, <Self as TryFrom<Response>>::Error> {
|
||||||
|
let map = resp_to_serde_map(resp)?;
|
||||||
|
if let Some(error) = map.get("error") {
|
||||||
|
if let Some(err) = error.as_str() {
|
||||||
|
Ok(Self::Error(err.to_owned()))
|
||||||
|
} else {
|
||||||
|
Ok(Self::ErrorFormat)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match serde_json::from_value(Value::Object(map)) {
|
||||||
|
Ok(v) => Ok(Self::Data(v)),
|
||||||
|
Err(_) => Ok(Self::DataTypeFormat),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
242
src/hbbs_http/account.rs
Normal file
242
src/hbbs_http/account.rs
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
use super::HbbHttpResponse;
|
||||||
|
use hbb_common::{config::Config, log, sleep, tokio, tokio::sync::RwLock, ResultType};
|
||||||
|
use serde_derive::Deserialize;
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
sync::Arc,
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
static ref API_SERVER: String = crate::get_api_server(
|
||||||
|
Config::get_option("api-server"), Config::get_option("custom-rendezvous-server"));
|
||||||
|
static ref OIDC_SESSION: Arc<RwLock<OidcSession>> = Arc::new(RwLock::new(OidcSession::new()));
|
||||||
|
}
|
||||||
|
|
||||||
|
const QUERY_INTERVAL_SECS: f32 = 1.0;
|
||||||
|
const QUERY_TIMEOUT_SECS: u64 = 60;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone)]
|
||||||
|
pub struct OidcAuthUrl {
|
||||||
|
code: String,
|
||||||
|
url: Url,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Default, Clone)]
|
||||||
|
pub struct UserPayload {
|
||||||
|
pub id: String,
|
||||||
|
pub name: String,
|
||||||
|
pub email: Option<String>,
|
||||||
|
pub note: Option<String>,
|
||||||
|
pub status: Option<i64>,
|
||||||
|
pub grp: Option<String>,
|
||||||
|
pub is_admin: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
pub struct AuthBody {
|
||||||
|
access_token: String,
|
||||||
|
token_type: String,
|
||||||
|
user: UserPayload,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum OidcState {
|
||||||
|
// initial request
|
||||||
|
OidcRequest = 1,
|
||||||
|
// initial request failed
|
||||||
|
OidcRequestFailed = 2,
|
||||||
|
// request succeeded, loop querying
|
||||||
|
OidcQuerying = 11,
|
||||||
|
// loop querying failed
|
||||||
|
OidcQueryFailed = 12,
|
||||||
|
// query sucess before
|
||||||
|
OidcNotExists = 13,
|
||||||
|
// query timeout
|
||||||
|
OidcQueryTimeout = 14,
|
||||||
|
// already login
|
||||||
|
OidcLogin = 21,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct OidcSession {
|
||||||
|
client: reqwest::Client,
|
||||||
|
state: OidcState,
|
||||||
|
failed_msg: String,
|
||||||
|
code_url: Option<OidcAuthUrl>,
|
||||||
|
auth_body: Option<AuthBody>,
|
||||||
|
keep_querying: bool,
|
||||||
|
running: bool,
|
||||||
|
query_timeout: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OidcSession {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
client: reqwest::Client::new(),
|
||||||
|
state: OidcState::OidcRequest,
|
||||||
|
failed_msg: "".to_owned(),
|
||||||
|
code_url: None,
|
||||||
|
auth_body: None,
|
||||||
|
keep_querying: false,
|
||||||
|
running: false,
|
||||||
|
query_timeout: Duration::from_secs(QUERY_TIMEOUT_SECS),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn auth(op: &str, id: &str, uuid: &str) -> ResultType<HbbHttpResponse<OidcAuthUrl>> {
|
||||||
|
Ok(OIDC_SESSION
|
||||||
|
.read()
|
||||||
|
.await
|
||||||
|
.client
|
||||||
|
.post(format!("{}/api/oidc/auth", *API_SERVER))
|
||||||
|
.json(&HashMap::from([("op", op), ("id", id), ("uuid", uuid)]))
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.try_into()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn query(code: &str, id: &str, uuid: &str) -> ResultType<HbbHttpResponse<AuthBody>> {
|
||||||
|
let url = reqwest::Url::parse_with_params(
|
||||||
|
&format!("{}/api/oidc/auth-query", *API_SERVER),
|
||||||
|
&[("code", code), ("id", id), ("uuid", uuid)],
|
||||||
|
)?;
|
||||||
|
Ok(OIDC_SESSION
|
||||||
|
.read()
|
||||||
|
.await
|
||||||
|
.client
|
||||||
|
.get(url)
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.try_into()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
self.state = OidcState::OidcRequest;
|
||||||
|
self.failed_msg = "".to_owned();
|
||||||
|
self.keep_querying = true;
|
||||||
|
self.running = false;
|
||||||
|
self.code_url = None;
|
||||||
|
self.auth_body = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn before_task(&mut self) {
|
||||||
|
self.reset();
|
||||||
|
self.running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn after_task(&mut self) {
|
||||||
|
self.running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn auth_task(op: String, id: String, uuid: String) {
|
||||||
|
let code_url = match Self::auth(&op, &id, &uuid).await {
|
||||||
|
Ok(HbbHttpResponse::<_>::Data(code_url)) => code_url,
|
||||||
|
Ok(HbbHttpResponse::<_>::Error(err)) => {
|
||||||
|
OIDC_SESSION
|
||||||
|
.write()
|
||||||
|
.await
|
||||||
|
.set_state(OidcState::OidcRequestFailed, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Ok(_) => {
|
||||||
|
OIDC_SESSION.write().await.set_state(
|
||||||
|
OidcState::OidcRequestFailed,
|
||||||
|
"Invalid auth response".to_owned(),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
OIDC_SESSION
|
||||||
|
.write()
|
||||||
|
.await
|
||||||
|
.set_state(OidcState::OidcRequestFailed, err.to_string());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
OIDC_SESSION
|
||||||
|
.write()
|
||||||
|
.await
|
||||||
|
.set_state(OidcState::OidcQuerying, "".to_owned());
|
||||||
|
OIDC_SESSION.write().await.code_url = Some(code_url.clone());
|
||||||
|
|
||||||
|
let begin = Instant::now();
|
||||||
|
let query_timeout = OIDC_SESSION.read().await.query_timeout;
|
||||||
|
while OIDC_SESSION.read().await.keep_querying && begin.elapsed() < query_timeout {
|
||||||
|
match Self::query(&code_url.code, &id, &uuid).await {
|
||||||
|
Ok(HbbHttpResponse::<_>::Data(auth_body)) => {
|
||||||
|
OIDC_SESSION
|
||||||
|
.write()
|
||||||
|
.await
|
||||||
|
.set_state(OidcState::OidcLogin, "".to_owned());
|
||||||
|
OIDC_SESSION.write().await.auth_body = Some(auth_body);
|
||||||
|
return;
|
||||||
|
// to-do, set access-token
|
||||||
|
}
|
||||||
|
Ok(HbbHttpResponse::<_>::Error(err)) => {
|
||||||
|
if err.contains("No authed oidc is found") {
|
||||||
|
// ignore, keep querying
|
||||||
|
} else {
|
||||||
|
OIDC_SESSION
|
||||||
|
.write()
|
||||||
|
.await
|
||||||
|
.set_state(OidcState::OidcQueryFailed, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(_) => {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::trace!("Failed query oidc {}", err);
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sleep(QUERY_INTERVAL_SECS).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
if begin.elapsed() >= query_timeout {
|
||||||
|
OIDC_SESSION
|
||||||
|
.write()
|
||||||
|
.await
|
||||||
|
.set_state(OidcState::OidcQueryTimeout, "timeout".to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
// no need to handle "keep_querying == false"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_state(&mut self, state: OidcState, failed_msg: String) {
|
||||||
|
self.state = state;
|
||||||
|
self.failed_msg = failed_msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn account_auth(op: String, id: String, uuid: String) {
|
||||||
|
if OIDC_SESSION.read().await.running {
|
||||||
|
OIDC_SESSION.write().await.keep_querying = false;
|
||||||
|
}
|
||||||
|
let wait_secs = 0.3;
|
||||||
|
sleep(wait_secs).await;
|
||||||
|
while OIDC_SESSION.read().await.running {
|
||||||
|
sleep(wait_secs).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
OIDC_SESSION.write().await.before_task().await;
|
||||||
|
Self::auth_task(op, id, uuid).await;
|
||||||
|
OIDC_SESSION.write().await.after_task().await;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_result_(&self) -> (u8, String, Option<AuthBody>) {
|
||||||
|
(
|
||||||
|
self.state as u8,
|
||||||
|
self.failed_msg.clone(),
|
||||||
|
self.auth_body.clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_result() -> (u8, String, Option<AuthBody>) {
|
||||||
|
OIDC_SESSION.read().await.get_result_()
|
||||||
|
}
|
||||||
|
}
|
@ -48,6 +48,8 @@ mod ui_cm_interface;
|
|||||||
mod ui_interface;
|
mod ui_interface;
|
||||||
mod ui_session_interface;
|
mod ui_session_interface;
|
||||||
|
|
||||||
|
mod hbbs_http;
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub mod clipboard_file;
|
pub mod clipboard_file;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user