-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrua.py
153 lines (112 loc) · 3.84 KB
/
rua.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
"""
<Name>
rua.py
<Author>
Lukas Puehringer <[email protected]>
<Started>
May, 2018
<Copyright>
See LICENSE for licensing information.
<Purpose>
Methods to generate and validate XML formatted DMARC aggregate reports.
The following snippet can be used to generate, validate and store a sample
DMARC aggregate report under its corresponding name using a Python
interpreter
```
from rua import generate_report, validate_report
from data import sample_report
# Generate a DMARC aggregate report passing it some sample data
report, report_name = generate_report(sample_report)
# Verify that report follows the right schema
# If no exception is raised we're good
validate_report(report)
# Write to file using the typical filename format
with open(report_name, "w") as f: f.write(report)
```
"""
from lxml import etree
from jinja2 import FileSystemLoader, Environment
# Filename constants
REPORT_TEMPLATE_DIR = "."
REPORT_TEMPLATE_NAME = "rua.xml.j2"
REPORT_SCHEMA_NAME = "rua.xsd"
# Prepare DMARC aggregate report template
LOADER = FileSystemLoader(REPORT_TEMPLATE_DIR)
ENV = Environment(loader=LOADER, trim_blocks=True, lstrip_blocks=True)
REPORT_TEMPLATE = ENV.get_template(REPORT_TEMPLATE_NAME)
# Prepare DMARC aggregate report schema
with open(REPORT_SCHEMA_NAME, "r") as f:
schema_data = f.read()
REPORT_SCHEMA = schema_data
# Define typical filename format for DMARC aggregate reports
REPORT_FILENAME_FORMAT = "{org_name}!{domain}!{begin}!{end}.xml"
def _get_report_filename_from_context(context):
"""Generate report filename from context used with `generate_report`. """
return REPORT_FILENAME_FORMAT.format(
org_name=context["report_metadata"]["org_name"],
domain=context["policy_published"]["domain"],
begin=context["report_metadata"]["date_range"]["begin"],
end=context["report_metadata"]["date_range"]["end"])
def generate_report(context):
"""
<Purpose>
Populate DMARC aggregate report based on passed context returning a tuple
containing report (UTF-8 string) and report name.
<Arguments>
context:
A python dictionary containing DMARC aggregate report data.
(see data.sample_report for the required format)
<Returns>
A tuple containing the generated report as UTF-8 encoded string and the
report name corresponding to REPORT_FILENAME_FORMAT.
"""
# Render template using passed context
report_string = REPORT_TEMPLATE.render(context)
# Encode as `UTF-8` string (required e.g. by validate functions)
report_string = report_string.encode("utf-8")
# Create report name
report_name = _get_report_filename_from_context(context)
return report_string, report_name
def validate_report(dmarc_report):
"""
<Purpose>
Validate passed DMARC report using REPORT_SCHEMA.
<Arguments>
dmarc_report:
UTF-8 encoded DMARC aggregate report data.
<Raises>
ParseError,
if xml_data can' be parsed.
InvalidDocument,
if xml_data is not valid against xsd_data.
<Returns>
None.
"""
validate(dmarc_report, REPORT_SCHEMA)
def validate(xml_data, xsd_data):
"""
<Purpose>
Validate passed xml data against xsd data and raise Exception
if data is invalid.
<Arguments>
xml_data:
UTF-8 encoded XML data to be validated.
xsd_data:
UTF-8 encoded XSD schema used for validation.
<Raises>
ParseError,
if xml_data can' be parsed.
XMLSchemaParseError,
if xsd_data can't be parsed.
InvalidDocument,
if xml_data is not valid against xsd_data.
<Returns>
None.
"""
# Create XML objects from xml and xsd data
xml = etree.fromstring(xml_data)
schema_xml = etree.fromstring(xsd_data)
# Create a schema object from schema xml object
schema = etree.XMLSchema(schema_xml)
# Validate
schema.assertValid(xml)