From 38489ba25a84f10fd1b2b154c83fc90841535e63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aar=C3=B3n=20Ayerdis=20Espinoza?= Date: Wed, 8 Apr 2026 16:10:47 -0600 Subject: [PATCH 1/5] refactor: move deactivate_account route out of /api/ namespace --- testbed/core/urls/api_urls.py | 7 ------- testbed/core/urls/views_urls.py | 9 ++++++++- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/testbed/core/urls/api_urls.py b/testbed/core/urls/api_urls.py index a37b575..eddd979 100644 --- a/testbed/core/urls/api_urls.py +++ b/testbed/core/urls/api_urls.py @@ -2,7 +2,6 @@ from testbed.core.views import ( actor_detail, portability_outbox_detail, - deactivate_account, following_collection, followers_collection, content_collection, @@ -49,10 +48,4 @@ blocked_collection, name="blocked-collection", ), - # Deactivate Account: API endpoint for account deactivation - path( - "actors//deactivate/", - deactivate_account, - name="deactivate-account", - ), ] diff --git a/testbed/core/urls/views_urls.py b/testbed/core/urls/views_urls.py index 41f2862..42404cf 100644 --- a/testbed/core/urls/views_urls.py +++ b/testbed/core/urls/views_urls.py @@ -1,5 +1,6 @@ from django.urls import path from testbed.core.views import ( + deactivate_account, index, trigger_account, report_activity, @@ -7,7 +8,7 @@ test_error_view, oauth_callback, test_token_exchange_view, - ) +) urlpatterns = [ path("", index, name="home"), @@ -20,4 +21,10 @@ path("test/oauth/error/", test_error_view, name="test-oauth-error"), path("test/oauth/token/", test_token_exchange_view, name="test-oauth-token"), path("callback", oauth_callback, name="oauth-callback"), + # Staff-only admin action: deactivate a user account + path( + "actors//deactivate/", + deactivate_account, + name="deactivate-account", + ), ] From 38e610e5234bc52e989ec9f6136731eb68d602dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aar=C3=B3n=20Ayerdis=20Espinoza?= Date: Wed, 8 Apr 2026 16:58:11 -0600 Subject: [PATCH 2/5] refactor: normalize views imports, fix ruff issues in oauth_utils --- testbed/core/utils/oauth_utils.py | 6 ++---- testbed/core/views/oauth_demo.py | 7 +++---- testbed/core/views/pages.py | 4 ++-- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/testbed/core/utils/oauth_utils.py b/testbed/core/utils/oauth_utils.py index 9bc1184..ef91efe 100644 --- a/testbed/core/utils/oauth_utils.py +++ b/testbed/core/utils/oauth_utils.py @@ -82,7 +82,7 @@ def get_user_application(user, request=None): credentials.save() application.raw_client_secret = client_secret - logger.info(f"Successfully upgraded client secret to encrypted storage") + logger.info("Successfully upgraded client secret to encrypted storage") # Clean up session storage request.session.pop(CLIENT_SECRET_SESSION_KEY, None) @@ -258,9 +258,7 @@ def store_token_in_session(request, token_data): scope = token_data.get('scope', '') request.session[TOKEN_SCOPE_SESSION_KEY] = scope - # Get expires_in for logging purposes only (not stored for validation) - expires_in = token_data.get('expires_in', 3600) - logger.info(f"OAuth token stored in session for demo authentication (server will validate expiry)") + logger.info("OAuth token stored in session for demo authentication (server will validate expiry)") def get_token_from_session(request): """ diff --git a/testbed/core/views/oauth_demo.py b/testbed/core/views/oauth_demo.py index e1bb794..cd1fbe0 100644 --- a/testbed/core/views/oauth_demo.py +++ b/testbed/core/views/oauth_demo.py @@ -24,10 +24,11 @@ from django.urls import reverse from ..models import Actor -from testbed.core.utils.oauth_utils import ( +from ..utils.oauth_utils import ( generate_secure_state, get_user_application, store_state_in_session, + store_token_in_session, validate_state_from_session, ) @@ -264,9 +265,7 @@ def test_token_exchange_view(request): logger.info("Successfully exchanged authorization code for token") context["token_response"] = token_json - # NEW: Store token in session for seamless demo authentication - from testbed.core.utils.oauth_utils import store_token_in_session - + # Store token in session for seamless demo authentication store_token_in_session(request, token_json) context["session_auth_enabled"] = True logger.info("Token stored in session - demo authentication now active") diff --git a/testbed/core/views/pages.py b/testbed/core/views/pages.py index 56274f6..9df7d08 100644 --- a/testbed/core/views/pages.py +++ b/testbed/core/views/pages.py @@ -20,8 +20,8 @@ from django.shortcuts import redirect, render from ..models import Actor -from testbed.core.forms.oauth_connection_form import OAuthApplicationForm -from testbed.core.utils.oauth_utils import get_user_application +from ..forms.oauth_connection_form import OAuthApplicationForm +from ..utils.oauth_utils import get_user_application logger = logging.getLogger(__name__) From 152b217c3a8532c6a28fabb7cc3c19cad51f4587 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aar=C3=B3n=20Ayerdis=20Espinoza?= Date: Wed, 8 Apr 2026 18:45:37 -0600 Subject: [PATCH 3/5] refactor: consolidate OAuth files into oauth/ package --- testbed/core/json_ld_builders.py | 2 +- testbed/core/oauth/__init__.py | 42 +++++++++++++++++++ .../core/{utils => oauth}/authentication.py | 2 +- .../forms.py} | 0 .../{utils/oauth_utils.py => oauth/utils.py} | 0 .../validators.py} | 0 testbed/core/tests/test_oauth_validators.py | 2 +- testbed/core/views/api.py | 2 +- testbed/core/views/oauth_demo.py | 2 +- testbed/core/views/pages.py | 4 +- testbed/settings/base.py | 3 +- 11 files changed, 50 insertions(+), 9 deletions(-) create mode 100644 testbed/core/oauth/__init__.py rename testbed/core/{utils => oauth}/authentication.py (98%) rename testbed/core/{forms/oauth_connection_form.py => oauth/forms.py} (100%) rename testbed/core/{utils/oauth_utils.py => oauth/utils.py} (100%) rename testbed/core/{utils/oauth_validators.py => oauth/validators.py} (100%) diff --git a/testbed/core/json_ld_builders.py b/testbed/core/json_ld_builders.py index 020f217..e376773 100644 --- a/testbed/core/json_ld_builders.py +++ b/testbed/core/json_ld_builders.py @@ -4,7 +4,7 @@ build_activity_id, build_note_id, build_outbox_id) -from .utils.oauth_utils import build_oauth_endpoint_url +from .oauth.utils import build_oauth_endpoint_url from .models import CreateActivity, LikeActivity, FollowActivity # Build JSON-LD Actor with LOLA compliance. diff --git a/testbed/core/oauth/__init__.py b/testbed/core/oauth/__init__.py new file mode 100644 index 0000000..75289ec --- /dev/null +++ b/testbed/core/oauth/__init__.py @@ -0,0 +1,42 @@ +""" +OAuth package — all OAuth, authentication, and authorization components for LOLA. + +Submodules: + authentication -- OptionalOAuth2Authentication (dual-mode: public + LOLA) + forms -- OAuthApplicationForm (OAuth app settings UI form) + utils -- State params, token sessions, client credential management + validators -- ActivityPubOAuth2Validator (scope + redirect validation) + +Public symbols are re-exported here so that the rest of the project can import +them via ``from testbed.core.oauth import ``. When adding a new public +symbol, add it to the appropriate submodule AND to this file's imports and +``__all__`` list. +""" + +from .authentication import OptionalOAuth2Authentication +from .forms import OAuthApplicationForm +from .utils import ( + clear_token_from_session, + generate_secure_state, + get_token_from_session, + get_token_scope_from_session, + get_user_application, + store_state_in_session, + store_token_in_session, + validate_state_from_session, +) +from .validators import ActivityPubOAuth2Validator + +__all__ = [ + "OptionalOAuth2Authentication", + "OAuthApplicationForm", + "ActivityPubOAuth2Validator", + "clear_token_from_session", + "generate_secure_state", + "get_token_from_session", + "get_token_scope_from_session", + "get_user_application", + "store_state_in_session", + "store_token_in_session", + "validate_state_from_session", +] diff --git a/testbed/core/utils/authentication.py b/testbed/core/oauth/authentication.py similarity index 98% rename from testbed/core/utils/authentication.py rename to testbed/core/oauth/authentication.py index 46d1804..20da9d3 100644 --- a/testbed/core/utils/authentication.py +++ b/testbed/core/oauth/authentication.py @@ -155,7 +155,7 @@ def _try_session_auth(self, request): A tuple of (user, token) if authentication succeeds, None otherwise """ from oauth2_provider.models import AccessToken - from testbed.core.utils.oauth_utils import get_token_from_session, clear_token_from_session + from testbed.core.oauth.utils import get_token_from_session, clear_token_from_session # Check if public_only parameter is set (for demo comparison) if request.GET.get('public_only'): diff --git a/testbed/core/forms/oauth_connection_form.py b/testbed/core/oauth/forms.py similarity index 100% rename from testbed/core/forms/oauth_connection_form.py rename to testbed/core/oauth/forms.py diff --git a/testbed/core/utils/oauth_utils.py b/testbed/core/oauth/utils.py similarity index 100% rename from testbed/core/utils/oauth_utils.py rename to testbed/core/oauth/utils.py diff --git a/testbed/core/utils/oauth_validators.py b/testbed/core/oauth/validators.py similarity index 100% rename from testbed/core/utils/oauth_validators.py rename to testbed/core/oauth/validators.py diff --git a/testbed/core/tests/test_oauth_validators.py b/testbed/core/tests/test_oauth_validators.py index 5e2b206..b374c8c 100644 --- a/testbed/core/tests/test_oauth_validators.py +++ b/testbed/core/tests/test_oauth_validators.py @@ -3,7 +3,7 @@ from django.contrib.auth import get_user_model from oauth2_provider.models import get_application_model -from testbed.core.utils.oauth_validators import ActivityPubOAuth2Validator +from testbed.core.oauth.validators import ActivityPubOAuth2Validator User = get_user_model() Application = get_application_model() diff --git a/testbed/core/views/api.py b/testbed/core/views/api.py index 7bf4999..807e2aa 100644 --- a/testbed/core/views/api.py +++ b/testbed/core/views/api.py @@ -40,7 +40,7 @@ Note, PortabilityOutbox, ) -from ..utils.authentication import OptionalOAuth2Authentication +from ..oauth.authentication import OptionalOAuth2Authentication from ..utils.errors import build_actor_not_found_error from .decorators import activitypub_content, build_auth_context, validate_lola_access diff --git a/testbed/core/views/oauth_demo.py b/testbed/core/views/oauth_demo.py index cd1fbe0..63eeb82 100644 --- a/testbed/core/views/oauth_demo.py +++ b/testbed/core/views/oauth_demo.py @@ -24,7 +24,7 @@ from django.urls import reverse from ..models import Actor -from ..utils.oauth_utils import ( +from ..oauth.utils import ( generate_secure_state, get_user_application, store_state_in_session, diff --git a/testbed/core/views/pages.py b/testbed/core/views/pages.py index 9df7d08..9ea9952 100644 --- a/testbed/core/views/pages.py +++ b/testbed/core/views/pages.py @@ -20,8 +20,8 @@ from django.shortcuts import redirect, render from ..models import Actor -from ..forms.oauth_connection_form import OAuthApplicationForm -from ..utils.oauth_utils import get_user_application +from ..oauth.forms import OAuthApplicationForm +from ..oauth.utils import get_user_application logger = logging.getLogger(__name__) diff --git a/testbed/settings/base.py b/testbed/settings/base.py index 3d9f326..b36c6ca 100644 --- a/testbed/settings/base.py +++ b/testbed/settings/base.py @@ -12,7 +12,6 @@ from pathlib import Path import os -import sys import logging import environ import structlog @@ -246,7 +245,7 @@ 'ACCESS_TOKEN_EXPIRE_SECONDS': 3600, # 1 hour 'REFRESH_TOKEN_EXPIRE_SECONDS': 86400, # 1 day 'AUTHORIZATION_CODE_EXPIRE_SECONDS': 600, # 10 minutes - 'OAUTH2_VALIDATOR_CLASS': 'testbed.core.utils.oauth_validators.ActivityPubOAuth2Validator', + 'OAUTH2_VALIDATOR_CLASS': 'testbed.core.oauth.validators.ActivityPubOAuth2Validator', # For testing purposes -> make PKCE optional 'PKCE_REQUIRED': False, } From bfd2df4b4cfaf8f78410f9fd1fa92f531755b402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aar=C3=B3n=20Ayerdis=20Espinoza?= Date: Wed, 8 Apr 2026 18:56:16 -0600 Subject: [PATCH 4/5] refactor: deleting comments from __init__.py --- testbed/core/oauth/__init__.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/testbed/core/oauth/__init__.py b/testbed/core/oauth/__init__.py index 75289ec..dd03010 100644 --- a/testbed/core/oauth/__init__.py +++ b/testbed/core/oauth/__init__.py @@ -1,18 +1,3 @@ -""" -OAuth package — all OAuth, authentication, and authorization components for LOLA. - -Submodules: - authentication -- OptionalOAuth2Authentication (dual-mode: public + LOLA) - forms -- OAuthApplicationForm (OAuth app settings UI form) - utils -- State params, token sessions, client credential management - validators -- ActivityPubOAuth2Validator (scope + redirect validation) - -Public symbols are re-exported here so that the rest of the project can import -them via ``from testbed.core.oauth import ``. When adding a new public -symbol, add it to the appropriate submodule AND to this file's imports and -``__all__`` list. -""" - from .authentication import OptionalOAuth2Authentication from .forms import OAuthApplicationForm from .utils import ( From 8f4a0960e92d6e93bb63628672a83a0ccb1775da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aar=C3=B3n=20Ayerdis=20Espinoza?= Date: Sat, 11 Apr 2026 11:12:26 -0600 Subject: [PATCH 5/5] chore: trigger CI