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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iaptoolkit
3
- Version: 0.5.2
3
+ Version: 0.5.3
4
4
  Summary: Library of common utils for interacting with Identity-Aware Proxies
5
5
  License-File: LICENSE
6
6
  Author: Rob Voigt
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "iaptoolkit"
3
- version = "0.5.2"
3
+ version = "0.5.3"
4
4
  description = "Library of common utils for interacting with Identity-Aware Proxies"
5
5
  authors = ["Rob Voigt <code@ravoigt.com>"]
6
6
  readme = "README.md"
@@ -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