mirror of
https://github.com/fastapi/fastapi.git
synced 2026-10-30 05:41:02 -04:00
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
162 lines
4.7 KiB
Python
162 lines
4.7 KiB
Python
from functools import partial
|
|
|
|
import pytest
|
|
from fastapi import FastAPI, Security, WebSocket
|
|
from fastapi.encoders import jsonable_encoder
|
|
from fastapi.security import (
|
|
APIKeyCookie,
|
|
APIKeyHeader,
|
|
APIKeyQuery,
|
|
HTTPBasic,
|
|
HTTPBearer,
|
|
HTTPDigest,
|
|
OAuth2,
|
|
OAuth2AuthorizationCodeBearer,
|
|
OAuth2PasswordBearer,
|
|
OpenIdConnect,
|
|
)
|
|
from fastapi.security.http import HTTPBase
|
|
from fastapi.testclient import TestClient
|
|
from starlette.testclient import WebSocketDenialResponse
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"security_factory, path, headers, expected, challenge",
|
|
[
|
|
pytest.param(
|
|
partial(HTTPBase, scheme="Other"),
|
|
"/",
|
|
{"Authorization": "Other secret"},
|
|
{"scheme": "Other", "credentials": "secret"},
|
|
"Other",
|
|
id="http-base",
|
|
),
|
|
pytest.param(
|
|
HTTPBasic,
|
|
"/",
|
|
{"Authorization": "Basic am9objpzZWNyZXQ="},
|
|
{"username": "john", "password": "secret"},
|
|
"Basic",
|
|
id="http-basic",
|
|
),
|
|
pytest.param(
|
|
HTTPBearer,
|
|
"/",
|
|
{"Authorization": "Bearer secret"},
|
|
{"scheme": "Bearer", "credentials": "secret"},
|
|
"Bearer",
|
|
id="http-bearer",
|
|
),
|
|
pytest.param(
|
|
HTTPDigest,
|
|
"/",
|
|
{"Authorization": "Digest secret"},
|
|
{"scheme": "Digest", "credentials": "secret"},
|
|
"Digest",
|
|
id="http-digest",
|
|
),
|
|
pytest.param(
|
|
partial(APIKeyQuery, name="key"),
|
|
"/?key=secret",
|
|
{},
|
|
"secret",
|
|
"APIKey",
|
|
id="api-key-query",
|
|
),
|
|
pytest.param(
|
|
partial(APIKeyHeader, name="key"),
|
|
"/",
|
|
{"key": "secret"},
|
|
"secret",
|
|
"APIKey",
|
|
id="api-key-header",
|
|
),
|
|
pytest.param(
|
|
partial(APIKeyCookie, name="key"),
|
|
"/",
|
|
{"Cookie": "key=secret"},
|
|
"secret",
|
|
"APIKey",
|
|
id="api-key-cookie",
|
|
),
|
|
pytest.param(
|
|
OAuth2,
|
|
"/",
|
|
{"Authorization": "Bearer secret"},
|
|
"Bearer secret",
|
|
"Bearer",
|
|
id="oauth2",
|
|
),
|
|
pytest.param(
|
|
partial(OAuth2PasswordBearer, tokenUrl="/token"),
|
|
"/",
|
|
{"Authorization": "Bearer secret"},
|
|
"secret",
|
|
"Bearer",
|
|
id="oauth2-password-bearer",
|
|
),
|
|
pytest.param(
|
|
partial(
|
|
OAuth2AuthorizationCodeBearer,
|
|
authorizationUrl="/authorize",
|
|
tokenUrl="/token",
|
|
),
|
|
"/",
|
|
{"Authorization": "Bearer secret"},
|
|
"secret",
|
|
"Bearer",
|
|
id="oauth2-authorization-code-bearer",
|
|
),
|
|
pytest.param(
|
|
partial(OpenIdConnect, openIdConnectUrl="/openid"),
|
|
"/",
|
|
{"Authorization": "Bearer secret"},
|
|
"Bearer secret",
|
|
"Bearer",
|
|
id="openid-connect",
|
|
),
|
|
],
|
|
)
|
|
@pytest.mark.parametrize(
|
|
"auto_error, has_credentials",
|
|
[
|
|
pytest.param(True, True, id="valid-credentials"),
|
|
pytest.param(True, False, id="missing-required-credentials"),
|
|
pytest.param(False, False, id="missing-optional-credentials"),
|
|
],
|
|
)
|
|
def test_security_websocket(
|
|
security_factory,
|
|
path: str,
|
|
headers: dict[str, str],
|
|
expected: dict[str, str] | str,
|
|
challenge: str,
|
|
auto_error: bool,
|
|
has_credentials: bool,
|
|
):
|
|
security = security_factory(auto_error=auto_error)
|
|
app = FastAPI()
|
|
|
|
@app.websocket("/")
|
|
async def endpoint(websocket: WebSocket, credentials=Security(security)):
|
|
await websocket.accept()
|
|
await websocket.send_json(jsonable_encoder(credentials))
|
|
await websocket.close()
|
|
|
|
with TestClient(app) as client:
|
|
if auto_error and not has_credentials:
|
|
with pytest.raises(WebSocketDenialResponse) as exc_info:
|
|
with client.websocket_connect("/"):
|
|
pass # pragma: no cover
|
|
assert exc_info.value.status_code == 401
|
|
assert exc_info.value.json() == {"detail": "Not authenticated"}
|
|
assert exc_info.value.headers["WWW-Authenticate"] == challenge
|
|
else:
|
|
with client.websocket_connect(
|
|
path if has_credentials else "/",
|
|
headers=headers if has_credentials else {},
|
|
) as websocket:
|
|
assert websocket.receive_json() == (
|
|
expected if has_credentials else None
|
|
)
|