|
3 | 3 | 2.x Changelog
|
4 | 4 | =============
|
5 | 5 |
|
| 6 | +.. changelog:: 2.11.0 |
| 7 | + :date: 2024-08-27 |
| 8 | + |
| 9 | + .. change:: Use PyJWT instead of python-jose |
| 10 | + :type: feature |
| 11 | + :pr: 3684 |
| 12 | + |
| 13 | + The functionality in :mod:`litestar.security.jwt` is now backed by |
| 14 | + `PyJWT <https://pyjwt.readthedocs.io/en/stable/>`_ instead of |
| 15 | + `python-jose <https://github.com/mpdavis/python-jose/>`_, due to the unclear |
| 16 | + maintenance status of the latter. |
| 17 | + |
| 18 | + .. change:: DTO: Introduce ``forbid_unknown_fields`` config |
| 19 | + :type: feature |
| 20 | + :pr: 3690 |
| 21 | + |
| 22 | + Add a new config option to :class:`~litestar.dto.config.DTOConfig`: |
| 23 | + :attr:`~litestar.dto.config.DTOConfig.forbid_unknown_fields` |
| 24 | + When set to ``True``, a validation error response will be returned if the source |
| 25 | + data contains fields not defined on the model. |
| 26 | + |
| 27 | + .. change:: DTO: Support ``extra="forbid"`` model config for ``PydanticDTO`` |
| 28 | + :type: feature |
| 29 | + :pr: 3691 |
| 30 | + |
| 31 | + For Pydantic models with `extra="forbid" <https://docs.pydantic.dev/latest/api/config/#pydantic.config.ConfigDict.extra>`_ |
| 32 | + in their configuration: |
| 33 | + |
| 34 | + .. tab-set:: |
| 35 | + |
| 36 | + .. tab-item:: Pydantic 2 |
| 37 | + |
| 38 | + .. code-block:: python |
| 39 | +
|
| 40 | + class User(BaseModel): |
| 41 | + model_config = ConfigDict(extra='ignore') |
| 42 | + name: str |
| 43 | +
|
| 44 | + .. tab-item:: Pydantic 1 |
| 45 | + |
| 46 | + .. code-block:: python |
| 47 | +
|
| 48 | + class User(BaseModel): |
| 49 | + class Config: |
| 50 | + extra = "ignore" |
| 51 | + name: str |
| 52 | +
|
| 53 | + :attr:`~litestar.dto.config.DTOConfig.forbid_unknown_fields` will be set to ``True`` by default. |
| 54 | + |
| 55 | + .. note:: |
| 56 | + It's still possible to override this configuration at the DTO level |
| 57 | + |
| 58 | + |
| 59 | + To facilitate this feature, :meth:`~litestar.dto.base_dto.AbstractDTO.get_config_for_model_type` |
| 60 | + has been added to :class:`~litestar.dto.base_dto.AbstractDTO`, allowing the |
| 61 | + customization of the base config defined on the DTO factory for a specific model |
| 62 | + type. It will be called on DTO factory initialization, and receives the concrete |
| 63 | + DTO model type along side the :class:`~litestar.dto.config.DTOConfig` defined |
| 64 | + on the base DTO, which it can alter and return a new version to be used within |
| 65 | + the DTO instance. |
| 66 | + |
| 67 | + .. change:: Custom JWT payload classes |
| 68 | + :type: feature |
| 69 | + :pr: 3692 |
| 70 | + |
| 71 | + Support extending the default :class:`~litestar.security.jwt.Token` class used |
| 72 | + by the JWT backends decode the payload into. |
| 73 | + |
| 74 | + - Add new ``token_cls`` field on the JWT auth config classes |
| 75 | + - Add new ``token_cls`` parameter to JWT auth middlewares |
| 76 | + - Switch to using msgspec to convert the JWT payload into instances of the token |
| 77 | + class |
| 78 | + |
| 79 | + .. code-block:: python |
| 80 | +
|
| 81 | + import dataclasses |
| 82 | + import secrets |
| 83 | + from typing import Any, Dict |
| 84 | +
|
| 85 | + from litestar import Litestar, Request, get |
| 86 | + from litestar.connection import ASGIConnection |
| 87 | + from litestar.security.jwt import JWTAuth, Token |
| 88 | +
|
| 89 | + @dataclasses.dataclass |
| 90 | + class CustomToken(Token): |
| 91 | + token_flag: bool = False |
| 92 | +
|
| 93 | + @dataclasses.dataclass |
| 94 | + class User: |
| 95 | + id: str |
| 96 | +
|
| 97 | + async def retrieve_user_handler(token: CustomToken, connection: ASGIConnection) -> User: |
| 98 | + return User(id=token.sub) |
| 99 | +
|
| 100 | + TOKEN_SECRET = secrets.token_hex() |
| 101 | +
|
| 102 | + jwt_auth = JWTAuth[User]( |
| 103 | + token_secret=TOKEN_SECRET, |
| 104 | + retrieve_user_handler=retrieve_user_handler, |
| 105 | + token_cls=CustomToken, |
| 106 | + ) |
| 107 | +
|
| 108 | + @get("/") |
| 109 | + def handler(request: Request[User, CustomToken, Any]) -> Dict[str, Any]: |
| 110 | + return {"id": request.user.id, "token_flag": request.auth.token_flag} |
| 111 | +
|
| 112 | +
|
| 113 | + .. change:: Extended JWT configuration options |
| 114 | + :type: feature |
| 115 | + :pr: 3695 |
| 116 | + |
| 117 | + **New JWT backend fields** |
| 118 | + |
| 119 | + - :attr:`~litestar.security.jwt.JWTAuth.accepted_audiences` |
| 120 | + - :attr:`~litestar.security.jwt.JWTAuth.accepted_issuers` |
| 121 | + - :attr:`~litestar.security.jwt.JWTAuth.require_claims` |
| 122 | + - :attr:`~litestar.security.jwt.JWTAuth.verify_expiry` |
| 123 | + - :attr:`~litestar.security.jwt.JWTAuth.verify_not_before` |
| 124 | + - :attr:`~litestar.security.jwt.JWTAuth.strict_audience` |
| 125 | + |
| 126 | + **New JWT middleware parameters** |
| 127 | + |
| 128 | + - :paramref:`~litestar.security.jwt.JWTAuthenticationMiddleware.token_audience` |
| 129 | + - :paramref:`~litestar.security.jwt.JWTAuthenticationMiddleware.token_issuer` |
| 130 | + - :paramref:`~litestar.security.jwt.JWTAuthenticationMiddleware.require_claims` |
| 131 | + - :paramref:`~litestar.security.jwt.JWTAuthenticationMiddleware.verify_expiry` |
| 132 | + - :paramref:`~litestar.security.jwt.JWTAuthenticationMiddleware.verify_not_before` |
| 133 | + - :paramref:`~litestar.security.jwt.JWTAuthenticationMiddleware.strict_audience` |
| 134 | + |
| 135 | + **New ``Token.decode`` parameters** |
| 136 | + |
| 137 | + - :paramref:`~litestar.security.jwt.Token.decode.audience` |
| 138 | + - :paramref:`~litestar.security.jwt.Token.decode.issuer` |
| 139 | + - :paramref:`~litestar.security.jwt.Token.decode.require_claims` |
| 140 | + - :paramref:`~litestar.security.jwt.Token.decode.verify_exp` |
| 141 | + - :paramref:`~litestar.security.jwt.Token.decode.verify_nbf` |
| 142 | + - :paramref:`~litestar.security.jwt.Token.decode.strict_audience` |
| 143 | + |
| 144 | + **Other changes** |
| 145 | + |
| 146 | + :meth`Token.decode_payload <~litestar.security.jwt.Token.decode_payload>` has |
| 147 | + been added to make customization of payload decoding / verification easier |
| 148 | + without having to re-implement the functionality of the base class method. |
| 149 | + |
| 150 | + .. seealso:: |
| 151 | + :doc:`/usage/security/jwt` |
| 152 | + |
| 153 | + .. change:: Warn about greedy exclude patterns in middlewares |
| 154 | + :type: feature |
| 155 | + :pr: 3700 |
| 156 | + |
| 157 | + Raise a warning when a middlewares ``exclude`` pattern greedily matches all |
| 158 | + paths. |
| 159 | + |
| 160 | + .. code-block:: python |
| 161 | +
|
| 162 | + from litestar.middlewares |
| 163 | +
|
| 164 | + class MyMiddleware(AbstractMiddleware): |
| 165 | + exclude = ["/", "/home"] |
| 166 | +
|
| 167 | + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: |
| 168 | + await self.app(scope, receive, send) |
| 169 | +
|
| 170 | + Middleware like this would silently be disabled for every route, since the |
| 171 | + exclude pattern ``/`` matches all paths. If a configuration like this is |
| 172 | + detected, a warning will now be raised at application startup. |
| 173 | + |
| 174 | + .. change:: RFC 9457 *Problem Details* plugin |
| 175 | + :type: feature |
| 176 | + :pr: 3323 |
| 177 | + :issue: 3199 |
| 178 | + |
| 179 | + Add a plugin to support `RFC 9457 <https://datatracker.ietf.org/doc/html/rfc9457>`_ |
| 180 | + *Problem Details* responses for error response. |
| 181 | + |
| 182 | + :class:`~litestar.plugins.problem_details.ProblemDetailsPlugin` enables to |
| 183 | + selectively or collectively turn responses with an error status code into |
| 184 | + *Problem Detail* responses. |
| 185 | + |
| 186 | + .. seealso:: |
| 187 | + :doc:`/usage/plugins/problem_details` |
| 188 | + |
| 189 | + .. change:: Fix creation of ``FormMultiDict`` in ``Request.form`` to properly handle multi-keys |
| 190 | + :type: bugfix |
| 191 | + :pr: 3639 |
| 192 | + :issue: 3627 |
| 193 | + |
| 194 | + Fix https://github.com/litestar-org/litestar/issues/3627 by properly handling |
| 195 | + the creation of :class:`~litestar.datastructures.FormMultiDict` where multiple |
| 196 | + values are given for a single key, to make |
| 197 | + :meth:`~litestar.connection.Request.form` match the behaviour of receiving form |
| 198 | + data via the ``data`` kwarg inside a route handler. |
| 199 | + |
| 200 | + **Before** |
| 201 | + |
| 202 | + .. code-block:: python |
| 203 | +
|
| 204 | + @post("/") |
| 205 | + async def handler(request: Request) -> Any: |
| 206 | + return (await request.form()).getall("foo") |
| 207 | +
|
| 208 | + with create_test_client(handler) as client: |
| 209 | + print(client.post("/", data={"foo": ["1", "2"]}).json()) # [["1", "2"]] |
| 210 | +
|
| 211 | + **After** |
| 212 | + |
| 213 | + .. code-block:: python |
| 214 | +
|
| 215 | + @post("/") |
| 216 | + async def handler(request: Request) -> Any: |
| 217 | + return (await request.form()).getall("foo") |
| 218 | +
|
| 219 | + with create_test_client(handler) as client: |
| 220 | + print(client.post("/", data={"foo": ["1", "2"]}).json()) # ["1", "2"] |
| 221 | +
|
| 222 | + .. change:: DTO: Fix inconsistent use of strict decoding mode |
| 223 | + :type: bugfix |
| 224 | + :pr: 3685 |
| 225 | + |
| 226 | + Fix inconsistent usage of msgspec's ``strict`` mode in the base DTO backend. |
| 227 | + |
| 228 | + ``strict=False`` was being used when transferring from builtins, while |
| 229 | + ``strict=True`` was used transferring from raw data, causing an unwanted |
| 230 | + discrepancy in behaviour. |
| 231 | + |
| 232 | + .. change:: Use path template for prometheus metrics |
| 233 | + :type: bugfix |
| 234 | + :pr: 3687 |
| 235 | + |
| 236 | + Changed previous 1-by-1 replacement logic for |
| 237 | + ``PrometheusMiddleware.group_path=true`` with a more robust and slightly faster |
| 238 | + solution. |
| 239 | + |
| 240 | + .. change:: Ensure OpenTelemetry captures exceptions in the outermost application layers |
| 241 | + :type: bugfix |
| 242 | + :pr: 3689 |
| 243 | + :issue: 3663 |
| 244 | + |
| 245 | + A bug was fixed that resulted in exception occurring in the outermost |
| 246 | + application layer not being captured under the current request span, which led |
| 247 | + to incomplete traces. |
| 248 | + |
| 249 | + .. change:: Fix CSRFMiddleware sometimes setting cookies for excluded paths |
| 250 | + :type: bugfix |
| 251 | + :pr: 3698 |
| 252 | + :issue: 3688 |
| 253 | + |
| 254 | + Fix a bug that would cause :class:`~litestar.middleware.csrf.CSRFMiddleware` to |
| 255 | + set a cookie (which would not be used subsequently) on routes it had been |
| 256 | + excluded from via a path pattern. |
| 257 | + |
| 258 | + .. change:: Make override behaviour consistent between ``signature_namespace`` and ``signature_types`` |
| 259 | + :type: bugfix |
| 260 | + :pr: 3696 |
| 261 | + :issue: 3681 |
| 262 | + |
| 263 | + Ensure that adding signature types to ``signature_namespace`` and |
| 264 | + ``signature_types`` behaves the same way when a name was already present in the |
| 265 | + namespace. |
| 266 | + |
| 267 | + Both will now issue a warning if a name is being overwritten with a different |
| 268 | + type. If a name is registered again for the same type, no warning will be given. |
| 269 | + |
| 270 | + .. note:: |
| 271 | + |
| 272 | + You can disable this warning globally by setting |
| 273 | + ``LITESTAR_WARN_SIGNATURE_NAMESPACE_OVERRIDE=0`` in your environment |
| 274 | + |
6 | 275 | .. changelog:: 2.10.0
|
7 | 276 | :date: 2024-07-26
|
8 | 277 |
|
|
0 commit comments