Skip to content

Commit 528f9fa

Browse files
committed
Fix user can get in bad auth state while navigating
1 parent b5a71f9 commit 528f9fa

File tree

8 files changed

+85
-57
lines changed

8 files changed

+85
-57
lines changed

django/domains/iam/auth/adapters.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,14 @@ def is_open_for_signup(self, request: HttpRequest):
3636
def _consume_handoff_messages(self, request: HttpRequest, redirect_url: str) -> str:
3737
handoff_url = get_stored_account_post_auth_handoff(request)
3838
if handoff_url and redirect_url == handoff_url:
39-
list(get_messages(request))
39+
storage = get_messages(request)
40+
list(storage)
41+
if hasattr(storage, "_queued_messages"):
42+
storage._queued_messages.clear()
43+
if hasattr(storage, "_loaded_data"):
44+
storage._loaded_data.clear()
45+
if hasattr(storage, "used"):
46+
storage.used = True
4047
return redirect_url
4148

4249
def _should_skip_handoff_message(
Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
1+
from pathlib import Path
2+
13
from django.core.management.base import BaseCommand
24
from django.core.management import call_command
35

46

7+
BASE_DIR = Path(__file__).resolve().parents[4]
8+
9+
510
class Command(BaseCommand):
611
help = "Loads test data from fixtures"
712

813
def handle(self, *args, **kwargs):
914
fixtures = [
10-
"tests/fixtures/test_etl_data_connections.yaml",
11-
"tests/fixtures/test_etl_orchestration_systems.yaml",
12-
"tests/fixtures/test_etl_tasks.yaml",
15+
BASE_DIR / "tests/fixtures/test_etl_data_connections.yaml",
16+
BASE_DIR / "tests/fixtures/test_etl_orchestration_systems.yaml",
17+
BASE_DIR / "tests/fixtures/test_etl_tasks.yaml",
1318
]
1419

1520
for fixture in fixtures:
1621
self.stdout.write(self.style.NOTICE(f"Loading fixture: {fixture}"))
17-
try:
18-
call_command("loaddata", fixture)
19-
self.stdout.write(self.style.SUCCESS(f"Successfully loaded {fixture}"))
20-
except Exception as e:
21-
self.stderr.write(self.style.ERROR(f"Failed to load {fixture}: {e}"))
22+
call_command("loaddata", str(fixture))
23+
self.stdout.write(self.style.SUCCESS(f"Successfully loaded {fixture}"))
Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
1+
from pathlib import Path
2+
13
from django.core.management.base import BaseCommand
24
from django.core.management import call_command
35

46

7+
BASE_DIR = Path(__file__).resolve().parents[4]
8+
9+
510
class Command(BaseCommand):
611
help = "Loads test data from fixtures"
712

813
def handle(self, *args, **kwargs):
914
fixtures = [
10-
"tests/fixtures/test_users.yaml",
11-
"tests/fixtures/test_workspaces.yaml",
12-
"tests/fixtures/test_roles.yaml",
13-
"tests/fixtures/test_collaborators.yaml",
14-
"tests/fixtures/test_api_keys.yaml",
15+
BASE_DIR / "tests/fixtures/test_users.yaml",
16+
BASE_DIR / "tests/fixtures/test_workspaces.yaml",
17+
BASE_DIR / "tests/fixtures/test_roles.yaml",
18+
BASE_DIR / "tests/fixtures/test_collaborators.yaml",
19+
BASE_DIR / "tests/fixtures/test_api_keys.yaml",
1520
]
1621

1722
for fixture in fixtures:
1823
self.stdout.write(self.style.NOTICE(f"Loading fixture: {fixture}"))
19-
try:
20-
call_command("loaddata", fixture)
21-
self.stdout.write(self.style.SUCCESS(f"Successfully loaded {fixture}"))
22-
except Exception as e:
23-
self.stderr.write(self.style.ERROR(f"Failed to load {fixture}: {e}"))
24+
call_command("loaddata", str(fixture))
25+
self.stdout.write(self.style.SUCCESS(f"Successfully loaded {fixture}"))
Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,32 @@
1+
from pathlib import Path
2+
13
from django.core.management.base import BaseCommand
24
from django.core.management import call_command
35

46

7+
BASE_DIR = Path(__file__).resolve().parents[4]
8+
9+
510
class Command(BaseCommand):
611
help = "Loads test data from fixtures"
712

813
def handle(self, *args, **kwargs):
914
fixtures = [
10-
"tests/fixtures/test_users.yaml",
11-
"tests/fixtures/test_workspaces.yaml",
12-
"tests/fixtures/test_roles.yaml",
13-
"tests/fixtures/test_collaborators.yaml",
14-
"tests/fixtures/test_things.yaml",
15-
"tests/fixtures/test_observed_properties.yaml",
16-
"tests/fixtures/test_processing_levels.yaml",
17-
"tests/fixtures/test_result_qualifiers.yaml",
18-
"tests/fixtures/test_sensors.yaml",
19-
"tests/fixtures/test_units.yaml",
20-
"tests/fixtures/test_datastreams.yaml",
21-
"tests/fixtures/test_observations.yaml",
15+
BASE_DIR / "tests/fixtures/test_users.yaml",
16+
BASE_DIR / "tests/fixtures/test_workspaces.yaml",
17+
BASE_DIR / "tests/fixtures/test_roles.yaml",
18+
BASE_DIR / "tests/fixtures/test_collaborators.yaml",
19+
BASE_DIR / "tests/fixtures/test_things.yaml",
20+
BASE_DIR / "tests/fixtures/test_observed_properties.yaml",
21+
BASE_DIR / "tests/fixtures/test_processing_levels.yaml",
22+
BASE_DIR / "tests/fixtures/test_result_qualifiers.yaml",
23+
BASE_DIR / "tests/fixtures/test_sensors.yaml",
24+
BASE_DIR / "tests/fixtures/test_units.yaml",
25+
BASE_DIR / "tests/fixtures/test_datastreams.yaml",
26+
BASE_DIR / "tests/fixtures/test_observations.yaml",
2227
]
2328

2429
for fixture in fixtures:
2530
self.stdout.write(self.style.NOTICE(f"Loading fixture: {fixture}"))
26-
try:
27-
call_command("loaddata", fixture)
28-
self.stdout.write(self.style.SUCCESS(f"Successfully loaded {fixture}"))
29-
except Exception as e:
30-
self.stderr.write(self.style.ERROR(f"Failed to load {fixture}: {e}"))
31+
call_command("loaddata", str(fixture))
32+
self.stdout.write(self.style.SUCCESS(f"Successfully loaded {fixture}"))

django/interfaces/http/auth/oidc.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from allauth.idp.oidc.adapter import get_adapter
44
from allauth.idp.oidc.internal.tokens import decode_jwt_token
55
from allauth.idp.oidc.models import Client, Token
6-
from ninja.errors import HttpError
76
from ninja.security import HttpBearer
87

98

@@ -17,8 +16,7 @@ def authenticate(self, request, token):
1716
request.user = user
1817
request.principal = user
1918
return user
20-
elif token:
21-
raise HttpError(401, "Invalid token")
19+
return None
2220

2321
def _authenticate_opaque_token(self, token):
2422
access_token = Token.objects.lookup(Token.Type.ACCESS_TOKEN, token)

django/templates/account/login.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
{% endelement %}
3636
{% endslot %}
3737
{% endelement %}
38+
3839
{% endif %}
3940

4041
{% if LOGIN_BY_CODE_ENABLED or PASSKEY_LOGIN_ENABLED %}

django/tests/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def django_db_setup(django_db_setup, django_db_blocker):
1212
with django_db_blocker.unblock():
1313
call_command("migrate")
1414

15-
call_command("loaddata", "domains/iam/fixtures/default_roles.yaml")
15+
call_command("loaddata", "default_roles")
1616
call_command("load_iam_test_data")
1717
call_command("load_sta_test_data")
1818
call_command("load_etl_test_data")

django/tests/hydroserver/test_oidc_auth.py

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22
import uuid
33

44
import jwt
5-
import pytest
65
from django.contrib.auth import get_user_model
76
from django.test import RequestFactory
8-
from ninja.errors import HttpError
7+
import pytest
98

109
from allauth.core.context import request_context
1110
from allauth.core.internal import jwkkit
@@ -113,10 +112,7 @@ def test_oidc_auth_rejects_expired_opaque_tokens(request_factory, oidc_client):
113112
access_token.set_scopes(["openid", "profile", "email"])
114113
access_token.save(update_fields=["scopes"])
115114

116-
with pytest.raises(HttpError) as exc_info:
117-
oidc_auth.authenticate(request, token_value)
118-
119-
assert exc_info.value.status_code == 401
115+
assert oidc_auth.authenticate(request, token_value) is None
120116

121117

122118
def test_oidc_auth_rejects_jwt_access_tokens_without_matching_audience(
@@ -144,10 +140,7 @@ def test_oidc_auth_rejects_jwt_access_tokens_without_matching_audience(
144140
)
145141

146142
with request_context(request):
147-
with pytest.raises(HttpError) as exc_info:
148-
oidc_auth.authenticate(request, token_value)
149-
150-
assert exc_info.value.status_code == 401
143+
assert oidc_auth.authenticate(request, token_value) is None
151144

152145

153146
def test_oidc_auth_rejects_expired_jwt_tokens(request_factory, oidc_client, settings):
@@ -173,19 +166,42 @@ def test_oidc_auth_rejects_expired_jwt_tokens(request_factory, oidc_client, sett
173166
)
174167

175168
with request_context(request):
176-
with pytest.raises(HttpError) as exc_info:
177-
oidc_auth.authenticate(request, token_value)
178-
179-
assert exc_info.value.status_code == 401
169+
assert oidc_auth.authenticate(request, token_value) is None
180170

181171

182172
def test_oidc_auth_rejects_invalid_access_tokens(request_factory):
183173
request = request_factory.get("/api/data/workspaces")
184174

185-
with pytest.raises(HttpError) as exc_info:
186-
oidc_auth.authenticate(request, "invalid-access-token")
175+
assert oidc_auth.authenticate(request, "invalid-access-token") is None
176+
177+
178+
def test_public_workspace_listing_ignores_invalid_bearer_token(client):
179+
response = client.get(
180+
"/api/data/workspaces?expand_related=true",
181+
HTTP_AUTHORIZATION="Bearer invalid-access-token",
182+
)
183+
184+
assert response.status_code == 200
185+
186+
187+
def test_public_thing_markers_ignore_invalid_bearer_token(client):
188+
response = client.get(
189+
"/api/data/things/markers",
190+
HTTP_AUTHORIZATION="Bearer invalid-access-token",
191+
)
192+
193+
assert response.status_code == 200
194+
195+
196+
def test_protected_workspace_creation_with_invalid_bearer_token_returns_401(client):
197+
response = client.post(
198+
"/api/data/workspaces?expand_related=true",
199+
data={"name": "Unauthorized"},
200+
content_type="application/json",
201+
HTTP_AUTHORIZATION="Bearer invalid-access-token",
202+
)
187203

188-
assert exc_info.value.status_code == 401
204+
assert response.status_code == 401
189205

190206

191207
def test_browser_session_endpoint_returns_authenticated_account(client):

0 commit comments

Comments
 (0)