-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathdate_validators.py
More file actions
110 lines (82 loc) · 3.02 KB
/
date_validators.py
File metadata and controls
110 lines (82 loc) · 3.02 KB
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
"""Validators of the date header to use in the mock query API."""
import contextlib
import datetime
import logging
from collections.abc import Mapping
from zoneinfo import ZoneInfo
from beartype import beartype
from mock_vws._query_validators.exceptions import (
DateFormatNotValidError,
DateHeaderNotGivenError,
RequestTimeTooSkewedError,
)
_LOGGER = logging.getLogger(name=__name__)
@beartype
def validate_date_header_given(*, request_headers: Mapping[str, str]) -> None:
"""Validate the date header is given to the query endpoint.
Args:
request_headers: The headers sent with the request.
Raises:
DateHeaderNotGivenError: The date is not given.
"""
if "Date" in request_headers:
return
_LOGGER.warning(msg="The date header is not given.")
raise DateHeaderNotGivenError
@beartype
def _accepted_date_formats() -> set[str]:
"""Return all known accepted date formats.
We expect that more formats than this will be accepted. These are
the accepted ones we know of at the time of writing.
"""
known_accepted_formats = {
"%a, %b %d %H:%M:%S %Y",
"%a %b %d %H:%M:%S %Y",
"%a, %d %b %Y %H:%M:%S",
"%a %d %b %Y %H:%M:%S",
}
return known_accepted_formats.union(
{f"{date_format} GMT" for date_format in known_accepted_formats},
)
@beartype
def validate_date_format(*, request_headers: Mapping[str, str]) -> None:
"""Validate the format of the date header given to the query endpoint.
Args:
request_headers: The headers sent with the request.
Raises:
DateFormatNotValidError: The date is in the wrong format.
"""
date_header = request_headers["Date"]
for date_format in _accepted_date_formats():
with contextlib.suppress(ValueError):
datetime.datetime.strptime(date_header, date_format).astimezone()
return
_LOGGER.warning(msg="The date header is in the wrong format.")
raise DateFormatNotValidError
@beartype
def validate_date_in_range(*, request_headers: Mapping[str, str]) -> None:
"""Validate date in the date header given to the query endpoint.
Args:
request_headers: The headers sent with the request.
Raises:
RequestTimeTooSkewedError: The date is out of range.
"""
date_header = request_headers["Date"]
gmt = ZoneInfo(key="GMT")
dates: list[datetime.datetime] = []
for date_format in _accepted_date_formats():
with contextlib.suppress(ValueError):
date = datetime.datetime.strptime(
date_header,
date_format,
).astimezone()
dates.append(date)
date = dates[0]
now = datetime.datetime.now(tz=gmt)
date_from_header = date.replace(tzinfo=gmt)
time_difference = now - date_from_header
maximum_time_difference = datetime.timedelta(minutes=65)
if abs(time_difference) < maximum_time_difference:
return
_LOGGER.warning(msg="The date header is out of range.")
raise RequestTimeTooSkewedError