44"""
55
66import datetime
7- import itertools
87import unicodedata
98from unittest .mock import Mock , patch
109
1413from django .contrib .auth .hashers import make_password
1514from django .contrib .auth .models import User # lint-amnesty, pylint: disable=imported-auth-user
1615from django .http import HttpResponse
17- from django .test import TestCase
16+ from django .test import TestCase , override_settings
1817from django .test .client import RequestFactory
1918from django .urls import reverse
2019from pytz import UTC
21- from social_django .models import UserSocialAuth
2220
2321from common .djangoapps .student .models import (
2422 AccountRecovery ,
5149)
5250from openedx .core .djangolib .testing .utils import skip_unless_lms
5351from openedx .features .enterprise_support .tests .factories import EnterpriseCustomerUserFactory
52+ from openedx_filters import PipelineStep
5453
5554
5655def mock_render_to_string (template_name , context ):
@@ -67,6 +66,18 @@ def mock_render_to_response(template_name):
6766 return HttpResponse (template_name )
6867
6968
69+ class TestAccountSettingsReadOnlyFieldsPipelineStep (PipelineStep ):
70+ """
71+ Test pipeline step for AccountSettingsReadOnlyFieldsRequested.
72+ """
73+
74+ def run_filter (self , user , readonly_fields , ** kwargs ): # pylint: disable=arguments-differ
75+ return {
76+ "readonly_fields" : {"name" },
77+ "updated_account_settings" : None ,
78+ }
79+
80+
7081class CreateAccountMixin : # lint-amnesty, pylint: disable=missing-class-docstring
7182 def create_account (self , username , password , email ):
7283 # pylint: disable=missing-docstring
@@ -104,11 +115,6 @@ def setUp(self):
104115 self .staff_user = UserFactory (is_staff = True , password = self .password )
105116 self .reset_tracker ()
106117
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 )
111-
112118 def test_get_username_provided (self ):
113119 """Test the difference in behavior when a username is supplied to get_account_settings."""
114120 account_settings = get_account_settings (self .default_request )[0 ]
@@ -248,73 +254,26 @@ def test_update_success_for_enterprise(self):
248254 account_settings = get_account_settings (self .default_request )[0 ]
249255 assert level_of_education == account_settings ['level_of_education' ]
250256
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- )
264- )
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- }
257+ @override_settings (
258+ OPEN_EDX_FILTERS_CONFIG = {
259+ "org.openedx.learning.account.settings.read_only_fields.requested.v1" : {
260+ "pipeline" : [
261+ "openedx.core.djangoapps.user_api.accounts.tests.test_api.TestAccountSettingsReadOnlyFieldsPipelineStep" ,
286262 ],
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 ]}
263+ "fail_silently" : False ,
264+ },
265+ },
266+ )
267+ def test_update_name_blocked_by_account_settings_read_only_fields_filter (self ):
268+ """
269+ Test that update_account_settings honors the configured read-only-fields filter.
270+ """
271+ with pytest .raises (AccountValidationError ) as context_manager :
272+ update_account_settings (self .user , {"name" : "Blocked Name" })
292273
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 ]]
274+ field_errors = context_manager .value .field_errors
275+ assert "name" in field_errors
276+ assert field_errors ["name" ]["developer_message" ] == "This field is not editable via this API"
318277
319278 def test_update_error_validating (self ):
320279 """Test that AccountValidationError is thrown if incorrect values are supplied."""
@@ -472,17 +431,12 @@ def test_email_changes_disabled(self):
472431 assert 'Email address changes have been disabled' in context_manager .value .developer_message
473432
474433 @patch .dict (settings .FEATURES , dict (ALLOW_EMAIL_ADDRESS_CHANGE = True ))
475- def test_email_changes_blocked_by_saml_provider (self ):
434+ @patch ('openedx.core.djangoapps.user_api.accounts.api.get_saml_provider_for_user' )
435+ def test_email_changes_blocked_by_saml_provider (self , mock_get_saml_provider ):
476436 """
477437 Test that email changes are rejected when the user's SAML provider has disable_email_editing=True.
478438 """
479- from common .djangoapps .third_party_auth .tests .factories import SAMLProviderConfigFactory
480- saml_config = SAMLProviderConfigFactory (disable_email_editing = True )
481- UserSocialAuth .objects .create (
482- user = self .user ,
483- provider = 'tpa-saml' ,
484- uid = f'{ saml_config .slug } :remote-user-id' ,
485- )
439+ mock_get_saml_provider .return_value = Mock (disable_email_editing = True )
486440
487441 with pytest .raises (AccountValidationError ) as context_manager :
488442 update_account_settings (self .user , {"email" : "new@example.com" })
0 commit comments