use std::ffi::c_void;
use std::sync::Arc;
use async_trait::async_trait;
use drasi_lib::identity::{CredentialContext, Credentials, IdentityProvider};
use drasi_plugin_sdk::descriptor::IdentityProviderPluginDescriptor;
use drasi_plugin_sdk::ffi::{FfiStr, IdentityProviderPluginVtable};
use libloading::Library;
pub struct HostIdentityProviderProxy {
vtable: drasi_plugin_sdk::ffi::identity::IdentityProviderVtable,
_library: Arc<Library>,
}
unsafe impl Send for HostIdentityProviderProxy {}
unsafe impl Sync for HostIdentityProviderProxy {}
impl HostIdentityProviderProxy {
pub fn new(
vtable: drasi_plugin_sdk::ffi::identity::IdentityProviderVtable,
library: Arc<Library>,
) -> Self {
Self {
vtable,
_library: library,
}
}
}
#[async_trait]
impl IdentityProvider for HostIdentityProviderProxy {
async fn get_credentials(&self, context: &CredentialContext) -> anyhow::Result<Credentials> {
let state_addr = self.vtable.state as usize;
let get_fn = self.vtable.get_credentials_fn;
let context_json =
serde_json::to_string(&context.properties).unwrap_or_else(|_| "{}".to_string());
let result = std::thread::spawn(move || {
let state = state_addr as *const c_void;
let ffi_result = (get_fn)(state, context_json.as_ptr(), context_json.len());
unsafe { ffi_result.into_result() }
})
.join()
.map_err(|_| anyhow::anyhow!("IdentityProvider thread panicked"))?;
result
}
fn clone_box(&self) -> Box<dyn IdentityProvider> {
assert!(
!self.vtable.state.is_null(),
"IdentityProvider state is null during clone"
);
let cloned_state = (self.vtable.clone_fn)(self.vtable.state as *const c_void);
let cloned_vtable = drasi_plugin_sdk::ffi::identity::IdentityProviderVtable {
state: cloned_state,
get_credentials_fn: self.vtable.get_credentials_fn,
clone_fn: self.vtable.clone_fn,
drop_fn: self.vtable.drop_fn,
};
Box::new(HostIdentityProviderProxy {
vtable: cloned_vtable,
_library: self._library.clone(),
})
}
}
impl Drop for HostIdentityProviderProxy {
fn drop(&mut self) {
(self.vtable.drop_fn)(self.vtable.state);
}
}
pub struct IdentityProviderPluginProxy {
vtable: IdentityProviderPluginVtable,
library: Arc<Library>,
cached_kind: String,
cached_config_version: String,
cached_config_schema_name: String,
}
unsafe impl Send for IdentityProviderPluginProxy {}
unsafe impl Sync for IdentityProviderPluginProxy {}
impl IdentityProviderPluginProxy {
pub fn new(vtable: IdentityProviderPluginVtable, library: Arc<Library>) -> Self {
let cached_kind = unsafe { (vtable.kind_fn)(vtable.state as *const c_void).to_string() };
let cached_config_version =
unsafe { (vtable.config_version_fn)(vtable.state as *const c_void).to_string() };
let cached_config_schema_name =
unsafe { (vtable.config_schema_name_fn)(vtable.state as *const c_void).to_string() };
Self {
vtable,
library,
cached_kind,
cached_config_version,
cached_config_schema_name,
}
}
}
#[async_trait]
impl IdentityProviderPluginDescriptor for IdentityProviderPluginProxy {
fn kind(&self) -> &str {
&self.cached_kind
}
fn config_version(&self) -> &str {
&self.cached_config_version
}
fn config_schema_json(&self) -> String {
unsafe {
(self.vtable.config_schema_json_fn)(self.vtable.state as *const c_void).into_string()
}
}
fn config_schema_name(&self) -> &str {
&self.cached_config_schema_name
}
async fn create_identity_provider(
&self,
config_json: &serde_json::Value,
) -> anyhow::Result<Box<dyn IdentityProvider>> {
let config_str = serde_json::to_string(config_json)?;
let config_ffi = FfiStr::from_str(&config_str);
let state = self.vtable.state;
let create_fn = self.vtable.create_identity_provider_fn;
let vtable_ptr = (create_fn)(state, config_ffi);
if vtable_ptr.is_null() {
return Err(anyhow::anyhow!(
"Plugin factory returned null for identity provider '{}'",
self.cached_kind
));
}
let vtable = unsafe { *Box::from_raw(vtable_ptr) };
Ok(Box::new(HostIdentityProviderProxy::new(
vtable,
self.library.clone(),
)))
}
}
impl Drop for IdentityProviderPluginProxy {
fn drop(&mut self) {
(self.vtable.drop_fn)(self.vtable.state);
}
}