81 refactor i uzupełnienie swaggera do metod auth#174
Conversation
…t + move it to authentication section
| return HttpResponseForbidden() | ||
| return redirect(add_query_params(redirect_url, {"error": "no_email"})) | ||
| messages.error(request, "Brak adresu email w profilu użytkownika.") | ||
| return redirect(redirect_url) |
Check warning
Code scanning / CodeQL
URL redirection from remote source Medium
Copilot Autofix
AI 2 days ago
Copilot could not generate an autofix suggestion
Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.
| auth_login(request, user) | ||
| return redirect(redirect_url) | ||
|
|
||
| await async_auth_login(request, user) |
Check warning
Code scanning / CodeQL
URL redirection from remote source Medium
Copilot Autofix
AI 2 days ago
Copilot could not generate an autofix suggestion
Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.
| messages.error(request, f"Twoje konto zostało zablokowane: {user.ban_reason or 'Brak powodu'}") | ||
| return redirect(redirect_url) | ||
|
|
||
| if not user.is_student_and_not_staff: |
Check warning
Code scanning / CodeQL
URL redirection from remote source Medium
Copilot Autofix
AI 2 days ago
Copilot could not generate an autofix suggestion
Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.
| await async_auth_login(request, user) | ||
| return redirect(redirect_url) | ||
|
|
||
|
|
Check warning
Code scanning / CodeQL
URL redirection from remote source Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 days ago
In general, the safest approach is to ensure that any user-provided redirect URL is normalized and strictly validated before use. That means:
- Normalize input (strip whitespace, normalize backslashes, and lowercase schemes/hosts).
- Reject protocol-relative (
//...) and backslash-prefixed (\\...) URLs. - For non-absolute paths, only allow safe relative URLs.
- For absolute URLs, accept only
http/https, and only when their origin is inALLOWED_REDIRECT_ORIGINS, matches the current host, or matches a carefully defined preview pattern. - Fall back to a safe default path when validation fails.
The current helper already aims to do this, but we can tighten and clarify it:
- Normalize the input early:
- Strip leading/trailing whitespace.
- Replace
\with/before parsing, to avoid browser/backslash quirks.
- Use a single
urlparsecall and re-use its result; avoid parsing different string variants. - Treat URLs without a scheme but with a netloc-like prefix (e.g.,
example.com/path) as unsafe, even though browsers can resolve them. - For relative URLs, rely on Django’s
url_has_allowed_host_and_schemewithallowed_hosts={request.get_host(), *parsed allowlist hosts}andrequire_httpsas configured. - For absolute URLs, compare normalized origins (
scheme://netloclowercased and stripped of trailing slashes) againstALLOWED_REDIRECT_ORIGINSsimilarly normalized, and apply the preview regexes to that normalized origin. - Ensure that
get_safe_redirect_urlcan still safely handle the"index"default and whatever URL pattern your code already uses there.
Concretely, we’ll modify:
is_safe_redirect_urlto:- Normalize backslashes to forward slashes before any
startswith/urlparseoperation. - Lowercase the scheme and host in comparisons.
- Normalize
ALLOWED_REDIRECT_ORIGINSto comparableoriginstrings. - Use a single
urlparseresult and reduce duplication.
- Normalize backslashes to forward slashes before any
get_safe_redirect_urlstays as is logically, but continues to rely on the improvedis_safe_redirect_url; its fallbackdefaultremains unchanged to preserve behavior.
No changes are needed to the call site in UsosAuthorizeView; after the sanitizer is strengthened, redirect(redirect_url) will be safe.
…ttps://github.com/Solvro/backend-testownik into 81-refactor-i-uzupełnienie-swaggera-do-metod-auth
|
@JanekDr co myślisz żeby zrobić helper |
|
i pamiętaj też żeby dać |
Myśle ze to dobry pomysł i uszczupli lekko kod w metodach auth dla usosa i solvro |
There was a problem hiding this comment.
Pull request overview
Refactors the authentication-related OAuth and OTP endpoints in users to improve DRF Spectacular (Swagger/OpenAPI) schema generation, while tightening redirect safety checks and documenting request/response variants.
Changes:
- Migrates Solvro/USOS OAuth endpoints from function-based views to DRF
APIView/adrfasync API views. - Expands
@extend_schemadocumentation across JWT, OAuth, and OTP endpoints (params, examples, and response codes). - Updates redirect safety logic and adds tests for DEBUG-dependent HTTP/HTTPS redirect handling; adds initial OAuth flow tests.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
users/views.py |
New class-based OAuth endpoints, enhanced schema docs, updated redirect safety logic, and public OTP views made explicitly unauthenticated. |
users/urls.py |
Routes updated to point to the new class-based OAuth views. |
users/tests/test_redirect_url.py |
Adds DEBUG=True/False coverage for HTTP vs HTTPS redirect acceptance. |
users/tests/test_oauth.py |
Adds tests asserting callback URL composition and a mocked Solvro authorize flow. |
Comments suppressed due to low confidence (1)
users/views.py:574
- Same
add_query_params+ view-name issue in the banned-userjwt=truebranch: ifredirect_urlis the default view name,redirect(add_query_params(...))can raiseNoReverseMatchand return 500 instead of redirecting with an error.
if request.GET.get("jwt", "false") == "true":
auth_params = {"error": "user_banned"}
if user.ban_reason:
auth_params["ban_reason"] = user.ban_reason
return redirect(add_query_params(redirect_url, auth_params))
| "USOS credentials not configured. USOS_CONSUMER_KEY=%s, USOS_CONSUMER_SECRET=%s", | ||
| "<set>" if usos_key else "<missing>", | ||
| "<set>" if usos_secret else "<missing>", | ||
| ) | ||
| return redirect(add_query_params(redirect_url, {"error": "usos_unavailable"})) |
There was a problem hiding this comment.
redirect_url is allowed to be empty here (missing/blocked redirect param). In that case add_query_params(redirect_url, ...) will produce a string like ?error=..., and django.shortcuts.redirect() will raise NoReverseMatch because it treats it as a view name (no / or .). Consider falling back to a concrete URL/path (e.g., resolved index URL) when redirect_url is empty, or return a 400 response instead of attempting to redirect.
| if attempt < max_retries - 1: | ||
| await sleep(retry_delay) | ||
| continue | ||
|
|
||
| return redirect(add_query_params(redirect_url, {"error": "usos_unavailable"})) | ||
|
|
||
| return redirect(add_query_params(redirect_url, {"error": "usos_unavailable"})) |
There was a problem hiding this comment.
Same issue as above: on the final retry, redirect_url may still be empty, and redirect(add_query_params(redirect_url, ...)) can crash with NoReverseMatch. Handle the empty/invalid redirect case explicitly before redirecting with query params.
| 302: OpenApiResponse( | ||
| description="Redirect to USOS OAuth provider, or redirect with `?error=usos_unavailable` on failure." | ||
| ), | ||
| 400: OpenApiResponse(description="Invalid redirect URL."), |
There was a problem hiding this comment.
The OpenAPI schema advertises a 400 "Invalid redirect URL" response, but this view no longer returns 400 for an invalid redirect value (it gets replaced by the default in get_safe_redirect_url). Either reintroduce an explicit 400 for invalid redirects or remove/adjust the documented 400 so the schema matches runtime behavior.
| 400: OpenApiResponse(description="Invalid redirect URL."), |
| if not profile.get("email"): | ||
| logger.error("Solvro user profile missing email. Profile keys: %s", list(profile.keys())) | ||
| if request.GET.get("jwt", "false") == "true": | ||
| return redirect(add_query_params(redirect_url, {"error": "authorization_failed"})) | ||
| return HttpResponseForbidden() | ||
| return redirect(add_query_params(redirect_url, {"error": "no_email"})) | ||
| messages.error(request, "Brak adresu email w profilu użytkownika.") |
There was a problem hiding this comment.
In the jwt=true path, if redirect is missing/blocked then redirect_url falls back to the view name index. Calling add_query_params('index', ...) produces a string like index?error=no_email, and redirect() will raise NoReverseMatch (500) because it treats that as a view name. Ensure redirect_url is a real URL/path before appending query params (e.g., resolve the view name first or use a path default like /).
| from django.test import TestCase, override_settings | ||
| from django.urls import reverse | ||
| from rest_framework.test import APIClient | ||
|
|
||
| from users.models import User | ||
|
|
||
|
|
||
| @override_settings(DEBUG=True) | ||
| class SolvroOAuthTests(TestCase): | ||
| def setUp(self): | ||
| self.client = APIClient() |
There was a problem hiding this comment.
These are API endpoint tests but they use django.test.TestCase + APIClient. Elsewhere in this repo, API tests consistently use rest_framework.test.APITestCase (e.g., users/tests/test_jwt.py, users/tests/test_guest.py), which provides better DRF integration and consistent helpers. Consider switching this test class to APITestCase for consistency.
| from django.test import TestCase, override_settings | |
| from django.urls import reverse | |
| from rest_framework.test import APIClient | |
| from users.models import User | |
| @override_settings(DEBUG=True) | |
| class SolvroOAuthTests(TestCase): | |
| def setUp(self): | |
| self.client = APIClient() | |
| from django.test import override_settings | |
| from django.urls import reverse | |
| from rest_framework.test import APITestCase | |
| from users.models import User | |
| @override_settings(DEBUG=True) | |
| class SolvroOAuthTests(APITestCase): |
APIView/AsyncAPIViewdla poprawnego generowania schematu. Wszelkie opcje redirect korzystają z parametryzacji docelowych widoków klasowych.@extend_schemaze szczegółowym wykazem rzucanych kodów błędów (200,302,400,401,403,404) dla każdej ścieżki JWT i OTP.authentication_classes = []do publicznych widoków OTP i LoginLinkView. Eliminacja błędnych response401 Unauthorizeddla starych sesji.Authenticationw dokumentacji.