33"""
44
55import copy
6+ import logging
67import re
78from importlib import import_module
89
1819from eventtracking import tracker
1920
2021from common .djangoapps import third_party_auth
22+ from common .djangoapps .third_party_auth .models import SAMLProviderConfig
2123from common .djangoapps .edxmako .shortcuts import marketing_link
2224from common .djangoapps .student .models import CourseEnrollmentAllowed , UserProfile , email_exists_or_retired
2325from common .djangoapps .util .password_policy_validators import (
3638from openedx .features .enterprise_support .api import enterprise_customer_for_request
3739
3840
41+ log = logging .getLogger (__name__ )
42+
43+
3944class TrueCheckbox (widgets .CheckboxInput ):
4045 """
4146 A checkbox widget that only accepts "true" (case-insensitive) as true.
@@ -410,6 +415,7 @@ def __init__(self):
410415 field_order .extend (sorted (difference ))
411416
412417 self .field_order = field_order
418+ self .request = None # Will be set by get_registration_form
413419
414420 def get_registration_form (self , request ):
415421 """Return a description of the registration form.
@@ -426,6 +432,7 @@ def get_registration_form(self, request):
426432 Returns:
427433 HttpResponse
428434 """
435+ self .request = request
429436 form_desc = FormDescription ("post" , self ._get_registration_submit_url (request ))
430437 self ._apply_third_party_auth_overrides (request , form_desc )
431438
@@ -703,13 +710,63 @@ def _add_marketing_emails_opt_in_field(self, form_desc, required=False):
703710 platform_name = configuration_helpers .get_value ('PLATFORM_NAME' , settings .PLATFORM_NAME ),
704711 )
705712
713+ # Check if marketing opt-in should be optional for this SAML provider
714+ # If the SAML provider config says the field is optional, set default to False
715+ # and clear any existing field overrides that may have been set by the TPA provider
716+ default_value = True # Default: checkbox is checked
717+ field_required = required
718+
719+ if self .request and third_party_auth .is_enabled ():
720+ running_pipeline = third_party_auth .pipeline .get (self .request )
721+ if running_pipeline :
722+ try :
723+ from common .djangoapps .third_party_auth .models import SAMLProviderConfig
724+ # idp_name can be in kwargs directly or in kwargs['details']
725+ saml_provider_name = running_pipeline .get ('kwargs' , {}).get ('idp_name' )
726+ if not saml_provider_name :
727+ saml_provider_name = running_pipeline .get ('kwargs' , {}).get ('details' , {}).get ('idp_name' )
728+
729+ if saml_provider_name :
730+ try :
731+ # Query the SAML provider config
732+ saml_config = SAMLProviderConfig .objects .get (
733+ slug = saml_provider_name
734+ )
735+
736+ if saml_config .marketing_emails_opt_in_optional :
737+ log .info (
738+ "SAML provider %s has marketing_emails_opt_in_optional=True, "
739+ "setting default to False and required to False" ,
740+ saml_provider_name
741+ )
742+ default_value = False # When optional, user opts out by default
743+ field_required = False # Make field optional
744+
745+ # Set field override to ensure our SAML-specific values are used
746+ # This will override any values set by the TPA provider, or create
747+ # a new override if one doesn't exist
748+ log .info (
749+ "Setting field override for marketing_emails_opt_in to defaultValue=False, required=False"
750+ )
751+ form_desc ._field_overrides ['marketing_emails_opt_in' ] = {
752+ 'defaultValue' : False ,
753+ 'required' : False
754+ }
755+ except SAMLProviderConfig .DoesNotExist :
756+ log .debug (
757+ "SAML provider config not found for idp_name: %s" ,
758+ saml_provider_name
759+ )
760+ except (ImportError , Exception ) as e : # pylint: disable=broad-except
761+ log .debug ("Error checking SAML provider config in field handler: %s" , str (e ))
762+
706763 form_desc .add_field (
707764 'marketing_emails_opt_in' ,
708765 label = opt_in_label ,
709766 field_type = "checkbox" ,
710767 exposed = True ,
711- default = True , # the checkbox will automatically be checked; meaning user has opted in
712- required = required ,
768+ default = default_value ,
769+ required = field_required ,
713770 )
714771
715772 def _add_field_with_configurable_select_options (self , field_name , field_label , form_desc , required = False ):
@@ -1150,22 +1207,73 @@ def _apply_third_party_auth_overrides(self, request, form_desc):
11501207
11511208 for field_name in self .DEFAULT_FIELDS + self .EXTRA_FIELDS :
11521209 if field_name in field_overrides :
1153- form_desc .override_field_properties (
1154- field_name , default = field_overrides [field_name ]
1155- )
1156-
1157- if (
1158- field_name not in ['terms_of_service' , 'honor_code' ] and
1159- field_overrides [field_name ] and
1160- hide_registration_fields_except_tos
1161- ):
1210+ # Special handling for marketing_emails_opt_in:
1211+ # If SAML provider config has set marketing_emails_opt_in_optional=True,
1212+ # don't let the provider's get_register_form_data override the default
1213+ skip_override = False
1214+ if field_name == 'marketing_emails_opt_in' :
1215+ try :
1216+ from common .djangoapps .third_party_auth .models import SAMLProviderConfig
1217+ # idp_name can be in kwargs directly or in kwargs['details']
1218+ saml_provider_name = running_pipeline .get ('kwargs' , {}).get ('idp_name' )
1219+ if not saml_provider_name :
1220+ saml_provider_name = running_pipeline .get ('kwargs' , {}).get ('details' , {}).get ('idp_name' )
1221+ if saml_provider_name :
1222+ try :
1223+ # Try to find the SAML provider config
1224+ # First try with current_set(), then fall back to direct query
1225+ try :
1226+ saml_config = SAMLProviderConfig .objects .current_set ().get (
1227+ slug = saml_provider_name
1228+ )
1229+ except Exception : # pylint: disable=broad-except
1230+ # Fallback to direct query without current_set()
1231+ saml_config = SAMLProviderConfig .objects .get (
1232+ slug = saml_provider_name
1233+ )
1234+
1235+ if saml_config .marketing_emails_opt_in_optional :
1236+ log .debug (
1237+ "Skipping provider override for marketing_emails_opt_in "
1238+ "due to SAML config for provider: %s" ,
1239+ saml_provider_name
1240+ )
1241+ skip_override = True
1242+ except SAMLProviderConfig .DoesNotExist :
1243+ log .debug (
1244+ "SAML provider config not found for idp_name: %s" ,
1245+ saml_provider_name
1246+ )
1247+ except (ImportError , Exception ) as e : # pylint: disable=broad-except
1248+ log .debug ("Error checking SAML provider config: %s" , str (e ))
1249+
1250+ if not skip_override :
11621251 form_desc .override_field_properties (
1163- field_name ,
1164- field_type = "hidden" ,
1165- label = "" ,
1166- instructions = "" ,
1252+ field_name , default = field_overrides [field_name ]
11671253 )
11681254
1255+ if (
1256+ field_name not in ['terms_of_service' , 'honor_code' ] and
1257+ field_overrides [field_name ] and
1258+ hide_registration_fields_except_tos
1259+ ):
1260+ # When hiding a field, set default to False for checkbox fields
1261+ # like marketing_emails_opt_in to avoid auto-opting users in
1262+ field_default = field_overrides [field_name ]
1263+ if field_name == 'marketing_emails_opt_in' :
1264+ field_default = False
1265+ log .info (
1266+ "Hiding marketing_emails_opt_in field and setting default to False"
1267+ )
1268+
1269+ form_desc .override_field_properties (
1270+ field_name ,
1271+ field_type = "hidden" ,
1272+ default = field_default ,
1273+ label = "" ,
1274+ instructions = "" ,
1275+ )
1276+
11691277 # Hide the confirm_email field
11701278 form_desc .override_field_properties (
11711279 "confirm_email" ,
@@ -1195,3 +1303,5 @@ def _apply_third_party_auth_overrides(self, request, form_desc):
11951303 default = current_provider .name if current_provider .name else "Third Party" ,
11961304 required = False ,
11971305 )
1306+
1307+
0 commit comments