feat: replace enterprise logistration logic with openedx-filters hooks - #407
feat: replace enterprise logistration logic with openedx-filters hooks#407pwnage101 wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR removes enterprise-specific logic from the user_authn logistration/login/registration flow and replaces it with Open edX Filters hooks so downstream plugins can customize behavior without importing enterprise_support.
Changes:
- Removed enterprise-specific redirect and context/cookie logic from legacy logistration and post-login redirect handling.
- Introduced/used six generic Open edX Filters hook points for logistration context/response, MFE redirect veto, post-login redirect, and TPA form overrides.
- Updated and expanded test coverage to validate the new filter hook behavior and removed enterprise-specific test cases.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| openedx/core/djangoapps/user_authn/views/utils.py | Removes enterprise URL regex constants that are no longer needed. |
| openedx/core/djangoapps/user_authn/views/login_form.py | Replaces enterprise logistration branching with filter hooks (context, response, MFE redirect veto, login-form TPA overrides). |
| openedx/core/djangoapps/user_authn/views/registration_form.py | Adds registration-form TPA override filter hook and returns the modified form_desc. |
| openedx/core/djangoapps/user_authn/views/login.py | Replaces enterprise multi-enterprise redirect logic with a post-login redirect filter hook. |
| openedx/core/djangoapps/user_authn/views/tests/test_logistration.py | Removes enterprise-specific logistration assertions and related SAML/enterprise tests. |
| openedx/core/djangoapps/user_authn/views/tests/test_login.py | Removes enterprise multi-enterprise redirect tests tied to the deleted logic. |
| openedx/core/djangoapps/user_authn/views/tests/test_filters.py | Adds new filter tests for logistration/login/registration/post-login redirect hooks. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
openedx/core/djangoapps/user_authn/views/login_form.py:225
- The PR description lists a new LogistrationMFERedirectRequested hook, but the logistration view still redirects to the Authn MFE directly when should_redirect_to_authn_microfrontend() is true (no filter invocation or tests for that hook). Either wire in the LogistrationMFERedirectRequested filter at this decision point (so plugins can override/suppress the redirect), or update the PR description to reflect the actual set of filters implemented.
# Redirect to the authn MFE when it is enabled, unless auth is provided externally
# (SAML/TPA users must remain on the legacy login/registration page).
has_external_provider = bool(tpa_hint_provider or saml_provider)
if should_redirect_to_authn_microfrontend() and not has_external_provider:
query_params = request.GET.urlencode()
url_path = '/{}{}'.format(
initial_mode,
'?' + query_params if query_params else ''
)
return redirect(settings.AUTHN_MICROFRONTEND_URL + url_path)
openedx/core/djangoapps/user_authn/views/utils.py:25
- The PR description says this change “remov[es] all enterprise_support imports”, but this file still imports enterprise_support in get_mfe_context() (openedx/core/djangoapps/user_authn/views/utils.py:117-118). If the intent is only to remove enterprise logic from the legacy logistration views, the PR description should be narrowed; otherwise these remaining imports should be removed/refactored as well.
API_V1 = 'v1'
cbc2739 to
3858d01
Compare
3858d01 to
c50f851
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
openedx/core/djangoapps/user_authn/views/tests/test_filters.py:3
- Docstring typo: “vies” should be “views”.
"""
Test that various filters are fired for the vies in the user_authn app.
"""
c50f851 to
979ef58
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.
Suppressed comments (2)
openedx/core/djangoapps/user_authn/views/login.py:625
LoginAltRedirectURLRequestedis passed the same value for bothredirect_urlandnext_url. Whenthird_party_auth_requestedis true,finish_auth_urlmay be non-empty whilenext_urlstill represents the user’s intended destination; passingfinish_auth_urlasnext_urlcan prevent filter implementations from making decisions based on the real next URL.
alt_redirect_url, __, __ = LoginAltRedirectURLRequested.run_filter(
redirect_url=finish_auth_url or next_url,
user=possibly_authenticated_user,
next_url=finish_auth_url or next_url,
)
openedx/core/djangoapps/user_authn/serializers.py:74
- Merging
extra_contextinto the serialized output withdict.update()allows plugin-provided keys to overwrite declared serializer fields (e.g.,platformName,countryCode). That undermines the serializer’s control of the response schema and can cause hard-to-debug collisions.
representation = super().to_representation(instance)
representation.update(instance.get('extra_context') or {})
return representation
979ef58 to
a9d783d
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.
Suppressed comments (3)
openedx/core/djangoapps/user_authn/views/tests/test_filters.py:2
- Typo in module docstring: "vies" should be "views".
Test that various filters are fired for the vies in the user_authn app.
openedx/core/djangoapps/user_authn/serializers.py:74
extra_contextis merged into the serialized payload withrepresentation.update(...), which allows plugin-provided keys to overwrite declared/validated fields (e.g.,platformName,providers) and can also raise ifextra_contextis not a dict. It’s safer to only merge non-colliding keys and ignore/guard invalid types so the serializer retains control of its declared fields.
representation = super().to_representation(instance)
representation.update(instance.get('extra_context') or {})
return representation
openedx/core/djangoapps/user_authn/views/login_form.py:20
- The PR description says logistration logic is replaced with 5 filters named
LogistrationContextRequested,LoginFormTPAOverridesRequested,RegistrationFormTPAOverridesRequested,LogistrationResponseRendered, andPostLoginRedirectURLRequested, but this change set introduces/uses different filter names/types (e.g.,LoginFormGenerated,LogistrationViewContextGenerated,LogistrationViewRenderCompleted,LoginAltRedirectURLRequested,AuthnMFEContextGenerated). Please align the PR description (and any referenced integration/testing instructions) with the actual filter contract being implemented, or rename the filters here if the listed names are the intended public API.
from openedx_filters.authentication.filters import (
LoginFormGenerated,
LogistrationViewContextGenerated,
LogistrationViewRenderCompleted,
)
a9d783d to
42cb4a3
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.
Suppressed comments (3)
openedx/core/djangoapps/user_authn/views/tests/test_filters.py:3
- Typo in module docstring: “vies” → “views”.
"""
Test that various filters are fired for the vies in the user_authn app.
"""
openedx/core/djangoapps/user_authn/serializers.py:74
to_representationmergesextra_contextviadict.update, which will overwrite declared fields if a plugin uses a colliding key (and can also raise ifextra_contextis not a mapping). If the intent is to add plugin-provided keys alongside declared fields, avoid overwriting existing keys and guard for non-dict values.
representation = super().to_representation(instance)
representation.update(instance.get('extra_context') or {})
return representation
openedx/core/djangoapps/user_authn/views/login_form.py:20
- PR description lists 5 filter hooks with different names (e.g., LogistrationContextRequested, PostLoginRedirectURLRequested), but this change introduces/uses a different set of filters (e.g., LoginFormGenerated, LogistrationViewContextGenerated, LoginAltRedirectURLRequested, AuthnMFEContextGenerated) and appears to add 6 hooks. Please align the PR description (and any linked acceptance criteria) with the actual filter types/names implemented here to avoid confusion for integrators.
from openedx_filters.authentication.filters import (
LoginFormGenerated,
LogistrationViewContextGenerated,
LogistrationViewRenderCompleted,
)
42cb4a3 to
3882714
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.
Suppressed comments (3)
openedx/core/djangoapps/user_authn/views/tests/test_filters.py:3
- Typo in the module docstring: “vies” should be “views”.
"""
Test that various filters are fired for the vies in the user_authn app.
"""
openedx/core/djangoapps/user_authn/serializers.py:74
ContextDataSerializer.to_representationblindly mergesextra_contextinto the serialized output, which can accidentally (or intentionally) overwrite declared fields (e.g.,platformName,countryCode) and bypass the serializer’s shape/validation guarantees. Since declared fields are already overridable by modifyingcontextbefore serialization,extra_contextshould only add new keys (or explicitly guard against collisions).
def to_representation(self, instance):
"""
Serialize the declared fields, then merge in the context's ``extra_context`` entries.
``extra_context`` holds entries contributed by plugins, which this serializer cannot
declare fields for. They are merged as-is: the contributor owns their shape, and no
coercion is applied.
"""
representation = super().to_representation(instance)
representation.update(instance.get('extra_context') or {})
return representation
openedx/core/djangoapps/user_authn/views/utils.py:129
- The docstring for
get_running_third_party_auth_statesays “Both are None … when the provider … could not be determined”, but the implementation returns the running pipeline even ifRegistry.get_from_pipeline(...)returnsNone. Either update the behavior to match the docstring, or (preferably) update the docstring/return description to reflect thatrunning_pipelinemay be non-None whencurrent_provideris None.
Returns:
tuple[dict, ProviderConfig]: the running pipeline and its provider. Both are None
when third party auth is disabled, when no pipeline is running for the request, or
when the provider of the running pipeline could not be determined.
"""
3882714 to
3687447
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 17 out of 17 changed files in this pull request and generated no new comments.
Suppressed comments (4)
openedx/core/djangoapps/user_authn/serializers.py:74
ContextDataSerializer.to_representationmergesextra_contextviadict.update(), which allows plugin-provided keys to overwrite declared serializer fields (e.g.,platformName,countryCode). This contradicts the docstring (“entries the serializer cannot declare fields for”) and can unexpectedly change the response shape/values.
representation = super().to_representation(instance)
representation.update(instance.get('extra_context') or {})
return representation
openedx/core/djangoapps/user_authn/views/login.py:515
is_safe_login_or_logout_redirect(...)can raise (e.g., ifclient_idis present but invalid,Application.objects.get(client_id=...)raises DoesNotExist). That would turn a badclient_idinto a 500 during login when an alt redirect is returned.
if not alt_redirect_url or not is_safe_login_or_logout_redirect(
redirect_to=alt_redirect_url,
request_host=request.get_host(),
dot_client_id=request.POST.get("client_id"),
require_https=request.is_secure(),
openedx/core/djangoapps/user_authn/views/utils.py:128
- The
get_running_third_party_auth_statedocstring return type saystuple[RunningPipeline, ProviderConfig], but the function (and type annotation) can returnNonefor either element. This makes the contract harder to follow for callers.
Returns:
tuple[RunningPipeline, ProviderConfig]: the running pipeline and its provider.
openedx/core/djangoapps/user_authn/views/login.py:499
- The PR description lists a
PostLoginRedirectURLRequestedhook, but the implementation here usesLoginAltRedirectURLRequested(filter typeorg.openedx.authentication.login.alt_redirect_url.requested.v1). Please confirm the intended filter name/type and update either the PR description or the code so they match.
# .. filter_implemented_name: LoginAltRedirectURLRequested
# .. filter_type: org.openedx.authentication.login.alt_redirect_url.requested.v1
alt_redirect_url, __ = LoginAltRedirectURLRequested.run_filter(
redirect_url=redirect_url,
user=user,
3687447 to
bdbe952
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 22 out of 22 changed files in this pull request and generated no new comments.
Suppressed comments (3)
openedx/core/djangoapps/user_authn/views/tests/test_filters.py:2
- Typo in module docstring: "vies" → "views".
Test that various filters are fired for the vies in the user_authn app.
openedx/core/djangoapps/user_authn/serializers.py:74
extra_contextis described as holding only undeclared keys, butrepresentation.update(extra_context)will silently overwrite declared fields (e.g.,platformName) if a plugin returns a colliding key. This makes the response schema less predictable and can mask bugs in filter implementations.
representation = super().to_representation(instance)
representation.update(instance.get('extra_context') or {})
return representation
openedx/core/djangoapps/user_authn/views/utils.py:128
- The docstring return type says
tuple[RunningPipeline, ProviderConfig], but the function can (and does) return(None, None)and(RunningPipeline, None). Updating the signature in the docstring avoids misleading API documentation.
Returns:
tuple[RunningPipeline, ProviderConfig]: the running pipeline and its provider.
Replace the enterprise-specific logic in the user_authn logistration views with six generic filters, removing all enterprise_support imports and enterprise-specific logic: - LogistrationViewContextGenerated - AuthnMFEContextGenerated - LogistrationViewRenderCompleted - LoginFormGenerated - RegistrationFormGenerated - LoginAltRedirectURLRequested ENT-11568
bdbe952 to
e24703c
Compare
Replace the enterprise-specific logic in the user_authn logistration views with six generic filters, removing all enterprise_support imports and enterprise-specific logic:
ENT-11568
Integration Testing
See openedx/edx-enterprise#2551 (comment)
Related: