def test_that_existing_permissions_are_added(app, client): security = FastAPISecurity() permission = security.user_permission("users:list") # noqa @app.get("/users/me") def get_user_info(user: User = Depends(security.authenticated_user_or_401)): return user.without_access_token() security.init_oauth2_through_jwks(dummy_jwks_uri, audiences=[dummy_audience]) access_token = make_access_token( sub="test-subject", permissions=["users:list"], ) with aioresponses() as mock: mock.get(dummy_jwks_uri, payload=dummy_jwks_response_data) resp = client.get( "/users/me", headers={"Authorization": f"Bearer {access_token}"} ) assert resp.status_code == 200 data = resp.json()["auth"] del data["expires_at"] del data["issued_at"] assert data == { "audience": ["https://some-resource"], "auth_method": "oauth2", "issuer": "https://identity-provider/", "permissions": ["users:list"], "scopes": [], "subject": "test-subject", }
def test_that_user_must_have_all_permissions(app, client): security = FastAPISecurity() can_list = security.user_permission("users:list") # noqa can_view = security.user_permission("users:view") # noqa @app.get("/users") def get_user_list(user: User = Depends(security.user_holding(can_list, can_view))): return [user] security.init_oauth2_through_jwks(dummy_jwks_uri, audiences=[dummy_audience]) bad_token = make_access_token(sub="test-user", permissions=["users:list"]) valid_token = make_access_token( sub="JaneDoe", permissions=["users:list", "users:view"], ) with aioresponses() as mock: mock.get(dummy_jwks_uri, payload=dummy_jwks_response_data) resp = client.get("/users", headers={"Authorization": f"Bearer {bad_token}"}) assert resp.status_code == 403 assert resp.json() == {"detail": "Missing required permission users:view"} resp = client.get("/users", headers={"Authorization": f"Bearer {valid_token}"}) assert resp.status_code == 200 (user1,) = resp.json() assert user1["auth"]["subject"] == "JaneDoe"
def test_that_missing_permission_results_in_403(app, client): security = FastAPISecurity() can_list = security.user_permission("users:list") # noqa @app.get("/users") def get_user_list(user: User = Depends(security.user_holding(can_list))): return [user] security.init_oauth2_through_jwks(dummy_jwks_uri, audiences=[dummy_audience]) access_token = make_access_token(sub="test-user", permissions=[]) with aioresponses() as mock: mock.get(dummy_jwks_uri, payload=dummy_jwks_response_data) resp = client.get("/users", headers={"Authorization": f"Bearer {access_token}"}) assert resp.status_code == 403 assert resp.json() == {"detail": "Missing required permission users:list"}
def test_that_explicit_permission_overrides_are_applied(app, client): cred = HTTPBasicCredentials(username="******", password="******") security = FastAPISecurity() create_product_perm = security.user_permission("products:create") security.init_basic_auth([cred]) security.add_permission_overrides({"johndoe": ["products:create"]}) @app.post("/products") def create_product( user: User = Depends(security.user_holding(create_product_perm)), ): return {"ok": True} resp = client.post("/products", auth=("johndoe", "123")) assert resp.status_code == 200 assert resp.json() == {"ok": True}
def test_that_assigned_permission_result_in_200(app, client): security = FastAPISecurity() can_list = security.user_permission("users:list") # noqa @app.get("/users") def get_user_list(user: User = Depends(security.user_holding(can_list))): return [user] security.init_oauth2_through_jwks(dummy_jwks_uri, audiences=[dummy_audience]) access_token = make_access_token(sub="test-user", permissions=["users:list"]) with aioresponses() as mock: mock.get(dummy_jwks_uri, payload=dummy_jwks_response_data) resp = client.get("/users", headers={"Authorization": f"Bearer {access_token}"}) assert resp.status_code == 200 (user1,) = resp.json() assert user1["auth"]["subject"] == "test-user"
def test_that_permission_overrides_can_be_an_exhaustable_iterator(app, client): cred = HTTPBasicCredentials(username="******", password="******") security = FastAPISecurity() create_product_perm = security.user_permission("products:create") security.init_basic_auth([cred]) overrides = iter(["products:create"]) security.add_permission_overrides({"johndoe": overrides}) @app.post("/products") def create_product( user: User = Depends(security.user_holding(create_product_perm)), ): return {"ok": True} # NOTE: Before v0.3.1, the second iteration would give a HTTP403, as the overrides # iterator had been exhausted on the first try. for _ in range(2): resp = client.post("/products", auth=("johndoe", "123")) assert resp.status_code == 200 assert resp.json() == {"ok": True}
if settings.oidc_discovery_url: security.init_oauth2_through_oidc( settings.oidc_discovery_url, audiences=settings.oauth2_audiences, ) elif settings.oauth2_jwks_url: security.init_oauth2_through_jwks( settings.oauth2_jwks_url, audiences=settings.oauth2_audiences, ) security.add_permission_overrides(settings.permission_overrides or {}) logger = logging.getLogger(__name__) create_product_perm = security.user_permission("products:create") @app.get("/users/me") async def get_user_details(user: User = Depends(security.user_with_info)): """Return user details, regardless of whether user is authenticated or not""" return user.without_access_token() @app.get("/users/me/permissions", response_model=List[str]) def get_user_permissions(user: User = Depends( security.authenticated_user_or_401)): """Return user permissions or HTTP401 if not authenticated""" return user.permissions