lib.rs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. extern crate base64;
  2. extern crate openidconnect;
  3. extern crate url;
  4. use std::sync::{Arc, Mutex};
  5. use std::thread::{sleep, spawn, JoinHandle};
  6. use std::time::Duration;
  7. use openidconnect::core::{CoreClient, CoreProviderMetadata, CoreResponseType};
  8. use openidconnect::reqwest::http_client;
  9. use openidconnect::AuthenticationFlow;
  10. use openidconnect::{ClientId, CsrfToken, IssuerUrl, Nonce, PkceCodeChallenge, RedirectUrl, Scope};
  11. use url::Url;
  12. use std::ffi::CStr;
  13. use std::os::raw::c_char;
  14. pub struct ZeroIDC {
  15. inner: Arc<Mutex<Inner>>,
  16. }
  17. struct Inner {
  18. running: bool,
  19. auth_endpoint: String,
  20. oidc_thread: Option<JoinHandle<()>>,
  21. oidc_client: Option<openidconnect::core::CoreClient>,
  22. }
  23. fn csrf_func(csrf_token: String) -> Box<dyn Fn() -> CsrfToken> {
  24. return Box::new(move || CsrfToken::new(csrf_token.to_string()));
  25. }
  26. fn nonce_func(nonce: String) -> Box<dyn Fn() -> Nonce> {
  27. return Box::new(move || Nonce::new(nonce.to_string()));
  28. }
  29. struct authres {
  30. url: Url,
  31. csrf_token: CsrfToken,
  32. nonce: Nonce,
  33. }
  34. impl ZeroIDC {
  35. fn new(
  36. issuer: &str,
  37. client_id: &str,
  38. auth_ep: &str,
  39. local_web_port: u16,
  40. ) -> Result<ZeroIDC, String> {
  41. let idc = ZeroIDC {
  42. inner: Arc::new(Mutex::new(Inner {
  43. running: false,
  44. auth_endpoint: auth_ep.to_string(),
  45. oidc_thread: None,
  46. oidc_client: None,
  47. })),
  48. };
  49. let iss = match IssuerUrl::new(issuer.to_string()) {
  50. Ok(i) => i,
  51. Err(e) => return Err(e.to_string()),
  52. };
  53. let provider_meta = match CoreProviderMetadata::discover(&iss, http_client) {
  54. Ok(m) => m,
  55. Err(e) => return Err(e.to_string()),
  56. };
  57. let r = format!("http://localhost:{}/sso", local_web_port);
  58. let redir_url = match Url::parse(&r) {
  59. Ok(s) => s,
  60. Err(e) => return Err(e.to_string()),
  61. };
  62. let redirect = match RedirectUrl::new(redir_url.to_string()) {
  63. Ok(s) => s,
  64. Err(e) => return Err(e.to_string()),
  65. };
  66. (*idc.inner.lock().unwrap()).oidc_client = Some(
  67. CoreClient::from_provider_metadata(
  68. provider_meta,
  69. ClientId::new(client_id.to_string()),
  70. None,
  71. )
  72. .set_redirect_uri(redirect),
  73. );
  74. Ok(idc)
  75. }
  76. fn start(&mut self) {
  77. let local = Arc::clone(&self.inner);
  78. if !(*local.lock().unwrap()).running {
  79. let inner_local = Arc::clone(&self.inner);
  80. (*local.lock().unwrap()).oidc_thread = Some(spawn(move || {
  81. (*inner_local.lock().unwrap()).running = true;
  82. while (*inner_local.lock().unwrap()).running {
  83. println!("tick");
  84. sleep(Duration::from_secs(1));
  85. }
  86. println!("thread done!")
  87. }));
  88. }
  89. }
  90. fn stop(&mut self) {
  91. let local = self.inner.clone();
  92. if (*local.lock().unwrap()).running {
  93. if let Some(u) = (*local.lock().unwrap()).oidc_thread.take() {
  94. u.join().expect("join failed");
  95. }
  96. }
  97. }
  98. fn get_auth_url(&mut self) -> Option<authres> {
  99. let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256();
  100. let r = (*self.inner.lock().unwrap()).oidc_client.as_ref().map(|c| {
  101. let (auth_url, csrf_token, nonce) = c
  102. .authorize_url(
  103. AuthenticationFlow::<CoreResponseType>::AuthorizationCode,
  104. csrf_func("my-csrf".to_string()),
  105. nonce_func("my-nonce".to_string()),
  106. )
  107. .add_scope(Scope::new("read".to_string()))
  108. .add_scope(Scope::new("read".to_string()))
  109. .add_scope(Scope::new("openid".to_string()))
  110. .set_pkce_challenge(pkce_challenge)
  111. .url();
  112. return authres {
  113. url: auth_url,
  114. csrf_token,
  115. nonce,
  116. };
  117. });
  118. r
  119. }
  120. }
  121. #[no_mangle]
  122. pub extern "C" fn zeroidc_new(
  123. issuer: *const c_char,
  124. client_id: *const c_char,
  125. auth_endpoint: *const c_char,
  126. web_listen_port: u16,
  127. ) -> *mut ZeroIDC {
  128. if issuer.is_null() {
  129. println!("issuer is null");
  130. return std::ptr::null_mut();
  131. }
  132. if client_id.is_null() {
  133. println!("client_id is null");
  134. return std::ptr::null_mut();
  135. }
  136. if auth_endpoint.is_null() {
  137. println!("auth_endpoint is null");
  138. return std::ptr::null_mut();
  139. }
  140. let iss = unsafe { CStr::from_ptr(issuer) };
  141. let c_id = unsafe { CStr::from_ptr(client_id) };
  142. let auth_endpoint = unsafe { CStr::from_ptr(auth_endpoint) };
  143. match ZeroIDC::new(
  144. iss.to_str().unwrap(),
  145. c_id.to_str().unwrap(),
  146. auth_endpoint.to_str().unwrap(),
  147. web_listen_port,
  148. ) {
  149. Ok(idc) => {
  150. return Box::into_raw(Box::new(idc));
  151. }
  152. Err(s) => {
  153. println!("Error creating ZeroIDC instance: {}", s);
  154. return std::ptr::null_mut();
  155. }
  156. }
  157. }
  158. #[no_mangle]
  159. pub extern "C" fn zeroidc_delete(ptr: *mut ZeroIDC) {
  160. if ptr.is_null() {
  161. return;
  162. }
  163. unsafe {
  164. Box::from_raw(ptr);
  165. }
  166. }
  167. #[no_mangle]
  168. pub extern "C" fn zeroidc_start(ptr: *mut ZeroIDC) {
  169. let idc = unsafe {
  170. assert!(!ptr.is_null());
  171. &mut *ptr
  172. };
  173. idc.start();
  174. }
  175. #[no_mangle]
  176. pub extern "C" fn zeroidc_stop(ptr: *mut ZeroIDC) {
  177. let idc = unsafe {
  178. assert!(!ptr.is_null());
  179. &mut *ptr
  180. };
  181. idc.stop();
  182. }