Browse Source

Forbid passing str to Security `scopes` parameter. Add descriptive error message

pull/14373/head
Yurii Motov 7 months ago
parent
commit
81e4a639af
  1. 36
      fastapi/param_functions.py
  2. 21
      fastapi/params.py
  3. 26
      tests/test_security_scopes_parameter.py

36
fastapi/param_functions.py

@ -1,8 +1,19 @@
from typing import Any, Callable, Dict, List, Optional, Sequence, Union from typing import (
Any,
Callable,
Dict,
FrozenSet,
List,
Optional,
Set,
Tuple,
Union,
)
from annotated_doc import Doc from annotated_doc import Doc
from fastapi import params from fastapi import params
from fastapi._compat import Undefined from fastapi._compat import Undefined
from fastapi.exceptions import FastAPIError
from fastapi.openapi.models import Example from fastapi.openapi.models import Example
from typing_extensions import Annotated, Literal, deprecated from typing_extensions import Annotated, Literal, deprecated
@ -2312,7 +2323,14 @@ def Security( # noqa: N802
] = None, ] = None,
*, *,
scopes: Annotated[ scopes: Annotated[
Optional[Sequence[str]], Optional[
Union[
List[str],
Tuple[str, ...],
Set[str],
FrozenSet[str],
]
],
Doc( Doc(
""" """
OAuth2 scopes required for the *path operation* that uses this Security OAuth2 scopes required for the *path operation* that uses this Security
@ -2378,4 +2396,18 @@ def Security( # noqa: N802
return [{"item_id": "Foo", "owner": current_user.username}] return [{"item_id": "Foo", "owner": current_user.username}]
``` ```
""" """
if isinstance(scopes, str):
if scopes in ("function", "request"):
raise FastAPIError(
"Invalid value for `scopes` parameter in Security(). "
"You probably meant to use the `scope` parameter instead of `scopes`. "
"Expected a sequence of strings (e.g., ['admin', 'user']), but received a single string."
)
raise FastAPIError(
"Invalid value for `scopes` parameter in Security(). "
"Expected a sequence of strings (e.g., ['admin', 'user']), but received a single string. "
"Wrap it in a list: scopes=['your_scope'] instead of scopes='your_scope'."
)
return params.Security(dependency=dependency, scopes=scopes, use_cache=use_cache) return params.Security(dependency=dependency, scopes=scopes, use_cache=use_cache)

21
fastapi/params.py

@ -1,7 +1,17 @@
import warnings import warnings
from dataclasses import dataclass from dataclasses import dataclass
from enum import Enum from enum import Enum
from typing import Any, Callable, Dict, List, Optional, Sequence, Union from typing import (
Any,
Callable,
Dict,
FrozenSet,
List,
Optional,
Set,
Tuple,
Union,
)
from fastapi.openapi.models import Example from fastapi.openapi.models import Example
from pydantic.fields import FieldInfo from pydantic.fields import FieldInfo
@ -771,4 +781,11 @@ class Depends:
@dataclass @dataclass
class Security(Depends): class Security(Depends):
scopes: Optional[Sequence[str]] = None scopes: Optional[
Union[
List[str],
Tuple[str, ...],
Set[str],
FrozenSet[str],
]
] = None

26
tests/test_security_scopes_parameter.py

@ -0,0 +1,26 @@
import pytest
from fastapi import Security
from fastapi.exceptions import FastAPIError
def test_pass_single_str():
with pytest.raises(FastAPIError) as exc_info:
Security(dependency=lambda: None, scopes="admin")
assert str(exc_info.value) == (
"Invalid value for `scopes` parameter in Security(). "
"Expected a sequence of strings (e.g., ['admin', 'user']), but received a single string. "
"Wrap it in a list: scopes=['your_scope'] instead of scopes='your_scope'."
)
@pytest.mark.parametrize("value", ["function", "request"])
def test_pass_scope_instead_of_scopes(value: str):
with pytest.raises(FastAPIError) as exc_info:
Security(dependency=lambda: None, scopes=value)
assert str(exc_info.value) == (
"Invalid value for `scopes` parameter in Security(). "
"You probably meant to use the `scope` parameter instead of `scopes`. "
"Expected a sequence of strings (e.g., ['admin', 'user']), but received a single string."
)
Loading…
Cancel
Save