lab_resource_manager/application/usecases/
grant_user_resource_access.rs1use crate::application::error::ApplicationError;
2use crate::domain::aggregates::identity_link::{
3 entity::IdentityLink,
4 value_objects::{ExternalIdentity, ExternalSystem},
5};
6use crate::domain::common::EmailAddress;
7use crate::domain::ports::repositories::IdentityLinkRepository;
8use crate::domain::ports::resource_collection_access::{
9 ResourceCollectionAccessError, ResourceCollectionAccessService,
10};
11use std::sync::Arc;
12
13pub struct GrantUserResourceAccessUseCase {
17 identity_repo: Arc<dyn IdentityLinkRepository>,
18 collection_access: Arc<dyn ResourceCollectionAccessService>,
19 collection_ids: Vec<String>,
21}
22
23impl GrantUserResourceAccessUseCase {
24 pub fn new(
25 identity_repo: Arc<dyn IdentityLinkRepository>,
26 collection_access: Arc<dyn ResourceCollectionAccessService>,
27 collection_ids: Vec<String>,
28 ) -> Self {
29 Self {
30 identity_repo,
31 collection_access,
32 collection_ids,
33 }
34 }
35
36 pub async fn execute(
37 &self,
38 external_system: ExternalSystem,
39 external_user_id: String,
40 email: EmailAddress,
41 ) -> Result<(), ApplicationError> {
42 let mut identity = self.resolve_or_create_identity_link(&email).await?;
43 self.link_external_identity(&mut identity, external_system, external_user_id)?;
44
45 self.grant_access_to_all_resources(&email).await?;
47
48 self.save_identity_link(identity).await?;
50 Ok(())
51 }
52
53 async fn resolve_or_create_identity_link(
54 &self,
55 email: &EmailAddress,
56 ) -> Result<IdentityLink, ApplicationError> {
57 match self.identity_repo.find_by_email(email).await? {
58 Some(existing) => Ok(existing),
59 None => Ok(IdentityLink::new(email.clone())),
60 }
61 }
62
63 fn link_external_identity(
64 &self,
65 identity: &mut IdentityLink,
66 external_system: ExternalSystem,
67 external_user_id: String,
68 ) -> Result<(), ApplicationError> {
69 if identity.has_identity_for_system(&external_system) {
71 return Err(ApplicationError::ExternalSystemAlreadyLinked {
72 email: identity.email().as_str().to_string(),
73 external_system: external_system.as_str().to_string(),
74 });
75 }
76
77 let external_identity = ExternalIdentity::new(external_system, external_user_id);
78 identity.link_external_identity(external_identity)?;
79 Ok(())
80 }
81
82 async fn grant_access_to_all_resources(
83 &self,
84 email: &EmailAddress,
85 ) -> Result<(), ApplicationError> {
86 for collection_id in &self.collection_ids {
87 match self
88 .collection_access
89 .grant_access(collection_id, email)
90 .await
91 {
92 Ok(_) => {
93 }
95 Err(ResourceCollectionAccessError::AlreadyGranted(_)) => {
96 }
98 Err(e) => {
99 tracing::warn!(
101 "Failed to grant access to collection '{}' for {}: {}",
102 collection_id,
103 email.as_str(),
104 e
105 );
106 }
107 }
108 }
109
110 Ok(())
111 }
112
113 async fn save_identity_link(&self, identity: IdentityLink) -> Result<(), ApplicationError> {
114 self.identity_repo.save(identity).await?;
115 Ok(())
116 }
117}