use std::{collections::BTreeMap, fmt::Display, vec};
use jsonwebtoken;
use reqwest::Response;
use serde_json;
use x509_parser::prelude::*;
use super::{networkconfig::*, user::*};
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct CasdoorSDK {
endpoint: String,
client_id: String,
client_secret: String,
certificate: Box<[u8]>,
org_name: String,
front_endpoint: String,
grant_type: String,
algorithms: Vec<String>,
}
#[macro_export]
macro_rules! newsdk {
($endpoint:expr, $client_id:expr, $client_secret:expr, $certificate:expr, $org_name:expr) => {
CasdoorSDK::new(
$endpoint,
$client_id,
$client_secret,
$certificate,
$org_name,
None,
None,
None,
)
};
($endpoint:expr, $client_id:expr, $client_secret:expr, $certificate:expr, $org_name:expr, $front_endpoint:expr) => {
CasdoorSDK::new(
$endpoint,
$client_id,
$client_secret,
$certificate,
$org_name,
$front_endpoint,
None,
None,
)
};
($endpoint:expr, $client_id:expr, $client_secret:expr, $certificate:expr, $org_name:expr, $front_endpoint:expr, $grant_type:expr, $algorithms:expr) => {
CasdoorSDK::new(
$endpoint,
$client_id,
$client_secret,
$certificate,
$org_name,
$front_endpoint,
$grant_type,
$algorithms,
)
};
}
impl Default for CasdoorSDK {
fn default() -> Self {
CasdoorSDK::new("", "", "", Box::new([0]), "", None, None, None)
}
}
impl CasdoorSDK {
pub fn new(
endpoint: &str,
client_id: &str,
client_secret: &str,
certificate: Box<[u8]>,
org_name: &str,
front_endpoint: Option<&str>,
grant_type: Option<&str>,
algorithms: Option<Vec<String>>,
) -> Self {
let front_endpoint = front_endpoint
.unwrap_or(&endpoint.replace(":8000", ":7001"))
.to_owned();
CasdoorSDK {
endpoint: endpoint.to_string(),
client_id: client_id.to_string(),
client_secret: client_secret.to_string(),
certificate,
org_name: org_name.to_string(),
front_endpoint,
grant_type: grant_type.unwrap_or("authorization_code").to_string(),
algorithms: algorithms.unwrap_or(vec!["RS256".to_owned()]).to_vec(),
}
}
#[tokio::main]
pub async fn get_auth_link(
&self,
config: &NetWorkConfig,
) -> Result<String, Box<dyn std::error::Error>> {
let mut url = self.front_endpoint.clone() + "/login/oauth/authorize";
let mut params = config.into_map();
params.insert("client_id".to_owned(), self.client_id.clone());
get_formed_url(¶ms, &mut url);
let client = reqwest::Client::new();
let res = client.get(&url).send().await?.error_for_status()?;
Ok(res.url().to_string())
}
pub async fn get_oauth_token(&self, code: &str) -> Result<String, Box<dyn std::error::Error>> {
let mut url = self.endpoint.clone() + "/api/login/oauth/access_token";
let mut params = BTreeMap::new();
params.insert("grant_type".to_owned(), self.grant_type.clone());
params.insert("code".to_owned(), code.to_string());
params.insert("client_id".to_owned(), self.client_id.clone());
params.insert("client_secret".to_owned(), self.client_secret.clone());
get_formed_url(¶ms, &mut url);
let client = reqwest::Client::new();
let res = client.post(&url).send().await?;
let json: serde_json::Value = res.json().await?;
Ok(json["access_token"].as_str().unwrap().to_string())
}
pub fn parse_jwt_token(&self, token: &str) -> Result<User, Box<dyn std::error::Error>> {
Err(Box::new(X509Error::InvalidCertificate))
}
pub async fn get_users(&self) -> Result<Vec<User>, Box<dyn std::error::Error>> {
let mut url = self.endpoint.clone() + "/api/get-users";
let mut params = BTreeMap::new();
params.insert("owner".to_owned(), &self.org_name);
params.insert("clientId".to_owned(), &self.client_id);
params.insert("clientSecret".to_owned(), &self.client_secret);
get_formed_url(¶ms, &mut url);
let client = reqwest::Client::new();
let res = client.get(&url).send().await?;
let json: serde_json::Value = res.json().await?;
let users: Vec<User> = serde_json::from_value(json)?;
Ok(users)
}
pub async fn get_user_by_name(
&self,
name: &str,
) -> Result<Option<User>, Box<dyn std::error::Error>> {
let mut url = self.endpoint.clone() + "/api/get-user";
let user_info = format!("{}/{}", &self.org_name, name);
let mut params: BTreeMap<String, &str> = BTreeMap::new();
params.insert("clientId".to_owned(), &self.client_id);
params.insert("clientSecret".to_owned(), &self.client_secret);
params.insert("id".to_owned(), &user_info);
get_formed_url(¶ms, &mut url);
let client = reqwest::Client::new();
let res = client.get(&url).send().await?;
let json = res.json::<serde_json::Value>().await?;
match json {
serde_json::Value::Null => {
return Ok(None);
}
_ => {
let user = serde_json::from_value::<User>(json)?;
return Ok(Some(user));
}
}
}
async fn modify_user(
&self,
action: &str,
user: &mut User,
) -> Result<Response, Box<dyn std::error::Error>> {
let mut url = self.endpoint.clone() + format!("/api/{}", action).as_str();
user.owner = self.org_name.clone();
let mut params = BTreeMap::new();
let id = format!("{}/{}", user.owner, user.name);
params.insert("id".to_owned(), &id);
params.insert("clientId".to_owned(), &self.client_id);
params.insert("clientSecret".to_owned(), &self.client_secret);
get_formed_url(¶ms, &mut url);
let client = reqwest::Client::new();
let res = client
.post(&url)
.body(serde_json::to_string(&user).unwrap())
.send()
.await?;
Ok(res)
}
pub async fn add_user(&self, user: &mut User) -> Result<Response, Box<dyn std::error::Error>> {
self.modify_user("add-user", user).await
}
pub async fn update_user(
&self,
user: &mut User,
) -> Result<Response, Box<dyn std::error::Error>> {
self.modify_user("update-user", user).await
}
pub async fn delete_user(
&self,
user: &mut User,
) -> Result<Response, Box<dyn std::error::Error>> {
self.modify_user("delete-user", user).await
}
}
fn get_formed_url<T, P>(params: &BTreeMap<T, P>, url: &mut String)
where
T: Display,
P: Display,
{
url.push('?');
for (key, value) in params {
url.push_str(&format!("{}={}&", key, value));
}
match url.pop() {
Some(ch) if ch != '&' => url.push(ch),
_ => {}
}
}