iaptoolkit 0.5.2__tar.gz → 0.5.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {iaptoolkit-0.5.2 → iaptoolkit-0.5.3}/PKG-INFO +1 -1
- {iaptoolkit-0.5.2 → iaptoolkit-0.5.3}/pyproject.toml +1 -1
- iaptoolkit-0.5.3/src/iaptoolkit/utils/urls.py +45 -0
- iaptoolkit-0.5.2/src/iaptoolkit/utils/urls.py +0 -119
- {iaptoolkit-0.5.2 → iaptoolkit-0.5.3}/LICENSE +0 -0
- {iaptoolkit-0.5.2 → iaptoolkit-0.5.3}/README.md +0 -0
- {iaptoolkit-0.5.2 → iaptoolkit-0.5.3}/src/iaptoolkit/__init__.py +0 -0
- {iaptoolkit-0.5.2 → iaptoolkit-0.5.3}/src/iaptoolkit/constants.py +0 -0
- {iaptoolkit-0.5.2 → iaptoolkit-0.5.3}/src/iaptoolkit/exceptions.py +0 -0
- {iaptoolkit-0.5.2 → iaptoolkit-0.5.3}/src/iaptoolkit/headers.py +0 -0
- {iaptoolkit-0.5.2 → iaptoolkit-0.5.3}/src/iaptoolkit/tokens/__init__.py +0 -0
- {iaptoolkit-0.5.2 → iaptoolkit-0.5.3}/src/iaptoolkit/tokens/service_account.py +0 -0
- {iaptoolkit-0.5.2 → iaptoolkit-0.5.3}/src/iaptoolkit/tokens/structs.py +0 -0
- {iaptoolkit-0.5.2 → iaptoolkit-0.5.3}/src/iaptoolkit/tokens/token_datastore.py +0 -0
- {iaptoolkit-0.5.2 → iaptoolkit-0.5.3}/src/iaptoolkit/utils/__init__.py +0 -0
- {iaptoolkit-0.5.2 → iaptoolkit-0.5.3}/src/iaptoolkit/utils/verify.py +0 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import typing as t
|
|
2
|
+
from urllib.parse import ParseResult
|
|
3
|
+
|
|
4
|
+
from kvcommon import logger
|
|
5
|
+
from kvcommon.urls import get_netloc_without_port_from_url_parts
|
|
6
|
+
|
|
7
|
+
from iaptoolkit.exceptions import InvalidDomain
|
|
8
|
+
|
|
9
|
+
LOG = logger.get_logger("iaptk")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def is_url_safe_for_token(
|
|
13
|
+
url_parts: ParseResult, allowed_domains: t.Optional[t.List[str] | t.Set[str] | t.Tuple[str]] = None,
|
|
14
|
+
) -> bool:
|
|
15
|
+
"""Determines if the given url is considered a safe to receive a token in request headers.
|
|
16
|
+
|
|
17
|
+
If URL validation is enabled, check that the URL's netloc is in the list of valid domains.
|
|
18
|
+
"""
|
|
19
|
+
if not isinstance(url_parts, ParseResult):
|
|
20
|
+
raise TypeError(
|
|
21
|
+
f"Invalid url_parts - Expected a ParseResult - Got: "
|
|
22
|
+
f"'{str(url_parts)}' (type#: {type(url_parts).__name__})"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
if allowed_domains is not None and not isinstance(allowed_domains, (list, set, tuple)):
|
|
26
|
+
raise TypeError("allowed_domains must be a list, set or tuple if not None")
|
|
27
|
+
|
|
28
|
+
netloc = get_netloc_without_port_from_url_parts(url_parts)
|
|
29
|
+
if not netloc:
|
|
30
|
+
return False
|
|
31
|
+
|
|
32
|
+
if not allowed_domains:
|
|
33
|
+
return True
|
|
34
|
+
|
|
35
|
+
for domain in allowed_domains:
|
|
36
|
+
if domain == "" or not isinstance(domain, str):
|
|
37
|
+
raise InvalidDomain(
|
|
38
|
+
f"Empty or non-string domain in allowed_domains: "
|
|
39
|
+
f"'{str(domain)}' (type#: {type(domain).__name__})"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
if netloc.endswith(domain):
|
|
43
|
+
return True
|
|
44
|
+
|
|
45
|
+
return False
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
import httpx
|
|
3
|
-
import requests
|
|
4
|
-
import typing as t
|
|
5
|
-
from urllib.parse import parse_qs
|
|
6
|
-
from urllib.parse import ParseResult
|
|
7
|
-
from urllib.parse import urlparse
|
|
8
|
-
|
|
9
|
-
from kvcommon import logger
|
|
10
|
-
from kvcommon.urls import get_netloc_without_port_from_url_parts
|
|
11
|
-
|
|
12
|
-
from iaptoolkit.exceptions import IAPClientIDException
|
|
13
|
-
from iaptoolkit.exceptions import InvalidDomain
|
|
14
|
-
|
|
15
|
-
LOG = logger.get_logger("iaptk")
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def is_url_safe_for_token(
|
|
19
|
-
url_parts: ParseResult, allowed_domains: t.Optional[t.List[str] | t.Set[str] | t.Tuple[str]] = None,
|
|
20
|
-
) -> bool:
|
|
21
|
-
"""Determines if the given url is considered a safe to receive a token in request headers.
|
|
22
|
-
|
|
23
|
-
If URL validation is enabled, check that the URL's netloc is in the list of valid domains.
|
|
24
|
-
"""
|
|
25
|
-
if not isinstance(url_parts, ParseResult):
|
|
26
|
-
raise TypeError(
|
|
27
|
-
f"Invalid url_parts - Expected a ParseResult - Got: "
|
|
28
|
-
f"'{str(url_parts)}' (type#: {type(url_parts).__name__})"
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
if allowed_domains is not None and not isinstance(allowed_domains, (list, set, tuple)):
|
|
32
|
-
raise TypeError("allowed_domains must be a list, set or tuple if not None")
|
|
33
|
-
|
|
34
|
-
netloc = get_netloc_without_port_from_url_parts(url_parts)
|
|
35
|
-
if not netloc:
|
|
36
|
-
return False
|
|
37
|
-
|
|
38
|
-
if not allowed_domains:
|
|
39
|
-
return True
|
|
40
|
-
|
|
41
|
-
for domain in allowed_domains:
|
|
42
|
-
if domain == "" or not isinstance(domain, str):
|
|
43
|
-
raise InvalidDomain(
|
|
44
|
-
f"Empty or non-string domain in allowed_domains: "
|
|
45
|
-
f"'{str(domain)}' (type#: {type(domain).__name__})"
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
if netloc.endswith(domain):
|
|
49
|
-
return True
|
|
50
|
-
|
|
51
|
-
return False
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
@dataclass(kw_only=True)
|
|
55
|
-
class IAPURLState:
|
|
56
|
-
url: str
|
|
57
|
-
protected: bool = False
|
|
58
|
-
iap_audience: str | None = None
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def _determine_iap_state_from_response(response: requests.Response | httpx.Response, url: str):
|
|
62
|
-
# This approach may not be reliable - Undocumented?
|
|
63
|
-
|
|
64
|
-
iap_audience = None
|
|
65
|
-
requires_iap = False
|
|
66
|
-
|
|
67
|
-
if response.status_code == 302:
|
|
68
|
-
location = response.headers.get("location")
|
|
69
|
-
qs = str(urlparse(location).query)
|
|
70
|
-
query = parse_qs(qs) or {}
|
|
71
|
-
if "client_id" in query:
|
|
72
|
-
iap_audience = str(query["client_id"][0])
|
|
73
|
-
requires_iap = True
|
|
74
|
-
|
|
75
|
-
return IAPURLState(url=url, protected=requires_iap, iap_audience=iap_audience)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def get_url_iap_state(url: str) -> IAPURLState:
|
|
79
|
-
response = requests.get(url, allow_redirects=False)
|
|
80
|
-
return _determine_iap_state_from_response(response, url)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
async def get_url_iap_state_async(url: str) -> IAPURLState:
|
|
84
|
-
async with httpx.AsyncClient() as client:
|
|
85
|
-
response = await client.get(url, follow_redirects=False)
|
|
86
|
-
return _determine_iap_state_from_response(response, url)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def is_url_iap_protected(url: str) -> bool:
|
|
90
|
-
url_state: IAPURLState = get_url_iap_state(url)
|
|
91
|
-
return url_state.protected
|
|
92
|
-
|
|
93
|
-
async def is_url_iap_protected_async(url: str) -> bool:
|
|
94
|
-
url_state: IAPURLState = await get_url_iap_state_async(url)
|
|
95
|
-
return url_state.protected
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
def _handle_url_state_for_audience(url_state: IAPURLState) -> str | None:
|
|
99
|
-
if not url_state.protected:
|
|
100
|
-
raise IAPClientIDException(f"URL does not appear to be IAP-protected: '{url_state.url}'")
|
|
101
|
-
|
|
102
|
-
iap_audience = url_state.iap_audience
|
|
103
|
-
if not iap_audience:
|
|
104
|
-
raise IAPClientIDException(
|
|
105
|
-
f"No client_id returned in redirect for query when trying to retrieve IAP Client ID for url: '{url_state.url}'"
|
|
106
|
-
)
|
|
107
|
-
return iap_audience
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
def get_iap_audience_for_url(url: str) -> str | None:
|
|
111
|
-
url_state: IAPURLState = get_url_iap_state(url)
|
|
112
|
-
|
|
113
|
-
# TODO: Legacy terminology - Deprecate
|
|
114
|
-
get_iap_client_id_for_url = get_iap_audience_for_url
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
async def get_iap_audience_for_url_async(url: str) -> str | None:
|
|
118
|
-
url_state: IAPURLState = await get_url_iap_state_async(url)
|
|
119
|
-
return _handle_url_state_for_audience(url_state)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|