potree_auth/project_asset/application/
service.rs1use std::path::Path;
2use std::sync::Arc;
3
4use http::HeaderMap;
5
6use super::super::ports::project_asset_store::ProjectAssetStore;
7use super::error::ProjectAssetsServiceError;
8use crate::authorization::domain::action::Action;
9use crate::authorization::ports::authorization_engine::AuthorizationEngine;
10use crate::common::domain::StaticAsset;
11use crate::project::domain::ProjectId;
12use crate::project::ports::project_repository::ProjectRepository;
13use crate::project_asset::domain::authorization::ProjectAssetResource;
14use crate::user::domain::User;
15
16#[derive(Debug, Clone)]
18pub struct ProjectAssetService {
19 project_datastore: Arc<dyn ProjectRepository>,
20 project_asset_store: Arc<dyn ProjectAssetStore>,
21 authorization_engine: Arc<dyn AuthorizationEngine>,
22}
23
24impl ProjectAssetService {
25 pub fn new(
26 project_datastore: Arc<dyn ProjectRepository>,
27 project_asset_store: Arc<dyn ProjectAssetStore>,
28 authorization_engine: Arc<dyn AuthorizationEngine>,
29 ) -> Self {
30 Self {
31 project_datastore,
32 project_asset_store,
33 authorization_engine,
34 }
35 }
36
37 pub async fn read_asset(
41 &self,
42 user: &Option<User>,
43 project_id: &ProjectId,
44 asset_path: &Path,
45 request_headers: Option<HeaderMap>,
46 ) -> Result<StaticAsset, ProjectAssetsServiceError> {
47 let project = self.project_datastore.read(project_id).await?;
48
49 let project_asset = ProjectAssetResource {
50 associated_project: &project,
51 asset_path,
52 };
53 self.authorization_engine
54 .can_on_instance(user, &Action::Read, &project_asset)?;
55
56 let asset_path = Path::new(project_id.as_str()).join(asset_path);
58
59 Ok(self
60 .project_asset_store
61 .get_asset(&asset_path, request_headers)
62 .await?)
63 }
64}
65
66#[cfg(test)]
67mod project_asset_service_tests {
68 use fake::Fake;
69 use fake::Faker;
70
71 use super::super::super::ports::project_asset_store::MockProjectAssetStore;
72 use super::*;
73 use crate::authorization::domain::error::AuthorizationEngineError;
74 use crate::authorization::ports::authorization_engine::MockAuthorizationEngine;
75 use crate::project::ports::project_repository::MockProjectRepository;
76
77 mod request_asset {
78
79 use super::*;
80
81 #[tokio::test]
82 async fn should_return_the_correct_error_if_user_not_authenticated() {
83 let mut project_datastore = MockProjectRepository::new();
85 project_datastore
86 .expect_read()
87 .return_const(Ok(Faker.fake()));
88 let mut authorization_engine = MockAuthorizationEngine::new();
89 authorization_engine
90 .expect_can_on_instance()
91 .return_const(Err(AuthorizationEngineError::NotAuthenticated));
92 let project_asset_store = MockProjectAssetStore::new();
93
94 let project_asset_service = ProjectAssetService::new(
95 Arc::new(project_datastore),
96 Arc::new(project_asset_store),
97 Arc::new(authorization_engine),
98 );
99
100 let res = project_asset_service
102 .read_asset(&Faker.fake(), &Faker.fake(), Path::new(""), Faker.fake())
103 .await;
104
105 assert!(matches!(
107 res,
108 Err(ProjectAssetsServiceError::NotAuthenticated)
109 ))
110 }
111 }
112
113 #[tokio::test]
114 async fn should_return_the_correct_error_if_user_not_authorized() {
115 let dummy_user = Faker.fake::<User>();
117
118 let mut project_datastore = MockProjectRepository::new();
119 project_datastore
120 .expect_read()
121 .return_const(Ok(Faker.fake()));
122 let mut authorization_engine = MockAuthorizationEngine::new();
123 authorization_engine
124 .expect_can_on_instance()
125 .return_const(Err(AuthorizationEngineError::NotAuthorized {
126 user: Box::new(dummy_user.clone()),
127 action: Action::Read,
128 resource_identifier: Some(Faker.fake()),
129 resource_type: Faker.fake(),
130 }));
131 let project_asset_store = MockProjectAssetStore::new();
132
133 let project_asset_service = ProjectAssetService::new(
134 Arc::new(project_datastore),
135 Arc::new(project_asset_store),
136 Arc::new(authorization_engine),
137 );
138
139 let res = project_asset_service
141 .read_asset(&Faker.fake(), &Faker.fake(), Path::new(""), Faker.fake())
142 .await;
143
144 assert!(matches!(
146 res,
147 Err(ProjectAssetsServiceError::NotAuthorized { .. })
148 ))
149 }
150}