Skip to content

Commit 5da2567

Browse files
authored
feat: add AccountSettingsReadOnlyFieldsStep pipeline step
1 parent a53755d commit 5da2567

File tree

17 files changed

+323
-30
lines changed

17 files changed

+323
-30
lines changed

CHANGELOG.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ Unreleased
1717
----------
1818
* nothing unreleased
1919

20+
[6.8.4] - 2026-03-30
21+
---------------------
22+
* feat: add AccountSettingsEnterpriseReadOnlyFieldsStep pipeline step (ENT-11510)
23+
2024
[6.8.3] - 2026-03-27
2125
---------------------
2226
* fix: Move settings reads out of AppConfig, into consumers

enterprise/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
Your project description goes here.
33
"""
44

5-
__version__ = "6.8.3"
5+
__version__ = "6.8.4"

enterprise/filters/__init__.py

Whitespace-only changes.

enterprise/filters/accounts.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
"""
2+
Pipeline step for determining read-only account settings fields.
3+
"""
4+
from openedx_filters.filters import PipelineStep
5+
from social_django.models import UserSocialAuth
6+
7+
from django.conf import settings
8+
9+
try:
10+
from common.djangoapps import third_party_auth
11+
except ImportError:
12+
third_party_auth = None
13+
14+
from enterprise.models import EnterpriseCustomerIdentityProvider, EnterpriseCustomerUser
15+
16+
17+
class AccountSettingsEnterpriseReadOnlyFieldsStep(PipelineStep):
18+
"""
19+
Adds SSO-managed fields to the read-only account settings fields set.
20+
21+
This step is intended to be registered as a pipeline step for the
22+
``org.openedx.learning.account.settings.read_only_fields.requested.v1`` filter.
23+
24+
When a user is linked to an enterprise customer whose SSO identity provider has
25+
``sync_learner_profile_data`` enabled, the fields listed in
26+
``settings.ENTERPRISE_READONLY_ACCOUNT_FIELDS`` are added to ``readonly_fields``.
27+
The ``"name"`` field is only added when the user has an existing ``UserSocialAuth``
28+
record for the enterprise IdP backend.
29+
"""
30+
31+
def run_filter(self, readonly_fields, user): # pylint: disable=arguments-differ
32+
"""
33+
Add enterprise SSO-managed fields to the read-only fields set.
34+
35+
Arguments:
36+
readonly_fields (set): current set of read-only account field names.
37+
user (User): the Django User whose account settings are being updated.
38+
39+
Returns:
40+
dict: updated pipeline data with ``readonly_fields`` key.
41+
"""
42+
enterprise_customer_user = (
43+
EnterpriseCustomerUser.objects.filter(user_id=user.id)
44+
.order_by('-active', '-modified')
45+
.select_related('enterprise_customer')
46+
.first()
47+
)
48+
if not enterprise_customer_user:
49+
return {"readonly_fields": readonly_fields, "user": user}
50+
51+
enterprise_customer = enterprise_customer_user.enterprise_customer
52+
53+
try:
54+
idp_record = EnterpriseCustomerIdentityProvider.objects.get(
55+
enterprise_customer=enterprise_customer
56+
)
57+
except EnterpriseCustomerIdentityProvider.DoesNotExist:
58+
return {"readonly_fields": readonly_fields, "user": user}
59+
60+
identity_provider = third_party_auth.provider.Registry.get(
61+
provider_id=idp_record.provider_id
62+
)
63+
64+
if not identity_provider or not getattr(identity_provider, 'sync_learner_profile_data', False):
65+
return {"readonly_fields": readonly_fields, "user": user}
66+
67+
enterprise_readonly = set(getattr(settings, 'ENTERPRISE_READONLY_ACCOUNT_FIELDS', []))
68+
69+
if 'name' in enterprise_readonly:
70+
backend_name = getattr(identity_provider, 'backend_name', None)
71+
has_social_auth = (
72+
backend_name
73+
and UserSocialAuth.objects.filter(provider=backend_name, user=user).exists()
74+
)
75+
if not has_social_auth:
76+
enterprise_readonly = enterprise_readonly - {'name'}
77+
78+
return {"readonly_fields": readonly_fields | enterprise_readonly, "user": user}

enterprise/management/commands/update_enterprise_social_auth_uids.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,12 @@
55
import csv
66
import logging
77

8+
from social_django.models import UserSocialAuth
9+
810
from django.core.exceptions import ValidationError
911
from django.core.management.base import BaseCommand
1012
from django.db import transaction
1113

12-
try:
13-
from social_django.models import UserSocialAuth
14-
except ImportError:
15-
UserSocialAuth = None
16-
1714
logger = logging.getLogger(__name__)
1815

1916

enterprise/tpa_pipeline.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,14 @@
66
from datetime import datetime
77
from logging import getLogger
88

9+
from social_core.pipeline.partial import partial
10+
from social_django.models import UserSocialAuth
11+
912
from django.urls import reverse
1013

1114
from enterprise.models import EnterpriseCustomer, EnterpriseCustomerUser
1215
from enterprise.utils import get_identity_provider, get_social_auth_from_idp
1316

14-
try:
15-
from social_core.pipeline.partial import partial
16-
except ImportError:
17-
from enterprise.decorators import null_decorator as partial
18-
19-
try:
20-
from social_django.models import UserSocialAuth
21-
except ImportError:
22-
UserSocialAuth = None
23-
2417
try:
2518
from common.djangoapps.third_party_auth.provider import Registry
2619
except ImportError:

enterprise/utils.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from edx_django_utils.cache import TieredCache
1818
from edx_django_utils.cache import get_cache_key as get_django_cache_key
1919
from slumber.exceptions import HttpClientError
20+
from social_django.models import UserSocialAuth
2021

2122
from django.apps import apps
2223
from django.conf import settings
@@ -88,11 +89,6 @@
8889
except ImportError:
8990
get_url = None
9091

91-
try:
92-
from social_django.models import UserSocialAuth
93-
except ImportError:
94-
UserSocialAuth = None
95-
9692
# Only create manual enrollments if running in edx-platform
9793
try:
9894
from common.djangoapps.student.api import (

requirements/base.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ jsondiff
3232
jsonfield
3333
openedx-atlas
3434
openedx-events
35+
openedx-filters
3536
paramiko
3637
path.py
3738
pillow

requirements/dev.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ isort # to standardize order of imports
1111
pip-tools # Requirements file management
1212
pycodestyle # PEP 8 compliance validation
1313
pydocstyle # PEP 257 compliance validation
14+
social-auth-app-django # adding explicit requirement to prevent defensive imports
1415
testfixtures # Mock objects for unit tests and doc tests
1516
tox # virtualenv management for tests
1617
twine==1.11.0 # Utility for PyPI package uploads

requirements/dev.txt

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# This file is autogenerated by pip-compile with Python 3.12
33
# by the following command:
44
#
5-
# make upgrade
5+
# pip-compile --output-file=requirements/dev.txt requirements/dev.in
66
#
77
accessible-pygments==0.0.5
88
# via
@@ -204,6 +204,8 @@ defusedxml==0.7.1
204204
# -r requirements/test-master.txt
205205
# -r requirements/test.txt
206206
# djangorestframework-xml
207+
# python3-openid
208+
# social-auth-core
207209
diff-cover==10.2.0
208210
# via -r requirements/test.txt
209211
dill==0.4.1
@@ -239,6 +241,8 @@ django==5.2.12
239241
# edx-toggles
240242
# jsonfield
241243
# openedx-events
244+
# openedx-filters
245+
# social-auth-app-django
242246
django-cache-memoize==0.2.1
243247
# via
244248
# -r requirements/doc.txt
@@ -413,6 +417,7 @@ edx-opaque-keys[django]==3.1.0
413417
# edx-ccx-keys
414418
# edx-drf-extensions
415419
# openedx-events
420+
# openedx-filters
416421
edx-rbac==3.0.0
417422
# via
418423
# -r requirements/doc.txt
@@ -671,6 +676,8 @@ oauthlib==3.3.1
671676
# -r requirements/test-master.txt
672677
# -r requirements/test.txt
673678
# django-oauth-toolkit
679+
# requests-oauthlib
680+
# social-auth-core
674681
openedx-atlas==0.7.0
675682
# via
676683
# -r requirements/doc.txt
@@ -681,6 +688,11 @@ openedx-events==11.1.0
681688
# -r requirements/doc.txt
682689
# -r requirements/test-master.txt
683690
# -r requirements/test.txt
691+
openedx-filters==2.1.0
692+
# via
693+
# -r requirements/doc.txt
694+
# -r requirements/test-master.txt
695+
# -r requirements/test.txt
684696
packaging==26.0
685697
# via
686698
# -r requirements/doc.txt
@@ -823,6 +835,7 @@ pyjwt[crypto]==2.12.1
823835
# edx-rest-api-client
824836
# firebase-admin
825837
# snowflake-connector-python
838+
# social-auth-core
826839
pylint==3.3.9
827840
# via
828841
# -c requirements/constraints.txt
@@ -898,6 +911,10 @@ python-slugify==8.0.4
898911
# -r requirements/test-master.txt
899912
# -r requirements/test.txt
900913
# code-annotations
914+
python3-openid==3.2.0
915+
# via
916+
# -r requirements/test.txt
917+
# social-auth-core
901918
pytz==2026.1.post1
902919
# via
903920
# -r requirements/doc.txt
@@ -930,13 +947,19 @@ requests==2.32.5
930947
# edx-rest-api-client
931948
# google-api-core
932949
# google-cloud-storage
950+
# requests-oauthlib
933951
# requests-toolbelt
934952
# responses
935953
# sailthru-client
936954
# slumber
937955
# snowflake-connector-python
956+
# social-auth-core
938957
# sphinx
939958
# twine
959+
requests-oauthlib==2.0.0
960+
# via
961+
# -r requirements/test.txt
962+
# social-auth-core
940963
requests-toolbelt==1.0.0
941964
# via twine
942965
responses==0.26.0
@@ -1007,6 +1030,12 @@ snowflake-connector-python==4.3.0
10071030
# -r requirements/doc.txt
10081031
# -r requirements/test-master.txt
10091032
# -r requirements/test.txt
1033+
social-auth-app-django==5.7.0
1034+
# via -r requirements/test.txt
1035+
social-auth-core==4.8.5
1036+
# via
1037+
# -r requirements/test.txt
1038+
# social-auth-app-django
10101039
sortedcontainers==2.4.0
10111040
# via
10121041
# -r requirements/doc.txt

0 commit comments

Comments
 (0)