koprogo_api/infrastructure/external/
signature_provider_universign.rs1use super::signature_provider_common::{compute_document_hmac, RestSignatureProvider, RetryPolicy};
14use crate::application::ports::electronic_signature_provider::{
15 ElectronicSignatureProvider, SignatureProviderError, SignatureRequest, SignatureRequestAck,
16};
17use crate::domain::plateforme::{QualifiedSignature, SignatureProviderKind};
18use async_trait::async_trait;
19use chrono::Utc;
20use uuid::Uuid;
21
22pub enum UniversignSignatureProvider {
23 Http(RestSignatureProvider),
24 Simulated {
26 hmac_secret: Vec<u8>,
27 },
28}
29
30impl UniversignSignatureProvider {
31 pub fn new(base_url: String, hmac_secret: Vec<u8>) -> Self {
32 Self::Http(RestSignatureProvider::new(
33 SignatureProviderKind::Universign,
34 base_url,
35 hmac_secret,
36 ))
37 }
38
39 pub fn simulated(hmac_secret: Vec<u8>) -> Self {
41 Self::Simulated { hmac_secret }
42 }
43
44 pub fn with_retry_policy(self, retry_policy: RetryPolicy) -> Self {
46 match self {
47 Self::Http(inner) => Self::Http(inner.with_retry_policy(retry_policy)),
48 simulated => simulated,
49 }
50 }
51}
52
53fn simulated_reference(document_id: Uuid, subject_user_id: Uuid) -> String {
57 format!("sim:{document_id}:{subject_user_id}")
58}
59
60fn parse_simulated_reference(reference: &str) -> Result<(Uuid, Uuid), SignatureProviderError> {
61 let invalid = || {
62 SignatureProviderError::InvalidResponse(
63 SignatureProviderKind::Universign.to_string(),
64 format!("référence simulée invalide : {reference}"),
65 )
66 };
67 let rest = reference.strip_prefix("sim:").ok_or_else(invalid)?;
68 let mut parts = rest.split(':');
69 let document_id = parts
70 .next()
71 .and_then(|s| Uuid::parse_str(s).ok())
72 .ok_or_else(invalid)?;
73 let subject_user_id = parts
74 .next()
75 .and_then(|s| Uuid::parse_str(s).ok())
76 .ok_or_else(invalid)?;
77 Ok((document_id, subject_user_id))
78}
79
80#[async_trait]
81impl ElectronicSignatureProvider for UniversignSignatureProvider {
82 fn kind(&self) -> SignatureProviderKind {
83 SignatureProviderKind::Universign
84 }
85
86 async fn request_signature(
87 &self,
88 request: SignatureRequest,
89 ) -> Result<SignatureRequestAck, SignatureProviderError> {
90 match self {
91 Self::Http(inner) => inner.request_signature(request).await,
92 Self::Simulated { hmac_secret } => {
93 let document_hash = compute_document_hmac(hmac_secret, &request.document_bytes);
94 Ok(SignatureRequestAck {
95 provider_reference: simulated_reference(
96 request.document_id,
97 request.subject_user_id,
98 ),
99 document_hash,
100 })
101 }
102 }
103 }
104
105 async fn fetch_signature(
106 &self,
107 provider_reference: &str,
108 expected_document_hash: &str,
109 ) -> Result<QualifiedSignature, SignatureProviderError> {
110 match self {
111 Self::Http(inner) => {
112 inner
113 .fetch_signature(provider_reference, expected_document_hash)
114 .await
115 }
116 Self::Simulated { .. } => {
117 let (document_id, subject_user_id) = parse_simulated_reference(provider_reference)?;
118 QualifiedSignature::new(
119 SignatureProviderKind::Universign,
120 subject_user_id,
121 document_id,
122 expected_document_hash.to_string(),
123 provider_reference.to_string(),
124 Utc::now(),
125 )
126 .map_err(|e| {
127 SignatureProviderError::InvalidResponse(
128 SignatureProviderKind::Universign.to_string(),
129 e.to_string(),
130 )
131 })
132 }
133 }
134 }
135}