Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Saml redirect mfe #36197

Open
wants to merge 32 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
86be84e
fix: Redirect non-enterprise SAML to authn MFE
angonz Nov 22, 2024
fb78803
chore: Removed edx-token-utils dep and moved necessary logic to the repo
rijuma Jan 7, 2025
fcf4172
chore: Removed unused dependency
rijuma Jan 7, 2025
ae08820
chore: Run make compile-requirements to update dependencies
rijuma Jan 7, 2025
ff68815
chore: fix tests
rijuma Jan 9, 2025
c03a51f
chore: Moved jwt file to openedx.core.lib
rijuma Jan 15, 2025
7ac6cc4
chore: Updated TOKEN_SIGNING on cms
rijuma Jan 15, 2025
0d96780
chore: Updated defaults for token handling on CMS
rijuma Jan 15, 2025
8af707e
fix: Removed JWT constants from CMS and added comments on how to gene…
rijuma Jan 23, 2025
856d304
docs: Update lms/envs/common.py
feanil Jan 23, 2025
eb9061d
fix: remove publish-ci-docker-image workflow file (#36164)
UsamaSadiq Jan 24, 2025
45fcef0
feat!: Removing the long-deprecated legacy course_modes chooser (#36156)
deborahgu Jan 24, 2025
2998143
chore: Upgrade Python requirements (#36166)
edx-requirements-bot Jan 24, 2025
3989b84
feat: Upgrade Python dependency edx-enterprise
pwnage101 Jan 24, 2025
d81f987
fix: bypass edx ace for sending goal reminder email using ses (#36148)
AhtishamShahid Jan 27, 2025
eab97d9
feat: add and switch to downstream-only fields [FC-0076] (#36158)
pomegranited Jan 27, 2025
7a9eb89
fix: remove pylint constraint (#36169)
UsamaSadiq Jan 27, 2025
9d9c9a5
fix: catch a possible exception in beta course configuration (#36172)
deborahgu Jan 27, 2025
b259211
feat: dump_settings management command (#36162)
kdmccormick Jan 27, 2025
3fba328
chore: linting as a separate commit (#36179)
deborahgu Jan 27, 2025
c7e4969
feat: increase frequency of scheduled notify credentials (#36180)
deborahgu Jan 27, 2025
76748c4
feat: Make dump for fns, classes more stable and helpful (#36188)
kdmccormick Jan 29, 2025
6c4cfce
feat: Upgrade Python dependency edx-enterprise
pwnage101 Jan 29, 2025
2572150
fix: enable pylint warnings (#36195)
UsamaSadiq Jan 30, 2025
5af5c32
revert: revert: refactor: Clean up lms/envs/production.py cruft
kdmccormick Jan 16, 2025
739f339
fix: SHARED_kCOOKIE_DOMAIN -> SHARED_COOKIE_DOMAIN
kdmccormick Jan 17, 2025
8d299a5
fix: Exactly preserve legacy settings dicts; rm KEYS_WITH_MERGED_VALUES
kdmccormick Jan 28, 2025
ec8b443
fix: swagger docs ref_name conflicts (#36189)
navinkarkera Jan 30, 2025
60dbe70
feat: filter by current site organizations
andrey-canon Sep 18, 2024
c94038f
feat: add site-aware unit test
andrey-canon Jan 23, 2025
75196ac
fix: advanced editor styling on library authoring [FC-0076] (#36146)
rpenido Jan 30, 2025
967a981
test: Add test for enterprise SAML authentication MFE redirection logic
leoaulasneo98 Jan 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions .github/workflows/check_python_dependencies.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ jobs:
steps:
- name: Checkout Repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install repo-tools
run: pip install edx-repo-tools[find_dependencies]

- name: Install setuptool
run: pip install setuptools
run: pip install setuptools

- name: Run Python script
run: |
find_python_dependencies \
Expand All @@ -35,6 +35,5 @@ jobs:
--ignore https://github.com/edx/braze-client \
--ignore https://github.com/edx/edx-name-affirmation \
--ignore https://github.com/mitodl/edx-sga \
--ignore https://github.com/edx/token-utils \
--ignore https://github.com/open-craft/xblock-poll

Empty file.
1 change: 1 addition & 0 deletions cms/djangoapps/api/v1/views/course_runs.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class CourseRunViewSet(viewsets.GenericViewSet): # lint-amnesty, pylint: disabl
lookup_value_regex = settings.COURSE_KEY_REGEX
permission_classes = (permissions.IsAdminUser,)
serializer_class = CourseRunSerializer
queryset = []

def get_object(self):
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
Expand Down
2 changes: 1 addition & 1 deletion cms/djangoapps/contentstore/api/views/course_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class CourseImportView(CourseImportExportViewMixin, GenericAPIView):
# TODO: ARCH-91
# This view is excluded from Swagger doc generation because it
# does not specify a serializer class.
exclude_from_schema = True
swagger_schema = None

@course_author_access_required
def post(self, request, course_key):
Expand Down
5 changes: 5 additions & 0 deletions cms/djangoapps/contentstore/api/views/course_quality.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ class CourseQualityView(DeveloperErrorViewMixin, GenericAPIView):
* mode

"""
# TODO: ARCH-91
# This view is excluded from Swagger doc generation because it
# does not specify a serializer class.
swagger_schema = None

@course_author_access_required
def get(self, request, course_key):
"""
Expand Down
5 changes: 5 additions & 0 deletions cms/djangoapps/contentstore/api/views/course_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ class CourseValidationView(DeveloperErrorViewMixin, GenericAPIView):
* has_proctoring_escalation_email - whether the course has a proctoring escalation email

"""
# TODO: ARCH-91
# This view is excluded from Swagger doc generation because it
# does not specify a serializer class.
swagger_schema = None

@course_author_access_required
def get(self, request, course_key):
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,5 @@ def handle(self, *args, **options):

if error_keys:
msg = 'The following courses encountered errors and were not updated:\n'
for error_key in error_keys:
msg += f' - {error_key}\n'
msg += '\n'.join(f' - {error_key}' for error_key in error_keys)
logger.info(msg)
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,15 @@ def handle(self, *args, **options):
tarball = tasks.create_export_tarball(library, library_key, {}, None)
except Exception as e:
raise CommandError(f'Failed to export "{library_key}" with "{e}"') # lint-amnesty, pylint: disable=raise-missing-from
else:
with tarball:
# Save generated archive with keyed filename
prefix, suffix, n = str(library_key).replace(':', '+'), '.tar.gz', 0
while os.path.exists(prefix + suffix):
n += 1
prefix = '{}_{}'.format(prefix.rsplit('_', 1)[0], n) if n > 1 else f'{prefix}_1'
filename = prefix + suffix
target = os.path.join(dest_path, filename)
tarball.file.seek(0)
with open(target, 'wb') as f:
shutil.copyfileobj(tarball.file, f)
print(f'Library "{library.location.library_key}" exported to "{target}"')
with tarball:
# Save generated archive with keyed filename
prefix, suffix, n = str(library_key).replace(':', '+'), '.tar.gz', 0
while os.path.exists(prefix + suffix):
n += 1
prefix = '{}_{}'.format(prefix.rsplit('_', 1)[0], n) if n > 1 else f'{prefix}_1'
filename = prefix + suffix
target = os.path.join(dest_path, filename)
tarball.file.seek(0)
with open(target, 'wb') as f:
shutil.copyfileobj(tarball.file, f)
print(f'Library "{library.location.library_key}" exported to "{target}"')
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ class GradersSerializer(serializers.Serializer):
weight = serializers.IntegerField()
id = serializers.IntegerField()

class Meta:
ref_name = "authoring_grading.Graders.v0"


class CourseGradingModelSerializer(serializers.Serializer):
""" Serializer for course grading model data """
graders = GradersSerializer(many=True, allow_null=True, allow_empty=True)

class Meta:
ref_name = "authoring_grading.CourseGrading.v0"
2 changes: 1 addition & 1 deletion cms/djangoapps/contentstore/rest_api/v0/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
authoring_videos.VideoEncodingsDownloadView.as_view(), name='cms_api_videos_encodings'
),
re_path(
fr'grading/{settings.COURSE_ID_PATTERN}',
fr'grading/{settings.COURSE_ID_PATTERN}$',
AuthoringGradingView.as_view(), name='cms_api_update_grading'
),
path(
Expand Down
10 changes: 10 additions & 0 deletions cms/djangoapps/contentstore/rest_api/v0/views/authoring_videos.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ class VideoEncodingsDownloadView(DeveloperErrorViewMixin, RetrieveAPIView):
course_key: required argument, needed to authorize course authors and identify relevant videos.
"""

# TODO: ARCH-91
# This view is excluded from Swagger doc generation because it
# does not specify a serializer class.
swagger_schema = None

def dispatch(self, request, *args, **kwargs):
# TODO: probably want to refactor this to a decorator.
"""
Expand All @@ -151,6 +156,11 @@ class VideoFeaturesView(DeveloperErrorViewMixin, RetrieveAPIView):
public rest API endpoint providing a list of enabled video features.
"""

# TODO: ARCH-91
# This view is excluded from Swagger doc generation because it
# does not specify a serializer class.
swagger_schema = None

def dispatch(self, request, *args, **kwargs):
# TODO: probably want to refactor this to a decorator.
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,6 @@ def _copy_paste_and_assert_link(key_to_copy):
assert new_block.upstream == str(self.lib_block_key)
assert new_block.upstream_version == 3
assert new_block.upstream_display_name == "MCQ-draft"
assert new_block.upstream_max_attempts == 5
return new_block_key

# first verify link for copied block from library
Expand Down
3 changes: 3 additions & 0 deletions cms/envs/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,7 @@

# Public domain name of Studio (should be resolvable from the end-user's browser)
CMS_BASE = 'localhost:18010'
CMS_ROOT_URL = "https://localhost:18010"

LOG_DIR = '/edx/var/log/edx'

Expand Down Expand Up @@ -2520,6 +2521,8 @@
CREDENTIALS_INTERNAL_SERVICE_URL = 'http://localhost:8005'
CREDENTIALS_PUBLIC_SERVICE_URL = 'http://localhost:8005'
CREDENTIALS_SERVICE_USERNAME = 'credentials_service_user'
# time between scheduled runs, in seconds
NOTIFY_CREDENTIALS_FREQUENCY = 14400

ANALYTICS_DASHBOARD_URL = 'http://localhost:18110/courses'
ANALYTICS_DASHBOARD_NAME = 'Your Platform Name Here Insights'
Expand Down
1 change: 1 addition & 0 deletions cms/envs/production.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ def get_env_setting(setting):
CMS_BASE = ENV_TOKENS.get('CMS_BASE')
LMS_BASE = ENV_TOKENS.get('LMS_BASE')
LMS_ROOT_URL = ENV_TOKENS.get('LMS_ROOT_URL')
CMS_ROOT_URL = ENV_TOKENS.get('CMS_ROOT_URL')
LMS_INTERNAL_ROOT_URL = ENV_TOKENS.get('LMS_INTERNAL_ROOT_URL', LMS_ROOT_URL)
ENTERPRISE_API_URL = ENV_TOKENS.get('ENTERPRISE_API_URL', LMS_INTERNAL_ROOT_URL + '/enterprise/api/v1/')
ENTERPRISE_CONSENT_API_URL = ENV_TOKENS.get('ENTERPRISE_CONSENT_API_URL', LMS_INTERNAL_ROOT_URL + '/consent/api/v1/')
Expand Down
3 changes: 3 additions & 0 deletions cms/envs/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@
LMS_ROOT_URL = f"http://{LMS_BASE}"
FEATURES['PREVIEW_LMS_BASE'] = "preview.localhost"

CMS_BASE = "localhost:8001"
CMS_ROOT_URL = f"http://{CMS_BASE}"

COURSE_AUTHORING_MICROFRONTEND_URL = "http://course-authoring-mfe"
DISCUSSIONS_MICROFRONTEND_URL = "http://discussions-mfe"

Expand Down
118 changes: 117 additions & 1 deletion cms/lib/xblock/test/test_upstream_sync.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
"""
Test CMS's upstream->downstream syncing system
"""
import datetime
import ddt
from pytz import utc

from organizations.api import ensure_organization
from organizations.models import Organization
Expand Down Expand Up @@ -42,13 +44,33 @@ def setUp(self):
title="Test Upstream Library",
)
self.upstream_key = libs.create_library_block(self.library.key, "html", "test-upstream").usage_key
libs.create_library_block(self.library.key, "video", "video-upstream")

upstream = xblock.load_block(self.upstream_key, self.user)
upstream.display_name = "Upstream Title V2"
upstream.data = "<html><body>Upstream content V2</body></html>"
upstream.save()

self.upstream_problem_key = libs.create_library_block(self.library.key, "problem", "problem-upstream").usage_key
libs.set_library_block_olx(self.upstream_problem_key, (
'<problem'
' attempts_before_showanswer_button="1"'
' display_name="Upstream Problem Title V2"'
' due="2024-01-01T00:00:00Z"'
' force_save_button="false"'
' graceperiod="1d"'
' grading_method="last_attempt"'
' matlab_api_key="abc"'
' max_attempts="10"'
' rerandomize="&quot;always&quot;"'
' show_correctness="never"'
' show_reset_button="false"'
' showanswer="on_correct"'
' submission_wait_seconds="10"'
' use_latex_compiler="false"'
' weight="1"'
'/>\n'
))

libs.publish_changes(self.library.key, self.user.id)

self.taxonomy_all_org = tagging_api.create_taxonomy(
Expand Down Expand Up @@ -179,6 +201,100 @@ def test_sync_updates_happy_path(self):
for object_tag in object_tags:
assert object_tag.value in new_upstream_tags

# pylint: disable=too-many-statements
def test_sync_updates_to_downstream_only_fields(self):
"""
If we sync to modified content, will it preserve downstream-only fields, and overwrite the rest?
"""
downstream = BlockFactory.create(category='problem', parent=self.unit, upstream=str(self.upstream_problem_key))

# Initial sync
sync_from_upstream(downstream, self.user)

# These fields are copied from upstream
assert downstream.upstream_display_name == "Upstream Problem Title V2"
assert downstream.display_name == "Upstream Problem Title V2"
assert downstream.rerandomize == '"always"'
assert downstream.matlab_api_key == 'abc'
assert not downstream.use_latex_compiler

# These fields are "downstream only", so field defaults are preserved, and values are NOT copied from upstream
assert downstream.attempts_before_showanswer_button == 0
assert downstream.due is None
assert not downstream.force_save_button
assert downstream.graceperiod is None
assert downstream.grading_method == 'last_score'
assert downstream.max_attempts is None
assert downstream.show_correctness == 'always'
assert not downstream.show_reset_button
assert downstream.showanswer == 'finished'
assert downstream.submission_wait_seconds == 0
assert downstream.weight is None

# Upstream updates
libs.set_library_block_olx(self.upstream_problem_key, (
'<problem'
' attempts_before_showanswer_button="10"'
' display_name="Upstream Problem Title V3"'
' due="2024-02-02T00:00:00Z"'
' force_save_button="false"'
' graceperiod=""'
' grading_method="final_attempt"'
' matlab_api_key="def"'
' max_attempts="11"'
' rerandomize="&quot;per_student&quot;"'
' show_correctness="past_due"'
' show_reset_button="false"'
' showanswer="attempted"'
' submission_wait_seconds="11"'
' use_latex_compiler="true"'
' weight="2"'
'/>\n'
))
libs.publish_changes(self.library.key, self.user.id)

# Modifing downstream-only fields are "safe" customizations
downstream.display_name = "Downstream Title Override"
downstream.attempts_before_showanswer_button = 2
downstream.due = datetime.datetime(2025, 2, 2, tzinfo=utc)
downstream.force_save_button = True
downstream.graceperiod = '2d'
downstream.grading_method = 'last_score'
downstream.max_attempts = 100
downstream.show_correctness = 'always'
downstream.show_reset_button = True
downstream.showanswer = 'on_expired'
downstream.submission_wait_seconds = 100
downstream.weight = 3

# Modifying synchronized fields are "unsafe" customizations
downstream.rerandomize = '"onreset"'
downstream.matlab_api_key = 'hij'
downstream.save()

# Follow-up sync.
sync_from_upstream(downstream, self.user)

# "unsafe" customizations are overridden by upstream
assert downstream.upstream_display_name == "Upstream Problem Title V3"
assert downstream.rerandomize == '"per_student"'
assert downstream.matlab_api_key == 'def'
assert downstream.use_latex_compiler

# but "safe" customizations survive
assert downstream.display_name == "Downstream Title Override"
assert downstream.attempts_before_showanswer_button == 2
assert downstream.due == datetime.datetime(2025, 2, 2, tzinfo=utc)
assert downstream.force_save_button
assert downstream.graceperiod == '2d'
assert downstream.grading_method == 'last_score'
assert downstream.max_attempts == 100
assert downstream.show_correctness == 'always'
assert downstream.show_reset_button
assert downstream.showanswer == 'on_expired'
assert downstream.submission_wait_seconds == 100
assert downstream.weight == 3

def test_sync_updates_to_modified_content(self):
"""
If we sync to modified content, will it preserve customizable fields, but overwrite the rest?
Expand Down
Loading