Skip to content

Commit a7a5a6f

Browse files
authored
feat: replace enterprise_support import with AccountSettingsReadOnlyFieldsRequested filter
1 parent aa1b064 commit a7a5a6f

8 files changed

Lines changed: 56 additions & 78 deletions

File tree

lms/envs/common.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3695,6 +3695,18 @@ def _should_send_certificate_events(settings):
36953695
# The project ID should be obtained from the Google Cloud Console when creating a reCAPTCHA
36963696
RECAPTCHA_PROJECT_ID = None
36973697

3698+
# .. setting_name: OPEN_EDX_FILTERS_CONFIG
3699+
# .. setting_default: {}
3700+
# .. setting_description: Configuration dict for openedx-filters pipeline steps.
3701+
# Keys are filter type strings; values are dicts with 'fail_silently' (bool) and
3702+
# 'pipeline' (list of dotted-path strings to PipelineStep subclasses).
3703+
OPEN_EDX_FILTERS_CONFIG = {
3704+
"org.openedx.learning.account.settings.read_only_fields.requested.v1": {
3705+
"fail_silently": True,
3706+
"pipeline": ["enterprise.filters.accounts.AccountSettingsReadOnlyFieldsStep"],
3707+
},
3708+
}
3709+
36983710
############################## Miscellaneous ###############################
36993711

37003712
# To limit the number of courses displayed on learner dashboard

lms/envs/production.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ def get_env_setting(setting):
8484
'EVENT_BUS_PRODUCER_CONFIG',
8585
'DEFAULT_FILE_STORAGE',
8686
'STATICFILES_STORAGE',
87+
'OPEN_EDX_FILTERS_CONFIG',
8788
]
8889
})
8990

@@ -281,6 +282,19 @@ def get_env_setting(setting):
281282
EVENT_TRACKING_SEGMENTIO_EMIT_WHITELIST
282283
)
283284

285+
# Merge OPEN_EDX_FILTERS_CONFIG from YAML into the default defined in common.py.
286+
# Pipeline steps from YAML are appended after steps defined in common.py.
287+
# The fail_silently value from YAML takes precedence over the one in common.py.
288+
for _filter_type, _filter_config in _YAML_TOKENS.get('OPEN_EDX_FILTERS_CONFIG', {}).items():
289+
if _filter_type in OPEN_EDX_FILTERS_CONFIG:
290+
OPEN_EDX_FILTERS_CONFIG[_filter_type]['pipeline'].extend(
291+
_filter_config.get('pipeline', [])
292+
)
293+
if 'fail_silently' in _filter_config:
294+
OPEN_EDX_FILTERS_CONFIG[_filter_type]['fail_silently'] = _filter_config['fail_silently']
295+
else:
296+
OPEN_EDX_FILTERS_CONFIG[_filter_type] = _filter_config
297+
284298
if ENABLE_THIRD_PARTY_AUTH:
285299
AUTHENTICATION_BACKENDS = _YAML_TOKENS.get('THIRD_PARTY_AUTH_BACKENDS', [
286300
'social_core.backends.google.GoogleOAuth2',

openedx/core/djangoapps/user_api/accounts/api.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@
3838
from openedx.core.djangoapps.user_authn.utils import check_pwned_password
3939
from openedx.core.djangoapps.user_authn.views.registration_form import validate_name, validate_username
4040
from openedx.core.lib.api.view_utils import add_serializer_errors
41-
from openedx.features.enterprise_support.utils import get_enterprise_readonly_account_fields
4241
from openedx.features.name_affirmation_api.utils import is_name_affirmation_installed
42+
from openedx_filters.learning.filters import AccountSettingsReadOnlyFieldsRequested
4343

4444
from .serializers import AccountLegacyProfileSerializer, AccountUserSerializer, UserReadOnlySerializer, _visible_fields
4545

@@ -193,11 +193,17 @@ def update_account_settings(requesting_user, update, username=None):
193193

194194
def _validate_read_only_fields(user, data, field_errors):
195195
# Check for fields that are not editable. Marking them read-only causes them to be ignored, but we wish to 400.
196+
plugin_readonly_fields, __ = AccountSettingsReadOnlyFieldsRequested.run_filter(
197+
readonly_fields=set(),
198+
user=user,
199+
)
200+
plugin_readonly_fields = plugin_readonly_fields or set()
201+
196202
read_only_fields = set(data.keys()).intersection(
197203
# Remove email since it is handled separately below when checking for changing_email.
198204
(set(AccountUserSerializer.get_read_only_fields()) - {"email"}) |
199205
set(AccountLegacyProfileSerializer.get_read_only_fields() or set()) |
200-
get_enterprise_readonly_account_fields(user)
206+
plugin_readonly_fields
201207
)
202208

203209
for read_only_field in read_only_fields:

openedx/core/djangoapps/user_api/accounts/tests/test_api.py

Lines changed: 18 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"""
55

66
import datetime
7-
import itertools
87
import unicodedata
98
from unittest.mock import Mock, patch
109

@@ -18,7 +17,6 @@
1817
from django.test.client import RequestFactory
1918
from django.urls import reverse
2019
from pytz import UTC
21-
from social_django.models import UserSocialAuth
2220

2321
from common.djangoapps.student.models import (
2422
AccountRecovery,
@@ -104,10 +102,12 @@ def setUp(self):
104102
self.staff_user = UserFactory(is_staff=True, password=self.password)
105103
self.reset_tracker()
106104

107-
enterprise_patcher = patch('openedx.features.enterprise_support.api.enterprise_customer_for_request')
108-
enterprise_learner_patcher = enterprise_patcher.start()
109-
enterprise_learner_patcher.return_value = {}
110-
self.addCleanup(enterprise_learner_patcher.stop)
105+
filter_patcher = patch(
106+
'openedx.core.djangoapps.user_api.accounts.api.AccountSettingsReadOnlyFieldsRequested.run_filter',
107+
return_value=(set(), None),
108+
)
109+
filter_patcher.start()
110+
self.addCleanup(filter_patcher.stop)
111111

112112
def test_get_username_provided(self):
113113
"""Test the difference in behavior when a username is supplied to get_account_settings."""
@@ -248,73 +248,19 @@ def test_update_success_for_enterprise(self):
248248
account_settings = get_account_settings(self.default_request)[0]
249249
assert level_of_education == account_settings['level_of_education']
250250

251-
@patch('openedx.features.enterprise_support.api.enterprise_customer_for_request')
252-
@patch('openedx.features.enterprise_support.utils.third_party_auth.provider.Registry.get')
253-
@ddt.data(
254-
*itertools.product(
255-
# field_name_value values
256-
(("email", "new_email@example.com"), ("name", "new name"), ("country", "IN")),
257-
# is_enterprise_user
258-
(True, False),
259-
# is_synch_learner_profile_data
260-
(True, False),
261-
# has `UserSocialAuth` record
262-
(True, False),
263-
)
251+
@patch(
252+
'openedx.core.djangoapps.user_api.accounts.api.AccountSettingsReadOnlyFieldsRequested.run_filter',
253+
return_value=({'country'}, None),
264254
)
265-
@ddt.unpack
266-
def test_update_validation_error_for_enterprise(
267-
self,
268-
field_name_value,
269-
is_enterprise_user,
270-
is_synch_learner_profile_data,
271-
has_user_social_auth_record,
272-
mock_auth_provider,
273-
mock_customer,
274-
):
275-
idp_backend_name = 'tpa-saml'
276-
mock_customer.return_value = {}
277-
if is_enterprise_user:
278-
mock_customer.return_value.update({
279-
'uuid': 'real-ent-uuid',
280-
'name': 'Dummy Enterprise',
281-
'identity_provider': 'saml-ubc',
282-
'identity_providers': [
283-
{
284-
"provider_id": "saml-ubc",
285-
}
286-
],
287-
})
288-
mock_auth_provider.return_value.sync_learner_profile_data = is_synch_learner_profile_data
289-
mock_auth_provider.return_value.backend_name = idp_backend_name
290-
291-
update_data = {field_name_value[0]: field_name_value[1]}
292-
293-
user_fullname_editable = False
294-
if has_user_social_auth_record:
295-
UserSocialAuth.objects.create(
296-
provider=idp_backend_name,
297-
user=self.user
298-
)
299-
else:
300-
UserSocialAuth.objects.all().delete()
301-
# user's fullname is editable if no `UserSocialAuth` record exists
302-
user_fullname_editable = field_name_value[0] == 'name'
303-
304-
# prevent actual email change requests
305-
with patch('openedx.core.djangoapps.user_api.accounts.api.student_views.do_email_change_request'):
306-
# expect field un-editability only when all of the following conditions are met
307-
if is_enterprise_user and is_synch_learner_profile_data and not user_fullname_editable:
308-
with pytest.raises(AccountValidationError) as validation_error:
309-
update_account_settings(self.user, update_data)
310-
field_errors = validation_error.value.field_errors
311-
assert 'This field is not editable via this API' == \
312-
field_errors[field_name_value[0]]['developer_message']
313-
else:
314-
update_account_settings(self.user, update_data)
315-
account_settings = get_account_settings(self.default_request)[0]
316-
if field_name_value[0] != "email":
317-
assert field_name_value[1] == account_settings[field_name_value[0]]
255+
def test_readonly_field_from_filter_is_rejected(self, mock_run_filter): # pylint: disable=unused-argument
256+
"""
257+
When AccountSettingsReadOnlyFieldsRequested.run_filter returns a field as read-only,
258+
update_account_settings should raise AccountValidationError for that field.
259+
"""
260+
with pytest.raises(AccountValidationError) as exc_info:
261+
update_account_settings(self.user, {"country": "IN"})
262+
field_errors = exc_info.value.field_errors
263+
assert 'This field is not editable via this API' == field_errors['country']['developer_message']
318264

319265
def test_update_error_validating(self):
320266
"""Test that AccountValidationError is thrown if incorrect values are supplied."""

requirements/edx/base.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -830,7 +830,7 @@ openedx-events==10.5.0
830830
# edx-name-affirmation
831831
# event-tracking
832832
# ora2
833-
openedx-filters==2.1.0
833+
openedx-filters==3.1.0
834834
# via
835835
# -r requirements/edx/kernel.in
836836
# edx-enterprise

requirements/edx/development.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1379,7 +1379,7 @@ openedx-events==10.5.0
13791379
# edx-name-affirmation
13801380
# event-tracking
13811381
# ora2
1382-
openedx-filters==2.1.0
1382+
openedx-filters==3.1.0
13831383
# via
13841384
# -r requirements/edx/doc.txt
13851385
# -r requirements/edx/testing.txt

requirements/edx/doc.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1004,7 +1004,7 @@ openedx-events==10.5.0
10041004
# edx-name-affirmation
10051005
# event-tracking
10061006
# ora2
1007-
openedx-filters==2.1.0
1007+
openedx-filters==3.1.0
10081008
# via
10091009
# -r requirements/edx/base.txt
10101010
# edx-enterprise

requirements/edx/testing.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1049,7 +1049,7 @@ openedx-events==10.5.0
10491049
# edx-name-affirmation
10501050
# event-tracking
10511051
# ora2
1052-
openedx-filters==2.1.0
1052+
openedx-filters==3.1.0
10531053
# via
10541054
# -r requirements/edx/base.txt
10551055
# edx-enterprise

0 commit comments

Comments
 (0)