2022-10-19 22:48:51 +08:00
|
|
|
use super::HbbHttpResponse;
|
|
|
|
use hbb_common::{config::Config, log, sleep, tokio, tokio::sync::RwLock, ResultType};
|
2022-10-20 23:03:54 +08:00
|
|
|
use serde_derive::{Deserialize, Serialize};
|
2022-10-19 22:48:51 +08:00
|
|
|
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;
|
2022-10-20 23:03:54 +08:00
|
|
|
const REQUESTING_ACCOUNT_AUTH: &str = "Requesting account auth";
|
|
|
|
const WAITING_ACCOUNT_AUTH: &str = "Waiting account auth";
|
|
|
|
const LOGIN_ACCOUNT_AUTH: &str = "Login account auth";
|
2022-10-19 22:48:51 +08:00
|
|
|
|
|
|
|
#[derive(Deserialize, Clone)]
|
|
|
|
pub struct OidcAuthUrl {
|
|
|
|
code: String,
|
|
|
|
url: Url,
|
|
|
|
}
|
|
|
|
|
2022-10-20 23:03:54 +08:00
|
|
|
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
2022-10-19 22:48:51 +08:00
|
|
|
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>,
|
|
|
|
}
|
|
|
|
|
2022-10-20 23:03:54 +08:00
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
2022-10-19 22:48:51 +08:00
|
|
|
pub struct AuthBody {
|
2022-10-20 23:03:54 +08:00
|
|
|
pub access_token: String,
|
|
|
|
pub token_type: String,
|
|
|
|
pub user: UserPayload,
|
2022-10-19 22:48:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct OidcSession {
|
|
|
|
client: reqwest::Client,
|
2022-10-20 23:03:54 +08:00
|
|
|
state_msg: &'static str,
|
2022-10-19 22:48:51 +08:00
|
|
|
failed_msg: String,
|
|
|
|
code_url: Option<OidcAuthUrl>,
|
|
|
|
auth_body: Option<AuthBody>,
|
|
|
|
keep_querying: bool,
|
|
|
|
running: bool,
|
|
|
|
query_timeout: Duration,
|
|
|
|
}
|
|
|
|
|
2022-10-20 23:03:54 +08:00
|
|
|
#[derive(Serialize)]
|
|
|
|
pub struct AuthResult {
|
|
|
|
pub state_msg: String,
|
|
|
|
pub failed_msg: String,
|
|
|
|
pub url: Option<String>,
|
|
|
|
pub auth_body: Option<AuthBody>,
|
|
|
|
}
|
|
|
|
|
2022-10-19 22:48:51 +08:00
|
|
|
impl OidcSession {
|
|
|
|
fn new() -> Self {
|
|
|
|
Self {
|
|
|
|
client: reqwest::Client::new(),
|
2022-10-20 23:03:54 +08:00
|
|
|
state_msg: REQUESTING_ACCOUNT_AUTH,
|
2022-10-19 22:48:51 +08:00
|
|
|
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) {
|
2022-10-20 23:03:54 +08:00
|
|
|
self.state_msg = REQUESTING_ACCOUNT_AUTH;
|
2022-10-19 22:48:51 +08:00
|
|
|
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
|
2022-10-20 23:03:54 +08:00
|
|
|
.set_state(REQUESTING_ACCOUNT_AUTH, err);
|
2022-10-19 22:48:51 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
Ok(_) => {
|
2022-10-20 23:03:54 +08:00
|
|
|
OIDC_SESSION
|
|
|
|
.write()
|
|
|
|
.await
|
|
|
|
.set_state(REQUESTING_ACCOUNT_AUTH, "Invalid auth response".to_owned());
|
2022-10-19 22:48:51 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
OIDC_SESSION
|
|
|
|
.write()
|
|
|
|
.await
|
2022-10-20 23:03:54 +08:00
|
|
|
.set_state(REQUESTING_ACCOUNT_AUTH, err.to_string());
|
2022-10-19 22:48:51 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
OIDC_SESSION
|
|
|
|
.write()
|
|
|
|
.await
|
2022-10-20 23:03:54 +08:00
|
|
|
.set_state(WAITING_ACCOUNT_AUTH, "".to_owned());
|
2022-10-19 22:48:51 +08:00
|
|
|
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
|
2022-10-20 23:03:54 +08:00
|
|
|
.set_state(LOGIN_ACCOUNT_AUTH, "".to_owned());
|
2022-10-19 22:48:51 +08:00
|
|
|
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
|
2022-10-20 23:03:54 +08:00
|
|
|
.set_state(WAITING_ACCOUNT_AUTH, err);
|
2022-10-19 22:48:51 +08:00
|
|
|
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
|
2022-10-20 23:03:54 +08:00
|
|
|
.set_state(WAITING_ACCOUNT_AUTH, "timeout".to_owned());
|
2022-10-19 22:48:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// no need to handle "keep_querying == false"
|
|
|
|
}
|
|
|
|
|
2022-10-20 23:03:54 +08:00
|
|
|
fn set_state(&mut self, state_msg: &'static str, failed_msg: String) {
|
|
|
|
self.state_msg = state_msg;
|
2022-10-19 22:48:51 +08:00
|
|
|
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;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-10-20 23:03:54 +08:00
|
|
|
fn get_result_(&self) -> AuthResult {
|
|
|
|
AuthResult {
|
|
|
|
state_msg: self.state_msg.to_string(),
|
|
|
|
failed_msg: self.failed_msg.clone(),
|
|
|
|
url: self.code_url.as_ref().map(|x| x.url.to_string()),
|
|
|
|
auth_body: self.auth_body.clone(),
|
|
|
|
}
|
2022-10-19 22:48:51 +08:00
|
|
|
}
|
|
|
|
|
2022-10-20 23:03:54 +08:00
|
|
|
pub async fn get_result() -> AuthResult {
|
2022-10-19 22:48:51 +08:00
|
|
|
OIDC_SESSION.read().await.get_result_()
|
|
|
|
}
|
|
|
|
}
|