17 changed files with 675 additions and 97 deletions
@ -0,0 +1,5 @@ |
|||
def get_authorization_scheme_param(authorization_header_value: str): |
|||
if not authorization_header_value: |
|||
return "", "" |
|||
scheme, _, param = authorization_header_value.partition(" ") |
|||
return scheme, param |
@ -0,0 +1,23 @@ |
|||
from fastapi import APIRouter, FastAPI |
|||
from starlette.requests import Request |
|||
from starlette.responses import JSONResponse |
|||
from starlette.testclient import TestClient |
|||
|
|||
app = FastAPI() |
|||
router = APIRouter() |
|||
|
|||
|
|||
@router.route("/items/") |
|||
def read_items(request: Request): |
|||
return JSONResponse({"hello": "world"}) |
|||
|
|||
|
|||
app.include_router(router) |
|||
|
|||
client = TestClient(app) |
|||
|
|||
|
|||
def test_sub_router(): |
|||
response = client.get("/items/") |
|||
assert response.status_code == 200 |
|||
assert response.json() == {"hello": "world"} |
@ -1,25 +0,0 @@ |
|||
from starlette.testclient import TestClient |
|||
|
|||
from .main import app |
|||
|
|||
client = TestClient(app) |
|||
|
|||
|
|||
def test_security_oauth2_password_bearer(): |
|||
response = client.get( |
|||
"/security/oauth2b", headers={"Authorization": "Bearer footokenbar"} |
|||
) |
|||
assert response.status_code == 200 |
|||
assert response.json() == {"username": "footokenbar"} |
|||
|
|||
|
|||
def test_security_oauth2_password_bearer_wrong_header(): |
|||
response = client.get("/security/oauth2b", headers={"Authorization": "footokenbar"}) |
|||
assert response.status_code == 403 |
|||
assert response.json() == {"detail": "Not authenticated"} |
|||
|
|||
|
|||
def test_security_oauth2_password_bearer_no_header(): |
|||
response = client.get("/security/oauth2b") |
|||
assert response.status_code == 403 |
|||
assert response.json() == {"detail": "Not authenticated"} |
@ -0,0 +1,68 @@ |
|||
from fastapi import Depends, FastAPI, Security |
|||
from fastapi.security import APIKeyCookie |
|||
from pydantic import BaseModel |
|||
from starlette.testclient import TestClient |
|||
|
|||
app = FastAPI() |
|||
|
|||
api_key = APIKeyCookie(name="key") |
|||
|
|||
|
|||
class User(BaseModel): |
|||
username: str |
|||
|
|||
|
|||
def get_current_user(oauth_header: str = Security(api_key)): |
|||
user = User(username=oauth_header) |
|||
return user |
|||
|
|||
|
|||
@app.get("/users/me") |
|||
def read_current_user(current_user: User = Depends(get_current_user)): |
|||
return current_user |
|||
|
|||
|
|||
client = TestClient(app) |
|||
|
|||
openapi_schema = { |
|||
"openapi": "3.0.2", |
|||
"info": {"title": "Fast API", "version": "0.1.0"}, |
|||
"paths": { |
|||
"/users/me": { |
|||
"get": { |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
} |
|||
}, |
|||
"summary": "Read Current User Get", |
|||
"operationId": "read_current_user_users_me_get", |
|||
"security": [{"APIKeyCookie": []}], |
|||
} |
|||
} |
|||
}, |
|||
"components": { |
|||
"securitySchemes": { |
|||
"APIKeyCookie": {"type": "apiKey", "name": "key", "in": "cookie"} |
|||
} |
|||
}, |
|||
} |
|||
|
|||
|
|||
def test_openapi_schema(): |
|||
response = client.get("/openapi.json") |
|||
assert response.status_code == 200 |
|||
assert response.json() == openapi_schema |
|||
|
|||
|
|||
def test_security_api_key(): |
|||
response = client.get("/users/me", cookies={"key": "secret"}) |
|||
assert response.status_code == 200 |
|||
assert response.json() == {"username": "secret"} |
|||
|
|||
|
|||
def test_security_api_key_no_key(): |
|||
response = client.get("/users/me") |
|||
assert response.status_code == 403 |
|||
assert response.json() == {"detail": "Not authenticated"} |
@ -0,0 +1,68 @@ |
|||
from fastapi import Depends, FastAPI, Security |
|||
from fastapi.security import APIKeyHeader |
|||
from pydantic import BaseModel |
|||
from starlette.testclient import TestClient |
|||
|
|||
app = FastAPI() |
|||
|
|||
api_key = APIKeyHeader(name="key") |
|||
|
|||
|
|||
class User(BaseModel): |
|||
username: str |
|||
|
|||
|
|||
def get_current_user(oauth_header: str = Security(api_key)): |
|||
user = User(username=oauth_header) |
|||
return user |
|||
|
|||
|
|||
@app.get("/users/me") |
|||
def read_current_user(current_user: User = Depends(get_current_user)): |
|||
return current_user |
|||
|
|||
|
|||
client = TestClient(app) |
|||
|
|||
openapi_schema = { |
|||
"openapi": "3.0.2", |
|||
"info": {"title": "Fast API", "version": "0.1.0"}, |
|||
"paths": { |
|||
"/users/me": { |
|||
"get": { |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
} |
|||
}, |
|||
"summary": "Read Current User Get", |
|||
"operationId": "read_current_user_users_me_get", |
|||
"security": [{"APIKeyHeader": []}], |
|||
} |
|||
} |
|||
}, |
|||
"components": { |
|||
"securitySchemes": { |
|||
"APIKeyHeader": {"type": "apiKey", "name": "key", "in": "header"} |
|||
} |
|||
}, |
|||
} |
|||
|
|||
|
|||
def test_openapi_schema(): |
|||
response = client.get("/openapi.json") |
|||
assert response.status_code == 200 |
|||
assert response.json() == openapi_schema |
|||
|
|||
|
|||
def test_security_api_key(): |
|||
response = client.get("/users/me", headers={"key": "secret"}) |
|||
assert response.status_code == 200 |
|||
assert response.json() == {"username": "secret"} |
|||
|
|||
|
|||
def test_security_api_key_no_key(): |
|||
response = client.get("/users/me") |
|||
assert response.status_code == 403 |
|||
assert response.json() == {"detail": "Not authenticated"} |
@ -0,0 +1,68 @@ |
|||
from fastapi import Depends, FastAPI, Security |
|||
from fastapi.security import APIKeyQuery |
|||
from pydantic import BaseModel |
|||
from starlette.testclient import TestClient |
|||
|
|||
app = FastAPI() |
|||
|
|||
api_key = APIKeyQuery(name="key") |
|||
|
|||
|
|||
class User(BaseModel): |
|||
username: str |
|||
|
|||
|
|||
def get_current_user(oauth_header: str = Security(api_key)): |
|||
user = User(username=oauth_header) |
|||
return user |
|||
|
|||
|
|||
@app.get("/users/me") |
|||
def read_current_user(current_user: User = Depends(get_current_user)): |
|||
return current_user |
|||
|
|||
|
|||
client = TestClient(app) |
|||
|
|||
openapi_schema = { |
|||
"openapi": "3.0.2", |
|||
"info": {"title": "Fast API", "version": "0.1.0"}, |
|||
"paths": { |
|||
"/users/me": { |
|||
"get": { |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
} |
|||
}, |
|||
"summary": "Read Current User Get", |
|||
"operationId": "read_current_user_users_me_get", |
|||
"security": [{"APIKeyQuery": []}], |
|||
} |
|||
} |
|||
}, |
|||
"components": { |
|||
"securitySchemes": { |
|||
"APIKeyQuery": {"type": "apiKey", "name": "key", "in": "query"} |
|||
} |
|||
}, |
|||
} |
|||
|
|||
|
|||
def test_openapi_schema(): |
|||
response = client.get("/openapi.json") |
|||
assert response.status_code == 200 |
|||
assert response.json() == openapi_schema |
|||
|
|||
|
|||
def test_security_api_key(): |
|||
response = client.get("/users/me?key=secret") |
|||
assert response.status_code == 200 |
|||
assert response.json() == {"username": "secret"} |
|||
|
|||
|
|||
def test_security_api_key_no_key(): |
|||
response = client.get("/users/me") |
|||
assert response.status_code == 403 |
|||
assert response.json() == {"detail": "Not authenticated"} |
@ -0,0 +1,247 @@ |
|||
import pytest |
|||
from fastapi import Depends, FastAPI, Security |
|||
from fastapi.security import OAuth2 |
|||
from fastapi.security.oauth2 import OAuth2PasswordRequestFormStrict |
|||
from pydantic import BaseModel |
|||
from starlette.testclient import TestClient |
|||
|
|||
app = FastAPI() |
|||
|
|||
reusable_oauth2 = OAuth2( |
|||
flows={ |
|||
"password": { |
|||
"tokenUrl": "/token", |
|||
"scopes": {"read:users": "Read the users", "write:users": "Create users"}, |
|||
} |
|||
} |
|||
) |
|||
|
|||
|
|||
class User(BaseModel): |
|||
username: str |
|||
|
|||
|
|||
def get_current_user(oauth_header: str = Security(reusable_oauth2)): |
|||
user = User(username=oauth_header) |
|||
return user |
|||
|
|||
|
|||
@app.post("/login") |
|||
def read_current_user(form_data: OAuth2PasswordRequestFormStrict = Depends()): |
|||
return form_data |
|||
|
|||
|
|||
@app.get("/users/me") |
|||
def read_current_user(current_user: User = Depends(get_current_user)): |
|||
return current_user |
|||
|
|||
|
|||
client = TestClient(app) |
|||
|
|||
openapi_schema = { |
|||
"openapi": "3.0.2", |
|||
"info": {"title": "Fast API", "version": "0.1.0"}, |
|||
"paths": { |
|||
"/login": { |
|||
"post": { |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
"summary": "Read Current User Post", |
|||
"operationId": "read_current_user_login_post", |
|||
"requestBody": { |
|||
"content": { |
|||
"application/x-www-form-urlencoded": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/Body_read_current_user" |
|||
} |
|||
} |
|||
}, |
|||
"required": True, |
|||
}, |
|||
} |
|||
}, |
|||
"/users/me": { |
|||
"get": { |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
} |
|||
}, |
|||
"summary": "Read Current User Get", |
|||
"operationId": "read_current_user_users_me_get", |
|||
"security": [{"OAuth2": []}], |
|||
} |
|||
}, |
|||
}, |
|||
"components": { |
|||
"schemas": { |
|||
"Body_read_current_user": { |
|||
"title": "Body_read_current_user", |
|||
"required": ["grant_type", "username", "password"], |
|||
"type": "object", |
|||
"properties": { |
|||
"grant_type": { |
|||
"title": "Grant_Type", |
|||
"pattern": "password", |
|||
"type": "string", |
|||
}, |
|||
"username": {"title": "Username", "type": "string"}, |
|||
"password": {"title": "Password", "type": "string"}, |
|||
"scope": {"title": "Scope", "type": "string", "default": ""}, |
|||
"client_id": {"title": "Client_Id", "type": "string"}, |
|||
"client_secret": {"title": "Client_Secret", "type": "string"}, |
|||
}, |
|||
}, |
|||
"ValidationError": { |
|||
"title": "ValidationError", |
|||
"required": ["loc", "msg", "type"], |
|||
"type": "object", |
|||
"properties": { |
|||
"loc": { |
|||
"title": "Location", |
|||
"type": "array", |
|||
"items": {"type": "string"}, |
|||
}, |
|||
"msg": {"title": "Message", "type": "string"}, |
|||
"type": {"title": "Error Type", "type": "string"}, |
|||
}, |
|||
}, |
|||
"HTTPValidationError": { |
|||
"title": "HTTPValidationError", |
|||
"type": "object", |
|||
"properties": { |
|||
"detail": { |
|||
"title": "Detail", |
|||
"type": "array", |
|||
"items": {"$ref": "#/components/schemas/ValidationError"}, |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
"securitySchemes": { |
|||
"OAuth2": { |
|||
"type": "oauth2", |
|||
"flows": { |
|||
"password": { |
|||
"scopes": { |
|||
"read:users": "Read the users", |
|||
"write:users": "Create users", |
|||
}, |
|||
"tokenUrl": "/token", |
|||
} |
|||
}, |
|||
} |
|||
}, |
|||
}, |
|||
} |
|||
|
|||
|
|||
def test_openapi_schema(): |
|||
response = client.get("/openapi.json") |
|||
assert response.status_code == 200 |
|||
assert response.json() == openapi_schema |
|||
|
|||
|
|||
def test_security_oauth2(): |
|||
response = client.get("/users/me", headers={"Authorization": "Bearer footokenbar"}) |
|||
assert response.status_code == 200 |
|||
assert response.json() == {"username": "Bearer footokenbar"} |
|||
|
|||
|
|||
def test_security_oauth2_password_other_header(): |
|||
response = client.get("/users/me", headers={"Authorization": "Other footokenbar"}) |
|||
assert response.status_code == 200 |
|||
assert response.json() == {"username": "Other footokenbar"} |
|||
|
|||
|
|||
def test_security_oauth2_password_bearer_no_header(): |
|||
response = client.get("/users/me") |
|||
assert response.status_code == 403 |
|||
assert response.json() == {"detail": "Not authenticated"} |
|||
|
|||
|
|||
required_params = { |
|||
"detail": [ |
|||
{ |
|||
"loc": ["body", "grant_type"], |
|||
"msg": "field required", |
|||
"type": "value_error.missing", |
|||
}, |
|||
{ |
|||
"loc": ["body", "username"], |
|||
"msg": "field required", |
|||
"type": "value_error.missing", |
|||
}, |
|||
{ |
|||
"loc": ["body", "password"], |
|||
"msg": "field required", |
|||
"type": "value_error.missing", |
|||
}, |
|||
] |
|||
} |
|||
|
|||
grant_type_required = { |
|||
"detail": [ |
|||
{ |
|||
"loc": ["body", "grant_type"], |
|||
"msg": "field required", |
|||
"type": "value_error.missing", |
|||
} |
|||
] |
|||
} |
|||
|
|||
grant_type_incorrect = { |
|||
"detail": [ |
|||
{ |
|||
"loc": ["body", "grant_type"], |
|||
"msg": 'string does not match regex "password"', |
|||
"type": "value_error.str.regex", |
|||
"ctx": {"pattern": "password"}, |
|||
} |
|||
] |
|||
} |
|||
|
|||
|
|||
@pytest.mark.parametrize( |
|||
"data,expected_status,expected_response", |
|||
[ |
|||
(None, 422, required_params), |
|||
({"username": "johndoe", "password": "secret"}, 422, grant_type_required), |
|||
( |
|||
{"username": "johndoe", "password": "secret", "grant_type": "incorrect"}, |
|||
422, |
|||
grant_type_incorrect, |
|||
), |
|||
( |
|||
{"username": "johndoe", "password": "secret", "grant_type": "password"}, |
|||
200, |
|||
{ |
|||
"grant_type": "password", |
|||
"username": "johndoe", |
|||
"password": "secret", |
|||
"scopes": [], |
|||
"client_id": None, |
|||
"client_secret": None, |
|||
}, |
|||
), |
|||
], |
|||
) |
|||
def test_strict_login(data, expected_status, expected_response): |
|||
response = client.post("/login", data=data) |
|||
assert response.status_code == expected_status |
|||
assert response.json() == expected_response |
@ -0,0 +1,74 @@ |
|||
from fastapi import Depends, FastAPI, Security |
|||
from fastapi.security.open_id_connect_url import OpenIdConnect |
|||
from pydantic import BaseModel |
|||
from starlette.testclient import TestClient |
|||
|
|||
app = FastAPI() |
|||
|
|||
oid = OpenIdConnect(openIdConnectUrl="/openid") |
|||
|
|||
|
|||
class User(BaseModel): |
|||
username: str |
|||
|
|||
|
|||
def get_current_user(oauth_header: str = Security(oid)): |
|||
user = User(username=oauth_header) |
|||
return user |
|||
|
|||
|
|||
@app.get("/users/me") |
|||
def read_current_user(current_user: User = Depends(get_current_user)): |
|||
return current_user |
|||
|
|||
|
|||
client = TestClient(app) |
|||
|
|||
openapi_schema = { |
|||
"openapi": "3.0.2", |
|||
"info": {"title": "Fast API", "version": "0.1.0"}, |
|||
"paths": { |
|||
"/users/me": { |
|||
"get": { |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
} |
|||
}, |
|||
"summary": "Read Current User Get", |
|||
"operationId": "read_current_user_users_me_get", |
|||
"security": [{"OpenIdConnect": []}], |
|||
} |
|||
} |
|||
}, |
|||
"components": { |
|||
"securitySchemes": { |
|||
"OpenIdConnect": {"type": "openIdConnect", "openIdConnectUrl": "/openid"} |
|||
} |
|||
}, |
|||
} |
|||
|
|||
|
|||
def test_openapi_schema(): |
|||
response = client.get("/openapi.json") |
|||
assert response.status_code == 200 |
|||
assert response.json() == openapi_schema |
|||
|
|||
|
|||
def test_security_oauth2(): |
|||
response = client.get("/users/me", headers={"Authorization": "Bearer footokenbar"}) |
|||
assert response.status_code == 200 |
|||
assert response.json() == {"username": "Bearer footokenbar"} |
|||
|
|||
|
|||
def test_security_oauth2_password_other_header(): |
|||
response = client.get("/users/me", headers={"Authorization": "Other footokenbar"}) |
|||
assert response.status_code == 200 |
|||
assert response.json() == {"username": "Other footokenbar"} |
|||
|
|||
|
|||
def test_security_oauth2_password_bearer_no_header(): |
|||
response = client.get("/users/me") |
|||
assert response.status_code == 403 |
|||
assert response.json() == {"detail": "Not authenticated"} |
Loading…
Reference in new issue