1use crate::delegation_evidence::{
2 build_filter_delegation_request, verify_delegation_evidence, DelegationEvidence,
3 DelegationTarget, Environment, Policy, Resource, ResourceRules, ResourceTarget,
4};
5use crate::ishare;
6
7use super::delegation_request::{build_simple_delegation_request, DelegationRequestContainer};
8use super::ishare::{IshareClaimsWithExtra, IshareError, ISHARE};
9use base64::prelude::*;
10
11use chrono::Utc;
12use jsonwebtoken::TokenData;
13use reqwest::header::AUTHORIZATION;
14use reqwest::StatusCode;
15use serde::{Deserialize, Serialize};
16use uuid::Uuid;
17
18const YEAR_SECONDS: i64 = 31556926;
19
20#[derive(Deserialize)]
21pub struct LoginResponse {
22 pub access_token: String,
23 pub expires_in: i64,
24}
25
26#[derive(Deserialize)]
27pub struct DelegationTokenResponse {
28 delegation_token: String,
29}
30
31pub struct PDP<'a> {
32 ishare: &'a ISHARE,
33 eori: String,
34 url: String,
35}
36
37#[derive(Deserialize)]
38struct DelegationTokenClaims {
39 #[serde(rename = "delegationEvidence")]
40 pub delegation_evidence: DelegationEvidence,
41}
42
43#[derive(Serialize)]
44#[serde(rename_all = "camelCase")]
45struct PolicySetInsert {
46 policy_issuer: String,
47 licences: Vec<String>,
48 max_delegation_depth: i32,
49 target: DelegationTarget,
50 policies: Vec<Policy>,
51}
52
53#[derive(Deserialize)]
54pub struct PolicySetInsertResponse {
55 pub uuid: Uuid,
56}
57
58impl<'a> PDP<'a> {
59 pub fn new(ishare: &'a ISHARE, eori: String, url: String) -> PDP<'a> {
60 return Self { ishare, eori, url };
61 }
62
63 pub async fn authorize(
64 &self,
65 access_token: &str,
66 action: &str,
67 access_subject: &str,
68 policy_issuer: &str,
69 resource_type: &str,
70 identifiers: Option<Vec<String>>,
71 attributes: Option<Vec<String>>,
72 ) -> Result<bool, IshareError> {
73 let delegation_request = self.create_delegation_request(
74 action,
75 access_subject,
76 policy_issuer,
77 resource_type,
78 identifiers,
79 attributes,
80 );
81
82 let delegation_token = self.call_pdp(access_token, &delegation_request).await?;
83
84 return self
85 .check_delegation_token(&delegation_token, resource_type)
86 .map_err(|e| IshareError {
87 message: format!("{:?}", e),
88 });
89 }
90
91 fn check_delegation_token(
92 &self,
93 delegation_token: &str,
94 resource_type: &str,
95 ) -> Result<bool, ishare::DecodeTokenError> {
96 let decoded: TokenData<IshareClaimsWithExtra<DelegationTokenClaims>> =
97 self.ishare
98 .decode_token_custom_claims::<DelegationTokenClaims>(delegation_token, None)?;
99
100 let de = decoded.claims.extra.delegation_evidence;
101
102 let authorized = verify_delegation_evidence(&de, resource_type.to_string());
103
104 return Ok(authorized);
105 }
106
107 async fn call_pdp(
108 &self,
109 access_token: &str,
110 delegation_request: &DelegationRequestContainer,
111 ) -> Result<String, IshareError> {
112 let target_url = format!("{}/delegation", self.url);
113
114 let response = reqwest::Client::new()
115 .post(target_url)
116 .header("Authorization", format!("Bearer {access_token}"))
117 .json(delegation_request)
118 .send()
119 .await
120 .map_err(|e| IshareError {
121 message: e.to_string(),
122 })?
123 .error_for_status()
124 .map_err(|e| IshareError {
125 message: e.to_string(),
126 })?
127 .json::<DelegationTokenResponse>()
128 .await
129 .map_err(|e| IshareError {
130 message: e.to_string(),
131 })?;
132
133 Ok(response.delegation_token)
134 }
135
136 pub async fn put_policy_set(
137 &self,
138 access_token: &str,
139 policy_issuer: &str,
140 access_subject: &str,
141 service_provider: &str,
142 resource_type: String,
143 actions: Vec<String>,
144 identifiers: Option<Vec<String>>,
145 ) -> Result<PolicySetInsertResponse, IshareError> {
146 let target_url = format!("{}/policy-set", self.url);
147
148 let actual_identifiers = match identifiers {
149 None => vec!["*".to_owned()],
150 Some(id) => id,
151 };
152
153 let actual_attributes = vec!["*".to_owned()];
154
155 let service_providers = vec![service_provider.to_owned()];
156
157 let new_policy = Policy {
158 rules: vec![ResourceRules {
159 effect: "Permit".to_owned(),
160 }],
161 target: ResourceTarget {
162 actions,
163 resource: Resource {
164 resource_type,
165 identifiers: actual_identifiers,
166 attributes: actual_attributes,
167 },
168 environment: Some(Environment { service_providers }),
169 },
170 };
171
172 let policy_set = PolicySetInsert {
173 policy_issuer: policy_issuer.to_owned(),
174 licences: vec!["ISHARE.0001".to_owned()],
175 max_delegation_depth: 1,
176 policies: vec![new_policy],
177 target: DelegationTarget {
178 access_subject: access_subject.to_owned(),
179 },
180 };
181
182 let response = reqwest::Client::new()
183 .post(target_url)
184 .header("Authorization", format!("Bearer {access_token}"))
185 .json(&policy_set)
186 .send()
187 .await
188 .map_err(|e| IshareError {
189 message: e.to_string(),
190 })?
191 .error_for_status()
192 .map_err(|e| IshareError {
193 message: e.to_string(),
194 })?
195 .json::<PolicySetInsertResponse>()
196 .await
197 .map_err(|e| IshareError {
198 message: e.to_string(),
199 })?;
200
201 Ok(response)
202 }
203
204 pub async fn remove_policy_set(
205 &self,
206 access_token: &str,
207 policy_set_id: &str,
208 ) -> Result<(), IshareError> {
209 let target_url = format!("{}/policy-set/{}", self.url, policy_set_id);
210
211 reqwest::Client::new()
212 .delete(target_url)
213 .header("Authorization", format!("Bearer {access_token}"))
214 .send()
215 .await
216 .map_err(|e| IshareError {
217 message: e.to_string(),
218 })?
219 .error_for_status()
220 .map_err(|e| IshareError {
221 message: e.to_string(),
222 })?;
223
224 Ok(())
225 }
226
227 pub async fn put_policy_filter(
228 &self,
229 access_token: &str,
230 policy_issuer: &str,
231 access_subject: &str,
232 service_provider: &str,
233 resource_type: String,
234 actions: Vec<String>,
235 identifiers: Vec<String>,
236 ) -> Result<DelegationEvidence, IshareError> {
237 let target_url = format!("{}/ar/policy", self.url);
238 let policies_delegation_response = self
239 .get_policies(&access_token, policy_issuer, access_subject)
240 .await?;
241
242 let policies = match policies_delegation_response {
243 Some(de) => de.policy_sets.get(0).map_or(vec![], |p| p.policies.clone()),
244 None => vec![],
245 };
246
247 let now = Utc::now().timestamp();
248 let delegation_request = build_filter_delegation_request(
249 now,
250 now + YEAR_SECONDS,
251 policy_issuer.to_owned(),
252 access_subject.to_owned(),
253 resource_type,
254 service_provider.to_owned(),
255 actions,
256 identifiers,
257 vec!["*".to_owned()],
258 policies,
259 );
260
261 let response = reqwest::Client::new()
262 .post(target_url)
263 .header("Authorization", format!("Bearer {access_token}"))
264 .json(&delegation_request)
265 .send()
266 .await
267 .map_err(|e| IshareError {
268 message: e.to_string(),
269 })?
270 .error_for_status()
271 .map_err(|e| IshareError {
272 message: e.to_string(),
273 })?
274 .json::<DelegationEvidence>()
275 .await
276 .map_err(|e| IshareError {
277 message: e.to_string(),
278 })?;
279
280 Ok(response)
281 }
282
283 async fn get_policies(
284 &self,
285 access_token: &str,
286 issuer: &str,
287 access_subject: &str,
288 ) -> Result<Option<DelegationEvidence>, IshareError> {
289 let target_url = format!(
290 "{}/ar/policy?issuer={issuer}&access_subject={access_subject}",
291 self.url
292 );
293 let response = reqwest::Client::new()
294 .get(target_url)
295 .header("Authorization", format!("Bearer {access_token}"))
296 .send()
297 .await
298 .map_err(|e| IshareError {
299 message: e.to_string(),
300 })?;
301
302 if response.status() == StatusCode::NOT_FOUND {
303 return Ok(None);
304 }
305
306 let result = response
307 .error_for_status()
308 .map_err(|e| IshareError {
309 message: e.to_string(),
310 })?
311 .json::<DelegationEvidence>()
312 .await
313 .map_err(|e| IshareError {
314 message: e.to_string(),
315 })?;
316
317 return Ok(Some(result));
318 }
319
320 fn create_delegation_request(
321 &self,
322 action: &str,
323 access_subject: &str,
324 policy_issuer: &str,
325 resource_type: &str,
326 identifiers: Option<Vec<String>>,
327 attributes: Option<Vec<String>>,
328 ) -> DelegationRequestContainer {
329 let service_provider = self.ishare.get_client_eori();
330 let actions = vec![action.to_string()];
331
332 let delegation_request = build_simple_delegation_request(
333 policy_issuer.to_string(),
334 access_subject.to_string(),
335 resource_type.to_string(),
336 service_provider,
337 actions,
338 identifiers,
339 attributes,
340 );
341
342 delegation_request
343 }
344
345 pub async fn connect_admin(
346 &self,
347 admin_username: &str,
348 admin_password: &str,
349 pdp_app_id: &str,
350 pdp_app_secret: &str,
351 ) -> Result<LoginResponse, IshareError> {
352 let form_data = vec![
353 ("grant_type", "password"),
354 ("username", admin_username),
355 ("password", admin_password),
356 ];
357
358 let target_url = format!("{}/oauth2/token", self.url);
359 let basic_auth = base64::engine::general_purpose::STANDARD
360 .encode(format!("{pdp_app_id}:{pdp_app_secret}"));
361
362 let response = reqwest::Client::new()
363 .post(target_url)
364 .form(&form_data)
365 .header("Content-Type", "application/x-www-form-urlencoded")
366 .header(AUTHORIZATION, format!("Basic {basic_auth}"))
367 .send()
368 .await
369 .map_err(|e| IshareError {
370 message: e.to_string(),
371 })?
372 .error_for_status()
373 .map_err(|e| IshareError {
374 message: e.to_string(),
375 })?
376 .json::<LoginResponse>()
377 .await
378 .map_err(|e| IshareError {
379 message: e.to_string(),
380 })?;
381
382 return Ok(response);
383 }
384
385 pub async fn connect(&self) -> Result<LoginResponse, IshareError> {
386 let client_assertion = self.ishare.create_client_assertion(self.eori.clone())?;
387
388 let client_id = self.ishare.get_client_eori();
389
390 let form_data = vec![
391 ("grant_type", "client_credentials"),
392 (
393 "client_assertion_type",
394 "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
395 ),
396 ("scope", "iSHARE"),
397 ("client_id", &client_id),
398 ("client_assertion", &client_assertion),
399 ];
400
401 let target_url = format!("{}/connect/machine/token", self.url);
402
403 let response = reqwest::Client::new()
404 .post(target_url)
405 .form(&form_data)
406 .header("Content-Type", "application/x-www-form-urlencoded")
407 .send()
408 .await
409 .map_err(|e| IshareError {
410 message: e.to_string(),
411 })?
412 .error_for_status()
413 .map_err(|e| IshareError {
414 message: e.to_string(),
415 })?
416 .json::<LoginResponse>()
417 .await
418 .map_err(|e| IshareError {
419 message: e.to_string(),
420 })?;
421
422 return Ok(response);
423 }
424}