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,65 @@ 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+ # pylint: disable=too-many-nested-blocks
720+ if self .request and third_party_auth .is_enabled ():
721+ running_pipeline = third_party_auth .pipeline .get (self .request )
722+ if running_pipeline :
723+ try :
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 "
750+ "defaultValue=False, required=False"
751+ )
752+ # pylint: disable=protected-access
753+ form_desc ._field_overrides ['marketing_emails_opt_in' ] = {
754+ 'defaultValue' : False ,
755+ 'required' : False
756+ }
757+ except SAMLProviderConfig .DoesNotExist :
758+ log .exception (
759+ "SAML provider config not found for idp_name: %s" ,
760+ saml_provider_name
761+ )
762+ except Exception as exc : # pylint: disable=broad-except
763+ log .exception ("Error checking SAML provider config in field handler: %s" , str (exc ))
764+
706765 form_desc .add_field (
707766 'marketing_emails_opt_in' ,
708767 label = opt_in_label ,
709768 field_type = "checkbox" ,
710769 exposed = True ,
711- default = True , # the checkbox will automatically be checked; meaning user has opted in
712- required = required ,
770+ default = default_value ,
771+ required = field_required ,
713772 )
714773
715774 def _add_field_with_configurable_select_options (self , field_name , field_label , form_desc , required = False ):
@@ -1150,22 +1209,76 @@ def _apply_third_party_auth_overrides(self, request, form_desc):
11501209
11511210 for field_name in self .DEFAULT_FIELDS + self .EXTRA_FIELDS :
11521211 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- ):
1212+ # Special handling for marketing_emails_opt_in:
1213+ # If SAML provider config has set marketing_emails_opt_in_optional=True,
1214+ # don't let the provider's get_register_form_data override the default
1215+ skip_override = False
1216+ if field_name == 'marketing_emails_opt_in' :
1217+ try :
1218+ # idp_name can be in kwargs directly or in kwargs['details']
1219+ saml_provider_name = running_pipeline .get ('kwargs' , {}).get ('idp_name' )
1220+ if not saml_provider_name :
1221+ saml_provider_name = (
1222+ running_pipeline .get ('kwargs' , {})
1223+ .get ('details' , {})
1224+ .get ('idp_name' )
1225+ )
1226+ if saml_provider_name :
1227+ try :
1228+ # Try to find the SAML provider config
1229+ # First try with current_set(), then fall back to direct query
1230+ try :
1231+ saml_config = SAMLProviderConfig .objects .current_set ().get (
1232+ slug = saml_provider_name
1233+ )
1234+ except SAMLProviderConfig .DoesNotExist :
1235+ # Fallback to direct query without current_set()
1236+ saml_config = SAMLProviderConfig .objects .get (
1237+ slug = saml_provider_name
1238+ )
1239+
1240+ if saml_config .marketing_emails_opt_in_optional :
1241+ log .debug (
1242+ "Skipping provider override for marketing_emails_opt_in "
1243+ "due to SAML config for provider: %s" ,
1244+ saml_provider_name
1245+ )
1246+ skip_override = True
1247+ except SAMLProviderConfig .DoesNotExist :
1248+ log .exception (
1249+ "SAML provider config not found for idp_name: %s" ,
1250+ saml_provider_name
1251+ )
1252+ except Exception as exc : # pylint: disable=broad-except
1253+ log .exception ("Error checking SAML provider config: %s" , str (exc ))
1254+
1255+ if not skip_override :
11621256 form_desc .override_field_properties (
1163- field_name ,
1164- field_type = "hidden" ,
1165- label = "" ,
1166- instructions = "" ,
1257+ field_name , default = field_overrides [field_name ]
11671258 )
11681259
1260+ if (
1261+ field_name not in ['terms_of_service' , 'honor_code' ] and
1262+ field_overrides [field_name ] and
1263+ hide_registration_fields_except_tos
1264+ ):
1265+ # When hiding a field, set default to False for checkbox fields
1266+ # like marketing_emails_opt_in to avoid auto-opting users in
1267+ field_default = field_overrides [field_name ]
1268+ if field_name == 'marketing_emails_opt_in' :
1269+ field_default = False
1270+ log .info (
1271+ "Hiding marketing_emails_opt_in field and setting default to False"
1272+ )
1273+
1274+ form_desc .override_field_properties (
1275+ field_name ,
1276+ field_type = "hidden" ,
1277+ default = field_default ,
1278+ label = "" ,
1279+ instructions = "" ,
1280+ )
1281+
11691282 # Hide the confirm_email field
11701283 form_desc .override_field_properties (
11711284 "confirm_email" ,
0 commit comments