1use crate::auth::{
109 RiAuthConfig, RiJWTManager, RiSessionManager, RiPermissionManager, RiOAuthManager,
110 RiRole,
111};
112use std::ffi::{c_char, c_int};
113use std::collections::HashSet;
114
115c_wrapper!(CRiAuthConfig, RiAuthConfig);
116c_wrapper!(CRiJWTManager, RiJWTManager);
117c_wrapper!(CRiSessionManager, RiSessionManager);
118c_wrapper!(CRiPermissionManager, RiPermissionManager);
119c_wrapper!(CRiOAuthManager, RiOAuthManager);
120
121c_constructor!(ri_auth_config_new, CRiAuthConfig, RiAuthConfig, RiAuthConfig::default());
122c_destructor!(ri_auth_config_free, CRiAuthConfig);
123
124#[no_mangle]
125pub extern "C" fn ri_jwt_manager_new(secret: *const c_char, expiry_secs: u64) -> *mut CRiJWTManager {
126 if secret.is_null() {
127 return std::ptr::null_mut();
128 }
129 unsafe {
130 let secret_str = match std::ffi::CStr::from_ptr(secret).to_str() {
131 Ok(s) => s,
132 Err(_) => return std::ptr::null_mut(),
133 };
134 let manager = RiJWTManager::create(secret_str.to_string(), expiry_secs);
135 Box::into_raw(Box::new(CRiJWTManager::new(manager)))
136 }
137}
138
139c_destructor!(ri_jwt_manager_free, CRiJWTManager);
140
141#[no_mangle]
142pub extern "C" fn ri_jwt_manager_generate(
143 manager: *mut CRiJWTManager,
144 user_id: *const c_char,
145 roles: *const *const c_char,
146 roles_count: usize,
147 permissions: *const *const c_char,
148 permissions_count: usize,
149 out_token: *mut *mut c_char,
150) -> c_int {
151 if manager.is_null() || user_id.is_null() || out_token.is_null() {
152 return -1;
153 }
154
155 unsafe {
156 let user_id_str = match std::ffi::CStr::from_ptr(user_id).to_str() {
157 Ok(s) => s,
158 Err(_) => return -2,
159 };
160
161 let roles_vec: Vec<String> = if roles.is_null() || roles_count == 0 {
162 Vec::new()
163 } else {
164 let roles_slice = std::slice::from_raw_parts(roles, roles_count);
165 roles_slice
166 .iter()
167 .filter_map(|&r| {
168 if r.is_null() {
169 None
170 } else {
171 std::ffi::CStr::from_ptr(r).to_str().ok().map(|s| s.to_string())
172 }
173 })
174 .collect()
175 };
176
177 let permissions_vec: Vec<String> = if permissions.is_null() || permissions_count == 0 {
178 Vec::new()
179 } else {
180 let perms_slice = std::slice::from_raw_parts(permissions, permissions_count);
181 perms_slice
182 .iter()
183 .filter_map(|&p| {
184 if p.is_null() {
185 None
186 } else {
187 std::ffi::CStr::from_ptr(p).to_str().ok().map(|s| s.to_string())
188 }
189 })
190 .collect()
191 };
192
193 match (*manager).inner.generate_token(user_id_str, roles_vec, permissions_vec) {
194 Ok(token) => {
195 match std::ffi::CString::new(token) {
196 Ok(c_token) => {
197 *out_token = c_token.into_raw();
198 0
199 }
200 Err(_) => -4,
201 }
202 }
203 Err(_) => -3,
204 }
205 }
206}
207
208#[repr(C)]
209pub struct CRiJWTClaims {
210 pub sub: *mut c_char,
211 pub exp: u64,
212 pub iat: u64,
213}
214
215#[no_mangle]
216pub extern "C" fn ri_jwt_manager_validate(
217 manager: *mut CRiJWTManager,
218 token: *const c_char,
219 out_claims: *mut CRiJWTClaims,
220) -> c_int {
221 if manager.is_null() || token.is_null() || out_claims.is_null() {
222 return -1;
223 }
224
225 unsafe {
226 let token_str = match std::ffi::CStr::from_ptr(token).to_str() {
227 Ok(s) => s,
228 Err(_) => return -2,
229 };
230
231 match (*manager).inner.validate_token(token_str) {
232 Ok(claims) => {
233 let sub = match std::ffi::CString::new(claims.sub) {
234 Ok(s) => s.into_raw(),
235 Err(_) => return -4,
236 };
237
238 *out_claims = CRiJWTClaims {
239 sub,
240 exp: claims.exp,
241 iat: claims.iat,
242 };
243 0
244 }
245 Err(_) => -3,
246 }
247 }
248}
249
250#[no_mangle]
251pub extern "C" fn ri_jwt_claims_free(claims: *mut CRiJWTClaims) {
252 if claims.is_null() {
253 return;
254 }
255
256 unsafe {
257 let claims = Box::from_raw(claims);
258 if !claims.sub.is_null() {
259 let _ = std::ffi::CString::from_raw(claims.sub);
260 }
261 }
262}
263
264#[no_mangle]
265pub extern "C" fn ri_session_manager_new(timeout_secs: u64) -> *mut CRiSessionManager {
266 let manager = RiSessionManager::new(timeout_secs);
267 Box::into_raw(Box::new(CRiSessionManager::new(manager)))
268}
269
270c_destructor!(ri_session_manager_free, CRiSessionManager);
271
272#[no_mangle]
273pub extern "C" fn ri_session_manager_create(
274 manager: *mut CRiSessionManager,
275 user_id: *const c_char,
276 ip_address: *const c_char,
277 user_agent: *const c_char,
278 out_session_id: *mut *mut c_char,
279) -> c_int {
280 if manager.is_null() || user_id.is_null() || out_session_id.is_null() {
281 return -1;
282 }
283
284 unsafe {
285 let user_id_str = match std::ffi::CStr::from_ptr(user_id).to_str() {
286 Ok(s) => s,
287 Err(_) => return -2,
288 };
289
290 let ip_str = if ip_address.is_null() {
291 None
292 } else {
293 std::ffi::CStr::from_ptr(ip_address).to_str().ok().map(|s| s.to_string())
294 };
295
296 let ua_str = if user_agent.is_null() {
297 None
298 } else {
299 std::ffi::CStr::from_ptr(user_agent).to_str().ok().map(|s| s.to_string())
300 };
301
302 let rt = match tokio::runtime::Runtime::new() {
303 Ok(r) => r,
304 Err(_) => return -3,
305 };
306
307 let result = rt.block_on(async {
308 (*manager).inner.create_session(user_id_str.to_string(), ip_str, ua_str).await
309 });
310
311 match result {
312 Ok(session_id) => {
313 match std::ffi::CString::new(session_id) {
314 Ok(c_id) => {
315 *out_session_id = c_id.into_raw();
316 0
317 }
318 Err(_) => -5,
319 }
320 }
321 Err(_) => -4,
322 }
323 }
324}
325
326#[repr(C)]
327pub struct CRiSession {
328 pub id: *mut c_char,
329 pub user_id: *mut c_char,
330 pub created_at: u64,
331 pub expires_at: u64,
332}
333
334#[no_mangle]
335pub extern "C" fn ri_session_manager_get(
336 manager: *mut CRiSessionManager,
337 session_id: *const c_char,
338 out_session: *mut CRiSession,
339) -> c_int {
340 if manager.is_null() || session_id.is_null() || out_session.is_null() {
341 return -1;
342 }
343
344 unsafe {
345 let session_id_str = match std::ffi::CStr::from_ptr(session_id).to_str() {
346 Ok(s) => s,
347 Err(_) => return -2,
348 };
349
350 let rt = match tokio::runtime::Runtime::new() {
351 Ok(r) => r,
352 Err(_) => return -3,
353 };
354
355 let result = rt.block_on(async {
356 (*manager).inner.get_session(session_id_str).await
357 });
358
359 match result {
360 Ok(Some(session)) => {
361 let id = match std::ffi::CString::new(session.id) {
362 Ok(s) => s.into_raw(),
363 Err(_) => return -5,
364 };
365
366 let user_id = match std::ffi::CString::new(session.user_id) {
367 Ok(s) => s.into_raw(),
368 Err(_) => {
369 let _ = std::ffi::CString::from_raw(id);
370 return -6;
371 }
372 };
373
374 *out_session = CRiSession {
375 id,
376 user_id,
377 created_at: session.created_at,
378 expires_at: session.expires_at,
379 };
380 0
381 }
382 Ok(None) => 1,
383 Err(_) => -4,
384 }
385 }
386}
387
388#[no_mangle]
389pub extern "C" fn ri_session_free(session: *mut CRiSession) {
390 if session.is_null() {
391 return;
392 }
393
394 unsafe {
395 let session = Box::from_raw(session);
396 if !session.id.is_null() {
397 let _ = std::ffi::CString::from_raw(session.id);
398 }
399 if !session.user_id.is_null() {
400 let _ = std::ffi::CString::from_raw(session.user_id);
401 }
402 }
403}
404
405#[no_mangle]
406pub extern "C" fn ri_session_manager_destroy(
407 manager: *mut CRiSessionManager,
408 session_id: *const c_char,
409) -> c_int {
410 if manager.is_null() || session_id.is_null() {
411 return -1;
412 }
413
414 unsafe {
415 let session_id_str = match std::ffi::CStr::from_ptr(session_id).to_str() {
416 Ok(s) => s,
417 Err(_) => return -2,
418 };
419
420 let rt = match tokio::runtime::Runtime::new() {
421 Ok(r) => r,
422 Err(_) => return -3,
423 };
424
425 let result = rt.block_on(async {
426 (*manager).inner.destroy_session(session_id_str).await
427 });
428
429 match result {
430 Ok(destroyed) => if destroyed { 0 } else { 1 },
431 Err(_) => -4,
432 }
433 }
434}
435
436#[no_mangle]
437pub extern "C" fn ri_permission_manager_new() -> *mut CRiPermissionManager {
438 let manager = RiPermissionManager::new();
439 Box::into_raw(Box::new(CRiPermissionManager::new(manager)))
440}
441
442c_destructor!(ri_permission_manager_free, CRiPermissionManager);
443
444#[no_mangle]
445pub extern "C" fn ri_permission_manager_create_role(
446 manager: *mut CRiPermissionManager,
447 role_id: *const c_char,
448 role_name: *const c_char,
449 description: *const c_char,
450 permissions: *const *const c_char,
451 permissions_count: usize,
452) -> c_int {
453 if manager.is_null() || role_id.is_null() || role_name.is_null() {
454 return -1;
455 }
456
457 unsafe {
458 let role_id_str = match std::ffi::CStr::from_ptr(role_id).to_str() {
459 Ok(s) => s,
460 Err(_) => return -2,
461 };
462
463 let role_name_str = match std::ffi::CStr::from_ptr(role_name).to_str() {
464 Ok(s) => s,
465 Err(_) => return -3,
466 };
467
468 let desc_str = if description.is_null() {
469 ""
470 } else {
471 match std::ffi::CStr::from_ptr(description).to_str() {
472 Ok(s) => s,
473 Err(_) => "",
474 }
475 };
476
477 let perms_set: HashSet<String> = if permissions.is_null() || permissions_count == 0 {
478 HashSet::new()
479 } else {
480 let perms_slice = std::slice::from_raw_parts(permissions, permissions_count);
481 perms_slice
482 .iter()
483 .filter_map(|&p| {
484 if p.is_null() {
485 None
486 } else {
487 std::ffi::CStr::from_ptr(p).to_str().ok().map(|s| s.to_string())
488 }
489 })
490 .collect()
491 };
492
493 let role = RiRole::new(
494 role_id_str.to_string(),
495 role_name_str.to_string(),
496 desc_str.to_string(),
497 perms_set,
498 );
499
500 let rt = match tokio::runtime::Runtime::new() {
501 Ok(r) => r,
502 Err(_) => return -4,
503 };
504
505 let result = rt.block_on(async {
506 (*manager).inner.create_role(role).await
507 });
508
509 match result {
510 Ok(()) => 0,
511 Err(_) => -5,
512 }
513 }
514}
515
516#[no_mangle]
517pub extern "C" fn ri_permission_manager_assign_role(
518 manager: *mut CRiPermissionManager,
519 user_id: *const c_char,
520 role_id: *const c_char,
521) -> c_int {
522 if manager.is_null() || user_id.is_null() || role_id.is_null() {
523 return -1;
524 }
525
526 unsafe {
527 let user_id_str = match std::ffi::CStr::from_ptr(user_id).to_str() {
528 Ok(s) => s,
529 Err(_) => return -2,
530 };
531
532 let role_id_str = match std::ffi::CStr::from_ptr(role_id).to_str() {
533 Ok(s) => s,
534 Err(_) => return -3,
535 };
536
537 let rt = match tokio::runtime::Runtime::new() {
538 Ok(r) => r,
539 Err(_) => return -4,
540 };
541
542 let result = rt.block_on(async {
543 (*manager).inner.assign_role_to_user(user_id_str.to_string(), role_id_str.to_string()).await
544 });
545
546 match result {
547 Ok(assigned) => if assigned { 0 } else { 1 },
548 Err(_) => -5,
549 }
550 }
551}
552
553#[no_mangle]
554pub extern "C" fn ri_permission_manager_has_permission(
555 manager: *mut CRiPermissionManager,
556 user_id: *const c_char,
557 permission_id: *const c_char,
558) -> c_int {
559 if manager.is_null() || user_id.is_null() || permission_id.is_null() {
560 return -1;
561 }
562
563 unsafe {
564 let user_id_str = match std::ffi::CStr::from_ptr(user_id).to_str() {
565 Ok(s) => s,
566 Err(_) => return -2,
567 };
568
569 let perm_id_str = match std::ffi::CStr::from_ptr(permission_id).to_str() {
570 Ok(s) => s,
571 Err(_) => return -3,
572 };
573
574 let rt = match tokio::runtime::Runtime::new() {
575 Ok(r) => r,
576 Err(_) => return -4,
577 };
578
579 let result = rt.block_on(async {
580 (*manager).inner.has_permission(user_id_str, perm_id_str).await
581 });
582
583 match result {
584 Ok(has_perm) => if has_perm { 1 } else { 0 },
585 Err(_) => -5,
586 }
587 }
588}
589
590#[no_mangle]
591pub extern "C" fn ri_permission_manager_remove_role(
592 manager: *mut CRiPermissionManager,
593 user_id: *const c_char,
594 role_id: *const c_char,
595) -> c_int {
596 if manager.is_null() || user_id.is_null() || role_id.is_null() {
597 return -1;
598 }
599
600 unsafe {
601 let user_id_str = match std::ffi::CStr::from_ptr(user_id).to_str() {
602 Ok(s) => s,
603 Err(_) => return -2,
604 };
605
606 let role_id_str = match std::ffi::CStr::from_ptr(role_id).to_str() {
607 Ok(s) => s,
608 Err(_) => return -3,
609 };
610
611 let rt = match tokio::runtime::Runtime::new() {
612 Ok(r) => r,
613 Err(_) => return -4,
614 };
615
616 let result = rt.block_on(async {
617 (*manager).inner.remove_role_from_user(user_id_str, role_id_str).await
618 });
619
620 match result {
621 Ok(removed) => if removed { 0 } else { 1 },
622 Err(_) => -5,
623 }
624 }
625}
626
627#[no_mangle]
628pub extern "C" fn ri_permission_manager_delete_role(
629 manager: *mut CRiPermissionManager,
630 role_id: *const c_char,
631) -> c_int {
632 if manager.is_null() || role_id.is_null() {
633 return -1;
634 }
635
636 unsafe {
637 let role_id_str = match std::ffi::CStr::from_ptr(role_id).to_str() {
638 Ok(s) => s,
639 Err(_) => return -2,
640 };
641
642 let rt = match tokio::runtime::Runtime::new() {
643 Ok(r) => r,
644 Err(_) => return -3,
645 };
646
647 let result = rt.block_on(async {
648 (*manager).inner.delete_role(role_id_str).await
649 });
650
651 match result {
652 Ok(deleted) => if deleted { 0 } else { 1 },
653 Err(_) => -4,
654 }
655 }
656}