failed WRONG_FIX wrong_fix · 31 tool calls · 0 s · fastapi/fastapi
🐛 Fix hierarchical security scope propagation see defect #5623
| # | Tool | Arguments | Result |
|---|---|---|---|
| No trace captured. | |||
diff --git a/tests/test_hierarchical_security_scopes.py b/tests/test_hierarchical_security_scopes.py
new file mode 100644
index 00000000..20f282c1
--- /dev/null
+++ b/tests/test_hierarchical_security_scopes.py
@@ -0,0 +1,114 @@
+from typing import List
+from fastapi import Depends, FastAPI, Security
+from fastapi.security import SecurityScopes, OAuth2PasswordBearer
+from fastapi.testclient import TestClient
+
+app = FastAPI()
+oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
+
+def get_scopes(security_scopes: SecurityScopes):
+ return security_scopes.scopes
+
+def get_user(security_scopes: SecurityScopes, scopes: List[str] = Depends(get_scopes)):
+ return security_scopes.scopes, scopes
+
+@app.get("/test")
+def read_test(
+ user_data: tuple = Security(get_user, scopes=["root_scope"]),
+):
+ return {"user_scopes": user_data[0], "dep_scopes": user_data[1]}
+
+client = TestClient(app)
+
+def test_hierarchical_scopes():
+ response = client.get("/test", headers={"authorization": "Bearer token"})
+ assert response.status_code == 200
+ # Both should have ["root_scope"]
+ assert response.json() == {"user_scopes": ["root_scope"], "dep_scopes": ["root_scope"]}
+
+def test_hierarchical_scopes_nested():
+ def get_nested_scopes(security_scopes: SecurityScopes):
+ return security_scopes.scopes
+
+ def get_nested_user(
+ security_scopes: SecurityScopes,
+ nested_scopes: List[str] = Depends(get_nested_scopes)
+ ):
+ return security_scopes.scopes, nested_scopes
+
+ @app.get("/nested")
+ def read_nested(
+ user_data: tuple = Security(get_nested_user, scopes=["nested_scope"]),
+ ):
+ return {"user_scopes": user_data[0], "dep_scopes": user_data[1]}
+
+ response = client.get("/nested", headers={"authorization": "Bearer token"})
+ assert response.status_code == 200
+ assert response.json() == {"user_scopes": ["nested_scope"], "dep_scopes": ["nested_scope"]}
+
+def test_hierarchical_scopes_complex():
+ def get_deep_scopes(security_scopes: SecurityScopes):
+ return security_scopes.scopes
+
+ def get_mid_user(
+ security_scopes: SecurityScopes,
+ deep_scopes: List[str] = Depends(get_deep_scopes)
+ ):
+ return security_scopes.scopes, deep_scopes
+
+ @app.get("/complex")
+ def read_complex(
+ user_data: tuple = Security(get_mid_user, scopes=["complex_scope"]),
+ ):
+ return {"user_scopes": user_data[0], "dep_scopes": user_data[1]}
+
+ response = client.get("/complex", headers={"authorization": "Bearer token"})
+ assert response.status_code == 200
+ assert response.json() == {"user_scopes": ["complex_scope"], "dep_scopes": ["complex_scope"]}
+
+def test_hierarchical_scopes_multiple_levels():
+ def get_level_3(security_scopes: SecurityScopes):
+ return security_scopes.scopes
+
+ def get_level_2(security_scopes: SecurityScopes, l3: List[str] = Depends(get_level_3)):
+ return security_scopes.scopes, l3
+
+ def get_level_1(security_scopes: SecurityScopes, l2: tuple = Depends(get_level_2)):
+ return security_scopes.scopes, l2
+
+ @app.get("/multi")
+ def read_multi(
+ user_data: tuple = Security(get_level_1, scopes=["multi_scope"]),
+ ):
+ return {"l1": user_data[0], "l2": user_data[1][0], "l3": user_data[1][1]}
+
+ response = client.get("/multi", headers={"authorization": "Bearer token"})
+ assert response.status_code == 200
+ assert response.json() == {"l1": ["multi_scope"], "l2": ["multi_scope"], "l3": ["multi_scope"]}
+
+def test_hierarchical_scopes_mixed():
+ def get_scopes_a(security_scopes: SecurityScopes):
+ return security_scopes.scopes
+
+ def get_user_b(
+ security_scopes: SecurityScopes,
+ scopes_a: List[str] = Depends(get_scopes_a)
+ ):
+ return security_scopes.scopes, scopes_a
+
+ @app.get("/mixed")
+ def read_mixed(
+ user_data: tuple = Security(get_user_b, scopes=["scope_b"]),
+ other_data: List[str] = Security(get_scopes_a, scopes=["scope_a"]),
+ ):
+ return {"user_scopes": user_data[0], "dep_scopes": user_data[1], "other_scopes": other_data}
+
+ response = client.get("/mixed", headers={"authorization": "Bearer token"})
+ assert response.status_code == 200
+ # user_data should have scope_b, dep_scopes should have scope_b
+ # other_data should have scope_a
+ assert response.json() == {
+ "user_scopes": ["scope_b"],
+ "dep_scopes": ["scope_b"],
+ "other_scopes": ["scope_a"]
+ }
diff --git a/tests/test_hierarchical_security_scopes_fail.py b/tests/test_hierarchical_security_scopes_fail.py
new file mode 100644
index 00000000..bcafa50d
--- /dev/null
+++ b/tests/test_hierarchical_security_scopes_fail.py
@@ -0,0 +1,27 @@
+from typing import List
+from fastapi import Depends, FastAPI, Security
+from fastapi.security import SecurityScopes, OAuth2PasswordBearer
+from fastapi.testclient import TestClient
+
+app = FastAPI()
+oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
+
+def get_scopes(security_scopes: SecurityScopes):
+ return security_scopes.scopes
+
+def get_user(security_scopes: SecurityScopes, scopes: List[str] = Depends(get_scopes)):
+ return security_scopes.scopes, scopes
+
+@app.get("/test")
+def read_test(
+ user_data: tuple = Security(get_user, scopes=["root_scope"]),
+):
+ return {"user_scopes": user_data[0], "dep_scopes": user_data[1]}
+
+client = TestClient(app)
+
+def test_hierarchical_scopes():
+ response = client.get("/test", headers={"authorization": "Bearer token"})
+ assert response.status_code == 200
+ # Both should have ["root_scope"]
+ assert response.json() == {"user_scopes": ["root_scope"], "dep_scopes": ["root_scope"]}
F
=================================== FAILURES ===================================
_____________________ test_security_scopes_dont_propagate ______________________
def test_security_scopes_dont_propagate():
response = client.get("/scopes")
assert response.status_code == 200
> assert response.json() == {
"dep1": ["scope3", "scope1"],
"dep2": ["scope3", "scope2"],
}
E AssertionError: assert {'dep1': ['sc...1', 'scope2']} == {'dep1': ['sc...3', 'scope2']}
E
E Differing items:
E {'dep1': ['scope3', 'scope1', 'scope2']} != {'dep1': ['scope3', 'scope1']}
E {'dep2': ['scope3', 'scope1', 'scope2']} != {'dep2': ['scope3', 'scope2']}
E Use -v to get more diff
tests/test_security_scopes_dont_propagate.py:42: AssertionError
=============================== warnings summary ===============================
../../../../../../kaggle/tmp/envs/overlays/starlette-0.50.0-py3-none-any/starlette/testclient.py:45
/kaggle/tmp/envs/overlays/starlette-0.50.0-py3-none-any/starlette/testclient.py:45: DeprecationWarning: The anyio.abc.BlockingPortal alias is deprecated, use anyio.from_thread.BlockingPortal instead.
_PortalFactoryType = Callable[[], AbstractContextManager[anyio.abc.BlockingPortal]]
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed, 1 warning in 0.71s