def test_that_header_is_returned_for_basic_auth(app, client):
    security = FastAPISecurity()
    security.init_basic_auth([{"username": "******", "password": "******"}])

    @app.get("/")
    def get_products(user: User = Depends(security.authenticated_user_or_401)):
        return []

    resp = client.get("/")
    assert resp.headers["WWW-Authenticate"] == "Basic"
def test_that_basic_auth_accepts_correct_credentials(app, client):
    security = FastAPISecurity()

    @app.get("/")
    def get_products(user: User = Depends(security.authenticated_user_or_401)):
        return []

    credentials = [{"username": "******", "password": "******"}]
    security.init_basic_auth(credentials)

    resp = client.get("/", auth=("user", "pass"))
    assert resp.status_code == 200
def test_that_user_with_info_dependency_works_unauthenticated(app, client):

    security = FastAPISecurity()

    @app.get("/users/me")
    def get_user_info(user: User = Depends(security.user_with_info)):
        return user.without_access_token()

    security.init_basic_auth([{"username": "******", "password": "******"}])

    resp = client.get("/users/me")
    assert resp.status_code == 200
    info = resp.json()["info"]
    assert info["nickname"] is None
def test_that_headers_are_returned_for_oauth2_and_basic_auth(app, client):
    security = FastAPISecurity()
    security.init_basic_auth([{"username": "******", "password": "******"}])
    security.init_oauth2_through_jwks(dummy_jwks_uri,
                                      audiences=[dummy_audience])

    @app.get("/")
    def get_products(user: User = Depends(security.authenticated_user_or_401)):
        return []

    with aioresponses() as mock:
        mock.get(dummy_jwks_uri, payload=dummy_jwks_response_data)
        resp = client.get("/")
        # NOTE: They are actually set as separate headers
        assert resp.headers["WWW-Authenticate"] == "Basic, Bearer"
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_user_dependency_works_authenticated_or_not(app, client):

    security = FastAPISecurity()

    @app.get("/users/me")
    def get_user_info(user: User = Depends(security.user)):
        return user.without_access_token()

    security.init_basic_auth([{"username": "******", "password": "******"}])

    # Anonymous
    resp = client.get("/users/me")
    assert resp.status_code == 200
    data = resp.json()["auth"]
    del data["expires_at"]
    del data["issued_at"]
    assert data == {
        "audience": [],
        "auth_method": "none",
        "issuer": None,
        "permissions": [],
        "scopes": [],
        "subject": "anonymous",
    }

    # Authenticated
    resp = client.get("/users/me", auth=("JaneDoe", "abc123"))
    assert resp.status_code == 200
    data = resp.json()["auth"]
    del data["expires_at"]
    del data["issued_at"]
    assert data == {
        "audience": [],
        "auth_method": "basic_auth",
        "issuer": None,
        "permissions": [],
        "scopes": [],
        "subject": "JaneDoe",
    }
def test_that_authenticated_user_with_info_or_401_works_as_expected(app, client):
    security = FastAPISecurity()

    @app.get("/users/me")
    def get_user_info(
        user: User = Depends(security.authenticated_user_with_info_or_401),
    ):
        return user.without_access_token()

    security.init_oauth2_through_oidc(dummy_oidc_url, audiences=[dummy_audience])
    security.init_basic_auth([{"username": "******", "password": "******"}])

    with aioresponses() as mock:
        mock.get(
            dummy_oidc_url,
            payload={
                "userinfo_endpoint": dummy_userinfo_endpoint_url,
                "jwks_uri": dummy_jwks_uri,
            },
        )
        mock.get(dummy_jwks_uri, payload=dummy_jwks_response_data)
        mock.get(dummy_userinfo_endpoint_url, payload={"nickname": "jacobsvante"})
        token = make_access_token(sub="GMqBbybGfBQeR6NgCY4NyXKnpFzaaTAn@clients")
        resp = client.get("/users/me", headers={"Authorization": f"Bearer {token}"})
        assert resp.status_code == 200
        info = resp.json()["info"]
        assert info["nickname"] == "jacobsvante"

        # Basic auth
        resp = client.get("/users/me", auth=("a", "b"))
        assert resp.status_code == 200
        info = resp.json()["info"]
        assert info["nickname"] is None

        # Unauthenticated
        resp = client.get("/users/me")
        assert resp.status_code == 401
        assert resp.json() == {"detail": "Could not validate credentials"}
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}
Beispiel #9
0
from fastapi import Depends, FastAPI

from fastapi_security import FastAPISecurity, User

from . import db
from .models import Product
from .settings import get_settings

app = FastAPI()

settings = get_settings()

security = FastAPISecurity()

if settings.basic_auth_credentials:
    security.init_basic_auth(settings.basic_auth_credentials)

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__)