Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix hierarchical scope propagation #5624

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion fastapi/dependencies/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ def get_sub_dependant(
security_scopes = security_scopes or []
if isinstance(depends, params.Security):
dependency_scopes = depends.scopes
security_scopes.extend(dependency_scopes)
security_scopes = security_scopes + list(dependency_scopes)
if isinstance(dependency, SecurityBase):
use_scopes: List[str] = []
if isinstance(dependency, (OAuth2, OpenIdConnect)):
Expand Down
58 changes: 58 additions & 0 deletions tests/test_dependency_scopes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from unittest.mock import Mock, call, patch

import pytest
from fastapi import FastAPI, Security
from fastapi.security import SecurityScopes
from fastapi.testclient import TestClient


async def security1(scopes: SecurityScopes):
return _security1(scopes.scopes)


def _security1(scopes): # pragma: no cover
pass


async def security2(scopes: SecurityScopes):
return _security2(scopes.scopes)


def _security2(scopes): # pragma: no cover
pass


async def dep3(
dep=Security(security1, scopes=["scope1"]),
dep2=Security(security2, scopes=["scope2"]),
):
return {}


@pytest.fixture
def mocks():
with patch.dict(globals(), {"_security1": Mock()}):
with patch.dict(globals(), {"_security2": Mock()}):
yield


app = FastAPI()


@app.get("/recursive_scopes")
def recursive_scopes(dep=Security(dep3, scopes=["scope3"])):
return {}


client = TestClient(app)


# issue https://github.com/tiangolo/fastapi/issues/5623
def test_recursive_scopes(mocks):
"""
Test that scope recursion properly applies. Scopes added to a dependency should propagate
and be prepended correctly to all sub-dependencies.
"""
client.get("recursive_scopes")
_security1.assert_has_calls([call(["scope3", "scope1"])])
_security2.assert_has_calls([call(["scope3", "scope2"])])