Coverage for tests/test_security_oauth2_optional_description.py: 100%
56 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-08 03:53 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-08 03:53 +0000
1from typing import Optional 1abcde
3from dirty_equals import IsDict 1abcde
4from fastapi import Depends, FastAPI, Security 1abcde
5from fastapi.security import OAuth2, OAuth2PasswordRequestFormStrict 1abcde
6from fastapi.testclient import TestClient 1abcde
7from pydantic import BaseModel 1abcde
9app = FastAPI() 1abcde
11reusable_oauth2 = OAuth2( 1abcde
12 flows={
13 "password": {
14 "tokenUrl": "token",
15 "scopes": {"read:users": "Read the users", "write:users": "Create users"},
16 }
17 },
18 description="OAuth2 security scheme",
19 auto_error=False,
20)
23class User(BaseModel): 1abcde
24 username: str 1abcde
27def get_current_user(oauth_header: Optional[str] = Security(reusable_oauth2)): 1abcde
28 if oauth_header is None: 1abcde
29 return None 1abcde
30 user = User(username=oauth_header) 1abcde
31 return user 1abcde
34@app.post("/login") 1abcde
35def login(form_data: OAuth2PasswordRequestFormStrict = Depends()): 1abcde
36 return form_data 1abcde
39@app.get("/users/me") 1abcde
40def read_users_me(current_user: Optional[User] = Depends(get_current_user)): 1abcde
41 if current_user is None: 1abcde
42 return {"msg": "Create an account first"} 1abcde
43 return current_user 1abcde
46client = TestClient(app) 1abcde
49def test_security_oauth2(): 1abcde
50 response = client.get("/users/me", headers={"Authorization": "Bearer footokenbar"}) 1abcde
51 assert response.status_code == 200, response.text 1abcde
52 assert response.json() == {"username": "Bearer footokenbar"} 1abcde
55def test_security_oauth2_password_other_header(): 1abcde
56 response = client.get("/users/me", headers={"Authorization": "Other footokenbar"}) 1abcde
57 assert response.status_code == 200, response.text 1abcde
58 assert response.json() == {"username": "Other footokenbar"} 1abcde
61def test_security_oauth2_password_bearer_no_header(): 1abcde
62 response = client.get("/users/me") 1abcde
63 assert response.status_code == 200, response.text 1abcde
64 assert response.json() == {"msg": "Create an account first"} 1abcde
67def test_strict_login_None(): 1abcde
68 response = client.post("/login", data=None) 1abcde
69 assert response.status_code == 422 1abcde
70 assert response.json() == IsDict( 1abcde
71 {
72 "detail": [
73 {
74 "type": "missing",
75 "loc": ["body", "grant_type"],
76 "msg": "Field required",
77 "input": None,
78 },
79 {
80 "type": "missing",
81 "loc": ["body", "username"],
82 "msg": "Field required",
83 "input": None,
84 },
85 {
86 "type": "missing",
87 "loc": ["body", "password"],
88 "msg": "Field required",
89 "input": None,
90 },
91 ]
92 }
93 ) | IsDict(
94 # TODO: remove when deprecating Pydantic v1
95 {
96 "detail": [
97 {
98 "loc": ["body", "grant_type"],
99 "msg": "field required",
100 "type": "value_error.missing",
101 },
102 {
103 "loc": ["body", "username"],
104 "msg": "field required",
105 "type": "value_error.missing",
106 },
107 {
108 "loc": ["body", "password"],
109 "msg": "field required",
110 "type": "value_error.missing",
111 },
112 ]
113 }
114 )
117def test_strict_login_no_grant_type(): 1abcde
118 response = client.post("/login", data={"username": "johndoe", "password": "secret"}) 1abcde
119 assert response.status_code == 422 1abcde
120 assert response.json() == IsDict( 1abcde
121 {
122 "detail": [
123 {
124 "type": "missing",
125 "loc": ["body", "grant_type"],
126 "msg": "Field required",
127 "input": None,
128 }
129 ]
130 }
131 ) | IsDict(
132 # TODO: remove when deprecating Pydantic v1
133 {
134 "detail": [
135 {
136 "loc": ["body", "grant_type"],
137 "msg": "field required",
138 "type": "value_error.missing",
139 }
140 ]
141 }
142 )
145def test_strict_login_incorrect_grant_type(): 1abcde
146 response = client.post( 1abcde
147 "/login",
148 data={"username": "johndoe", "password": "secret", "grant_type": "incorrect"},
149 )
150 assert response.status_code == 422 1abcde
151 assert response.json() == IsDict( 1abcde
152 {
153 "detail": [
154 {
155 "type": "string_pattern_mismatch",
156 "loc": ["body", "grant_type"],
157 "msg": "String should match pattern 'password'",
158 "input": "incorrect",
159 "ctx": {"pattern": "password"},
160 }
161 ]
162 }
163 ) | IsDict(
164 # TODO: remove when deprecating Pydantic v1
165 {
166 "detail": [
167 {
168 "loc": ["body", "grant_type"],
169 "msg": 'string does not match regex "password"',
170 "type": "value_error.str.regex",
171 "ctx": {"pattern": "password"},
172 }
173 ]
174 }
175 )
178def test_strict_login_correct_correct_grant_type(): 1abcde
179 response = client.post( 1abcde
180 "/login",
181 data={"username": "johndoe", "password": "secret", "grant_type": "password"},
182 )
183 assert response.status_code == 200, response.text 1abcde
184 assert response.json() == { 1abcde
185 "grant_type": "password",
186 "username": "johndoe",
187 "password": "secret",
188 "scopes": [],
189 "client_id": None,
190 "client_secret": None,
191 }
194def test_openapi_schema(): 1abcde
195 response = client.get("/openapi.json") 1abcde
196 assert response.status_code == 200, response.text 1abcde
197 assert response.json() == { 1abcde
198 "openapi": "3.1.0",
199 "info": {"title": "FastAPI", "version": "0.1.0"},
200 "paths": {
201 "/login": {
202 "post": {
203 "responses": {
204 "200": {
205 "description": "Successful Response",
206 "content": {"application/json": {"schema": {}}},
207 },
208 "422": {
209 "description": "Validation Error",
210 "content": {
211 "application/json": {
212 "schema": {
213 "$ref": "#/components/schemas/HTTPValidationError"
214 }
215 }
216 },
217 },
218 },
219 "summary": "Login",
220 "operationId": "login_login_post",
221 "requestBody": {
222 "content": {
223 "application/x-www-form-urlencoded": {
224 "schema": {
225 "$ref": "#/components/schemas/Body_login_login_post"
226 }
227 }
228 },
229 "required": True,
230 },
231 }
232 },
233 "/users/me": {
234 "get": {
235 "responses": {
236 "200": {
237 "description": "Successful Response",
238 "content": {"application/json": {"schema": {}}},
239 }
240 },
241 "summary": "Read Users Me",
242 "operationId": "read_users_me_users_me_get",
243 "security": [{"OAuth2": []}],
244 }
245 },
246 },
247 "components": {
248 "schemas": {
249 "Body_login_login_post": {
250 "title": "Body_login_login_post",
251 "required": ["grant_type", "username", "password"],
252 "type": "object",
253 "properties": {
254 "grant_type": {
255 "title": "Grant Type",
256 "pattern": "password",
257 "type": "string",
258 },
259 "username": {"title": "Username", "type": "string"},
260 "password": {"title": "Password", "type": "string"},
261 "scope": {"title": "Scope", "type": "string", "default": ""},
262 "client_id": IsDict(
263 {
264 "title": "Client Id",
265 "anyOf": [{"type": "string"}, {"type": "null"}],
266 }
267 )
268 | IsDict(
269 # TODO: remove when deprecating Pydantic v1
270 {"title": "Client Id", "type": "string"}
271 ),
272 "client_secret": IsDict(
273 {
274 "title": "Client Secret",
275 "anyOf": [{"type": "string"}, {"type": "null"}],
276 }
277 )
278 | IsDict(
279 # TODO: remove when deprecating Pydantic v1
280 {"title": "Client Secret", "type": "string"}
281 ),
282 },
283 },
284 "ValidationError": {
285 "title": "ValidationError",
286 "required": ["loc", "msg", "type"],
287 "type": "object",
288 "properties": {
289 "loc": {
290 "title": "Location",
291 "type": "array",
292 "items": {
293 "anyOf": [{"type": "string"}, {"type": "integer"}]
294 },
295 },
296 "msg": {"title": "Message", "type": "string"},
297 "type": {"title": "Error Type", "type": "string"},
298 },
299 },
300 "HTTPValidationError": {
301 "title": "HTTPValidationError",
302 "type": "object",
303 "properties": {
304 "detail": {
305 "title": "Detail",
306 "type": "array",
307 "items": {"$ref": "#/components/schemas/ValidationError"},
308 }
309 },
310 },
311 },
312 "securitySchemes": {
313 "OAuth2": {
314 "type": "oauth2",
315 "flows": {
316 "password": {
317 "scopes": {
318 "read:users": "Read the users",
319 "write:users": "Create users",
320 },
321 "tokenUrl": "token",
322 }
323 },
324 "description": "OAuth2 security scheme",
325 }
326 },
327 },
328 }