Skip to content

Commit 0874606

Browse files
authored
Merge branch 'master' into pwnage101/ENT-11569
2 parents 98dabe9 + 4a49a95 commit 0874606

26 files changed

Lines changed: 232 additions & 374 deletions

File tree

cms/djangoapps/contentstore/toggles.py

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -86,25 +86,6 @@ def exam_setting_view_enabled(course_key):
8686
return not LEGACY_STUDIO_EXAM_SETTINGS.is_enabled(course_key)
8787

8888

89-
# .. toggle_name: legacy_studio.video_editor
90-
# .. toggle_implementation: WaffleFlag
91-
# .. toggle_default: False
92-
# .. toggle_description: Temporarily fall back to the old Video component (a.k.a. video block) editor.
93-
# .. toggle_use_cases: temporary
94-
# .. toggle_creation_date: 2025-03-14
95-
# .. toggle_target_removal_date: 2025-09-14
96-
# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275
97-
# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available.
98-
LEGACY_STUDIO_VIDEO_EDITOR = CourseWaffleFlag('legacy_studio.video_editor', __name__)
99-
100-
101-
def use_new_video_editor(course_key):
102-
"""
103-
Returns a boolean = true if new video editor is enabled
104-
"""
105-
return not LEGACY_STUDIO_VIDEO_EDITOR.is_enabled(course_key)
106-
107-
10889
# .. toggle_name: legacy_studio.pdf_editor
10990
# .. toggle_implementation: WaffleFlag
11091
# .. toggle_default: False
@@ -129,7 +110,7 @@ def use_new_pdf_editor():
129110
# .. toggle_use_cases: temporary
130111
# .. toggle_creation_date: 2023-04-03
131112
# .. toggle_target_removal_date: 2023-6-01
132-
# .. toggle_warning: You need to activate the `new_core_editors.use_new_video_editor` flag to use this new flow.
113+
# .. toggle_warning: This controls the new core video xblock editor flow.
133114
ENABLE_VIDEO_GALLERY_FLOW_FLAG = WaffleFlag('new_core_editors.use_video_gallery_flow', __name__)
134115

135116

cms/static/js/views/pages/container.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -538,12 +538,11 @@ function($, _, Backbone, gettext, BasePage,
538538
if (!options || options.view !== 'visibility_view') {
539539
const primaryHeader = $(event.target).closest('.xblock-header-primary, .nav-actions');
540540

541-
var useNewVideoEditor = primaryHeader.attr('use-new-editor-video'),
542-
blockType = primaryHeader.attr('data-block-type'),
541+
var blockType = primaryHeader.attr('data-block-type'),
543542
useNewPdfEditor = primaryHeader.attr('use-new-editor-pdf');
544543

545544
if((blockType === 'html')
546-
|| (useNewVideoEditor === 'True' && blockType === 'video')
545+
|| (blockType === 'video')
547546
|| (blockType === 'problem')
548547
|| (useNewPdfEditor === 'True' && blockType === 'pdf')
549548
) {
@@ -1204,8 +1203,7 @@ function($, _, Backbone, gettext, BasePage,
12041203
},
12051204

12061205
onNewXBlock: function(xblockElement, scrollOffset, is_duplicate, data) {
1207-
var useNewVideoEditor = this.$('.xblock-header-primary').attr('use-new-editor-video'),
1208-
useVideoGalleryFlow = this.$('.xblock-header-primary').attr("use-video-gallery-flow");
1206+
var useVideoGalleryFlow = this.$('.xblock-header-primary').attr("use-video-gallery-flow");
12091207

12101208
// find the block type in the locator if availible
12111209
if(data.hasOwnProperty('locator')) {
@@ -1214,7 +1212,7 @@ function($, _, Backbone, gettext, BasePage,
12141212
}
12151213
// open mfe editors for new blocks only and not for content imported from libraries
12161214
if(!data.hasOwnProperty('upstreamRef') && (blockType.includes('html')
1217-
|| (useNewVideoEditor === 'True' && blockType.includes('video'))
1215+
|| (blockType.includes('video'))
12181216
|| (blockType.includes('problem')))
12191217
){
12201218
if (this.options.isIframeEmbed && (this.isSplitTestContentPage || this.isVerticalContentPage)) {

cms/templates/container.html

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from django.utils.translation import gettext as _
1414

1515
from cms.djangoapps.contentstore.helpers import xblock_studio_url, xblock_type_display_name
16-
from cms.djangoapps.contentstore.toggles import use_new_pdf_editor, use_new_video_editor, use_video_gallery_flow
16+
from cms.djangoapps.contentstore.toggles import use_new_pdf_editor, use_video_gallery_flow
1717
from cms.djangoapps.contentstore.utils import get_editor_page_base_url
1818
from openedx.core.djangolib.js_utils import (
1919
dump_js_escaped_json, js_escaped_string
@@ -111,7 +111,6 @@
111111
<%block name="content">
112112

113113
<%
114-
use_new_editor_video = use_new_video_editor(xblock_locator.course_key)
115114
use_new_editor_pdf = use_new_pdf_editor()
116115
use_new_video_gallery_flow = use_video_gallery_flow()
117116
%>
@@ -167,7 +166,6 @@ <h1 class="page-header-title xblock-field-value incontext-editor-value"><span cl
167166
</div>
168167

169168
<nav class="nav-actions" aria-label="${_('Page Actions')}"
170-
use-new-editor-video = ${use_new_editor_video}
171169
use-new-editor-pdf = ${use_new_editor_pdf}
172170
use-video-gallery-flow = ${use_new_video_gallery_flow}
173171
authoring_MFE_base_url = ${get_editor_page_base_url(xblock_locator.course_key)}

cms/templates/studio_xblock_wrapper.html

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,11 @@
88
from openedx.core.djangolib.js_utils import (
99
dump_js_escaped_json, js_escaped_string
1010
)
11-
from cms.djangoapps.contentstore.toggles import use_new_pdf_editor, use_new_video_editor, use_video_gallery_flow
11+
from cms.djangoapps.contentstore.toggles import use_new_pdf_editor, use_video_gallery_flow
1212
from cms.lib.xblock.upstream_sync import UpstreamLink
1313
from openedx.core.djangoapps.content_tagging.toggles import is_tagging_feature_disabled
1414
%>
1515
<%
16-
use_new_editor_video = use_new_video_editor(xblock.context_key)
1716
use_new_editor_pdf = use_new_pdf_editor()
1817
use_new_video_gallery_flow = use_video_gallery_flow()
1918
use_tagging = not is_tagging_feature_disabled()
@@ -83,7 +82,6 @@
8382
is-collapsed
8483
% endif
8584
"
86-
use-new-editor-video = ${use_new_editor_video}
8785
use-new-editor-pdf = ${use_new_editor_pdf}
8886
use-video-gallery-flow = ${use_new_video_gallery_flow}
8987
authoring_MFE_base_url = ${get_editor_page_base_url(xblock.location.course_key)}
Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# pylint: disable=missing-module-docstring
22

33
from django.apps import AppConfig
4-
from django.conf import settings
54

65

76
class ThirdPartyAuthConfig(AppConfig): # pylint: disable=missing-class-docstring
@@ -11,12 +10,3 @@ class ThirdPartyAuthConfig(AppConfig): # pylint: disable=missing-class-docstrin
1110
def ready(self):
1211
# Import signal handlers to register them
1312
from .signals import handlers # noqa: F401 pylint: disable=unused-import
14-
15-
# Note: Third-party auth settings are now defined statically in lms/envs/common.py
16-
# However, the enterprise pipeline step must be inserted dynamically because
17-
# it requires checking if enterprise is enabled, which can't be done at
18-
# settings load time.
19-
# Only insert enterprise elements if SOCIAL_AUTH_PIPELINE exists (LMS only, not CMS).
20-
if hasattr(settings, 'SOCIAL_AUTH_PIPELINE'):
21-
from openedx.features.enterprise_support.api import insert_enterprise_pipeline_elements
22-
insert_enterprise_pipeline_elements(settings.SOCIAL_AUTH_PIPELINE)

common/djangoapps/third_party_auth/models.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,10 @@ class ProviderConfig(ConfigurationModel):
237237
help_text="Use the presence of a profile from a trusted third party as proof of identity verification.",
238238
)
239239

240+
# Enterprise-only field: excludes this provider from the EnterpriseCustomer Django admin IDP
241+
# dropdown. Added in ENT-1366 after social auth providers (Facebook, Google, etc.) were linked
242+
# as enterprise IDPs, incorrectly associating all their users with an enterprise. Should ideally
243+
# be migrated into the enterprise plugin.
240244
disable_for_enterprise_sso = models.BooleanField(
241245
default=False,
242246
verbose_name='Disabled for Enterprise TPA',

common/djangoapps/third_party_auth/pipeline.py

Lines changed: 8 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,7 @@ def B(*args, **kwargs):
8686
from common.djangoapps.edxmako.shortcuts import render_to_string
8787
from common.djangoapps.third_party_auth.utils import (
8888
get_associated_user_by_email_response,
89-
get_user_from_email,
90-
is_enterprise_customer_user,
9189
is_oauth_provider,
92-
is_saml_provider,
9390
user_exists,
9491
)
9592
from common.djangoapps.track import segment
@@ -780,80 +777,18 @@ def associate_by_email_if_oauth(auth_entry, backend, details, user, strategy, *a
780777
return association_response
781778

782779

783-
@partial.partial
784780
def associate_by_email_if_saml(auth_entry, backend, details, user, strategy, *args, **kwargs):
785781
"""
786-
This pipeline step associates the current social auth with the user with the
787-
same email address in the database. It defers to the social library's associate_by_email
788-
implementation, which verifies that only a single database user is associated with the email.
782+
Deprecated — enterprise SAML email association moved to
783+
enterprise.tpa_pipeline.enterprise_associate_by_email.
789784
790-
This association is done ONLY if the user entered the pipeline belongs to SAML provider.
785+
Retained as a no-op for backwards compatibility with custom pipeline configs.
791786
"""
792-
from openedx.features.enterprise_support.api import enterprise_is_enabled
793-
794-
def get_user():
795-
"""
796-
This is the helper method to get the user from system by matching email.
797-
"""
798-
user_details = {'email': details.get('email')} if details else None
799-
return get_user_from_email(user_details or {})
800-
801-
@enterprise_is_enabled()
802-
def associate_by_email_if_enterprise_user():
803-
"""
804-
If the learner arriving via SAML is already linked to the enterprise customer linked to the same IdP,
805-
they should not be prompted for their edX password.
806-
"""
807-
try:
808-
enterprise_customer_user = is_enterprise_customer_user(current_provider.provider_id, current_user)
809-
logger.info(
810-
'[Multiple_SSO_SAML_Accounts_Association_to_User] Enterprise user verification:' # noqa: UP032
811-
'User Email: {email}, User ID: {user_id}, Provider ID: {provider_id},'
812-
' is_enterprise_customer_user: {enterprise_customer_user}'.format(
813-
email=current_user.email,
814-
user_id=current_user.id,
815-
provider_id=current_provider.provider_id,
816-
enterprise_customer_user=enterprise_customer_user,
817-
)
818-
)
819-
820-
if enterprise_customer_user:
821-
# this is python social auth pipeline default method to automatically associate social accounts
822-
# if the email already matches a user account.
823-
association_response, user_is_active = get_associated_user_by_email_response(
824-
backend, details, user, *args, **kwargs)
825-
826-
if not user_is_active:
827-
logger.info(
828-
'[Multiple_SSO_SAML_Accounts_Association_to_User] User association account is not' # noqa: UP032 # pylint: disable=line-too-long
829-
' active: User Email: {email}, User ID: {user_id}, Provider ID: {provider_id},'
830-
' is_enterprise_customer_user: {enterprise_customer_user}'.format(
831-
email=current_user.email,
832-
user_id=current_user.id,
833-
provider_id=current_provider.provider_id,
834-
enterprise_customer_user=enterprise_customer_user
835-
)
836-
)
837-
return None
838-
839-
return association_response
840-
841-
except Exception as ex: # pylint: disable=broad-except
842-
logger.exception('[Multiple_SSO_SAML_Accounts_Association_to_User] Error in'
843-
' saml multiple accounts association: User ID: %s, User Email: %s:,'
844-
'Provider ID: %s, Exception: %s', current_user.id, current_user.email,
845-
current_provider.provider_id, ex)
846-
847-
saml_provider, current_provider = is_saml_provider(strategy.request.backend.name, kwargs)
848-
849-
if saml_provider:
850-
# get the user by matching email if the pipeline user is not available.
851-
current_user = user if user else get_user()
852-
853-
# Verify that the user linked to enterprise customer of current identity provider and an active user
854-
associate_response = associate_by_email_if_enterprise_user() if current_user else None
855-
if associate_response:
856-
return associate_response
787+
logger.warning(
788+
"associate_by_email_if_saml is deprecated and is now a no-op. "
789+
"Enterprise SAML email association has moved to "
790+
"enterprise.tpa_pipeline.enterprise_associate_by_email."
791+
)
857792

858793

859794
def user_details_force_sync(auth_entry, strategy, details, user=None, *args, **kwargs): # pylint: disable=keyword-arg-before-vararg

common/djangoapps/third_party_auth/saml.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,22 @@ def auth_url(self):
141141

142142
def disconnect(self, *args, **kwargs):
143143
"""
144-
Override of SAMLAuth.disconnect to unlink the learner from enterprise customer if associated.
145-
"""
146-
from openedx.features.enterprise_support.api import unlink_enterprise_user_from_idp
147-
user = kwargs.get('user', None)
148-
unlink_enterprise_user_from_idp(self.strategy.request, user, self.name)
144+
Override of SAMLAuth.disconnect to emit a signal when a user disconnects their SAML account.
145+
"""
146+
from common.djangoapps.third_party_auth.signals import SAMLAccountDisconnected
147+
# Emit the signal before super().disconnect() so that handlers (e.g. enterprise
148+
# user unlinking) run while the social auth record still exists.
149+
user = kwargs['user'] # Upstream social_core always passes a non-None user.
150+
log.info(
151+
'[THIRD_PARTY_AUTH] Emitting SAMLAccountDisconnected signal for user_id=%s, backend=%s',
152+
user.id, self.name,
153+
)
154+
SAMLAccountDisconnected.send(
155+
sender=self.__class__,
156+
request=self.strategy.request,
157+
user=user,
158+
saml_backend=self,
159+
)
149160
return super().disconnect(*args, **kwargs)
150161

151162
def _check_entitlements(self, idp, attributes):
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
1-
# Signal handlers for third_party_auth app
1+
"""
2+
Signals and signal handlers for third_party_auth app
3+
"""
4+
5+
from common.djangoapps.third_party_auth.signals.signals import SAMLAccountDisconnected # noqa: F401
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
"""
2+
Signals defined by the third_party_auth app.
3+
"""
4+
from django.dispatch import Signal
5+
6+
# Signal fired when a user disconnects a SAML account.
7+
#
8+
# Keyword arguments sent with this signal:
9+
# request (HttpRequest): The HTTP request during which the disconnect occurred.
10+
# user (User): The Django User disconnecting the social auth account.
11+
# saml_backend (social_core.backends.saml.SAMLAuth): The SAMLAuth backend instance (has a ``name`` attribute).
12+
SAMLAccountDisconnected = Signal()

0 commit comments

Comments
 (0)