forked from IABTechLab/fideslang
-
Notifications
You must be signed in to change notification settings - Fork 0
/
models.py
1093 lines (888 loc) · 35.9 KB
/
models.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# pylint: disable=too-many-lines
"""
Contains all of the Fides resources modeled as Pydantic models.
"""
from __future__ import annotations
from datetime import datetime
from enum import Enum
from typing import Any, Dict, List, Optional, Union
from pydantic import (
AnyUrl,
BaseModel,
ConstrainedStr,
Field,
HttpUrl,
PositiveInt,
root_validator,
validator,
)
from fideslang.validation import (
FidesKey,
FidesVersion,
deprecated_version_later_than_added,
has_versioning_if_default,
is_deprecated_if_replaced,
matching_parent_key,
no_self_reference,
parse_data_type_string,
sort_list_objects_by_name,
unique_items_in_list,
valid_data_type,
)
matching_parent_key_validator = validator("parent_key", allow_reuse=True, always=True)(
matching_parent_key
)
no_self_reference_validator = validator("parent_key", allow_reuse=True)(
no_self_reference
)
has_versioning_if_default_validator = validator(
"is_default", allow_reuse=True, always=True
)(has_versioning_if_default)
deprecated_version_later_than_added_validator = validator(
"version_deprecated", allow_reuse=True
)(deprecated_version_later_than_added)
is_deprecated_if_replaced_validator = validator("replaced_by", allow_reuse=True)(
is_deprecated_if_replaced
)
# Reusable Fields
name_field = Field(description="Human-Readable name for this resource.")
description_field = Field(
description="A detailed description of what this resource is."
)
meta_field = Field(
default=None,
description="An optional property to store any extra information for a resource. Data can be structured in any way: simple set of `key: value` pairs or deeply nested objects.",
)
class FidesModel(BaseModel):
"""The base model for most top-level Fides objects."""
fides_key: FidesKey = Field(
description="A unique key used to identify this resource."
)
organization_fides_key: FidesKey = Field(
default="default_organization",
description="Defines the Organization that this resource belongs to.",
)
tags: Optional[List[str]] = None
name: Optional[str] = name_field
description: Optional[str] = description_field
class Config:
"Config for the FidesModel"
extra = "ignore"
orm_mode = True
class DefaultModel(BaseModel):
"""
A model meant to be inherited by versioned parts of the Default Taxonomy.
"""
version_added: Optional[str] = Field(
default=None,
description="The version of Fideslang in which this label was added.",
)
version_deprecated: Optional[str] = Field(
default=None,
description="The version of Fideslang in which this label was deprecated.",
)
replaced_by: Optional[FidesKey] = Field(
default=None,
description="The new name, if applicable, for this label after deprecation.",
)
is_default: bool = Field(
default=False,
description="Denotes whether the resource is part of the default taxonomy or not.",
)
_has_versioning_if_default: classmethod = has_versioning_if_default_validator
_deprecated_version_later_than_added: classmethod = (
deprecated_version_later_than_added_validator
)
_is_deprecated_if_replaced: classmethod = is_deprecated_if_replaced_validator
@validator("version_added")
@classmethod
def validate_verion_added(
cls, version_added: Optional[str], values: Dict
) -> Optional[str]:
"""
Validate that the `version_added` field is a proper FidesVersion
"""
if not version_added:
return None
FidesVersion.validate(version_added)
return version_added
@validator("version_deprecated")
@classmethod
def validate_version_deprecated(
cls, version_deprecated: Optional[str], values: Dict
) -> Optional[str]:
"""
Validate that the `version_deprecated` is a proper FidesVersion
"""
if not version_deprecated:
return None
FidesVersion.validate(version_deprecated)
return version_deprecated
class DataResponsibilityTitle(str, Enum):
"""
The model defining the responsibility or role over
the system that processes personal data.
Used to identify whether the organization is a
Controller, Processor, or Sub-Processor of the data
"""
CONTROLLER = "Controller"
PROCESSOR = "Processor"
SUB_PROCESSOR = "Sub-Processor"
class IncludeExcludeEnum(str, Enum):
"""
Determine whether or not defined rights are
being included or excluded.
"""
ALL = "ALL"
EXCLUDE = "EXCLUDE"
INCLUDE = "INCLUDE"
NONE = "NONE"
class DataSubjectRightsEnum(str, Enum):
"""
The model for data subject rights over
personal data.
Based upon chapter 3 of the GDPR
"""
INFORMED = "Informed"
ACCESS = "Access"
RECTIFICATION = "Rectification"
ERASURE = "Erasure"
PORTABILITY = "Portability"
RESTRICT_PROCESSING = "Restrict Processing"
WITHDRAW_CONSENT = "Withdraw Consent"
OBJECT = "Object"
OBJECT_TO_AUTOMATED_PROCESSING = "Object to Automated Processing"
class LegalBasisForProcessingEnum(str, Enum):
"""
The model for allowable legal basis categories on privacy declarations.
Based upon article 6 of the GDPR
"""
CONSENT = "Consent"
CONTRACT = "Contract"
LEGAL_OBLIGATION = "Legal obligations"
VITAL_INTEREST = "Vital interests"
PUBLIC_INTEREST = "Public interest"
LEGITIMATE_INTEREST = "Legitimate interests"
class LegalBasisForProfilingEnum(str, Enum):
"""The model for describing the legal basis under which profiling is performed"""
EXPLICIT_CONSENT = "Explicit consent"
CONTRACT = "Contract"
AUTHORISED_BY_LAW = "Authorised by law"
class LegalBasisForTransfersEnum(str, Enum):
"""
The model for describing the legal basis under which data is transferred
We currently do _not_ enforce this enum on the `legal_basis_for_transfers`
field, because the set of allowable values seems to be changing frequently
and without clear notice in upstream, public data sources.
"""
ADEQUACY_DECISION = "Adequacy Decision"
SCCS = "SCCs"
BCRS = "BCRs"
SUPPLEMENTARY_MEASURES = "Supplementary Measures"
OTHER = "Other"
class SpecialCategoryLegalBasisEnum(str, Enum):
"""
The model for the legal basis for processing special categories of personal data
on privacy declarations
Based upon article 9 of the GDPR
"""
CONSENT = "Explicit consent"
EMPLOYMENT = "Employment, social security and social protection"
VITAL_INTEREST = "Vital interests"
NON_PROFIT_BODIES = "Not-for-profit bodies"
PUBLIC_BY_DATA_SUBJECT = "Made public by the data subject"
LEGAL_CLAIMS = "Legal claims or judicial acts"
PUBLIC_INTEREST = "Reasons of substantial public interest (with a basis in law)"
MEDICAL = "Health or social care (with a basis in law)"
PUBLIC_HEALTH_INTEREST = "Public health (with a basis in law)"
RESEARCH = "Archiving, research and statistics (with a basis in law)"
# Privacy Data Types
class DataCategory(FidesModel, DefaultModel):
"""The DataCategory resource model."""
parent_key: Optional[FidesKey]
_matching_parent_key: classmethod = matching_parent_key_validator
_no_self_reference: classmethod = no_self_reference_validator
class Cookies(BaseModel):
"""The Cookies resource model"""
name: str
path: Optional[str]
domain: Optional[str]
class Config:
"""Config for the cookies"""
orm_mode = True
class DataSubjectRights(BaseModel):
"""
The DataSubjectRights resource model.
Includes a strategy and optionally a
list of data subject rights to apply
via the set strategy.
"""
strategy: IncludeExcludeEnum = Field(
description="Defines the strategy used when mapping data rights to a data subject.",
)
values: Optional[List[DataSubjectRightsEnum]] = Field(
description="A list of valid data subject rights to be used when applying data rights to a data subject via a strategy.",
)
@root_validator()
@classmethod
def include_exclude_has_values(cls, values: Dict) -> Dict:
"""
Validate the if include or exclude is chosen, that at least one
value is present.
"""
strategy, rights = values.get("strategy"), values.get("values")
if strategy in ("INCLUDE", "EXCLUDE"):
assert (
rights is not None
), f"If {strategy} is chosen, rights must also be listed."
return values
class DataSubject(FidesModel, DefaultModel):
"""The DataSubject resource model."""
rights: Optional[DataSubjectRights] = Field(description=DataSubjectRights.__doc__)
automated_decisions_or_profiling: Optional[bool] = Field(
description="A boolean value to annotate whether or not automated decisions/profiling exists for the data subject.",
)
class DataUse(FidesModel, DefaultModel):
"""The DataUse resource model."""
parent_key: Optional[FidesKey] = None
_matching_parent_key: classmethod = matching_parent_key_validator
_no_self_reference: classmethod = no_self_reference_validator
# Dataset
class DatasetFieldBase(BaseModel):
"""Base DatasetField Resource model.
This model is available for cases where the DatasetField information needs to be
customized. In general this will not be the case and you will instead want to use
the DatasetField model.
When this model is used you will need to implement your own recursive field in
to adding any new needed fields.
Example:
```py
from typing import List, Optional
from fideslang import DatasetFieldBase
class MyDatasetField(DatasetFieldBase):
custom: str
fields: Optional[List[MyDatasetField]] = []
```
"""
name: str = name_field
description: Optional[str] = description_field
data_categories: Optional[List[FidesKey]] = Field(
description="Arrays of Data Categories, identified by `fides_key`, that applies to this field.",
)
class EdgeDirection(str, Enum):
"""Direction of a FidesDataSetReference"""
FROM = "from"
TO = "to"
class FidesDatasetReference(BaseModel):
"""Reference to a field from another Collection"""
dataset: FidesKey
field: str
direction: Optional[EdgeDirection]
class FidesMeta(BaseModel):
"""Supplementary metadata used by the Fides application for additional features."""
references: Optional[List[FidesDatasetReference]] = Field(
description="Fields that current field references or is referenced by. Used for drawing the edges of a DSR graph.",
default=None,
)
identity: Optional[str] = Field(
description="The type of the identity data that should be used to query this collection for a DSR."
)
primary_key: Optional[bool] = Field(
description="Whether the current field can be considered a primary key of the current collection"
)
data_type: Optional[str] = Field(
description="Optionally specify the data type. Fides will attempt to cast values to this type when querying."
)
length: Optional[PositiveInt] = Field(
description="Optionally specify the allowable field length. Fides will not generate values that exceed this size."
)
return_all_elements: Optional[bool] = Field(
description="Optionally specify to query for the entire array if the array is an entrypoint into the node. Default is False."
)
read_only: Optional[bool] = Field(
description="Optionally specify if a field is read-only, meaning it can't be updated or deleted."
)
@validator("data_type")
@classmethod
def valid_data_type(cls, value: Optional[str]) -> Optional[str]:
"""Validate that all annotated data types exist in the taxonomy"""
return valid_data_type(value)
class FidesopsMetaBackwardsCompat(BaseModel):
"""Mixin to convert fidesops_meta to fides_meta for backwards compatibility
as we add DSR concepts to fideslang"""
def __init__(self, **data: Union[Dataset, DatasetCollection, DatasetField]) -> None:
"""For Datasets, DatasetCollections, and DatasetFields, if old fidesops_meta field is specified,
convert this to a fides_meta field instead."""
fidesops_meta = data.pop("fidesops_meta", None)
fides_meta = data.pop("fides_meta", None)
super().__init__(
fides_meta=fides_meta or fidesops_meta,
**data,
)
class DatasetField(DatasetFieldBase, FidesopsMetaBackwardsCompat):
"""
The DatasetField resource model.
This resource is nested within a DatasetCollection.
"""
fides_meta: Optional[FidesMeta] = None
fields: Optional[List[DatasetField]] = Field(
description="An optional array of objects that describe hierarchical/nested fields (typically found in NoSQL databases).",
)
@validator("fides_meta")
@classmethod
def valid_meta(cls, meta_values: Optional[FidesMeta]) -> Optional[FidesMeta]:
"""Validate upfront that the return_all_elements flag can only be specified on array fields"""
if not meta_values:
return meta_values
is_array: bool = bool(
meta_values.data_type and meta_values.data_type.endswith("[]")
)
if not is_array and meta_values.return_all_elements is not None:
raise ValueError(
"The 'return_all_elements' attribute can only be specified on array fields."
)
return meta_values
@validator("fields")
@classmethod
def validate_object_fields( # type: ignore
cls,
fields: Optional[List["DatasetField"]],
values: Dict[str, Any],
) -> Optional[List["DatasetField"]]:
"""Two validation checks for object fields:
- If there are sub-fields specified, type should be either empty or 'object'
- Additionally object fields cannot have data_categories.
"""
declared_data_type = None
field_name: str = values.get("name") # type: ignore
if values.get("fides_meta"):
declared_data_type = values["fides_meta"].data_type
if fields and declared_data_type:
data_type, _ = parse_data_type_string(declared_data_type)
if data_type != "object":
raise ValueError(
f"The data type '{data_type}' on field '{field_name}' is not compatible with specified sub-fields. Convert to an 'object' field."
)
if (fields or declared_data_type == "object") and values.get("data_categories"):
raise ValueError(
f"Object field '{field_name}' cannot have specified data_categories. Specify category on sub-field instead"
)
return fields
# this is required for the recursive reference in the pydantic model:
DatasetField.update_forward_refs()
class FidesCollectionKey(ConstrainedStr):
"""
Dataset.Collection name where both dataset and collection names are valid FidesKeys
"""
@classmethod
def validate(cls, value: str) -> str:
"""
Overrides validation to check FidesCollectionKey format, and that both the dataset
and collection names have the FidesKey format.
"""
values = value.split(".")
if len(values) == 2:
FidesKey.validate(values[0])
FidesKey.validate(values[1])
return value
raise ValueError(
"FidesCollection must be specified in the form 'FidesKey.FidesKey'"
)
class CollectionMeta(BaseModel):
"""Collection-level specific annotations used for query traversal"""
after: Optional[List[FidesCollectionKey]]
skip_processing: Optional[bool] = False
class DatasetCollection(FidesopsMetaBackwardsCompat):
"""
The DatasetCollection resource model.
This resource is nested within a Dataset.
"""
name: str = name_field
description: Optional[str] = description_field
data_categories: Optional[List[FidesKey]] = Field(
description="Array of Data Category resources identified by `fides_key`, that apply to all fields in the collection.",
)
fields: List[DatasetField] = Field(
description="An array of objects that describe the collection's fields.",
)
fides_meta: Optional[CollectionMeta] = None
_sort_fields: classmethod = validator("fields", allow_reuse=True)(
sort_list_objects_by_name
)
_unique_items_in_list: classmethod = validator("fields", allow_reuse=True)(
unique_items_in_list
)
class ContactDetails(BaseModel):
"""
The contact details information model.
Used to capture contact information for controllers, used
as part of exporting a data map / ROPA.
This model is nested under an Organization and
potentially under a system/dataset.
"""
name: str = Field(
default="",
description="An individual name used as part of publishing contact information. Encrypted at rest on the server.",
)
address: str = Field(
default="",
description="An individual address used as part of publishing contact information. Encrypted at rest on the server.",
)
email: str = Field(
default="",
description="An individual email used as part of publishing contact information. Encrypted at rest on the server.",
)
phone: str = Field(
default="",
description="An individual phone number used as part of publishing contact information. Encrypted at rest on the server.",
)
class DatasetMetadata(BaseModel):
"""
The DatasetMetadata resource model.
Object used to hold application specific metadata for a dataset
"""
resource_id: Optional[str]
after: Optional[List[FidesKey]]
class Dataset(FidesModel, FidesopsMetaBackwardsCompat):
"""The Dataset resource model."""
meta: Optional[Dict] = meta_field
data_categories: Optional[List[FidesKey]] = Field(
description="Array of Data Category resources identified by `fides_key`, that apply to all collections in the Dataset.",
)
fides_meta: Optional[DatasetMetadata] = Field(
description=DatasetMetadata.__doc__, default=None
)
collections: List[DatasetCollection] = Field(
description="An array of objects that describe the Dataset's collections.",
)
_sort_collections: classmethod = validator("collections", allow_reuse=True)(
sort_list_objects_by_name
)
_unique_items_in_list: classmethod = validator("collections", allow_reuse=True)(
unique_items_in_list
)
# Evaluation
class ViolationAttributes(BaseModel):
"The model for attributes which led to an evaluation violation"
data_categories: List[str] = Field(
description="A list of data categories which led to an evaluation violation.",
)
data_subjects: List[str] = Field(
description="A list of data subjects which led to an evaluation violation.",
)
data_uses: List[str] = Field(
description="A list of data uses which led to an evaluation violation.",
)
class Violation(BaseModel):
"The model for violations within an evaluation."
violating_attributes: ViolationAttributes = Field(
description=ViolationAttributes.__doc__
)
detail: str = Field(
description="A human-readable string detailing the evaluation violation.",
)
class StatusEnum(str, Enum):
"The model for possible evaluation results."
FAIL = "FAIL"
PASS = "PASS"
class Evaluation(BaseModel):
"""
The Evaluation resource model.
This resource is created after an evaluation is executed.
"""
fides_key: FidesKey = Field(
description="A uuid generated for each unique evaluation.",
)
status: StatusEnum = Field(description=StatusEnum.__doc__)
violations: List[Violation] = Field(
default=[],
description=Violation.__doc__,
)
message: str = Field(
default="",
description="A human-readable string response for the evaluation.",
)
class Config:
"Config for the Evaluation"
extra = "ignore"
orm_mode = True
# Organization
class ResourceFilter(BaseModel):
"""
The ResourceFilter resource model.
"""
type: str = Field(
description="The type of filter to be used (i.e. ignore_resource_arn)",
)
value: str = Field(
description="A string representation of resources to be filtered. Can include wildcards.",
)
class OrganizationMetadata(BaseModel):
"""
The OrganizationMetadata resource model.
Object used to hold application specific metadata for an organization
"""
resource_filters: Optional[List[ResourceFilter]] = Field(
description="A list of filters that can be used when generating or scanning systems."
)
class Organization(FidesModel):
"""
The Organization resource model.
This resource is used as a way to organize all other resources.
"""
# It inherits this from FidesModel but Organizations don't have this field
organization_parent_key: None = Field(
default=None,
description="An inherited field from the FidesModel that is unused with an Organization.",
)
controller: Optional[ContactDetails] = Field(
description=ContactDetails.__doc__,
)
data_protection_officer: Optional[ContactDetails] = Field(
description=ContactDetails.__doc__,
)
fidesctl_meta: Optional[OrganizationMetadata] = Field(
description=OrganizationMetadata.__doc__,
)
representative: Optional[ContactDetails] = Field(
description=ContactDetails.__doc__,
)
security_policy: Optional[HttpUrl] = Field(
description="Am optional URL to the organization security policy."
)
# Policy
class MatchesEnum(str, Enum):
"""
The MatchesEnum resource model.
Determines how the listed resources are matched in the evaluation logic.
"""
ANY = "ANY"
ALL = "ALL"
NONE = "NONE"
OTHER = "OTHER"
class PrivacyRule(BaseModel):
"""
The PrivacyRule resource model.
A list of privacy data types and what match method to use.
"""
matches: MatchesEnum = Field(
description=MatchesEnum.__doc__,
)
values: List[FidesKey] = Field(
description="A list of fides keys to be used with the matching type in a privacy rule.",
)
class PolicyRule(BaseModel):
"""
The PolicyRule resource model.
Describes the allowed combination of the various privacy data types.
"""
name: str
data_categories: PrivacyRule = Field(
description=PrivacyRule.__doc__,
)
data_uses: PrivacyRule = Field(
description=PrivacyRule.__doc__,
)
data_subjects: PrivacyRule = Field(
description=PrivacyRule.__doc__,
)
class Policy(FidesModel):
"""
The Policy resource model.
An object used to organize a list of PolicyRules.
"""
rules: List[PolicyRule] = Field(
description=PolicyRule.__doc__,
)
_sort_rules: classmethod = validator("rules", allow_reuse=True)(
sort_list_objects_by_name
)
class PrivacyDeclaration(BaseModel):
"""
The PrivacyDeclaration resource model.
States a function of a system, and describes how it relates
to the privacy data types.
"""
name: Optional[str] = Field(
description="The name of the privacy declaration on the system.",
)
data_categories: List[FidesKey] = Field(
description="An array of data categories describing a system in a privacy declaration.",
)
data_use: FidesKey = Field(
description="The Data Use describing a system in a privacy declaration.",
)
data_subjects: List[FidesKey] = Field(
default_factory=list,
description="An array of data subjects describing a system in a privacy declaration.",
)
dataset_references: Optional[List[FidesKey]] = Field(
description="Referenced Dataset fides keys used by the system.",
)
egress: Optional[List[FidesKey]] = Field(
description="The resources to which data is sent. Any `fides_key`s included in this list reference `DataFlow` entries in the `egress` array of any `System` resources to which this `PrivacyDeclaration` is applied."
)
ingress: Optional[List[FidesKey]] = Field(
description="The resources from which data is received. Any `fides_key`s included in this list reference `DataFlow` entries in the `ingress` array of any `System` resources to which this `PrivacyDeclaration` is applied."
)
features: List[str] = Field(
default_factory=list, description="The features of processing personal data."
)
flexible_legal_basis_for_processing: bool = Field(
description="Whether the legal basis for processing is 'flexible' (i.e. can be overridden in a privacy notice) for this declaration.",
default=True,
)
legal_basis_for_processing: Optional[LegalBasisForProcessingEnum] = Field(
description="The legal basis under which personal data is processed for this purpose."
)
impact_assessment_location: Optional[str] = Field(
description="Where the legitimate interest impact assessment is stored"
)
retention_period: Optional[str] = Field(
description="An optional string to describe the time period for which data is retained for this purpose."
)
processes_special_category_data: bool = Field(
default=False,
description="This system processes special category data",
)
special_category_legal_basis: Optional[SpecialCategoryLegalBasisEnum] = Field(
description="The legal basis under which the special category data is processed.",
)
data_shared_with_third_parties: bool = Field(
default=False,
description="This system shares data with third parties for this purpose.",
)
third_parties: Optional[str] = Field(
description="The types of third parties the data is shared with.",
)
shared_categories: List[str] = Field(
default_factory=list,
description="The categories of personal data that this system shares with third parties.",
)
cookies: Optional[List[Cookies]] = Field(
description="Cookies associated with this data use to deliver services and functionality",
)
class Config:
"""Config for the Privacy Declaration"""
orm_mode = True
class SystemMetadata(BaseModel):
"""
The SystemMetadata resource model.
Object used to hold application specific metadata for a system
"""
resource_id: Optional[str] = Field(
description="The external resource id for the system being modeled."
)
endpoint_address: Optional[str] = Field(
description="The host of the external resource for the system being modeled."
)
endpoint_port: Optional[str] = Field(
description="The port of the external resource for the system being modeled."
)
class FlowableResources(str, Enum):
"""
The resource types with which DataFlows can be created.
"""
DATASET = "dataset"
SYSTEM = "system"
USER = "user"
class DataFlow(BaseModel):
"""
The DataFlow resource model.
Describes a resource model with which a given System resource communicates.
"""
fides_key: FidesKey = Field(
...,
description="Identifies the System or Dataset resource with which the communication occurs. May also be 'user', to represent communication with the user(s) of a System.",
)
type: str = Field(
...,
description=f"Specifies the resource model class for which the `fides_key` applies. May be any of {', '.join([member.value for member in FlowableResources])}.",
)
data_categories: Optional[List[FidesKey]] = Field(
description="An array of data categories describing the data in transit.",
)
@root_validator(skip_on_failure=True)
@classmethod
def user_special_case(cls, values: Dict) -> Dict:
"""
If either the `fides_key` or the `type` are set to "user",
then the other must also be set to "user".
"""
if values["fides_key"] == "user" or values["type"] == "user":
assert (
values["fides_key"] == "user" and values["type"] == "user"
), "The 'user' fides_key is required for, and requires, the type 'user'"
return values
@validator("type")
@classmethod
def verify_type_is_flowable(cls, value: str) -> str:
"""
Assert that the value of the `type` field is a member
of the `FlowableResources` Enum.
"""
flowables = [member.value for member in FlowableResources]
assert value in flowables, f"'type' must be one of {', '.join(flowables)}"
return value
class System(FidesModel):
"""
The System resource model.
Describes an application and includes a list of PrivacyDeclaration resources.
"""
meta: Optional[Dict] = meta_field
fidesctl_meta: Optional[SystemMetadata] = Field(
description=SystemMetadata.__doc__,
)
system_type: str = Field(
description="A required value to describe the type of system being modeled, examples include: Service, Application, Third Party, etc.",
)
egress: Optional[List[DataFlow]] = Field(
description="The resources to which the system sends data."
)
ingress: Optional[List[DataFlow]] = Field(
description="The resources from which the system receives data."
)
privacy_declarations: List[PrivacyDeclaration] = Field(
description=PrivacyDeclaration.__doc__,
)
administrating_department: Optional[str] = Field(
default="Not defined",
description="An optional value to identify the owning department or group of the system within your organization",
)
vendor_id: Optional[str] = Field(
description="The unique identifier for the vendor that's associated with this system."
)
previous_vendor_id: Optional[str] = Field(
description="If specified, the unique identifier for the vendor that was previously associated with this system."
)
vendor_deleted_date: Optional[datetime] = Field(
description="The deleted date of the vendor that's associated with this system."
)
dataset_references: List[FidesKey] = Field(
default_factory=list,
description="Referenced Dataset fides keys used by the system.",
)
processes_personal_data: bool = Field(
default=True,
description="This toggle indicates whether the system stores or processes personal data.",
)
exempt_from_privacy_regulations: bool = Field(
default=False,
description="This toggle indicates whether the system is exempt from privacy regulation if they do process personal data.",
)
reason_for_exemption: Optional[str] = Field(
description="The reason that the system is exempt from privacy regulation."
)
uses_profiling: bool = Field(
default=False,
description="Whether the vendor uses data to profile a consumer in a way that has a legal effect.",
)
legal_basis_for_profiling: List[LegalBasisForProfilingEnum] = Field(
default_factory=list,
description="The legal basis (or bases) for performing profiling that has a legal effect.",
)
does_international_transfers: bool = Field(
default=False,
description="Whether this system transfers data to other countries or international organizations.",
)
legal_basis_for_transfers: List[str] = Field(
default_factory=list,
description="The legal basis (or bases) under which the data is transferred.",
)
requires_data_protection_assessments: bool = Field(
default=False,
description="Whether this system requires data protection impact assessments.",
)
dpa_location: Optional[str] = Field(
description="Location where the DPAs or DIPAs can be found."
)
dpa_progress: Optional[str] = Field(
description="The optional status of a Data Protection Impact Assessment"
)
privacy_policy: Optional[AnyUrl] = Field(
description="A URL that points to the system's publicly accessible privacy policy."
)
legal_name: Optional[str] = Field(
description="The legal name for the business represented by the system."
)
legal_address: Optional[str] = Field(