cliV1 0.0.6__py3-none-any.whl

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.
Files changed (92) hide show
  1. cliv1-0.0.6.dist-info/METADATA +212 -0
  2. cliv1-0.0.6.dist-info/RECORD +92 -0
  3. cliv1-0.0.6.dist-info/WHEEL +4 -0
  4. cliv1-0.0.6.dist-info/licenses/LICENSE +21 -0
  5. swagger_petstore_open_api_3_1_0/__init__.py +15 -0
  6. swagger_petstore_open_api_3_1_0/apis/__init__.py +5 -0
  7. swagger_petstore_open_api_3_1_0/apis/pet_api.py +922 -0
  8. swagger_petstore_open_api_3_1_0/apis/store.py +432 -0
  9. swagger_petstore_open_api_3_1_0/apis/user_api.py +829 -0
  10. swagger_petstore_open_api_3_1_0/async_client.py +87 -0
  11. swagger_petstore_open_api_3_1_0/auth.py +17 -0
  12. swagger_petstore_open_api_3_1_0/base_client.py +27 -0
  13. swagger_petstore_open_api_3_1_0/client.py +85 -0
  14. swagger_petstore_open_api_3_1_0/core/__init__.py +395 -0
  15. swagger_petstore_open_api_3_1_0/core/_internal/__init__.py +9 -0
  16. swagger_petstore_open_api_3_1_0/core/_internal/content_disposition.py +154 -0
  17. swagger_petstore_open_api_3_1_0/core/_internal/flattening.py +104 -0
  18. swagger_petstore_open_api_3_1_0/core/_internal/headers.py +243 -0
  19. swagger_petstore_open_api_3_1_0/core/_internal/redaction.py +208 -0
  20. swagger_petstore_open_api_3_1_0/core/_internal/sse_parser.py +124 -0
  21. swagger_petstore_open_api_3_1_0/core/_internal/subscripts.py +50 -0
  22. swagger_petstore_open_api_3_1_0/core/_internal/urls.py +172 -0
  23. swagger_petstore_open_api_3_1_0/core/_internal/wire.py +201 -0
  24. swagger_petstore_open_api_3_1_0/core/adapters.py +177 -0
  25. swagger_petstore_open_api_3_1_0/core/auth/__init__.py +70 -0
  26. swagger_petstore_open_api_3_1_0/core/auth/composition.py +175 -0
  27. swagger_petstore_open_api_3_1_0/core/auth/models.py +216 -0
  28. swagger_petstore_open_api_3_1_0/core/auth/protocols.py +103 -0
  29. swagger_petstore_open_api_3_1_0/core/auth/schemes/__init__.py +94 -0
  30. swagger_petstore_open_api_3_1_0/core/auth/schemes/api_key_auth.py +53 -0
  31. swagger_petstore_open_api_3_1_0/core/auth/schemes/basic_auth.py +73 -0
  32. swagger_petstore_open_api_3_1_0/core/auth/schemes/bearer_auth.py +21 -0
  33. swagger_petstore_open_api_3_1_0/core/auth/schemes/oauth2_authorization_code.py +395 -0
  34. swagger_petstore_open_api_3_1_0/core/auth/schemes/oauth2_client_credentials.py +130 -0
  35. swagger_petstore_open_api_3_1_0/core/auth/schemes/oauth2_password.py +145 -0
  36. swagger_petstore_open_api_3_1_0/core/auth/schemes/oauth2_schemes.py +300 -0
  37. swagger_petstore_open_api_3_1_0/core/base_raw_response.py +49 -0
  38. swagger_petstore_open_api_3_1_0/core/bodies.py +590 -0
  39. swagger_petstore_open_api_3_1_0/core/converters/__init__.py +68 -0
  40. swagger_petstore_open_api_3_1_0/core/converters/date_time.py +237 -0
  41. swagger_petstore_open_api_3_1_0/core/converters/encoded_bytes.py +273 -0
  42. swagger_petstore_open_api_3_1_0/core/converters/open_enum.py +35 -0
  43. swagger_petstore_open_api_3_1_0/core/decoding.py +498 -0
  44. swagger_petstore_open_api_3_1_0/core/discriminators.py +129 -0
  45. swagger_petstore_open_api_3_1_0/core/event_streams.py +225 -0
  46. swagger_petstore_open_api_3_1_0/core/exceptions.py +78 -0
  47. swagger_petstore_open_api_3_1_0/core/file_responses.py +337 -0
  48. swagger_petstore_open_api_3_1_0/core/files.py +174 -0
  49. swagger_petstore_open_api_3_1_0/core/httpx2_transport.py +748 -0
  50. swagger_petstore_open_api_3_1_0/core/logging.py +472 -0
  51. swagger_petstore_open_api_3_1_0/core/models.py +143 -0
  52. swagger_petstore_open_api_3_1_0/core/optionality.py +144 -0
  53. swagger_petstore_open_api_3_1_0/core/pagination.py +452 -0
  54. swagger_petstore_open_api_3_1_0/core/paginator.py +55 -0
  55. swagger_petstore_open_api_3_1_0/core/params.py +157 -0
  56. swagger_petstore_open_api_3_1_0/core/raw_client.py +480 -0
  57. swagger_petstore_open_api_3_1_0/core/request_options.py +172 -0
  58. swagger_petstore_open_api_3_1_0/core/results.py +178 -0
  59. swagger_petstore_open_api_3_1_0/core/retries.py +237 -0
  60. swagger_petstore_open_api_3_1_0/core/runtime_env.py +43 -0
  61. swagger_petstore_open_api_3_1_0/core/servers.py +33 -0
  62. swagger_petstore_open_api_3_1_0/core/transport.py +218 -0
  63. swagger_petstore_open_api_3_1_0/errors/__init__.py +48 -0
  64. swagger_petstore_open_api_3_1_0/errors/add_pet_error.py +21 -0
  65. swagger_petstore_open_api_3_1_0/errors/delete_order_error.py +21 -0
  66. swagger_petstore_open_api_3_1_0/errors/delete_pet_error.py +21 -0
  67. swagger_petstore_open_api_3_1_0/errors/delete_user_error.py +21 -0
  68. swagger_petstore_open_api_3_1_0/errors/find_pets_by_status_error.py +21 -0
  69. swagger_petstore_open_api_3_1_0/errors/find_pets_by_tags_error.py +21 -0
  70. swagger_petstore_open_api_3_1_0/errors/get_order_by_id_error.py +21 -0
  71. swagger_petstore_open_api_3_1_0/errors/get_pet_by_id_error.py +21 -0
  72. swagger_petstore_open_api_3_1_0/errors/get_user_by_name_error.py +21 -0
  73. swagger_petstore_open_api_3_1_0/errors/login_user_error.py +21 -0
  74. swagger_petstore_open_api_3_1_0/errors/place_order_error.py +21 -0
  75. swagger_petstore_open_api_3_1_0/errors/update_pet_error.py +21 -0
  76. swagger_petstore_open_api_3_1_0/errors/update_pet_with_form_error.py +21 -0
  77. swagger_petstore_open_api_3_1_0/errors/update_user_error.py +21 -0
  78. swagger_petstore_open_api_3_1_0/errors/upload_file_error.py +21 -0
  79. swagger_petstore_open_api_3_1_0/models/__init__.py +23 -0
  80. swagger_petstore_open_api_3_1_0/models/api_response.py +18 -0
  81. swagger_petstore_open_api_3_1_0/models/category.py +15 -0
  82. swagger_petstore_open_api_3_1_0/models/enums/__init__.py +4 -0
  83. swagger_petstore_open_api_3_1_0/models/enums/order_status.py +17 -0
  84. swagger_petstore_open_api_3_1_0/models/enums/pet_status.py +17 -0
  85. swagger_petstore_open_api_3_1_0/models/order.py +27 -0
  86. swagger_petstore_open_api_3_1_0/models/pet.py +28 -0
  87. swagger_petstore_open_api_3_1_0/models/tag_model.py +15 -0
  88. swagger_petstore_open_api_3_1_0/models/user.py +29 -0
  89. swagger_petstore_open_api_3_1_0/py.typed +0 -0
  90. swagger_petstore_open_api_3_1_0/server/__init__.py +21 -0
  91. swagger_petstore_open_api_3_1_0/server/server.py +17 -0
  92. swagger_petstore_open_api_3_1_0/server/server_config.py +52 -0
@@ -0,0 +1,216 @@
1
+ """What every security scheme trades in, and the OAuth 2.0 vocabulary its grants share.
2
+
3
+ :class:`AuthParams` is what one scheme contributes to a request. The rest is managed OAuth 2.0's
4
+ grant-agnostic half: the token endpoint's success and error bodies (RFC 6749 §5.1 and §5.2, fixed for
5
+ every provider, which is why they are runtime types at all), §2.3.1's client-authentication placements,
6
+ §3.3's scope delimiter, and the scope vocabulary parameter every grant's credentials carry.
7
+
8
+ The bottom of the auth layer. It names no protocol and no scheme, so ``protocols``, ``composition`` and
9
+ ``schemes/`` all import from here and nothing imports back. It performs no token request either --
10
+ which is why no ``RawClient``, ``form_body`` or ``UrlTemplate`` appears below."""
11
+
12
+ from __future__ import annotations
13
+
14
+ from base64 import b64encode
15
+ from collections.abc import Callable, Sequence
16
+ from dataclasses import dataclass
17
+ from typing import Any, Final, TypeAlias
18
+ from urllib.parse import quote
19
+
20
+ from pydantic import BaseModel, ConfigDict, Field
21
+
22
+ # ``TypeVar`` from here and not ``typing``: PEP 696 defaults reach ``typing`` at 3.13, and ``ScopeT``
23
+ # below needs one at the 3.10 floor.
24
+ from typing_extensions import TypeVar
25
+
26
+ from .._internal.wire import parse_json, text_values
27
+ from ..decoding import ErrorMapper
28
+ from ..params import Param, param
29
+ from ..results import RawError
30
+
31
+
32
+ @dataclass(frozen=True, slots=True)
33
+ class AuthParams:
34
+ """What one scheme contributes to a request.
35
+
36
+ Held as :class:`Param` rather than plain strings, so a credential is validated, dumped and
37
+ encoded by the same code as an endpoint's own parameters."""
38
+
39
+ headers: tuple[Param[Any], ...] = ()
40
+ query_params: tuple[Param[Any], ...] = ()
41
+ cookies: tuple[Param[Any], ...] = ()
42
+
43
+ def request_headers(self) -> tuple[Param[Any], ...]:
44
+ """Every header this contributes, cookie pairs folded into one ``Cookie`` (RFC 6265).
45
+
46
+ Folding here rather than at each scheme is what lets two cookie-bearing schemes compose;
47
+ two ``Cookie`` parameters would see the second overwrite the first. This covers only what
48
+ *this request's* schemes contributed -- folding the jar across the API's, the endpoint's and
49
+ the caller's layers is ``_internal/headers.py``'s.
50
+
51
+ The jar is tested rather than the tuple, so cookies that all render to nothing contribute no
52
+ header at all instead of an empty one that would clobber a lower layer.
53
+
54
+ Returns:
55
+ The headers, plus one folded ``Cookie`` when any cookie was contributed."""
56
+ jar = "; ".join(f"{cookie.key}={value}" for cookie in self.cookies for value in text_values(cookie))
57
+ if not jar:
58
+ return self.headers
59
+
60
+ return *self.headers, param[str]("Cookie", jar)
61
+
62
+
63
+ ScopeT = TypeVar("ScopeT", bound=str, default=str)
64
+ """A grant's scope vocabulary, as its flow declares it.
65
+
66
+ Bound to ``str`` because RFC 6749 §3.3 puts scopes on the wire as space-delimited strings, and
67
+ defaulted to it so an unparametrized credential means what it always did: any scope the provider
68
+ accepts, which is also what a flow declaring no scopes emits."""
69
+
70
+
71
+ class OAuth2Credentials(BaseModel):
72
+ """The base every OAuth 2.0 grant's credentials share, carrying one thing: their repr.
73
+
74
+ Each grant's credentials are generic over :data:`ScopeT`, and pydantic names a parametrized model
75
+ after its parametrization -- so an unhelped repr prints the flow's whole scope vocabulary in place
76
+ of the class a caller wrote. This restores the latter.
77
+
78
+ ⚠️ **It declares no fields, and must not gain one.** Pydantic orders a base's fields ahead of a
79
+ subclass's, so a field here would move to the front of every credential's repr and ``model_dump``.
80
+ A grant's own fields stay in its own model, which is also where ``model_config``, its ``coerce``
81
+ and its validators stay."""
82
+
83
+ def __repr_name__(self) -> str:
84
+ """The class name without its parametrization.
85
+
86
+ Returns:
87
+ The unsubscripted class's name, or this class's own when it was never subscripted."""
88
+ origin = type(self).__pydantic_generic_metadata__["origin"]
89
+ return origin.__name__ if origin is not None else type(self).__name__
90
+
91
+
92
+ class OAuthToken(BaseModel):
93
+ """The token endpoint's success body (RFC 6749 §5.1), and what a token source returns.
94
+
95
+ ``expires_in`` is RECOMMENDED rather than required, so its absence means "no client-side
96
+ deadline". ``token_type`` is parsed because the RFC requires it and deliberately **not** used
97
+ when applying: echoing a wire keyword into ``Authorization`` would put unvalidated data there.
98
+
99
+ No ``refresh_token``: RFC 6749 §4.4.3 says the client-credentials grant SHOULD NOT be issued
100
+ one. A grant that may be returns :class:`OAuthTokenRefreshable` instead."""
101
+
102
+ model_config = ConfigDict(frozen=True)
103
+
104
+ access_token: str = Field(repr=False)
105
+ token_type: str
106
+ expires_in: int | None = None
107
+ scope: str | None = None
108
+
109
+
110
+ class OAuthTokenRefreshable(OAuthToken):
111
+ """The token endpoint's success body for a grant that may issue a refresh token.
112
+
113
+ RFC 6749 §4.1.4 and §4.3.3 both make ``refresh_token`` OPTIONAL. A subclass rather than an
114
+ optional field, so a source's declared return type says which tier it belongs to."""
115
+
116
+ refresh_token: str | None = Field(default=None, repr=False)
117
+
118
+
119
+ class OAuthProviderError(BaseModel):
120
+ """The token endpoint's error body (RFC 6749 §5.2).
121
+
122
+ Typed, unlike the rest of a failed token fetch, because §5.2 fixes these three members for every
123
+ provider."""
124
+
125
+ model_config = ConfigDict(frozen=True)
126
+
127
+ error: str
128
+ error_description: str | None = None
129
+ error_uri: str | None = None
130
+
131
+
132
+ OAuthError: TypeAlias = OAuthProviderError | RawError
133
+ """What a failed token fetch carries: the RFC 6749 §5.2 body, or the raw response without one."""
134
+
135
+
136
+ @dataclass(frozen=True, slots=True)
137
+ class OAuthErrorResponse:
138
+ """Error mapper for the token endpoint.
139
+
140
+ The only mapper here that selects a *schema*, and it can: RFC 6749 §5.2 fixes one error body for
141
+ every provider. A response that does not conform becomes a :class:`RawError` rather than raising
142
+ out of the mapper, because a failed fetch must surface the provider's answer, not a parse error."""
143
+
144
+ def map(self, status_code: int, content: bytes) -> OAuthError:
145
+ try:
146
+ return OAuthProviderError.model_validate(parse_json(content))
147
+ except ValueError:
148
+ return RawError(status_code, content)
149
+
150
+
151
+ oauth_error_response: Final[ErrorMapper[OAuthError]] = OAuthErrorResponse()
152
+
153
+
154
+ OAuthParams: TypeAlias = tuple[Param[Any], ...]
155
+ """One destination's worth of parameters: half a token request, or an authorization URL's query."""
156
+
157
+ CredentialsPlacement: TypeAlias = Callable[[str, str | None], tuple[OAuthParams, OAuthParams]]
158
+ """Where a client's own credentials travel on a token request: its headers, and its form fields.
159
+
160
+ Named after the specification extension it is read from, ``x-oauth2-credentials-placement``.
161
+ Grant-agnostic: RFC 6749 §2.3.1 authenticates the *client*, which every grant does identically."""
162
+
163
+
164
+ def scope_params(scopes: Sequence[str] | None) -> OAuthParams:
165
+ """The ``scope`` field, or nothing at all when no scopes were asked for.
166
+
167
+ RFC 6749 §3.3's space delimiter, written once, so a caller passes a list and never learns it.
168
+ Takes a ``Sequence`` so a credential's narrowed ``list[ScopeT]`` reaches it without a copy.
169
+
170
+ Args:
171
+ scopes: The scopes to request; absent or empty asks for the client's defaults.
172
+
173
+ Returns:
174
+ One ``scope`` parameter, or an empty tuple."""
175
+ if not scopes:
176
+ return ()
177
+ return (param[str]("scope", " ".join(scopes)),)
178
+
179
+
180
+ def client_secret_basic(client_id: str, client_secret: str | None) -> tuple[OAuthParams, OAuthParams]:
181
+ """RFC 6749 §2.3.1: the client authenticates with HTTP Basic on the token request.
182
+
183
+ Both halves are percent-encoded before base64, as §2.3.1 requires. A client with **no secret**
184
+ identifies itself with a ``client_id`` form field instead, which is §3.2.1's requirement exactly
185
+ then rather than a degraded fallback.
186
+
187
+ Args:
188
+ client_id: The client's identifier, as issued by the provider.
189
+ client_secret: Its secret, or ``None`` for a public client.
190
+
191
+ Returns:
192
+ The headers and the form fields the client's credentials contribute, in that order."""
193
+ if client_secret is None:
194
+ return (), (param[str]("client_id", client_id),)
195
+ raw = f"{quote(client_id, safe='')}:{quote(client_secret, safe='')}"
196
+ return (param[str]("Authorization", f"Basic {b64encode(raw.encode()).decode()}"),), ()
197
+
198
+
199
+ def client_secret_post(client_id: str, client_secret: str | None) -> tuple[OAuthParams, OAuthParams]:
200
+ """RFC 6749 §2.3.1's alternative: the credentials travel in the form body instead.
201
+
202
+ The empty header tuple is the invariant, not an oversight -- carrying the pair in both places
203
+ would authenticate the client twice.
204
+
205
+ Args:
206
+ client_id: The client's identifier, as issued by the provider.
207
+ client_secret: Its secret, omitted entirely when ``None`` (§3.2.1).
208
+
209
+ Returns:
210
+ An empty header tuple, and the form fields the client's credentials contribute."""
211
+ if client_secret is None:
212
+ return (), (param[str]("client_id", client_id),)
213
+ return (), (
214
+ param[str]("client_id", client_id),
215
+ param[str]("client_secret", client_secret),
216
+ )
@@ -0,0 +1,103 @@
1
+ """Every protocol the authentication layer defines: what a scheme is, and where a token comes from.
2
+
3
+ Two families, one file. :class:`AuthScheme` and its async twin are the whole contract for applying a
4
+ credential to a request, so a scheme this runtime does not ship is a class a *caller* writes; ``apply``
5
+ runs once per request, which is what makes rotation and just-in-time signing work.
6
+ :class:`TokenSource` and its three relatives are the seam managed OAuth 2.0 fetches through, so a
7
+ consumer replaces the built-in source without touching the caching scheme that calls it.
8
+
9
+ Nothing here has a body, so nothing here reaches a scheme, a composite or a transport -- only the
10
+ models its members name."""
11
+
12
+ from __future__ import annotations
13
+
14
+ from collections.abc import Awaitable
15
+ from typing import Protocol, TypeVar, runtime_checkable
16
+
17
+ from .models import AuthParams, OAuthToken, OAuthTokenRefreshable
18
+
19
+ CredentialsT_contra = TypeVar("CredentialsT_contra", contravariant=True)
20
+ """A grant's credentials, as a source accepts them.
21
+
22
+ Contravariant because the protocols below take it in an *argument* position and nowhere else."""
23
+
24
+
25
+ class AuthScheme(Protocol):
26
+ """A security scheme, as the synchronous client sees it.
27
+
28
+ Implementations:
29
+
30
+ - MUST be safe to call repeatedly -- ``apply`` runs once per request.
31
+ - SHOULD be frozen: one scheme instance serves every request a client makes.
32
+ - MAY block, but only if the synchronous client is the only one that will hold it."""
33
+
34
+ def apply(self) -> AuthParams: ...
35
+
36
+
37
+ class AsyncAuthScheme(Protocol):
38
+ """A security scheme, as the asynchronous client sees it.
39
+
40
+ ``apply`` may return the parameters directly *or* an awaitable of them, so one synchronous method
41
+ satisfies this protocol **and** :class:`AuthScheme` and no async twin of a static scheme is
42
+ needed. A scheme that must reach the network declares ``async def apply`` and satisfies only this
43
+ one, which is what keeps it off the synchronous transport it could not use."""
44
+
45
+ def apply(self) -> AuthParams | Awaitable[AuthParams]: ...
46
+
47
+
48
+ @runtime_checkable
49
+ class RevocableAuthScheme(Protocol):
50
+ """A scheme whose credential can go stale before it expires.
51
+
52
+ ``invalidate`` is a **hint, not a barrier**: it runs after a 401 and must not block, so a fetch
53
+ already in flight may repopulate the cache immediately afterwards. ``runtime_checkable`` because
54
+ ``composition.invalidate`` has to ask a scheme whose type it does not know."""
55
+
56
+ def invalidate(self) -> None: ...
57
+
58
+
59
+ class TokenSource(Protocol[CredentialsT_contra]):
60
+ """Where an OAuth 2.0 scheme gets its token, given that grant's credentials.
61
+
62
+ Generic because the credentials differ between grants, so a mismatch is reported at the client
63
+ constructor rather than at the first request. ``fetch`` always receives the validated credentials
64
+ model, never the mapping form.
65
+
66
+ Implementations:
67
+
68
+ - MUST be safe to call repeatedly, and MAY be called concurrently.
69
+ - MUST NOT issue a request that itself uses the scheme holding them -- ``fetch`` runs inside that
70
+ scheme's lock, so doing so deadlocks.
71
+ - SHOULD bound their own I/O. The built-in source inherits the client's timeout."""
72
+
73
+ def fetch(self, credentials: CredentialsT_contra) -> OAuthToken: ...
74
+
75
+
76
+ class AsyncTokenSource(Protocol[CredentialsT_contra]):
77
+ """:class:`TokenSource` as the asynchronous client sees it.
78
+
79
+ ``fetch`` is a coroutine, deliberately unlike :meth:`AsyncAuthScheme.apply`'s union: nothing about
80
+ obtaining a token is static, so declaring it makes a synchronous source a **type** error rather
81
+ than a ``TypeError`` at the first request."""
82
+
83
+ async def fetch(self, credentials: CredentialsT_contra) -> OAuthToken: ...
84
+
85
+
86
+ class RefreshableTokenSource(TokenSource[CredentialsT_contra], Protocol[CredentialsT_contra]):
87
+ """A :class:`TokenSource` that can also exchange a refresh token for a new access token.
88
+
89
+ Extends rather than replaces: ``fetch`` still runs when there is nothing to refresh, and again
90
+ when a refresh is refused. ``refresh`` returns ``None`` for a *provider* refusal rather than
91
+ raising; a transport failure still propagates, as does a success whose body will not decode."""
92
+
93
+ def fetch(self, credentials: CredentialsT_contra) -> OAuthTokenRefreshable: ...
94
+
95
+ def refresh(self, credentials: CredentialsT_contra, refresh_token: str) -> OAuthTokenRefreshable | None: ...
96
+
97
+
98
+ class AsyncRefreshableTokenSource(AsyncTokenSource[CredentialsT_contra], Protocol[CredentialsT_contra]):
99
+ """:class:`RefreshableTokenSource` as the asynchronous client sees it: both members are awaited."""
100
+
101
+ async def fetch(self, credentials: CredentialsT_contra) -> OAuthTokenRefreshable: ...
102
+
103
+ async def refresh(self, credentials: CredentialsT_contra, refresh_token: str) -> OAuthTokenRefreshable | None: ...
@@ -0,0 +1,94 @@
1
+ """Every scheme this runtime ships, plus the two caching tiers a managed OAuth 2.0 grant applies through.
2
+
3
+ One module per scheme *type* -- together the whole of OpenAPI's ``securitySchemes`` surface:
4
+ ``http``/``basic`` and ``http``/``bearer``, ``apiKey`` in a header, a query parameter or a cookie, and
5
+ three ``oauth2`` flows. ``openIdConnect`` and ``implicit`` need no module of their own -- both reach this
6
+ runtime as a plain bearer token. Adding a fourth grant is one more module here and nothing anywhere else.
7
+
8
+ ``oauth2_schemes`` is the exception that earns its place: it names no grant, holding the two
9
+ grant-agnostic tiers every flow's credentials and source are handed to."""
10
+
11
+ from .api_key_auth import ApiKeyCookieScheme, ApiKeyHeaderScheme, ApiKeyQueryScheme
12
+ from .basic_auth import (
13
+ BasicAuthCredentials,
14
+ BasicAuthCredentialsDict,
15
+ BasicAuthCredentialsOrDict,
16
+ BasicAuthScheme,
17
+ )
18
+ from .bearer_auth import BearerAuthScheme
19
+ from .oauth2_authorization_code import (
20
+ AsyncAuthorizationCodeCredentials,
21
+ AsyncAuthorizationCodeCredentialsDict,
22
+ AsyncAuthorizationCodeCredentialsOrDict,
23
+ AsyncAuthorizationCodePrompt,
24
+ AsyncAuthorizationCodeTokenSource,
25
+ AuthorizationCodeCredentials,
26
+ AuthorizationCodeCredentialsDict,
27
+ AuthorizationCodeCredentialsOrDict,
28
+ AuthorizationCodePrompt,
29
+ AuthorizationCodeTokenSource,
30
+ PkceMethod,
31
+ )
32
+ from .oauth2_client_credentials import (
33
+ AsyncClientCredentialsTokenSource,
34
+ ClientCredentials,
35
+ ClientCredentialsDict,
36
+ ClientCredentialsOrDict,
37
+ ClientCredentialsTokenSource,
38
+ )
39
+ from .oauth2_password import (
40
+ AsyncPasswordTokenSource,
41
+ PasswordCredentials,
42
+ PasswordCredentialsDict,
43
+ PasswordCredentialsOrDict,
44
+ PasswordTokenSource,
45
+ )
46
+ from .oauth2_schemes import (
47
+ AsyncOAuth2RefreshableScheme,
48
+ AsyncOAuth2Scheme,
49
+ OAuth2RefreshableScheme,
50
+ OAuth2Scheme,
51
+ )
52
+
53
+ __all__ = [
54
+ # http / basic
55
+ "BasicAuthCredentials",
56
+ "BasicAuthCredentialsDict",
57
+ "BasicAuthCredentialsOrDict",
58
+ "BasicAuthScheme",
59
+ # http / bearer
60
+ "BearerAuthScheme",
61
+ # apiKey
62
+ "ApiKeyHeaderScheme",
63
+ "ApiKeyQueryScheme",
64
+ "ApiKeyCookieScheme",
65
+ # oauth2 -- the two caching tiers, grant-agnostic
66
+ "OAuth2Scheme",
67
+ "AsyncOAuth2Scheme",
68
+ "OAuth2RefreshableScheme",
69
+ "AsyncOAuth2RefreshableScheme",
70
+ # oauth2 / clientCredentials
71
+ "ClientCredentials",
72
+ "ClientCredentialsDict",
73
+ "ClientCredentialsOrDict",
74
+ "ClientCredentialsTokenSource",
75
+ "AsyncClientCredentialsTokenSource",
76
+ # oauth2 / password
77
+ "PasswordCredentials",
78
+ "PasswordCredentialsDict",
79
+ "PasswordCredentialsOrDict",
80
+ "PasswordTokenSource",
81
+ "AsyncPasswordTokenSource",
82
+ # oauth2 / authorizationCode
83
+ "AuthorizationCodeCredentials",
84
+ "AuthorizationCodeCredentialsDict",
85
+ "AuthorizationCodeCredentialsOrDict",
86
+ "AsyncAuthorizationCodeCredentials",
87
+ "AsyncAuthorizationCodeCredentialsDict",
88
+ "AsyncAuthorizationCodeCredentialsOrDict",
89
+ "AuthorizationCodePrompt",
90
+ "AsyncAuthorizationCodePrompt",
91
+ "PkceMethod",
92
+ "AuthorizationCodeTokenSource",
93
+ "AsyncAuthorizationCodeTokenSource",
94
+ ]
@@ -0,0 +1,53 @@
1
+ """``apiKey`` -- one credential, three destinations.
2
+
3
+ OpenAPI's ``apiKey`` is a single type parameterised by ``in:``, so the three classes below differ
4
+ only in which bucket of :class:`AuthParams` they fill."""
5
+
6
+ from __future__ import annotations
7
+
8
+ from dataclasses import dataclass, field
9
+
10
+ from ...params import param
11
+ from ..models import AuthParams
12
+
13
+
14
+ @dataclass(frozen=True, slots=True)
15
+ class ApiKeyHeaderScheme:
16
+ """``apiKey`` with ``in: header``.
17
+
18
+ Also every custom ``Authorization`` keyword an API invents, which needs no scheme of its own:
19
+ pass ``name="Authorization"`` and a value that already carries the keyword."""
20
+
21
+ name: str
22
+ value: str = field(repr=False)
23
+
24
+ def apply(self) -> AuthParams:
25
+ return AuthParams(headers=(param[str](self.name, self.value),))
26
+
27
+
28
+ @dataclass(frozen=True, slots=True)
29
+ class ApiKeyQueryScheme:
30
+ """``apiKey`` with ``in: query``.
31
+
32
+ The value is percent-encoded, because :class:`Param` renders it exactly as an endpoint's own
33
+ query parameter -- the opposite obligation from the cookie below."""
34
+
35
+ name: str
36
+ value: str = field(repr=False)
37
+
38
+ def apply(self) -> AuthParams:
39
+ return AuthParams(query_params=(param[str](self.name, self.value),))
40
+
41
+
42
+ @dataclass(frozen=True, slots=True)
43
+ class ApiKeyCookieScheme:
44
+ """``apiKey`` with ``in: cookie``.
45
+
46
+ The value is sent verbatim. RFC 6265's ``cookie-value`` alphabet already admits every character
47
+ a base64 key uses, so percent-encoding it would corrupt the credential rather than protect it."""
48
+
49
+ name: str
50
+ value: str = field(repr=False)
51
+
52
+ def apply(self) -> AuthParams:
53
+ return AuthParams(cookies=(param[str](self.name, self.value),))
@@ -0,0 +1,73 @@
1
+ """``http`` / ``basic`` -- RFC 7617."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from base64 import b64encode
6
+ from dataclasses import dataclass
7
+ from typing import TypeAlias
8
+
9
+ from pydantic import BaseModel, ConfigDict, Field, field_validator
10
+ from typing_extensions import TypedDict
11
+
12
+ from ...params import param
13
+ from ..models import AuthParams
14
+
15
+
16
+ class BasicAuthCredentials(BaseModel):
17
+ """The user-id and password of an ``http``/``basic`` scheme (RFC 7617)."""
18
+
19
+ model_config = ConfigDict(frozen=True, extra="forbid")
20
+
21
+ username: str
22
+ password: str = Field(repr=False)
23
+
24
+ @field_validator("username")
25
+ @classmethod
26
+ def _reject_colon(cls, value: str) -> str:
27
+ if ":" in value:
28
+ raise ValueError(
29
+ "username must not contain ':' -- RFC 7617 encodes 'user-id:password', so a colon "
30
+ f"in the user-id cannot be decoded unambiguously; got {value!r}"
31
+ )
32
+ return value
33
+
34
+ @classmethod
35
+ def coerce(cls, value: BasicAuthCredentialsOrDict) -> BasicAuthCredentials:
36
+ """Accept either spelling, validating the mapping form.
37
+
38
+ Unlike the other coercion seams in this runtime, no credential model's ``coerce`` takes
39
+ ``None``: an absent credential means *no scheme*, which the client decides before this.
40
+
41
+ Args:
42
+ value: The credential, as the typed model or as a mapping.
43
+
44
+ Returns:
45
+ The validated model, passed straight through when it already is one.
46
+
47
+ Raises:
48
+ ValidationError: If the mapping is missing a key, carries an unknown one, or holds a
49
+ user-id with a colon. It subclasses ``ValueError``."""
50
+ if isinstance(value, cls):
51
+ return value
52
+ return cls.model_validate(value)
53
+
54
+
55
+ class BasicAuthCredentialsDict(TypedDict):
56
+ username: str
57
+ password: str
58
+
59
+
60
+ BasicAuthCredentialsOrDict: TypeAlias = BasicAuthCredentials | BasicAuthCredentialsDict
61
+ """What a Basic credential accepts: the typed model, or a mapping carrying the same two keys."""
62
+
63
+
64
+ @dataclass(frozen=True, slots=True)
65
+ class BasicAuthScheme:
66
+ """``http``/``basic``: ``Authorization: Basic base64(user-id:password)``."""
67
+
68
+ credentials: BasicAuthCredentials
69
+
70
+ def apply(self) -> AuthParams:
71
+ raw = f"{self.credentials.username}:{self.credentials.password}"
72
+ encoded = b64encode(raw.encode()).decode()
73
+ return AuthParams(headers=(param[str]("Authorization", f"Basic {encoded}"),))
@@ -0,0 +1,21 @@
1
+ """``http`` / ``bearer`` -- and, on the wire, ``openIdConnect`` too."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+
7
+ from ...params import param
8
+ from ..models import AuthParams
9
+
10
+
11
+ @dataclass(frozen=True, slots=True)
12
+ class BearerAuthScheme:
13
+ """``http``/``bearer``: ``Authorization: Bearer <token>``.
14
+
15
+ Also the wire form of an ``openIdConnect`` scheme, whose discovery document says how a token is
16
+ obtained but not how it is sent."""
17
+
18
+ token: str = field(repr=False)
19
+
20
+ def apply(self) -> AuthParams:
21
+ return AuthParams(headers=(param[str]("Authorization", f"Bearer {self.token}"),))