tap_msg/examples/
policy_examples.rs

1//! Examples for using policies according to TAIP-7.
2
3use crate::didcomm::PlainMessage;
4use crate::error::Result;
5use crate::message::{
6    policy::{Policy, RequireAuthorization, RequirePresentation, RequireProofOfControl},
7    tap_message_trait::{Authorizable, TapMessageBody},
8    Authorize, Participant, Transfer, UpdatePolicies,
9};
10
11use std::collections::HashMap;
12use std::str::FromStr;
13use tap_caip::AssetId;
14
15/// This example demonstrates how to create a participant with policies
16pub fn create_participant_with_policies_example() -> Result<Participant> {
17    // Create a RequireAuthorization policy
18    let auth_policy = RequireAuthorization {
19        from: Some(vec!["did:example:alice".to_string()]),
20        from_role: None,
21        from_agent: None,
22        purpose: Some("Authorization required from Alice".to_string()),
23    };
24
25    // Create a RequirePresentation policy
26    let presentation_policy = RequirePresentation {
27        context: Some(vec![
28            "https://www.w3.org/2018/credentials/v1".to_string(),
29            "https://w3id.org/security/suites/ed25519-2020/v1".to_string(),
30        ]),
31        from: Some(vec!["did:example:bob".to_string()]),
32        from_role: None,
33        from_agent: None,
34        about_party: Some("originator".to_string()),
35        about_agent: None,
36        purpose: Some("Please provide KYC credentials".to_string()),
37        presentation_definition: Some("https://example.com/presentations/kyc".to_string()),
38        credentials: None,
39    };
40
41    // Create the participant with policies
42    let participant = Participant {
43        id: "did:example:charlie".to_string(),
44        role: Some("beneficiary".to_string()),
45        leiCode: None,
46        name: None,
47        policies: Some(vec![
48            Policy::RequireAuthorization(auth_policy),
49            Policy::RequirePresentation(presentation_policy),
50        ]),
51    };
52
53    Ok(participant)
54}
55
56/// This example demonstrates how to update policies for a transaction
57pub fn update_policies_example(
58    transaction_id: &str,
59    creator_did: &str,
60    recipients: &[&str],
61) -> Result<PlainMessage> {
62    // Create a proof of control policy
63    let proof_policy = RequireProofOfControl {
64        from: Some(vec!["did:example:dave".to_string()]),
65        from_role: None,
66        from_agent: None,
67        address_id: "eip155:1:0x1234567890123456789012345678901234567890".to_string(),
68        purpose: Some("Please prove control of your account".to_string()),
69    };
70
71    // Create an UpdatePolicies message
72    let update = UpdatePolicies {
73        transaction_id: transaction_id.to_string(),
74        policies: vec![Policy::RequireProofOfControl(proof_policy)],
75    };
76
77    // Convert the update to a DIDComm message
78    let participants = recipients
79        .iter()
80        .filter(|&&did| did != creator_did)
81        .copied()
82        .collect::<Vec<_>>();
83    let mut message = update.to_didcomm_with_route(creator_did, participants)?;
84
85    // Set the thread ID to link this message to the existing thread
86    message.thid = Some(transaction_id.to_string());
87
88    Ok(message)
89}
90
91/// This example demonstrates a complete workflow for adding and updating policies
92pub fn policy_workflow_example() -> Result<()> {
93    println!("=== Starting Policy Workflow Example ===");
94
95    // Define DIDs for our example
96    let originator_did = "did:example:originator";
97    let beneficiary_did = "did:example:beneficiary";
98    let sender_vaspd_did = "did:example:sender_vasp";
99    let receiver_vaspd_did = "did:example:receiver_vasp";
100
101    // Step 1: Create a beneficiary with policies
102    println!("Step 1: Creating beneficiary with policies");
103    let auth_policy = RequireAuthorization {
104        from: Some(vec![originator_did.to_string()]),
105        from_role: None,
106        from_agent: None,
107        purpose: Some("Authorization required from originator".to_string()),
108    };
109
110    let beneficiary = Participant {
111        id: beneficiary_did.to_string(),
112        role: Some("beneficiary".to_string()),
113        leiCode: None,
114        name: None,
115        policies: Some(vec![Policy::RequireAuthorization(auth_policy)]),
116    };
117    println!("  Created beneficiary with policies: {:?}", beneficiary);
118
119    // Step 2: Create a transfer ID (this would normally be generated in practice)
120    let transfer_id = "transfer_12345";
121    println!("Step 2: Created transfer ID: {}", transfer_id);
122
123    // Step 3: Sender VASP wants to add a presentation requirement policy
124    println!("Step 3: Sender VASP adds a presentation requirement");
125    let presentation_policy = RequirePresentation {
126        context: Some(vec!["https://www.w3.org/2018/credentials/v1".to_string()]),
127        from: Some(vec![beneficiary_did.to_string()]),
128        from_role: None,
129        from_agent: None,
130        about_party: Some("beneficiary".to_string()),
131        about_agent: None,
132        purpose: Some("Please provide identity credentials".to_string()),
133        presentation_definition: None,
134        credentials: Some(HashMap::from([(
135            "type".to_string(),
136            vec!["IdentityCredential".to_string()],
137        )])),
138    };
139
140    let update_message = UpdatePolicies {
141        transaction_id: transfer_id.to_string(),
142        policies: vec![Policy::RequirePresentation(presentation_policy)],
143    };
144
145    // Convert to DIDComm message with proper routing
146    let participants = [
147        originator_did,
148        beneficiary_did,
149        sender_vaspd_did,
150        receiver_vaspd_did,
151    ];
152    let to = participants
153        .iter()
154        .filter(|&&did| did != sender_vaspd_did)
155        .copied()
156        .collect::<Vec<_>>();
157    let mut message = update_message.to_didcomm_with_route(sender_vaspd_did, to)?;
158
159    // Link to our transfer thread
160    message.thid = Some(transfer_id.to_string());
161
162    println!("  Created UpdatePolicies message: {:?}", message);
163    println!("  This message will be routed to all participants");
164
165    println!("=== Policy Workflow Example Completed ===");
166    Ok(())
167}
168
169/// This example demonstrates the use of the Authorizable trait's update_policies method
170pub fn create_update_policies_using_authorizable_example(
171    original_message: &Result<PlainMessage>,
172    policies: Vec<Policy>,
173    _transaction_id: &str,
174    creator_did: &str,
175    participant_dids: &[String],
176) -> Result<PlainMessage> {
177    // 1. Extract the body from the original DIDComm message
178    let body_json = original_message
179        .as_ref()
180        .map_err(Clone::clone)?
181        .body
182        .clone();
183    // 2. Deserialize the body into a Transfer struct
184    let transfer_body: Transfer = serde_json::from_value(body_json.clone())
185        .map_err(|e| crate::error::Error::SerializationError(e.to_string()))?;
186    // 3. Call update_policies on the Transfer struct (Authorizable trait impl)
187    // Extract or generate a transaction ID
188    let transaction_id = transfer_body.transaction_id.clone();
189    // Call update_policies on the Transfer struct (Authorizable trait impl)
190    let update_policies_message = transfer_body.update_policies(transaction_id, policies);
191
192    // Convert the update to a DIDComm message
193    let mut update_policies_reply = update_policies_message.to_didcomm(creator_did)?;
194
195    // Set thread ID to maintain conversation
196    update_policies_reply.thid = Some(original_message.as_ref().map_err(Clone::clone)?.id.clone());
197
198    // Set recipients
199    update_policies_reply.to = participant_dids.iter().map(|s| s.to_string()).collect();
200
201    Ok(update_policies_reply)
202}
203
204/// This example demonstrates a modified policy workflow using the Authorizable trait
205pub fn policy_workflow_with_authorizable_example() -> Result<()> {
206    println!("=== Starting Policy Workflow with Authorizable Example ===");
207
208    // Define DIDs for our example
209    let originator_did = "did:example:originator";
210    let beneficiary_did = "did:example:beneficiary";
211    let sender_vasp_did = "did:example:sender_vasp";
212    let receiver_vasp_did = "did:example:receiver_vasp";
213
214    // Step 1: Create a transfer message to initiate the workflow
215    let transfer = Transfer {
216        transaction_id: uuid::Uuid::new_v4().to_string(),
217        asset: AssetId::from_str("eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48")
218            .unwrap(),
219        originator: Participant {
220            id: originator_did.to_string(),
221            role: Some("originator".to_string()),
222            leiCode: None,
223            name: None,
224            policies: None,
225        },
226        beneficiary: Some(Participant {
227            id: beneficiary_did.to_string(),
228            role: Some("beneficiary".to_string()),
229            leiCode: None,
230            name: None,
231            policies: None,
232        }),
233        amount: "100.00".to_string(),
234        memo: None,
235        agents: vec![
236            Participant {
237                id: sender_vasp_did.to_string(),
238                role: Some("sender_vasp".to_string()),
239                leiCode: None,
240                name: None,
241                policies: None,
242            },
243            Participant {
244                id: receiver_vasp_did.to_string(),
245                role: Some("receiver_vasp".to_string()),
246                leiCode: None,
247                name: None,
248                policies: None,
249            },
250        ],
251        settlement_id: None,
252        metadata: HashMap::new(),
253    };
254
255    // Convert the transfer to a DIDComm message
256    let transfer_message = transfer.to_didcomm_with_route(
257        originator_did,
258        [beneficiary_did, sender_vasp_did, receiver_vasp_did]
259            .iter()
260            .copied(),
261    )?;
262
263    println!("Transfer message created: {:?}", transfer_message);
264
265    // Step 2: Create an UpdatePolicies message using the Authorizable trait
266    // This would typically be created by a VASP to enforce compliance
267    let participants = [
268        originator_did.to_string(),
269        beneficiary_did.to_string(),
270        sender_vasp_did.to_string(),
271        receiver_vasp_did.to_string(),
272    ];
273
274    let cloned_transfer_id = transfer_message.id.clone();
275    let update_policies_message = create_update_policies_using_authorizable_example(
276        &Ok(transfer_message),
277        vec![],
278        &cloned_transfer_id,
279        sender_vasp_did,
280        &participants,
281    )?;
282
283    println!(
284        "Update policies message created: {:?}",
285        update_policies_message
286    );
287
288    // Step 3: Create an authorization message in response to the updated policies
289    // Use the Authorizable trait's authorize method
290    let authorize_body =
291        transfer.authorize(Some("Authorization with policy constraints".to_string()));
292
293    // Create a reply to the update policies message
294    let mut authorize_reply = authorize_body.to_didcomm(beneficiary_did)?;
295
296    // Set thread ID to maintain conversation
297    authorize_reply.thid = Some(update_policies_message.id.clone());
298
299    // Set recipients
300    authorize_reply.to = participants.iter().map(|s| s.to_string()).collect();
301
302    println!("Authorization message created: {:?}", authorize_reply);
303
304    Ok(())
305}
306
307/// This example demonstrates a modified policy workflow using the Authorizable trait
308pub fn create_authorize_example() -> Result<()> {
309    // Create an example Authorize message body
310    let authorize_message = Authorize {
311        transaction_id: "transfer_12345".to_string(),
312        note: Some("Authorized with policy constraints".to_string()),
313    };
314
315    println!("Authorize message: {:#?}", authorize_message);
316
317    Ok(())
318}