use crate::{rbac::RpConfig, ClientRsp};
use anyhow::anyhow;
use dashmap::DashMap;
use serde::{Deserialize, Serialize};
use std::sync::{Arc, OnceLock};
#[derive(Deserialize, Serialize, Debug)]
pub struct AppReadRbacRsp {
pub rbac: Option<RpConfig>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct LoginReq {
pub appname: String,
pub appsecret: String,
pub account: String,
pub password: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct LoginRsp {
pub access_token: String,
pub refresh_token: String,
pub user_name: String,
pub user_account: String,
pub rbac: Option<RpConfig>,
}
#[derive(Clone, Debug)]
pub struct IdmManager {
pub server: String,
pub client: reqwest::Client,
pub rpinfo: Arc<DashMap<String, RpConfig>>,
}
static IDM: OnceLock<IdmManager> = OnceLock::<IdmManager>::new();
pub fn init_idm(server: String, app_name: String) -> &'static IdmManager {
IDM.get_or_init(|| IdmManager::new(server, app_name))
}
pub fn get_idm() -> &'static IdmManager {
IDM.get().unwrap()
}
impl IdmManager {
pub fn new(server: String, app_name: String) -> Self {
let client = reqwest::Client::builder()
.http1_only()
.connect_timeout(std::time::Duration::from_secs(5))
.timeout(std::time::Duration::from_secs(5))
.build()
.unwrap();
let manager = Self {
server: server.clone(),
client: client.clone(),
rpinfo: Arc::new(DashMap::default()),
};
let url = format!("{}/api/v1/janus/app/readrbac/{}", server, app_name);
let rbac = futures::executor::block_on(async {
let response = client
.get(url)
.send()
.await
.map_err(|e| {
tracing::error!("IDM服务器连接出错");
e
})
.unwrap();
let response = response
.json::<ClientRsp>()
.await
.map_err(|e| {
tracing::error!("IDM服务器返回的数据格式有误");
e
})
.unwrap();
if response.status {
let resp_data = serde_json::from_value::<AppReadRbacRsp>(response.message.clone())
.map_err(|e| {
tracing::error!("IDM服务器返回的RBAC数据格式有误");
e
})
.unwrap();
resp_data.rbac
} else {
panic!("获取IDM服务RBAC信息失败-{}", app_name);
}
});
if rbac.is_some() {
tracing::warn!("use remote rbac config file");
manager.rpinfo.insert(app_name, rbac.unwrap());
}
manager
}
pub async fn app_login(
&self,
app_name: String,
app_secret: String,
user_name: String,
user_pass: String,
) -> anyhow::Result<LoginRsp> {
let req_data = LoginReq {
appname: app_name.clone(),
appsecret: app_secret,
account: user_name.clone(),
password: user_pass.clone(),
};
let url = format!("{}/api/v1/janus/app/applogin", self.server);
let response = self.client.post(url).json(&req_data).send().await?;
if response.status().is_success() {
match response.json::<ClientRsp>().await {
Ok(resp) => {
let status = resp.status;
if status {
let resp_data =
serde_json::from_value::<LoginRsp>(resp.message.clone()).unwrap();
if resp_data.rbac.is_some() {
tracing::warn!("update remote rbac config file");
self.rpinfo
.insert(app_name, resp_data.rbac.clone().unwrap());
}
Ok(resp_data)
} else {
let reason =
serde_json::from_value::<String>(resp.message.clone()).unwrap();
return Err(anyhow!("login error reason: {}", reason));
}
}
Err(e) => {
return Err(anyhow!("login error as response data decoding: {:?}", e));
}
}
} else {
return Err(anyhow!("login error: {:?}", response.status()));
}
}
}