pbd/dua/
policy.rs

1//! ### Background
2//! This module implements the `Fideslang` model and taxonomy in an effort to promote a standardized the approach of creating Data Usage Agreements.
3//!
4//! Credit is to be given to [fides](https://ethyca.github.io/fideslang/) and adheres to the fides licensing:
5//! + [license](https://github.com/ethyca/fides/blob/main/LICENSE)
6//! + [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)
7//!
8//! You can use the [Privacy Taxonomy Explorer](https://ethyca.github.io/fideslang/explorer/) for a graphic representation of the Fides classification groups.
9//!
10//! ### Usage
11//! A DUP (Data Usage Policy) allows us to define standardized usage policies that can be easily understood by a user, while also being able to apply it programmatically during the application runtime.
12//! These "Opt-In" policies can then be added to the DUA (Data Usage Agreement) so that applications and processors (e.g.: microservices) can dynamically determine if and how they are permitted to utilize the data prior to processing it.
13//!
14//! ```rust
15//! extern crate pbd;
16//!
17//! use pbd::dua::policy::{Condition, DUP};
18//! use pbd::dua::data_category::DataCategoryFactory;
19//! use pbd::dua::data_subject::DataSubjectFactory;
20//! use pbd::dua::data_use::DataUseFactory;
21//!
22//! fn get_defined_policy() -> DUP {
23//!    let category_factory = DataCategoryFactory::new();
24//!    let subject_factory = DataSubjectFactory::new();
25//!    let use_factory = DataUseFactory::new();
26//!
27//!    let mut dup = DUP::new(
28//!        "General Policy".to_string(),
29//!        "This is a high-level policy.".to_string(),
30//!        "1.0.1".to_string()
31//!    );    
32//!
33//!    // associate some classifications to the policy
34//!    dup.associate_category(category_factory.get_category_by_key("system.authentication".to_string()).unwrap());
35//!    dup.associate_category(category_factory.get_category_by_key("user.contact.email".to_string()).unwrap());
36//!    dup.associate_category(category_factory.get_category_by_key("user.contact.phone_number".to_string()).unwrap());
37//!    dup.associate_subject(subject_factory.get_subject_by_key("customer".to_string()).unwrap());
38//!    dup.associate_use(use_factory.get_use_by_key("essential.service.authentication".to_string()).unwrap());
39//!
40//!    dup
41//! }
42//!
43//! fn get_processor_conditions() -> Vec<Condition> {
44//!    let mut conditions: Vec<Condition> = Vec::new();
45//!    let category_factory = DataCategoryFactory::new();
46//!    let subject_factory = DataSubjectFactory::new();
47//!    let use_factory = DataUseFactory::new();
48//!    
49//!    conditions.push(Condition::Category(category_factory.get_category_by_key("user.contact.email".to_string()).unwrap().get_key()));
50//!    conditions.push(Condition::Subject(subject_factory.get_subject_by_key("customer".to_string()).unwrap().get_key()));
51//!    conditions.push(Condition::Use(use_factory.get_use_by_key("marketing.advertising.profiling".to_string()).unwrap().get_key()));
52//!
53//!    conditions
54//! }
55//!
56//! fn main() {
57//!    // A policy that defines the acceptable conditions for using the data
58//!    let mut policy = get_defined_policy();
59//!
60//!    // A list of conditions that the processor has been configured to apply to data
61//!    let conditions = get_processor_conditions();
62//!
63//!    // Check to see if the processor is permitted to use the data based on its privacy configurations
64//!    let conflicts = policy.match_conditions(conditions);
65//!
66//!    match conflicts.len() > 0 {
67//!       true => {
68//!          for conflict in conflicts.iter() {
69//!             println!("Blocked due to Condition key {}", conflict.to_string());
70//!          }
71//!       },
72//!       false => println!("Allowed - Process the data."),
73//!    }
74//! }
75//! ```
76use super::data_category::DataCategory;
77use super::data_subject::DataSubject;
78use super::data_use::DataUse;
79use derive_more::Display;
80use std::collections::BTreeMap;
81
82/// An Enum of any possible item keys that can be associated to a policy
83#[derive(Display, Clone)]
84pub enum Condition {
85    Category(String),
86    Subject(String),
87    Use(String),
88}
89
90/// Represents a Data Usage Policy (DUP)
91#[derive(Serialize, Deserialize, Debug, Clone)]
92pub struct DUP {
93    /// The common name of the Data Usage Policy, (e.g.: For Billing Purpose)
94    pub name: String,
95    /// A textual description of the Data Usage Policy
96    pub description: String,
97    /// The version of the policy, (e.g.: 1.0.0)
98    pub version: String,
99    // The lists of Data Categories associated with the policy
100    categories: BTreeMap<String, DataCategory>,
101    // The lists of Data Subjects associated with the policy
102    subjects: BTreeMap<String, DataSubject>,
103    // The lists of Data Uses associated with the policy
104    uses: BTreeMap<String, DataUse>,
105}
106
107impl DUP {
108    /// Constructs a new Data Usage Policy object
109    ///
110    /// # Arguments
111    ///
112    /// * nme: String - The textual name of the Data Usage Policy.</br>
113    /// * descr: String - A textual description of the Data Usage Policy.</br>
114    /// * ver: String - The version of the policy, (e.g.: 1.0.0).</br>
115    ///
116    /// #Example
117    ///
118    /// ```rust
119    /// extern crate pbd;
120    ///
121    /// use pbd::dua::policy::DUP;
122    ///
123    /// fn main() {
124    ///     let dup = DUP::new(
125    ///         "General Policy".to_string(),
126    ///         "This is a high-level policy.".to_string(),
127    ///         "1.0.1".to_string()
128    ///     );
129    /// }
130    /// ```
131    pub fn new(nme: String, descr: String, ver: String) -> Self {
132        DUP {
133            name: nme,
134            description: descr,
135            version: ver,
136            categories: BTreeMap::new(),
137            subjects: BTreeMap::new(),
138            uses: BTreeMap::new(),
139        }
140    }
141
142    /// Associates a DataCategory object to the policy
143    /// __NOTE__: Call this function to associate a new DataCategory objects or replace pre-associated DataCategory objects
144    ///
145    /// # Arguments
146    ///
147    /// * category: DataCategory - The Data Category to associate.</br>
148    ///
149    /// #Example
150    ///
151    /// ```rust
152    /// extern crate pbd;
153    ///
154    /// use pbd::dua::policy::DUP;
155    /// use pbd::dua::data_category::DataCategory;
156    ///
157    /// fn main() {
158    ///     let mut dup = DUP::new(
159    ///         "General Policy".to_string(),
160    ///         "This is a high-level policy.".to_string(),
161    ///         "1.0.1".to_string()
162    ///     );
163    ///
164    ///     dup.associate_category(DataCategory::new(
165    ///        "Authentication Data".to_string(),
166    ///        "Data used to manage access to the system.".to_string(),
167    ///        "system.authentication".to_string(),
168    ///        "default_organization".to_string(),
169    ///        Some("system".to_string()),
170    ///        None,                       
171    ///        false,
172    ///        true,
173    ///    ));
174    /// }
175    /// ```
176    pub fn associate_category(&mut self, category: DataCategory) {
177        self.categories.insert(category.get_key().clone(), category);
178    }
179
180    /// Associates a DataSubject object to the policy
181    /// __NOTE__: Call this function to associate a new DataSubject objects or replace pre-associated DataSubject objects
182    ///
183    /// # Arguments
184    ///
185    /// * subject: DataSubject - The Data Subject to associate.</br>
186    ///
187    /// #Example
188    ///
189    /// ```rust
190    /// extern crate pbd;
191    ///
192    /// use pbd::dua::policy::DUP;
193    /// use pbd::dua::data_subject::{DataRights, DataSubject, Right, Strategy};
194    ///
195    /// fn main() {
196    ///     let mut dup = DUP::new(
197    ///         "General Policy".to_string(),
198    ///         "This is a high-level policy.".to_string(),
199    ///         "1.0.1".to_string()
200    ///     );
201    ///
202    ///     let subject = DataSubject::new(
203    ///         "Consultant".to_string(),
204    ///         "An individual employed in a consultative/temporary capacity by the organization.".to_string(),
205    ///         "consultant".to_string(),
206    ///         "default_organization".to_string(),
207    ///         Some(vec!["work".to_string(), "temporary".to_string()]),
208    ///         Some(DataRights::new(Strategy::ALL, vec![Right::Informed, Right::Access])),
209    ///         false,
210    ///         false,
211    ///         true
212    ///     );
213    ///
214    ///     dup.associate_subject(subject);
215    /// }
216    /// ```
217    pub fn associate_subject(&mut self, subject: DataSubject) {
218        self.subjects.insert(subject.get_key().clone(), subject);
219    }
220
221    /// Associates a DataUse object to the policy
222    /// __NOTE__: Call this function to associate a new DataUse objects or replace pre-associated DataUse objects
223    ///
224    /// # Arguments
225    ///
226    /// * usage: DataUse - The Data Use to associate.</br>
227    ///
228    /// #Example
229    ///
230    /// ```rust
231    /// extern crate pbd;
232    ///
233    /// use pbd::dua::policy::DUP;
234    /// use pbd::dua::data_use::{DataUse, LegalBasis, SpecialCategory};
235    ///
236    /// fn main() {
237    ///     let mut dup = DUP::new(
238    ///         "General Policy".to_string(),
239    ///         "This is a high-level policy.".to_string(),
240    ///         "1.0.1".to_string()
241    ///     );
242    ///
243    ///     let datause = DataUse::new(
244    ///         "Provide the capability".to_string(),
245    ///         "Provide, give, or make available the product, service, application or system.".to_string(),
246    ///         "provide".to_string(),
247    ///         "default_organization".to_string(),
248    ///         None,
249    ///         Some(LegalBasis::LegitimateInterest),
250    ///         Some(SpecialCategory::VitalInterests),
251    ///         Some(vec!("marketing team".to_string(), "dog shelter".to_string())),
252    ///         false,
253    ///         Some("https://example.org/legitimate_interest_assessment".to_string()),
254    ///         None,
255    ///         false,
256    ///         true
257    ///     );
258    ///
259    ///     dup.associate_use(datause);
260    /// }
261    /// ```
262    pub fn associate_use(&mut self, usage: DataUse) {
263        self.uses.insert(usage.get_key().clone(), usage);
264    }
265
266    fn readable_description(&mut self, mut policy: String, line_feed: &str) -> String {
267        // Data Subjects
268        policy.push_str("Data will be collected from ");
269        match self.get_subjects().len() {
270            0 => {
271                policy.push_str("all types of users.");
272            }
273            _ => {
274                policy.push_str("the following types of users: ");
275                let count = self.get_subjects().len();
276                for (idx, subject) in self.get_subjects().iter().enumerate() {
277                    policy.push_str(&subject.name);
278                    let delimiter = match idx < count - 2 {
279                        true => ", ",
280                        false => match idx == count - 1 {
281                            true => ".",
282                            false => " and ",
283                        },
284                    };
285                    policy.push_str(delimiter);
286                }
287                policy.push_str(line_feed);
288            }
289        }
290
291        // Data Categories
292        policy.push_str("The data being collected will be ");
293        match self.get_categories().len() {
294            0 => {
295                policy.push_str("include all types of data.");
296            }
297            _ => {
298                policy.push_str("limited to the following data: ");
299                let count = self.get_categories().len();
300                for (idx, category) in self.get_categories().iter().enumerate() {
301                    policy.push_str(&category.name);
302                    let delimiter = match idx < count - 2 {
303                        true => ", ",
304                        false => match idx == count - 1 {
305                            true => ".",
306                            false => " and ",
307                        },
308                    };
309                    policy.push_str(delimiter);
310                }
311                policy.push_str(line_feed);
312            }
313        }
314
315        // Data Uses
316        policy.push_str("The data collected can be used for ");
317        match self.get_uses().len() {
318            0 => {
319                policy.push_str("various purposes.");
320            }
321            _ => {
322                policy.push_str("the following purposes: ");
323                let count = self.get_uses().len();
324                for (idx, usage) in self.get_uses().iter().enumerate() {
325                    policy.push_str(&usage.name);
326                    let delimiter = match idx < count - 2 {
327                        true => ", ",
328                        false => match idx == count - 1 {
329                            true => ".",
330                            false => " and ",
331                        },
332                    };
333                    policy.push_str(delimiter);
334                }
335                policy.push_str(line_feed);
336            }
337        }
338
339        return policy;
340    }
341
342    /// Converts the policy to a human readable format as html
343    /// _NOTE:_ You can apply custom styling by referencing the `class` attribute of the elements.
344    ///
345    /// #Example
346    ///
347    /// ```rust
348    /// extern crate pbd;
349    ///
350    /// use pbd::dua::policy::DUP;
351    /// use pbd::dua::data_category::DataCategoryFactory;
352    /// use pbd::dua::data_subject::DataSubjectFactory;
353    /// use pbd::dua::data_use::DataUseFactory;
354    ///
355    /// fn main() {
356    ///     let cfactory = DataCategoryFactory::new();
357    ///     let sfactory = DataSubjectFactory::new();
358    ///     let ufactory = DataUseFactory::new();
359    ///     let mut dup = DUP::new(
360    ///         "General Marketing Policy".to_string(),
361    ///         "This policy explains the manner in which your data will be used for marketing purposes.".to_string(),
362    ///         "1.0.0".to_string()
363    ///     );
364    ///
365    ///     dup.associate_category(
366    ///         cfactory
367    ///             .get_category_by_key("user.behavior.browsing_history".to_string())
368    ///             .unwrap(),
369    ///     );
370    ///     dup.associate_category(
371    ///         cfactory
372    ///             .get_category_by_key("user.behavior.media_consumption".to_string())
373    ///             .unwrap(),
374    ///     );
375    ///     dup.associate_subject(sfactory.get_subject_by_key("customer".to_string()).unwrap());
376    ///     dup.associate_subject(sfactory.get_subject_by_key("prospect".to_string()).unwrap());
377    ///     dup.associate_use(
378    ///         ufactory
379    ///             .get_use_by_key("marketing.advertising.profiling".to_string())
380    ///             .unwrap(),
381    ///     );
382    ///     dup.associate_use(
383    ///         ufactory
384    ///             .get_use_by_key("marketing.advertising.serving".to_string())
385    ///             .unwrap(),
386    ///     );
387    ///     dup.associate_use(
388    ///         ufactory
389    ///             .get_use_by_key("marketing.communications.email".to_string())
390    ///             .unwrap(),
391    ///     );
392    ///     
393    ///     print!("{}", dup.as_html());
394    ///
395    ///     /* <div class='dup' name='General Policy'>General Policy</div></br>
396    ///      * <div class='dup-verion' name='1.0.1'>(version: 1.0.1)</div></br></br>
397    ///      * <div class='dup-description' name='General Policy Description'><b>This is a high-level policy.</b></br></br>
398    ///      * <p>
399    ///      * Data will be collected from the following types of users: Customer and Prospect.</br>
400    ///      * The data being collected will be limited to the following data: Browsing History and Media Consumption.</br>
401    ///      * The data collected can be used for the following purposes: Profiling for Advertising, Essential for Serving Ads and Marketing Email Communications.</br>
402    ///      * </p></div>
403    ///     */
404    /// }
405    /// ```
406    pub fn as_html(&mut self) -> String {
407        let line_feed = "</br>";
408        let mut policy = String::new();
409        policy.push_str("<div class='dup' name='");
410        policy.push_str(&self.name);
411        policy.push_str("'>");
412        policy.push_str(&self.name);
413        policy.push_str("</div>");
414        policy.push_str(line_feed);
415        policy.push_str("<div class='dup-verion' name='");
416        policy.push_str(&self.version);
417        policy.push_str("'>");
418        policy.push_str("(version: ");
419        policy.push_str(&self.version);
420        policy.push_str(")");
421        policy.push_str("</div>");
422        policy.push_str(line_feed);
423        policy.push_str(line_feed);
424        policy.push_str("<div class='dup-description' name='");
425        policy.push_str(&self.name);
426        policy.push_str(" Description'><b>");
427        policy.push_str(&self.description);
428        policy.push_str("</b>");
429        policy.push_str(line_feed);
430        policy.push_str(line_feed);
431        policy.push_str("<p>");
432
433        policy = self.readable_description(policy, line_feed);
434        policy.push_str("</p>");
435        policy.push_str("</div>");
436
437        policy
438    }
439
440    /// Converts the policy to a human readable format as text
441    ///
442    /// #Example
443    ///
444    /// ```rust
445    /// extern crate pbd;
446    ///
447    /// use pbd::dua::policy::DUP;
448    /// use pbd::dua::data_category::DataCategoryFactory;
449    /// use pbd::dua::data_subject::DataSubjectFactory;
450    /// use pbd::dua::data_use::DataUseFactory;
451    ///
452    /// fn main() {
453    ///     let cfactory = DataCategoryFactory::new();
454    ///     let sfactory = DataSubjectFactory::new();
455    ///     let ufactory = DataUseFactory::new();
456    ///     let mut dup = DUP::new(
457    ///         "General Marketing Policy".to_string(),
458    ///         "This policy explains the manner in which your data will be used for marketing purposes.".to_string(),
459    ///         "1.0.0".to_string()
460    ///     );
461    ///
462    ///     dup.associate_category(
463    ///         cfactory
464    ///             .get_category_by_key("user.behavior.browsing_history".to_string())
465    ///             .unwrap(),
466    ///     );
467    ///     dup.associate_category(
468    ///         cfactory
469    ///             .get_category_by_key("user.behavior.media_consumption".to_string())
470    ///             .unwrap(),
471    ///     );
472    ///     dup.associate_subject(sfactory.get_subject_by_key("customer".to_string()).unwrap());
473    ///     dup.associate_subject(sfactory.get_subject_by_key("prospect".to_string()).unwrap());
474    ///     dup.associate_use(
475    ///         ufactory
476    ///             .get_use_by_key("marketing.advertising.profiling".to_string())
477    ///             .unwrap(),
478    ///     );
479    ///     dup.associate_use(
480    ///         ufactory
481    ///             .get_use_by_key("marketing.advertising.serving".to_string())
482    ///             .unwrap(),
483    ///     );
484    ///     dup.associate_use(
485    ///         ufactory
486    ///             .get_use_by_key("marketing.communications.email".to_string())
487    ///             .unwrap(),
488    ///     );
489    ///     
490    ///     print!("{}", dup.as_text());
491    ///
492    ///     /* General Marketing Policy
493    ///      * (version: 1.0.0)
494    ///      *
495    ///      * This policy explains the manner in which your data will be used for marketing purposes.
496    ///      *
497    ///      * Data will be collected from the following types of users: Customer and Prospect.
498    ///      * The data being collected will be limited to the following data: Browsing History and Media Consumption.
499    ///      * The data collected can be used for the following purposes: Profiling for Advertising, Essential for Serving Ads and Marketing Email Communications.
500    ///     */
501    /// }
502    /// ```
503    pub fn as_text(&mut self) -> String {
504        let line_feed = "\r\n";
505        let mut policy = String::new();
506        policy.push_str(&self.name);
507        policy.push_str(line_feed);
508        policy.push_str("(version: ");
509        policy.push_str(&self.version);
510        policy.push_str(")");
511        policy.push_str(line_feed);
512        policy.push_str(line_feed);
513
514        policy.push_str(&self.description);
515        policy.push_str(line_feed);
516        policy.push_str(line_feed);
517
518        policy = self.readable_description(policy, line_feed);
519
520        policy
521    }
522
523    /// Disassociates the specified DataCategory object from the policy using the key
524    ///
525    /// # Arguments
526    ///
527    /// * key: String - The key of the Data Category to disassociate.</br>
528    ///
529    /// #Example
530    ///
531    /// ```rust
532    /// extern crate pbd;
533    ///
534    /// use pbd::dua::policy::DUP;
535    /// use pbd::dua::data_category::DataCategory;
536    ///
537    /// fn main() {
538    ///     let mut dup = DUP::new(
539    ///         "General Policy".to_string(),
540    ///         "This is a high-level policy.".to_string(),
541    ///         "1.0.1".to_string()
542    ///     );
543    ///     let cat = DataCategory::new(
544    ///        "Authentication Data".to_string(),
545    ///        "Data used to manage access to the system.".to_string(),
546    ///        "system.authentication".to_string(),
547    ///        "default_organization".to_string(),
548    ///        Some("system".to_string()),
549    ///        None,                       
550    ///        false,
551    ///        true,
552    ///    );
553    ///
554    ///    dup.associate_category(cat.clone());
555    ///
556    ///    dup.disassociate_category(cat.get_key());
557    /// }
558    /// ```
559    pub fn disassociate_category(&mut self, key: String) {
560        self.categories.remove(&key);
561    }
562
563    /// Disassociates the specified DataSubject object from the policy using the key
564    ///
565    /// # Arguments
566    ///
567    /// * key: String - The key of the Data Subject to disassociate.</br>
568    ///
569    /// #Example
570    ///
571    /// ```rust
572    /// extern crate pbd;
573    ///
574    /// use pbd::dua::policy::DUP;
575    /// use pbd::dua::data_subject::{DataRights, DataSubject, Right, Strategy};
576    ///
577    /// fn main() {
578    ///     let mut dup = DUP::new(
579    ///         "General Policy".to_string(),
580    ///         "This is a high-level policy.".to_string(),
581    ///         "1.0.1".to_string()
582    ///     );
583    ///
584    ///     let subject = DataSubject::new(
585    ///         "Consultant".to_string(),
586    ///         "An individual employed in a consultative/temporary capacity by the organization.".to_string(),
587    ///         "consultant".to_string(),
588    ///         "default_organization".to_string(),
589    ///         Some(vec!["work".to_string(), "temporary".to_string()]),
590    ///         Some(DataRights::new(Strategy::ALL, vec![Right::Informed, Right::Access])),
591    ///         false,
592    ///         false,
593    ///         true
594    ///     );
595    ///
596    ///    dup.associate_subject(subject.clone());
597    ///
598    ///    dup.disassociate_subject(subject.get_key());
599    /// }
600    /// ```
601    pub fn disassociate_subject(&mut self, key: String) {
602        self.subjects.remove(&key);
603    }
604
605    /// Disassociates the specified DataUse object from the policy using the key
606    ///
607    /// # Arguments
608    ///
609    /// * key: String - The key of the Data Use to disassociate.</br>
610    ///
611    /// #Example
612    ///
613    /// ```rust
614    /// extern crate pbd;
615    ///
616    /// use pbd::dua::policy::DUP;
617    /// use pbd::dua::data_use::{DataUse, LegalBasis, SpecialCategory};
618    ///
619    /// fn main() {
620    ///     let mut dup = DUP::new(
621    ///         "General Policy".to_string(),
622    ///         "This is a high-level policy.".to_string(),
623    ///         "1.0.1".to_string()
624    ///     );
625    ///
626    ///     let datause = DataUse::new(
627    ///         "Provide the capability".to_string(),
628    ///         "Provide, give, or make available the product, service, application or system.".to_string(),
629    ///         "provide".to_string(),
630    ///         "default_organization".to_string(),
631    ///         None,
632    ///         Some(LegalBasis::LegitimateInterest),
633    ///         Some(SpecialCategory::VitalInterests),
634    ///         Some(vec!("marketing team".to_string(), "dog shelter".to_string())),
635    ///         false,
636    ///         Some("https://example.org/legitimate_interest_assessment".to_string()),
637    ///         None,
638    ///         false,
639    ///         true
640    ///     );
641    ///
642    ///    dup.associate_use(datause.clone());
643    ///
644    ///    dup.disassociate_use(datause.get_key());
645    /// }
646    /// ```
647    pub fn disassociate_use(&mut self, key: String) {
648        self.uses.remove(&key);
649    }
650
651    /// Constructs a DUP object from a serialized string
652    ///
653    /// # Arguments
654    ///
655    /// * serialized: &str - The string that represents the serialized object.</br>
656    ///
657    /// #Example
658    ///
659    /// ```rust
660    /// extern crate pbd;
661    ///
662    /// use pbd::dua::policy::DUP;
663    ///
664    /// fn main() {
665    ///     let serialized = r#"{"name":"General Policy","description":"This is a high-level policy.","version":"1.0.1","categories":{"system.authentication":{"name":"Authentication Data","description":"Data used to manage access to the system.","fides_key":"system.authentication","organization_fides_key":"default_organization","parent_key":"system","tags":null,"is_default":true,"active":true}},"subjects":{"consultant":{"name":"Consultant","description":"An individual employed in a consultative/temporary capacity by the organization.","fides_key":"consultant","organization_fides_key":"default_organization","tags":null,"rights":null,"automated_decisions_or_profiling":false,"is_default":true,"active":true}},"uses":{"essential.service.authentication":{"name":"Essential Service Authentication","description":"Authenticate users to the product, service, application or system.","fides_key":"essential.service.authentication","organization_fides_key":"default_organization","parent_key":"essential.service","legal_basis":null,"special_category":null,"recipent":null,"legitimate_interest":false,"legitimate_interest_impact_assessment":null,"tags":null,"is_default":true,"active":true}}}"#;
666    ///     let mut dup = DUP::from_serialized(&serialized);
667    ///     
668    ///     assert_eq!(dup.get_categories().len(), 1);
669    /// }
670    /// ```
671    pub fn from_serialized(serialized: &str) -> DUP {
672        serde_json::from_str(&serialized).unwrap()
673    }
674
675    /// Retrieves all the associated DataCategory objects
676    ///
677    /// #Example
678    ///
679    /// ```rust
680    /// extern crate pbd;
681    ///
682    /// use pbd::dua::policy::DUP;
683    /// use pbd::dua::data_category::DataCategory;
684    ///
685    /// fn main() {
686    ///     let mut dup = DUP::new(
687    ///         "General Policy".to_string(),
688    ///         "This is a high-level policy.".to_string(),
689    ///         "1.0.1".to_string()
690    ///     );
691    ///
692    ///     dup.associate_category(DataCategory::new(
693    ///        "Authentication Data".to_string(),
694    ///        "Data used to manage access to the system.".to_string(),
695    ///        "system.authentication".to_string(),
696    ///        "default_organization".to_string(),
697    ///        Some("system".to_string()),
698    ///        None,                       
699    ///        false,
700    ///        true,
701    ///    ));
702    ///
703    ///    assert_eq!(dup.get_categories().len(), 1);
704    /// }
705    /// ```
706    pub fn get_categories(&mut self) -> Vec<DataCategory> {
707        self.categories.clone().into_values().collect()
708    }
709
710    /// Retrieves all the associated DataSubject objects
711    ///
712    /// #Example
713    ///
714    /// ```rust
715    /// extern crate pbd;
716    ///
717    /// use pbd::dua::policy::DUP;
718    /// use pbd::dua::data_subject::{DataRights, DataSubject, Right, Strategy};
719    ///
720    /// fn main() {
721    ///     let mut dup = DUP::new(
722    ///         "General Policy".to_string(),
723    ///         "This is a high-level policy.".to_string(),
724    ///         "1.0.1".to_string()
725    ///     );
726    ///
727    ///     let subject = DataSubject::new(
728    ///         "Consultant".to_string(),
729    ///         "An individual employed in a consultative/temporary capacity by the organization.".to_string(),
730    ///         "consultant".to_string(),
731    ///         "default_organization".to_string(),
732    ///         Some(vec!["work".to_string(), "temporary".to_string()]),
733    ///         Some(DataRights::new(Strategy::ALL, vec![Right::Informed, Right::Access])),
734    ///         false,
735    ///         false,
736    ///         true
737    ///     );
738    ///
739    ///     dup.associate_subject(subject);
740    ///
741    ///    assert_eq!(dup.get_subjects().len(), 1);
742    /// }
743    /// ```
744    pub fn get_subjects(&mut self) -> Vec<DataSubject> {
745        self.subjects.clone().into_values().collect()
746    }
747
748    /// Retrieves all the associated DataUse objects
749    ///
750    /// #Example
751    ///
752    /// ```rust
753    /// extern crate pbd;
754    ///
755    /// use pbd::dua::policy::DUP;
756    /// use pbd::dua::data_use::{DataUse, LegalBasis, SpecialCategory};
757    ///
758    /// fn main() {
759    ///     let mut dup = DUP::new(
760    ///         "General Policy".to_string(),
761    ///         "This is a high-level policy.".to_string(),
762    ///         "1.0.1".to_string()
763    ///     );
764    ///
765    ///     let datause = DataUse::new(
766    ///         "Provide the capability".to_string(),
767    ///         "Provide, give, or make available the product, service, application or system.".to_string(),
768    ///         "provide".to_string(),
769    ///         "default_organization".to_string(),
770    ///         None,
771    ///         Some(LegalBasis::LegitimateInterest),
772    ///         Some(SpecialCategory::VitalInterests),
773    ///         Some(vec!("marketing team".to_string(), "dog shelter".to_string())),
774    ///         false,
775    ///         Some("https://example.org/legitimate_interest_assessment".to_string()),
776    ///         None,
777    ///         false,
778    ///         true
779    ///     );
780    ///
781    ///     dup.associate_use(datause);
782    ///
783    ///    assert_eq!(dup.get_uses().len(), 1);
784    /// }
785    /// ```
786    pub fn get_uses(&mut self) -> Vec<DataUse> {
787        self.uses.clone().into_values().collect()
788    }
789
790    /// Retrieves a reference to the specified DataCategory that is associated with the policy
791    ///
792    /// # Arguments
793    ///
794    /// * key: String - The key of the Data Category to retrieve.</br>
795    ///
796    /// #Example
797    ///
798    /// ```rust
799    /// extern crate pbd;
800    ///
801    /// use pbd::dua::policy::DUP;
802    /// use pbd::dua::data_category::DataCategory;
803    ///
804    /// fn main() {
805    ///     let mut dup = DUP::new(
806    ///         "General Policy".to_string(),
807    ///         "This is a high-level policy.".to_string(),
808    ///         "1.0.1".to_string()
809    ///     );
810    ///
811    ///     let cat = DataCategory::new(
812    ///        "Authentication Data".to_string(),
813    ///        "Data used to manage access to the system.".to_string(),
814    ///        "system.authentication".to_string(),
815    ///        "default_organization".to_string(),
816    ///        Some("system".to_string()),
817    ///        None,                       
818    ///        false,
819    ///        true,
820    ///    );
821    ///
822    ///    dup.associate_category(cat.clone());
823    ///
824    ///    let retrieved_category = dup.get_category(cat.get_key()).unwrap();
825    ///    println!("{}", retrieved_category.description);
826    /// }
827    /// ```
828    pub fn get_category(&mut self, key: String) -> Option<&DataCategory> {
829        self.categories.get(&key)
830    }
831
832    /// Retrieves a reference to the specified DataSubject that is associated with the policy
833    ///
834    /// # Arguments
835    ///
836    /// * key: String - The key of the Data Subject to retrieve.</br>
837    ///
838    /// #Example
839    ///
840    /// ```rust
841    /// extern crate pbd;
842    ///
843    /// use pbd::dua::policy::DUP;
844    /// use pbd::dua::data_subject::{DataRights, DataSubject, Right, Strategy};
845    ///
846    /// fn main() {
847    ///     let mut dup = DUP::new(
848    ///         "General Policy".to_string(),
849    ///         "This is a high-level policy.".to_string(),
850    ///         "1.0.1".to_string()
851    ///     );
852    ///
853    ///     let subject = DataSubject::new(
854    ///         "Consultant".to_string(),
855    ///         "An individual employed in a consultative/temporary capacity by the organization.".to_string(),
856    ///         "consultant".to_string(),
857    ///         "default_organization".to_string(),
858    ///         Some(vec!["work".to_string(), "temporary".to_string()]),
859    ///         Some(DataRights::new(Strategy::ALL, vec![Right::Informed, Right::Access])),
860    ///         false,
861    ///         false,
862    ///         true
863    ///     );
864    ///
865    ///    dup.associate_subject(subject.clone());
866    ///
867    ///    let retrieved_subject = dup.get_subject(subject.get_key()).unwrap();
868    ///    println!("{}", retrieved_subject.description);
869    /// }
870    /// ```
871    pub fn get_subject(&mut self, key: String) -> Option<&DataSubject> {
872        self.subjects.get(&key)
873    }
874
875    /// Retrieves a reference to the specified DataUse that is associated with the policy
876    ///
877    /// # Arguments
878    ///
879    /// * key: String - The key of the Data Use to retrieve.</br>
880    ///
881    /// #Example
882    ///
883    /// ```rust
884    /// extern crate pbd;
885    ///
886    /// use pbd::dua::policy::DUP;
887    /// use pbd::dua::data_use::{DataUse, LegalBasis, SpecialCategory};
888    ///
889    /// fn main() {
890    ///     let mut dup = DUP::new(
891    ///         "General Policy".to_string(),
892    ///         "This is a high-level policy.".to_string(),
893    ///         "1.0.1".to_string()
894    ///     );
895    ///
896    ///     let datause = DataUse::new(
897    ///         "Provide the capability".to_string(),
898    ///         "Provide, give, or make available the product, service, application or system.".to_string(),
899    ///         "provide".to_string(),
900    ///         "default_organization".to_string(),
901    ///         None,
902    ///         Some(LegalBasis::LegitimateInterest),
903    ///         Some(SpecialCategory::VitalInterests),
904    ///         Some(vec!("marketing team".to_string(), "dog shelter".to_string())),
905    ///         false,
906    ///         Some("https://example.org/legitimate_interest_assessment".to_string()),
907    ///         None,
908    ///         false,
909    ///         true
910    ///     );
911    ///
912    ///    dup.associate_use(datause.clone());
913    ///
914    ///    let retrieved_use = dup.get_use(datause.get_key()).unwrap();
915    ///    println!("{}", retrieved_use.description);
916    /// }
917    /// ```
918    pub fn get_use(&mut self, key: String) -> Option<&DataUse> {
919        self.uses.get(&key)
920    }
921
922    /// Determines if the specified DataCategory key is associated with the policy
923    ///
924    /// # Arguments
925    ///
926    /// * key: String - The key of the Data Category to check.</br>
927    ///
928    /// #Example
929    ///
930    /// ```rust
931    /// extern crate pbd;
932    ///
933    /// use pbd::dua::policy::DUP;
934    /// use pbd::dua::data_category::DataCategory;
935    ///
936    /// fn main() {
937    ///     let mut dup = DUP::new(
938    ///         "General Policy".to_string(),
939    ///         "This is a high-level policy.".to_string(),
940    ///         "1.0.1".to_string()
941    ///     );
942    ///
943    ///     let cat = DataCategory::new(
944    ///        "Authentication Data".to_string(),
945    ///        "Data used to manage access to the system.".to_string(),
946    ///        "system.authentication".to_string(),
947    ///        "default_organization".to_string(),
948    ///        Some("system".to_string()),
949    ///        None,                       
950    ///        false,
951    ///        true,
952    ///    );
953    ///
954    ///    dup.associate_category(cat.clone());
955    ///
956    ///    assert_eq!(dup.has_category(cat.get_key()), true);
957    /// }
958    /// ```
959    pub fn has_category(&mut self, key: String) -> bool {
960        self.categories.contains_key(&key)
961    }
962
963    /// Determines if the specified DataSubejct key is associated with the policy
964    ///
965    /// # Arguments
966    ///
967    /// * key: String - The key of the Data Subject to check.</br>
968    ///
969    /// #Example
970    ///
971    /// ```rust
972    /// extern crate pbd;
973    ///
974    /// use pbd::dua::policy::DUP;
975    /// use pbd::dua::data_subject::{DataRights, DataSubject, Right, Strategy};
976    ///
977    /// fn main() {
978    ///     let mut dup = DUP::new(
979    ///         "General Policy".to_string(),
980    ///         "This is a high-level policy.".to_string(),
981    ///         "1.0.1".to_string()
982    ///     );
983    ///
984    ///     let subject = DataSubject::new(
985    ///         "Consultant".to_string(),
986    ///         "An individual employed in a consultative/temporary capacity by the organization.".to_string(),
987    ///         "consultant".to_string(),
988    ///         "default_organization".to_string(),
989    ///         Some(vec!["work".to_string(), "temporary".to_string()]),
990    ///         Some(DataRights::new(Strategy::ALL, vec![Right::Informed, Right::Access])),
991    ///         false,
992    ///         false,
993    ///         true
994    ///     );
995    ///
996    ///    dup.associate_subject(subject.clone());
997    ///
998    ///    assert_eq!(dup.has_subject(subject.get_key()), true);
999    /// }
1000    /// ```
1001    pub fn has_subject(&mut self, key: String) -> bool {
1002        self.subjects.contains_key(&key)
1003    }
1004
1005    /// Determines if the specified DataUse key is associated with the policy
1006    ///
1007    /// # Arguments
1008    ///
1009    /// * key: String - The key of the Data Use to check.</br>
1010    ///
1011    /// #Example
1012    ///
1013    /// ```rust
1014    /// extern crate pbd;
1015    ///
1016    /// use pbd::dua::policy::DUP;
1017    /// use pbd::dua::data_use::{DataUse, LegalBasis, SpecialCategory};
1018    ///
1019    /// fn main() {
1020    ///     let mut dup = DUP::new(
1021    ///         "General Policy".to_string(),
1022    ///         "This is a high-level policy.".to_string(),
1023    ///         "1.0.1".to_string()
1024    ///     );
1025    ///
1026    ///     let datause = DataUse::new(
1027    ///         "Provide the capability".to_string(),
1028    ///         "Provide, give, or make available the product, service, application or system.".to_string(),
1029    ///         "provide".to_string(),
1030    ///         "default_organization".to_string(),
1031    ///         None,
1032    ///         Some(LegalBasis::LegitimateInterest),
1033    ///         Some(SpecialCategory::VitalInterests),
1034    ///         Some(vec!("marketing team".to_string(), "dog shelter".to_string())),
1035    ///         false,
1036    ///         Some("https://example.org/legitimate_interest_assessment".to_string()),
1037    ///         None,
1038    ///         false,
1039    ///         true
1040    ///     );
1041    ///
1042    ///    dup.associate_use(datause.clone());
1043    ///
1044    ///    assert_eq!(dup.has_use(datause.get_key()), true);
1045    /// }
1046    /// ```
1047    pub fn has_use(&mut self, key: String) -> bool {
1048        self.uses.contains_key(&key)
1049    }
1050
1051    /// Determines if the specified Conditions can be met by the policy and returns a list of conditions that conflict wiht the policy.
1052    ///
1053    /// # Arguments
1054    ///
1055    /// * conditions: Vec<Condition> - The list of Conditions to check against the policy.</br>
1056    ///
1057    /// #Example
1058    ///
1059    /// ```rust
1060    /// extern crate pbd;
1061    ///
1062    /// use pbd::dua::policy::{Condition, DUP};
1063    /// use pbd::dua::data_category::DataCategory;
1064    /// use pbd::dua::data_subject::{DataRights, DataSubject, Right, Strategy};
1065    /// use pbd::dua::data_use::{DataUse, LegalBasis, SpecialCategory};
1066    ///
1067    /// fn main() {
1068    ///     let mut dup = DUP::new(
1069    ///         "General Policy".to_string(),
1070    ///         "This is a high-level policy.".to_string(),
1071    ///         "1.0.1".to_string()
1072    ///     );
1073    ///     let category = DataCategory::new(
1074    ///        "Authentication Data".to_string(),
1075    ///        "Data used to manage access to the system.".to_string(),
1076    ///        "system.authentication".to_string(),
1077    ///        "default_organization".to_string(),
1078    ///        Some("system".to_string()),
1079    ///        None,                       
1080    ///        false,
1081    ///        true,
1082    ///     );
1083    ///     let subject = DataSubject::new(
1084    ///         "Consultant".to_string(),
1085    ///         "An individual employed in a consultative/temporary capacity by the organization.".to_string(),
1086    ///         "consultant".to_string(),
1087    ///         "default_organization".to_string(),
1088    ///         Some(vec!["work".to_string(), "temporary".to_string()]),
1089    ///         Some(DataRights::new(Strategy::ALL, vec![Right::Informed, Right::Access])),
1090    ///         false,
1091    ///         false,
1092    ///         true
1093    ///     );
1094    ///     let datause = DataUse::new(
1095    ///         "Provide the capability".to_string(),
1096    ///         "Provide, give, or make available the product, service, application or system.".to_string(),
1097    ///         "provide".to_string(),
1098    ///         "default_organization".to_string(),
1099    ///         None,
1100    ///         Some(LegalBasis::LegitimateInterest),
1101    ///         Some(SpecialCategory::VitalInterests),
1102    ///         Some(vec!("marketing team".to_string(), "dog shelter".to_string())),
1103    ///         false,
1104    ///         Some("https://example.org/legitimate_interest_assessment".to_string()),
1105    ///         None,
1106    ///         false,
1107    ///         true
1108    ///     );
1109    ///
1110    ///    dup.associate_category(category.clone());
1111    ///    dup.associate_use(datause.clone());
1112    ///
1113    ///    let mut conditions: Vec<Condition> = Vec::new();
1114    ///    conditions.push(Condition::Category(category.get_key()));
1115    ///    conditions.push(Condition::Subject(subject.get_key()));
1116    ///    conditions.push(Condition::Use(datause.get_key()));
1117    ///    let conflicts = dup.match_conditions(conditions);
1118    ///
1119    ///    assert_eq!(conflicts.len(), 1);
1120    ///    assert_eq!(conflicts[0].to_string(), subject.get_key());
1121    /// }
1122    /// ```
1123    pub fn match_conditions(&mut self, conditions: Vec<Condition>) -> Vec<Condition> {
1124        let mut conflicts = Vec::new();
1125        for condition in conditions.into_iter() {
1126            match condition.clone() {
1127                Condition::Category(String) => {
1128                    match self.has_category(condition.to_string()) {
1129                        false => conflicts.push(condition),
1130                        true => {}
1131                    };
1132                }
1133                Condition::Subject(String) => {
1134                    match self.has_subject(condition.to_string()) {
1135                        false => conflicts.push(condition),
1136                        true => {}
1137                    };
1138                }
1139                Condition::Use(String) => {
1140                    match self.has_use(condition.to_string()) {
1141                        false => conflicts.push(condition),
1142                        true => {}
1143                    };
1144                }
1145            }
1146        }
1147
1148        conflicts
1149    }
1150
1151    /// Serialize a DUP object
1152    ///
1153    /// # Arguments
1154    ///
1155    /// * serialized: &str - The string that represents the serialized object.</br>
1156    ///
1157    /// #Example
1158    ///
1159    /// ```rust
1160    /// extern crate pbd;
1161    ///
1162    /// use pbd::dua::policy::{Condition, DUP};
1163    /// use pbd::dua::data_category::DataCategoryFactory;
1164    /// use pbd::dua::data_subject::DataSubjectFactory;
1165    /// use pbd::dua::data_use::DataUseFactory;
1166    ///
1167    /// fn main() {
1168    ///     let mut dup = DUP::new(
1169    ///         "General Policy".to_string(),
1170    ///         "This is a high-level policy.".to_string(),
1171    ///         "1.0.1".to_string()
1172    ///     );
1173    ///     let category_factory = DataCategoryFactory::new();
1174    ///     let subject_factory = DataSubjectFactory::new();
1175    ///     let use_factory = DataUseFactory::new();
1176    ///
1177    ///    dup.associate_category(category_factory.get_category_by_key("system.authentication".to_string()).unwrap());
1178    ///    dup.associate_subject(subject_factory.get_subject_by_key("consultant".to_string()).unwrap());
1179    ///    dup.associate_use(use_factory.get_use_by_key("analytics.reporting".to_string()).unwrap());
1180    ///     
1181    ///     println!("{:?}", dup.serialize());
1182    /// }
1183    /// ```
1184    pub fn serialize(&mut self) -> String {
1185        serde_json::to_string(&self).unwrap()
1186    }
1187}
1188
1189#[cfg(test)]
1190mod tests {
1191    use super::*;
1192    use crate::dua::data_category::DataCategoryFactory;
1193    use crate::dua::data_subject::DataSubjectFactory;
1194    use crate::dua::data_use::DataUseFactory;
1195    use std::fs::File;
1196    use std::io::prelude::*;
1197
1198    fn get_data_category() -> DataCategory {
1199        let factory = DataCategoryFactory::new();
1200        factory
1201            .get_category_by_key("system.authentication".to_string())
1202            .unwrap()
1203    }
1204
1205    fn get_data_subject() -> DataSubject {
1206        let factory = DataSubjectFactory::new();
1207        factory
1208            .get_subject_by_key("consultant".to_string())
1209            .unwrap()
1210    }
1211
1212    fn get_data_use() -> DataUse {
1213        let factory = DataUseFactory::new();
1214        factory
1215            .get_use_by_key("essential.service.authentication".to_string())
1216            .unwrap()
1217    }
1218
1219    fn get_dup() -> DUP {
1220        let dup = DUP::new(
1221            "General Policy".to_string(),
1222            "This is a high-level policy.".to_string(),
1223            "1.0.1".to_string(),
1224        );
1225        dup
1226    }
1227
1228    #[test]
1229    fn test_dup_associate_category_ok() {
1230        let mut dup = get_dup();
1231        dup.associate_category(get_data_category());
1232        assert_eq!(dup.get_categories().len(), 1);
1233    }
1234
1235    #[test]
1236    fn test_dup_associate_subject_ok() {
1237        let mut dup = get_dup();
1238        dup.associate_subject(get_data_subject());
1239        assert_eq!(dup.get_subjects().len(), 1);
1240    }
1241
1242    #[test]
1243    fn test_dup_as_html() {
1244        let cfactory = DataCategoryFactory::new();
1245        let sfactory = DataSubjectFactory::new();
1246        let ufactory = DataUseFactory::new();
1247        let mut dup = get_dup();
1248
1249        dup.associate_category(
1250            cfactory
1251                .get_category_by_key("user.behavior.browsing_history".to_string())
1252                .unwrap(),
1253        );
1254        dup.associate_category(
1255            cfactory
1256                .get_category_by_key("user.behavior.media_consumption".to_string())
1257                .unwrap(),
1258        );
1259        dup.associate_subject(sfactory.get_subject_by_key("customer".to_string()).unwrap());
1260        dup.associate_subject(sfactory.get_subject_by_key("prospect".to_string()).unwrap());
1261        dup.associate_use(
1262            ufactory
1263                .get_use_by_key("marketing.advertising.profiling".to_string())
1264                .unwrap(),
1265        );
1266        dup.associate_use(
1267            ufactory
1268                .get_use_by_key("marketing.advertising.serving".to_string())
1269                .unwrap(),
1270        );
1271        dup.associate_use(
1272            ufactory
1273                .get_use_by_key("marketing.communications.email".to_string())
1274                .unwrap(),
1275        );
1276
1277        print!("{}", dup.as_html());
1278        let mut file = File::create("./tests/output/policy.html").unwrap();
1279        file.write_all(dup.as_html().as_bytes()).unwrap();
1280    }
1281
1282    #[test]
1283    fn test_dup_as_text() {
1284        let cfactory = DataCategoryFactory::new();
1285        let sfactory = DataSubjectFactory::new();
1286        let ufactory = DataUseFactory::new();
1287        let mut dup = get_dup();
1288
1289        dup.associate_category(
1290            cfactory
1291                .get_category_by_key("user.behavior.browsing_history".to_string())
1292                .unwrap(),
1293        );
1294        dup.associate_category(
1295            cfactory
1296                .get_category_by_key("user.behavior.media_consumption".to_string())
1297                .unwrap(),
1298        );
1299        dup.associate_subject(sfactory.get_subject_by_key("customer".to_string()).unwrap());
1300        dup.associate_subject(sfactory.get_subject_by_key("prospect".to_string()).unwrap());
1301        dup.associate_use(
1302            ufactory
1303                .get_use_by_key("marketing.advertising.profiling".to_string())
1304                .unwrap(),
1305        );
1306        dup.associate_use(
1307            ufactory
1308                .get_use_by_key("marketing.advertising.serving".to_string())
1309                .unwrap(),
1310        );
1311        dup.associate_use(
1312            ufactory
1313                .get_use_by_key("marketing.communications.email".to_string())
1314                .unwrap(),
1315        );
1316
1317        print!("{}", dup.as_text());
1318        let mut file = File::create("./tests/output/policy.txt").unwrap();
1319        file.write_all(dup.as_text().as_bytes()).unwrap();
1320    }
1321
1322    #[test]
1323    fn test_dup_associate_use_ok() {
1324        let mut dup = get_dup();
1325        dup.associate_use(get_data_use());
1326        assert_eq!(dup.get_uses().len(), 1);
1327    }
1328
1329    #[test]
1330    fn test_dup_disassociate_category_ok() {
1331        let mut dup = get_dup();
1332        dup.associate_category(get_data_category());
1333        assert_eq!(dup.get_categories().len(), 1);
1334
1335        dup.disassociate_category(get_data_category().get_key());
1336        assert_eq!(dup.get_categories().len(), 0);
1337    }
1338
1339    #[test]
1340    fn test_dup_disassociate_subject_ok() {
1341        let mut dup = get_dup();
1342        dup.associate_subject(get_data_subject());
1343        assert_eq!(dup.get_subjects().len(), 1);
1344
1345        dup.disassociate_subject(get_data_subject().get_key());
1346        assert_eq!(dup.get_subjects().len(), 0);
1347    }
1348
1349    #[test]
1350    fn test_dup_disassociate_use_ok() {
1351        let mut dup = get_dup();
1352        dup.associate_use(get_data_use());
1353        assert_eq!(dup.get_uses().len(), 1);
1354
1355        dup.disassociate_use(get_data_use().get_key());
1356        assert_eq!(dup.get_uses().len(), 0);
1357    }
1358
1359    #[test]
1360    fn test_dup_get_category_ok() {
1361        let mut dup = get_dup();
1362        dup.associate_category(get_data_category());
1363
1364        let cat2 = dup.get_category(get_data_category().get_key()).unwrap();
1365        assert_eq!(cat2.description, get_data_category().description);
1366    }
1367
1368    #[test]
1369    fn test_dup_get_subject_ok() {
1370        let mut dup = get_dup();
1371        dup.associate_subject(get_data_subject());
1372
1373        let sub2 = dup.get_subject(get_data_subject().get_key()).unwrap();
1374        assert_eq!(sub2.description, get_data_subject().description);
1375    }
1376
1377    #[test]
1378    fn test_dup_get_use_ok() {
1379        let mut dup = get_dup();
1380        dup.associate_use(get_data_use());
1381
1382        let use2 = dup.get_use(get_data_use().get_key()).unwrap();
1383        assert_eq!(use2.description, get_data_use().description);
1384    }
1385
1386    #[test]
1387    fn test_dup_has_category_ok() {
1388        let mut dup = get_dup();
1389        dup.associate_category(get_data_category());
1390        assert_eq!(dup.has_category(get_data_category().get_key()), true);
1391    }
1392
1393    #[test]
1394    fn test_dup_has_subject_ok() {
1395        let mut dup = get_dup();
1396        dup.associate_subject(get_data_subject());
1397        assert_eq!(dup.has_subject(get_data_subject().get_key()), true);
1398    }
1399
1400    #[test]
1401    fn test_dup_has_use_ok() {
1402        let mut dup = get_dup();
1403        dup.associate_use(get_data_use());
1404        assert_eq!(dup.has_use(get_data_use().get_key()), true);
1405    }
1406
1407    #[test]
1408    fn test_dup_match_conditions_all_found() {
1409        let mut dup = get_dup();
1410        let mut conditions: Vec<Condition> = Vec::new();
1411        conditions.push(Condition::Category(get_data_category().get_key()));
1412        conditions.push(Condition::Subject(get_data_subject().get_key()));
1413        conditions.push(Condition::Use(get_data_use().get_key()));
1414        let conflicts = dup.match_conditions(conditions);
1415        assert_eq!(conflicts.len(), 3);
1416    }
1417
1418    #[test]
1419    fn test_dup_match_conditions_none_found() {
1420        let mut dup = get_dup();
1421        dup.associate_category(get_data_category());
1422        dup.associate_subject(get_data_subject());
1423        dup.associate_use(get_data_use());
1424
1425        let mut conditions: Vec<Condition> = Vec::new();
1426        conditions.push(Condition::Category(get_data_category().get_key()));
1427        conditions.push(Condition::Subject(get_data_subject().get_key()));
1428        conditions.push(Condition::Use(get_data_use().get_key()));
1429        let conflicts = dup.match_conditions(conditions);
1430        assert_eq!(conflicts.len(), 0);
1431    }
1432
1433    #[test]
1434    fn test_dup_match_conditions_some_found() {
1435        let mut dup = get_dup();
1436        dup.associate_category(get_data_category());
1437        dup.associate_use(get_data_use());
1438
1439        let mut conditions: Vec<Condition> = Vec::new();
1440        conditions.push(Condition::Category(get_data_category().get_key()));
1441        conditions.push(Condition::Subject(get_data_subject().get_key()));
1442        conditions.push(Condition::Use(get_data_use().get_key()));
1443        let conflicts = dup.match_conditions(conditions);
1444        assert_eq!(conflicts.len(), 1);
1445        assert_eq!(conflicts[0].to_string(), get_data_subject().get_key());
1446    }
1447
1448    #[test]
1449    fn test_dup_serialize_ok() {
1450        let serialized = r#"{"name":"General Policy","description":"This is a high-level policy.","version":"1.0.1","categories":{"system.authentication":{"name":"Authentication Data","description":"Data used to manage access to the system.","fides_key":"system.authentication","organization_fides_key":"default_organization","parent_key":"system","tags":null,"is_default":true,"active":true}},"subjects":{"consultant":{"name":"Consultant","description":"An individual employed in a consultative/temporary capacity by the organization.","fides_key":"consultant","organization_fides_key":"default_organization","tags":null,"rights":null,"automated_decisions_or_profiling":false,"is_default":true,"active":true}},"uses":{"essential.service.authentication":{"name":"Essential Service Authentication","description":"Authenticate users to the product, service, application or system.","fides_key":"essential.service.authentication","organization_fides_key":"default_organization","parent_key":"essential.service","legal_basis":null,"special_category":null,"recipent":null,"legitimate_interest":false,"legitimate_interest_impact_assessment":null,"tags":null,"is_default":true,"active":true}}}"#;
1451        let mut dup = get_dup();
1452
1453        dup.associate_category(get_data_category());
1454        dup.associate_subject(get_data_subject());
1455        dup.associate_use(get_data_use());
1456
1457        assert_eq!(dup.serialize(), serialized);
1458    }
1459}