koprogo_api/application/use_cases/
gdpr_use_cases.rs1use crate::application::dto::{GdprEraseResponseDto, GdprExportResponseDto};
2use crate::application::ports::{GdprRepository, UserRepository};
3use chrono::Utc;
4use std::sync::Arc;
5use uuid::Uuid;
6
7pub struct GdprUseCases {
10 gdpr_repository: Arc<dyn GdprRepository>,
11 user_repository: Arc<dyn UserRepository>,
12}
13
14impl GdprUseCases {
15 pub fn new(
16 gdpr_repository: Arc<dyn GdprRepository>,
17 user_repository: Arc<dyn UserRepository>,
18 ) -> Self {
19 Self {
20 gdpr_repository,
21 user_repository,
22 }
23 }
24
25 pub async fn export_user_data(
40 &self,
41 user_id: Uuid,
42 requesting_user_id: Uuid,
43 organization_id: Option<Uuid>,
44 ) -> Result<GdprExportResponseDto, String> {
45 if user_id != requesting_user_id && organization_id.is_some() {
48 return Err("Unauthorized: You can only export your own data".to_string());
49 }
50
51 let is_anonymized = self.gdpr_repository.is_user_anonymized(user_id).await?;
53 if is_anonymized {
54 return Err("User data has been anonymized and cannot be exported".to_string());
55 }
56
57 let export = self
59 .gdpr_repository
60 .aggregate_user_data(user_id, organization_id)
61 .await?;
62
63 Ok(GdprExportResponseDto::from(export))
65 }
66
67 pub async fn erase_user_data(
86 &self,
87 user_id: Uuid,
88 requesting_user_id: Uuid,
89 organization_id: Option<Uuid>,
90 ) -> Result<GdprEraseResponseDto, String> {
91 if user_id != requesting_user_id && organization_id.is_some() {
93 return Err("Unauthorized: You can only erase your own data".to_string());
94 }
95
96 let is_anonymized = self.gdpr_repository.is_user_anonymized(user_id).await?;
98 if is_anonymized {
99 return Err("User data is already anonymized".to_string());
100 }
101
102 let holds = self.gdpr_repository.check_legal_holds(user_id).await?;
104 if !holds.is_empty() {
105 return Err(format!(
106 "Cannot erase data due to legal holds: {}",
107 holds.join(", ")
108 ));
109 }
110
111 let user_data = self
113 .gdpr_repository
114 .aggregate_user_data(user_id, organization_id)
115 .await?;
116 let user_email = user_data.user_data.email.clone();
117 let user_first_name = user_data.user_data.first_name.clone();
118 let user_last_name = user_data.user_data.last_name.clone();
119
120 let owner_ids = self
122 .gdpr_repository
123 .find_owner_ids_by_user(user_id, organization_id)
124 .await?;
125
126 self.gdpr_repository.anonymize_user(user_id).await?;
128
129 let mut owners_anonymized = 0;
131 for owner_id in &owner_ids {
132 match self.gdpr_repository.anonymize_owner(*owner_id).await {
133 Ok(_) => owners_anonymized += 1,
134 Err(e) => {
135 eprintln!("Warning: Failed to anonymize owner {}: {}", owner_id, e);
137 }
138 }
139 }
140
141 Ok(GdprEraseResponseDto {
142 success: true,
143 message: "Personal data has been successfully anonymized".to_string(),
144 anonymized_at: Utc::now().to_rfc3339(),
145 user_id: user_id.to_string(),
146 user_email,
147 user_first_name,
148 user_last_name,
149 owners_anonymized,
150 })
151 }
152
153 pub async fn can_erase_user(&self, user_id: Uuid) -> Result<bool, String> {
163 let holds = self.gdpr_repository.check_legal_holds(user_id).await?;
164 Ok(holds.is_empty())
165 }
166
167 pub async fn rectify_user_data(
186 &self,
187 user_id: Uuid,
188 requesting_user_id: Uuid,
189 email: Option<String>,
190 first_name: Option<String>,
191 last_name: Option<String>,
192 ) -> Result<(), String> {
193 if user_id != requesting_user_id {
195 return Err("Unauthorized: You can only rectify your own data".to_string());
197 }
198
199 let mut user = self
201 .user_repository
202 .find_by_id(user_id)
203 .await?
204 .ok_or_else(|| format!("User not found: {}", user_id))?;
205
206 user.rectify_data(email, first_name, last_name)?;
208
209 self.user_repository.update(&user).await?;
211
212 Ok(())
213 }
214
215 pub async fn restrict_user_processing(
234 &self,
235 user_id: Uuid,
236 requesting_user_id: Uuid,
237 ) -> Result<(), String> {
238 if user_id != requesting_user_id {
240 return Err("Unauthorized: You can only restrict your own data processing".to_string());
241 }
242
243 let mut user = self
245 .user_repository
246 .find_by_id(user_id)
247 .await?
248 .ok_or_else(|| format!("User not found: {}", user_id))?;
249
250 user.restrict_processing()?;
252
253 self.user_repository.update(&user).await?;
255
256 Ok(())
257 }
258
259 pub async fn unrestrict_user_processing(&self, user_id: Uuid) -> Result<(), String> {
272 let mut user = self
274 .user_repository
275 .find_by_id(user_id)
276 .await?
277 .ok_or_else(|| format!("User not found: {}", user_id))?;
278
279 user.unrestrict_processing();
281
282 self.user_repository.update(&user).await?;
284
285 Ok(())
286 }
287
288 pub async fn set_marketing_preference(
304 &self,
305 user_id: Uuid,
306 requesting_user_id: Uuid,
307 opt_out: bool,
308 ) -> Result<(), String> {
309 if user_id != requesting_user_id {
311 return Err(
312 "Unauthorized: You can only change your own marketing preferences".to_string(),
313 );
314 }
315
316 let mut user = self
318 .user_repository
319 .find_by_id(user_id)
320 .await?
321 .ok_or_else(|| format!("User not found: {}", user_id))?;
322
323 user.set_marketing_opt_out(opt_out);
325
326 self.user_repository.update(&user).await?;
328
329 Ok(())
330 }
331}
332
333#[cfg(test)]
334mod tests {
335 use super::*;
336 use crate::application::ports::gdpr_repository::MockGdprRepo;
337 use crate::application::ports::user_repository::MockUserRepo;
338 use crate::domain::entities::gdpr_export::{GdprExport, UserData};
339 use chrono::Utc;
340
341 fn create_test_user_data(user_id: Uuid) -> UserData {
342 UserData {
343 id: user_id,
344 email: "test@example.com".to_string(),
345 first_name: "John".to_string(),
346 last_name: "Doe".to_string(),
347 organization_id: Some(Uuid::new_v4()),
348 is_active: true,
349 is_anonymized: false,
350 created_at: Utc::now(),
351 updated_at: Utc::now(),
352 }
353 }
354
355 #[tokio::test]
356 async fn test_export_user_data_success() {
357 let user_id = Uuid::new_v4();
358 let org_id = Uuid::new_v4();
359
360 let mut mock_repo = MockGdprRepo::new();
361 mock_repo
362 .expect_is_user_anonymized()
363 .times(1)
364 .returning(|_| Ok(false));
365 mock_repo
366 .expect_aggregate_user_data()
367 .times(1)
368 .returning(move |_, _| {
369 let user_data = create_test_user_data(user_id);
370 Ok(GdprExport::new(user_data))
371 });
372
373 let mock_user_repo = MockUserRepo::new();
374
375 let use_cases = GdprUseCases::new(Arc::new(mock_repo), Arc::new(mock_user_repo));
376 let result = use_cases
377 .export_user_data(user_id, user_id, Some(org_id))
378 .await;
379
380 assert!(result.is_ok());
381 let dto = result.unwrap();
382 assert_eq!(dto.user.email, "test@example.com");
383 }
384
385 #[tokio::test]
386 async fn test_export_user_data_unauthorized() {
387 let user_id = Uuid::new_v4();
388 let other_user_id = Uuid::new_v4();
389 let org_id = Uuid::new_v4();
390
391 let mock_repo = MockGdprRepo::new();
392 let mock_user_repo = MockUserRepo::new();
393
394 let use_cases = GdprUseCases::new(Arc::new(mock_repo), Arc::new(mock_user_repo));
395
396 let result = use_cases
397 .export_user_data(user_id, other_user_id, Some(org_id))
398 .await;
399
400 assert!(result.is_err());
401 assert!(result
402 .unwrap_err()
403 .contains("Unauthorized: You can only export your own data"));
404 }
405
406 #[tokio::test]
407 async fn test_export_anonymized_user_fails() {
408 let user_id = Uuid::new_v4();
409
410 let mut mock_repo = MockGdprRepo::new();
411 mock_repo
412 .expect_is_user_anonymized()
413 .times(1)
414 .returning(|_| Ok(true));
415
416 let mock_user_repo = MockUserRepo::new();
417
418 let use_cases = GdprUseCases::new(Arc::new(mock_repo), Arc::new(mock_user_repo));
419 let result = use_cases
420 .export_user_data(user_id, user_id, Some(Uuid::new_v4()))
421 .await;
422
423 assert!(result.is_err());
424 assert!(result
425 .unwrap_err()
426 .contains("User data has been anonymized"));
427 }
428
429 #[tokio::test]
430 async fn test_erase_user_data_success() {
431 let user_id = Uuid::new_v4();
432 let owner_id1 = Uuid::new_v4();
433 let owner_id2 = Uuid::new_v4();
434 let org_id = Uuid::new_v4();
435
436 let user_data = crate::domain::entities::gdpr_export::UserData {
438 id: user_id,
439 email: "test@example.com".to_string(),
440 first_name: "Test".to_string(),
441 last_name: "User".to_string(),
442 organization_id: Some(org_id),
443 is_active: true,
444 is_anonymized: false,
445 created_at: Utc::now(),
446 updated_at: Utc::now(),
447 };
448 let gdpr_export = crate::domain::entities::gdpr_export::GdprExport::new(user_data);
449
450 let mut mock_repo = MockGdprRepo::new();
451 mock_repo
452 .expect_is_user_anonymized()
453 .times(1)
454 .returning(|_| Ok(false));
455 mock_repo
456 .expect_check_legal_holds()
457 .times(1)
458 .returning(|_| Ok(vec![]));
459 mock_repo
460 .expect_aggregate_user_data()
461 .times(1)
462 .returning(move |_, _| Ok(gdpr_export.clone()));
463 mock_repo
464 .expect_find_owner_ids_by_user()
465 .times(1)
466 .returning(move |_, _| Ok(vec![owner_id1, owner_id2]));
467 mock_repo
468 .expect_anonymize_user()
469 .times(1)
470 .returning(|_| Ok(()));
471 mock_repo
472 .expect_anonymize_owner()
473 .times(2)
474 .returning(|_| Ok(()));
475
476 let mock_user_repo = MockUserRepo::new();
477
478 let use_cases = GdprUseCases::new(Arc::new(mock_repo), Arc::new(mock_user_repo));
479 let result = use_cases
480 .erase_user_data(user_id, user_id, Some(org_id))
481 .await;
482
483 assert!(result.is_ok());
484 let dto = result.unwrap();
485 assert!(dto.success);
486 assert_eq!(dto.owners_anonymized, 2);
487 assert_eq!(dto.user_email, "test@example.com");
488 assert_eq!(dto.user_first_name, "Test");
489 assert_eq!(dto.user_last_name, "User");
490 }
491
492 #[tokio::test]
493 async fn test_erase_user_data_unauthorized() {
494 let user_id = Uuid::new_v4();
495 let other_user_id = Uuid::new_v4();
496 let org_id = Uuid::new_v4();
497
498 let mock_repo = MockGdprRepo::new();
499 let mock_user_repo = MockUserRepo::new();
500
501 let use_cases = GdprUseCases::new(Arc::new(mock_repo), Arc::new(mock_user_repo));
502
503 let result = use_cases
504 .erase_user_data(user_id, other_user_id, Some(org_id))
505 .await;
506
507 assert!(result.is_err());
508 assert!(result
509 .unwrap_err()
510 .contains("Unauthorized: You can only erase your own data"));
511 }
512
513 #[tokio::test]
514 async fn test_erase_already_anonymized_user_fails() {
515 let user_id = Uuid::new_v4();
516
517 let mut mock_repo = MockGdprRepo::new();
518 mock_repo
519 .expect_is_user_anonymized()
520 .times(1)
521 .returning(|_| Ok(true));
522
523 let mock_user_repo = MockUserRepo::new();
524
525 let use_cases = GdprUseCases::new(Arc::new(mock_repo), Arc::new(mock_user_repo));
526 let result = use_cases
527 .erase_user_data(user_id, user_id, Some(Uuid::new_v4()))
528 .await;
529
530 assert!(result.is_err());
531 assert!(result.unwrap_err().contains("already anonymized"));
532 }
533
534 #[tokio::test]
535 async fn test_erase_with_legal_holds_fails() {
536 let user_id = Uuid::new_v4();
537
538 let mut mock_repo = MockGdprRepo::new();
539 mock_repo
540 .expect_is_user_anonymized()
541 .times(1)
542 .returning(|_| Ok(false));
543 mock_repo
544 .expect_check_legal_holds()
545 .times(1)
546 .returning(|_| Ok(vec!["Unpaid expenses".to_string()]));
547
548 let mock_user_repo = MockUserRepo::new();
549
550 let use_cases = GdprUseCases::new(Arc::new(mock_repo), Arc::new(mock_user_repo));
551 let result = use_cases
552 .erase_user_data(user_id, user_id, Some(Uuid::new_v4()))
553 .await;
554
555 assert!(result.is_err());
556 assert!(result.unwrap_err().contains("legal holds"));
557 }
558
559 #[tokio::test]
560 async fn test_can_erase_user_no_holds() {
561 let user_id = Uuid::new_v4();
562
563 let mut mock_repo = MockGdprRepo::new();
564 mock_repo
565 .expect_check_legal_holds()
566 .times(1)
567 .returning(|_| Ok(vec![]));
568
569 let mock_user_repo = MockUserRepo::new();
570
571 let use_cases = GdprUseCases::new(Arc::new(mock_repo), Arc::new(mock_user_repo));
572 let result = use_cases.can_erase_user(user_id).await;
573
574 assert!(result.is_ok());
575 assert!(result.unwrap());
576 }
577
578 #[tokio::test]
579 async fn test_can_erase_user_with_holds() {
580 let user_id = Uuid::new_v4();
581
582 let mut mock_repo = MockGdprRepo::new();
583 mock_repo
584 .expect_check_legal_holds()
585 .times(1)
586 .returning(|_| Ok(vec!["Unpaid expenses".to_string()]));
587
588 let mock_user_repo = MockUserRepo::new();
589
590 let use_cases = GdprUseCases::new(Arc::new(mock_repo), Arc::new(mock_user_repo));
591 let result = use_cases.can_erase_user(user_id).await;
592
593 assert!(result.is_ok());
594 assert!(!result.unwrap());
595 }
596}