Merge pull request #35779 from openedx/bmtcril/pii_safelist_update

chore: Add missing PII annotations, update safelist
This commit is contained in:
Brian Mesick
2024-11-06 16:27:09 +00:00
committed by GitHub
21 changed files with 188 additions and 10 deletions

View File

@@ -9,13 +9,13 @@
# Via Django
auth.Group:
".. no_pii:" : "No PII"
".. no_pii:": "No PII"
auth.Permission:
".. no_pii:" : "No PII"
".. no_pii:": "No PII"
auth.User:
".. pii:": "Contains username, password, and email address, retired in AccountRetirementView"
".. pii_types:" : username, email_address, password
".. pii_retirement:" : local_api
".. pii_types:": username, email_address, password
".. pii_retirement:": local_api
contenttypes.ContentType:
".. no_pii:": "No PII"
admin.LogEntry:
@@ -27,6 +27,66 @@ sessions.Session:
sites.Site:
".. no_pii:": "No PII"
# Automatically generated edx-platform models that can't be annotated
calendar_sync.HistoricalUserCalendarSyncConfig:
".. no_pii:": "No PII"
certificates.HistoricalCertificateAllowlist:
".. no_pii:": "No PII"
certificates.HistoricalCertificateDateOverride:
".. no_pii:": "No PII"
certificates.HistoricalCertificateInvalidation:
".. no_pii:": "No PII"
certificates.HistoricalGeneratedCertificate:
".. pii:": "PII can exist in the generated certificate linked to in this model. Certificate data is currently retained."
".. pii_types:": "name, username"
".. pii_retirement:": "retained"
course_apps.HistoricalCourseAppStatus:
".. no_pii:": "No PII"
course_goals.HistoricalCourseGoal:
".. no_pii:": "No PII"
course_live.HistoricalCourseLiveConfiguration:
".. no_pii:": "No PII"
course_modes.HistoricalCourseMode:
".. no_pii:": "No PII"
course_overviews.HistoricalCourseOverview:
".. no_pii:": "No PII"
discussions.HistoricalDiscussionsConfiguration:
".. no_pii:": "No PII"
entitlements.HistoricalCourseEntitlement:
".. no_pii:": "No PII"
entitlements.HistoricalCourseEntitlementSupportDetail:
".. no_pii:": "No PII"
experiments.HistoricalExperimentKeyValue:
".. no_pii:": "No PII"
external_user_ids.HistoricalExternalId:
".. no_pii:": "We store external_user_id here, but do not consider that PII under OEP-30."
external_user_ids.HistoricalExternalIdType:
".. no_pii:": "No PII"
grades.HistoricalPersistentSubsectionGradeOverride:
".. no_pii:": "No PII"
instructor_task.HistoricalInstructorTaskSchedule:
".. no_pii:": "No PII"
program_enrollments.HistoricalProgramCourseEnrollment:
".. no_pii:": "No PII"
program_enrollments.HistoricalProgramEnrollment:
".. pii:": "PII is found in the external key for a program enrollment"
".. pii_types:": "other"
".. pii_retirement:": "local_api"
programs.HistoricalProgramDiscussionsConfiguration:
".. no_pii:": "No PII"
programs.HistoricalProgramLiveConfiguration:
".. no_pii:": "No PII"
schedules.HistoricalSchedule:
".. no_pii:": "No PII"
split_modulestore_django.HistoricalSplitModulestoreCourseIndex:
".. no_pii:": "No PII"
student.HistoricalCourseEnrollment:
".. no_pii:": "No PII"
student.HistoricalManualEnrollmentAudit:
".. pii:": "Contains enrolled_email, retired in LMSAccountRetirementView"
".. pii_types:": "email_address"
".. pii_retirement:": "local_api"
# Automatically generated models in edx-enterprise that can't be annotated there
consent.HistoricalDataSharingConsent:
".. pii:": "The username field inherited from Consent contains PII."
@@ -45,7 +105,7 @@ enterprise.HistoricalEnterpriseCustomerCatalog:
enterprise.HistoricalEnterpriseCustomerEntitlement:
".. no_pii:": "No PII"
# Via ORA2
# Via edx-ora2, these can be removed once the models are annotated for real
assessment.Assessment:
".. no_pii:": "No PII"
assessment.AssessmentFeedback:
@@ -127,10 +187,24 @@ djcelery.TaskState:
djcelery.WorkerState:
".. no_pii:": "No PII"
# Via django-celery-results
django_celery_results.ChordCounter:
".. no_pii:": "No PII"
django_celery_results.GroupResult:
".. no_pii:": "No PII"
django_celery_results.TaskResult:
".. no_pii:": "No PII"
# Via edx-oauth2-provider https://github.com/edx/edx-oauth2-provider
edx_oauth2_provider.TrustedClient:
".. no_pii:": "No PII"
# Via edx-name-affirmation, not part of the openedx org
edx_name_affirmation.HistoricalVerifiedName:
".. pii:": "Contains name fields."
".. pii_types:": "name"
".. pii_retirement:": "local_api"
# Via VAL
edxval.CourseVideo:
".. no_pii:": "No PII"
@@ -149,6 +223,12 @@ edxval.VideoImage:
edxval.VideoTranscript:
".. no_pii:": "No PII"
# Via PyLTI1p3
lti1p3_tool_config.LtiTool:
".. no_pii:": "No PII"
lti1p3_tool_config.LtiToolKey:
".. no_pii:": "No PII"
# Via Milestones
milestones.CourseContentMilestone:
".. no_pii:": "No PII"
@@ -190,6 +270,10 @@ oauth2_provider.Grant:
".. pii:": "Contains 3rd party authentication secrets. Retired in DeactivateLogoutView."
".. pii_types:": password, other
".. pii_retirement:": local_api
oauth2_provider.IDToken:
".. pii:": "Contains 3rd party authentication secrets, currently this is retained until the token times out, but should be retired explicitly with the other models from this package."
".. pii_types:": password, other
".. pii_retirement:": retained
oauth2_provider.RefreshToken:
".. pii:": "Contains 3rd party authentication secrets. Retired in DeactivateLogoutView."
".. pii_types:": password, other
@@ -250,6 +334,8 @@ submissions.StudentItem:
".. no_pii:": "No PII"
submissions.Submission:
".. no_pii:": "No PII"
submissions.TeamSubmission:
".. no_pii:": "No PII"
# Via sorl-thumbnail https://github.com/jazzband/sorl-thumbnail
thumbnail.KVStore:

View File

@@ -1750,7 +1750,7 @@ class EnrollmentRefundConfiguration(ConfigurationModel):
class BulkUnenrollConfiguration(ConfigurationModel): # lint-amnesty, pylint: disable=empty-docstring
"""
.. no_pii:
"""
csv_file = models.FileField(
validators=[FileExtensionValidator(allowed_extensions=['csv'])],
@@ -1763,6 +1763,8 @@ class BulkUnenrollConfiguration(ConfigurationModel): # lint-amnesty, pylint: di
class BulkChangeEnrollmentConfiguration(ConfigurationModel):
"""
config model for the bulk_change_enrollment_csv command
.. no_pii:
"""
csv_file = models.FileField(
validators=[FileExtensionValidator(allowed_extensions=['csv'])],

View File

@@ -1685,6 +1685,8 @@ class AllowedAuthUser(TimeStampedModel):
class AccountRecoveryConfiguration(ConfigurationModel):
"""
configuration model for recover account management command
.. no_pii:
"""
csv_file = models.FileField(
validators=[FileExtensionValidator(allowed_extensions=['csv'])],
@@ -1824,6 +1826,8 @@ class UserCelebration(TimeStampedModel):
class UserPasswordToggleHistory(TimeStampedModel):
"""
Keeps track of user password disable/enable history
.. no_pii:
"""
user = models.ForeignKey(User, related_name='password_toggle_history', on_delete=models.CASCADE)
comment = models.CharField(max_length=255, help_text=_("Add a reason"), blank=True, null=True)

View File

@@ -79,6 +79,8 @@ class CourseGoalReminderStatus(TimeStampedModel):
Tracks whether we've sent a reminder about a particular goal this week.
See the management command goal_reminder_email for more detail about how this is used.
.. no_pii:
"""
class Meta:
verbose_name_plural = "Course goal reminder statuses"

View File

@@ -11,6 +11,8 @@ from openedx.core.djangoapps.config_model_utils.models import StackedConfigurati
class DisableProgressPageStackedConfig(StackedConfigurationModel):
"""
Stacked Config Model for disabling the frontend-app-learning progress page
.. no_pii:
"""
STACKABLE_FIELDS = ('disabled',)

View File

@@ -545,6 +545,8 @@ class LastSeenCoursewareTimezone(models.Model):
class FinancialAssistanceConfiguration(ConfigurationModel):
"""
Manages configuration for connecting to Financial Assistance backend service and using its API.
.. no_pii:
"""
api_base_url = models.URLField(

View File

@@ -37,6 +37,7 @@ class ExperimentKeyValue(TimeStampedModel):
"""
ExperimentData stores any generic key-value associated with experiments
identified by experiment_id.
.. no_pii:
"""
experiment_id = models.PositiveSmallIntegerField(

View File

@@ -21,6 +21,8 @@ register(UserSocialAuth, app=__package__)
class CourseResetCourseOptIn(TimeStampedModel):
"""
Model that represents a course which has opted in to the course reset feature.
.. no_pii:
"""
course_id = CourseKeyField(max_length=255, unique=True)
active = BooleanField()
@@ -40,6 +42,8 @@ class CourseResetCourseOptIn(TimeStampedModel):
class CourseResetAudit(TimeStampedModel):
"""
Model which records the course reset action's status and metadata
.. no_pii:
"""
class CourseResetStatus(TextChoices):
IN_PROGRESS = "in_progress"

View File

@@ -30,6 +30,8 @@ class UserTour(models.Model):
class UserDiscussionsTours(models.Model):
"""
Model to track which discussions tours a user has seen.
.. no_pii:
"""
tour_name = models.CharField(max_length=255)
show_tour = models.BooleanField(default=True)

View File

@@ -1177,8 +1177,10 @@ class VerificationDeadline(TimeStampedModel):
class SSPVerificationRetryConfig(ConfigurationModel): # pylint: disable=model-missing-unicode, useless-suppression
"""
SSPVerificationRetryConfig used to inject arguments
to retry_failed_photo_verifications management command
SSPVerificationRetryConfig used to inject arguments
to retry_failed_photo_verifications management command
.. no_pii:
"""
class Meta:
@@ -1201,6 +1203,10 @@ class VerificationAttempt(StatusModel):
Plugins that implement forms of IDV can store information about IDV attempts in this model for use across
the platform.
.. pii: Contains the name of the user
.. pii_types: name
.. pii_retirement: local_api
"""
user = models.ForeignKey(User, db_index=True, on_delete=models.CASCADE)
name = models.CharField(blank=True, max_length=255)

View File

@@ -27,6 +27,8 @@ class IntegritySignature(TimeStampedModel):
class LTIPIITool(TimeStampedModel):
"""
This model stores the relationship between a course and the LTI tools in the course that share PII.
.. no_pii:
"""
course_key = CourseKeyField(max_length=255, unique=True, db_index=True)
lti_tools = models.JSONField()
@@ -39,6 +41,8 @@ class LTIPIITool(TimeStampedModel):
class LTIPIISignature(TimeStampedModel):
"""
This model stores a user's acknowledgement to share PII via LTI tools in a particular course.
.. no_pii:
"""
user = models.ForeignKey(User, db_index=True, on_delete=models.CASCADE)
course_key = CourseKeyField(max_length=255, db_index=True)
@@ -57,6 +61,8 @@ class LTIPIISignature(TimeStampedModel):
class ProctoringPIISignature(TimeStampedModel):
"""
This model stores a user's acknowledgment to share PII via proctoring in a particular course.
.. no_pii:
"""
user = models.ForeignKey(User, db_index=True, on_delete=models.CASCADE)
course_key = CourseKeyField(max_length=255, db_index=True)

View File

@@ -53,6 +53,8 @@ class LearningContext(TimeStampedModel):
because this table can contain things that are not courses.
It is okay to make a foreign key against this table.
.. no_pii:
"""
id = models.BigAutoField(primary_key=True)
context_key = LearningContextKeyField(
@@ -74,6 +76,8 @@ class LearningContext(TimeStampedModel):
class CourseContext(TimeStampedModel):
"""
A model containing course specific information e.g course_visibility
.. no_pii:
"""
learning_context = models.OneToOneField(
LearningContext, on_delete=models.CASCADE, primary_key=True, related_name="course_context"
@@ -106,6 +110,8 @@ class LearningSequence(TimeStampedModel):
CourseSectionSequence.
It is okay to make a foreign key against this table.
.. no_pii:
"""
id = models.BigAutoField(primary_key=True)
learning_context = models.ForeignKey(
@@ -131,6 +137,8 @@ class CourseContentVisibilityMixin(models.Model):
We keep the XBlock field names here, even if they're somewhat misleading.
Please read the comments carefully for each field.
.. no_pii:
"""
# This is an obscure, OLX-only flag (there is no UI for it in Studio) that
# lets you define a Sequence that is reachable by direct URL but not shown
@@ -174,6 +182,8 @@ class UserPartitionGroup(models.Model):
UserPartitionGroups are not associated with LearningSequence directly
because User Partitions often carry course-level assumptions (e.g.
Enrollment Track) that don't make sense outside of a Course.
.. no_pii:
"""
id = models.BigAutoField(primary_key=True)
partition_id = models.BigIntegerField(null=False)
@@ -191,6 +201,8 @@ class UserPartitionGroup(models.Model):
class CourseSection(CourseContentVisibilityMixin, TimeStampedModel):
"""
Course Section data, mapping to the 'chapter' block type.
.. no_pii:
"""
id = models.BigAutoField(primary_key=True)
course_context = models.ForeignKey(
@@ -225,6 +237,8 @@ class SectionPartitionGroup(models.Model):
Used for the user_partition_groups ManyToManyField field in the CourseSection model above.
Adds a cascading delete which will delete these many-to-many relations
whenever a UserPartitionGroup or CourseSection object is deleted.
.. no_pii:
"""
class Meta:
unique_together = [
@@ -249,6 +263,8 @@ class CourseSectionSequence(CourseContentVisibilityMixin, TimeStampedModel):
Do NOT make a foreign key against this table, as the values are deleted and
re-created on course publish.
.. no_pii:
"""
id = models.BigAutoField(primary_key=True)
course_context = models.ForeignKey(
@@ -289,6 +305,8 @@ class SectionSequencePartitionGroup(models.Model):
Used for the user_partition_groups ManyToManyField field in the CourseSectionSequence model above.
Adds a cascading delete which will delete these many-to-many relations
whenever a UserPartitionGroup or CourseSectionSequence object is deleted.
.. no_pii:
"""
class Meta:
unique_together = [
@@ -303,6 +321,8 @@ class CourseSequenceExam(TimeStampedModel):
"""
This model stores XBlock information that affects outline level information
pertaining to special exams
.. no_pii:
"""
course_section_sequence = models.OneToOneField(CourseSectionSequence, on_delete=models.CASCADE, related_name='exam')
@@ -318,6 +338,8 @@ class PublishReport(models.Model):
All these fields could be derived with aggregate SQL functions, but it would
be slower and make the admin code more complex. Since we only write at
publish time, keeping things in sync is less of a concern.
.. no_pii:
"""
learning_context = models.OneToOneField(
LearningContext, on_delete=models.CASCADE, related_name='publish_report'
@@ -350,6 +372,8 @@ class ContentError(models.Model):
freeform messages. It is quite possible that at some point we will come up
with a more comprehensive taxonomy of error messages, at which point we
could do a backfill to regenerate this data in a more normalized way.
.. no_pii:
"""
id = models.BigAutoField(primary_key=True)
publish_report = models.ForeignKey(

View File

@@ -89,6 +89,8 @@ class ContentLibrary(models.Model):
re-imported on another Open edX instance should be kept in Learning Core. This
model in Studio should only be used to track settings specific to this Open
edX instance, like who has permission to edit this content library.
.. no_pii:
"""
objects: ContentLibraryManager[ContentLibrary] = ContentLibraryManager()
@@ -183,6 +185,8 @@ class ContentLibrary(models.Model):
class ContentLibraryPermission(models.Model):
"""
Row recording permissions for a content library
.. no_pii:
"""
library = models.ForeignKey(ContentLibrary, on_delete=models.CASCADE, related_name="permission_grants")
# One of the following must be set (but not both):
@@ -226,6 +230,8 @@ class ContentLibraryPermission(models.Model):
class ContentLibraryBlockImportTask(models.Model):
"""
Model of a task to import blocks from an external source (e.g. modulestore).
.. no_pii:
"""
library = models.ForeignKey(
@@ -331,6 +337,8 @@ class LtiProfile(models.Model):
Unless Anonymous, this should be a unique representation of the LTI subject
(as per the client token ``sub`` identify claim) that initiated an LTI
launch through Content Libraries.
.. no_pii:
"""
objects = LtiProfileManager()
@@ -453,6 +461,8 @@ class LtiGradedResource(models.Model):
launch. This model links the profile that launched the resource with the
resource itself, allowing identifcation of the link through its usage key
string and user id.
.. no_pii:
"""
objects = LtiGradedResourceManager()

View File

@@ -16,6 +16,8 @@ class TaxonomyOrg(models.Model):
We keep this as a separate class from ContentTaxonomy so that class can remain a proxy for Taxonomy, keeping the
data models and usage simple.
.. no_pii:
"""
class RelType(models.TextChoices):

View File

@@ -12,6 +12,8 @@ from simple_history.models import HistoricalRecords
class CourseLiveConfiguration(TimeStampedModel):
"""
Associates a Course with a LTI provider and configuration
.. no_pii:
"""
course_key = CourseKeyField(max_length=255, db_index=True, null=False)
enabled = models.BooleanField(

View File

@@ -318,6 +318,8 @@ def get_supported_providers() -> List[str]:
class ProviderFilter(StackedConfigurationModel):
"""
Associate allow/deny-lists of discussions providers with courses/orgs
.. no_pii:
"""
allow = ListCharField(
@@ -406,6 +408,8 @@ T = TypeVar('T', bound='DiscussionsConfiguration')
class DiscussionsConfiguration(TimeStampedModel):
"""
Associates a learning context with discussion provider and configuration
.. no_pii:
"""
context_key = LearningContextKeyField(
@@ -554,6 +558,8 @@ class DiscussionsConfiguration(TimeStampedModel):
class DiscussionTopicLink(models.Model):
"""
A model linking discussion topics ids to the part of a course they are linked to.
..no_pii:
"""
context_key = LearningContextKeyField(
db_index=True,

View File

@@ -72,8 +72,14 @@ class AbstractProgramLTIConfiguration(TimeStampedModel):
class ProgramLiveConfiguration(AbstractProgramLTIConfiguration):
"""
.. no_pii:
"""
history = HistoricalRecords()
class ProgramDiscussionsConfiguration(AbstractProgramLTIConfiguration):
"""
.. no_pii:
"""
history = HistoricalRecords()

View File

@@ -426,6 +426,8 @@ class UserRetirementStatus(TimeStampedModel):
class BulkUserRetirementConfig(ConfigurationModel):
"""
Configuration to store a csv file that will be used in retire_user management command.
.. no_pii:
"""
# Timeout set to 0 so that the model does not read from cached config in case the config entry is deleted.
cache_timeout = 0

View File

@@ -98,7 +98,8 @@ class CourseYoutubeBlockedFlag(ConfigurationModel):
Disables the playback of youtube videos for a given course.
If the flag is present for the course, and set to "enabled",
then youtube is disabled for that course.
.. no_pii
.. no_pii:
"""
KEY_FIELDS = ('course_id',)

View File

@@ -7,7 +7,11 @@ from django.db import models
class Announcement(models.Model):
"""Site-wide announcements to be displayed on the dashboard"""
"""
Site-wide announcements to be displayed on the dashboard
.. no_pii:
"""
class Meta:
app_label = 'announcements'

View File

@@ -15,6 +15,8 @@ from openedx.core.djangoapps.config_model_utils.models import StackedConfigurati
class DiscountRestrictionConfig(StackedConfigurationModel):
"""
A ConfigurationModel used to manage restrictons for lms-controlled discounts
.. no_pii:
"""
STACKABLE_FIELDS = ('disabled',)
@@ -43,6 +45,8 @@ class DiscountRestrictionConfig(StackedConfigurationModel):
class DiscountPercentageConfig(StackedConfigurationModel):
"""
A ConfigurationModel to configure the discount percentage for the first purchase discount
.. no_pii:
"""
STACKABLE_FIELDS = ('percentage',)
percentage = models.PositiveIntegerField()