From 4470dc9ef0f636fe15e355e075c18a925c0df1bd Mon Sep 17 00:00:00 2001 From: Maria Grimaldi Date: Thu, 25 Nov 2021 19:10:16 -0400 Subject: [PATCH] feat: add 1st batch of filters * Add PreRegisterFilter * Add PreLoginFilter --- CHANGELOG.rst | 11 +++ openedx_filters/filters.py | 6 +- openedx_filters/learning/enrollment.py | 33 --------- openedx_filters/learning/filters.py | 95 ++++++++++++++++++++++++++ openedx_filters/tests/test_tooling.py | 4 +- openedx_filters/utils.py | 35 ++++++++++ 6 files changed, 146 insertions(+), 38 deletions(-) delete mode 100644 openedx_filters/learning/enrollment.py create mode 100644 openedx_filters/learning/filters.py create mode 100644 openedx_filters/utils.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index db9cbc36..005d761c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -13,6 +13,17 @@ Change Log Unreleased ~~~~~~~~~~ +Added +_____ + +* Filter definitions for registration and login. +* Sensitive data mixin for filters. + +Changed +_______ + +* Pipeline runner from `run` to `run_filter`. +* Moved filters definitions to filters file inside their domain. [0.3.0] - 2021-11-24 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/openedx_filters/filters.py b/openedx_filters/filters.py index c1eb37a4..6b4b8b54 100644 --- a/openedx_filters/filters.py +++ b/openedx_filters/filters.py @@ -22,7 +22,7 @@ class PipelineStep: class MyFilterStep(PipelineStep): - def run(self, user, course_key, mode): + def run_filter(self, user, course_key, mode): if mode != "honor": return @@ -35,7 +35,7 @@ def run(self, user, course_key, mode): class MyFilterStep(PipelineStep): - def run(self, user, course_key, mode): + def run_filter(self, user, course_key, mode): if mode != "honor": return @@ -55,7 +55,7 @@ def __init__(self, filter_type, running_pipeline, **extra_config): self.running_pipeline = running_pipeline self.extra_config = extra_config - def run(self, **kwargs): # pylint: disable=unused-argument + def run_filter(self, **kwargs): # pylint: disable=unused-argument """ Abstract pipeline step runner. diff --git a/openedx_filters/learning/enrollment.py b/openedx_filters/learning/enrollment.py deleted file mode 100644 index 2d974e37..00000000 --- a/openedx_filters/learning/enrollment.py +++ /dev/null @@ -1,33 +0,0 @@ -""" -Package where filters related to the enrollment process are implemented. -""" -from openedx_filters.exceptions import OpenEdxFilterException -from openedx_filters.tooling import OpenEdxPublicFilter - - -class PreEnrollmentFilter(OpenEdxPublicFilter): - """ - Custom class used to create PreEnrollment filters. - """ - - filter_type = "org.openedx.learning.course.enrollment.started.v1" - - class PreventEnrollment(OpenEdxFilterException): - """ - Custom class used to stop the enrollment process. - """ - - @classmethod - def run(cls, user, course_key, mode): - """ - Execute a filter with the signature specified. - - Arguments: - user (User): is a Django User object. - course_key (CourseKey): name of the filter. - mode (str): is a string specifying what kind of enrollment. - """ - data = super().run_pipeline( - user=user, course_key=course_key, mode=mode, - ) - return data.get("user"), data.get("course_key"), data.get("mode") diff --git a/openedx_filters/learning/filters.py b/openedx_filters/learning/filters.py new file mode 100644 index 00000000..2867ef95 --- /dev/null +++ b/openedx_filters/learning/filters.py @@ -0,0 +1,95 @@ +""" +Package where filters related to the learning architectural subdomain are implemented. +""" +from openedx_filters.exceptions import OpenEdxFilterException +from openedx_filters.tooling import OpenEdxPublicFilter +from openedx_filters.utils import SensitiveDataManagementMixin + + +class StudentRegistrationRequested(OpenEdxPublicFilter, SensitiveDataManagementMixin): + """ + Custom class used to create PreRegister filters. + """ + + filter_type = "org.openedx.learning.student.registration.requested.v1" + sensitive_form_data = [ + "password", "newpassword", "new_password", "oldpassword", "old_password", "new_password1", "new_password2", + ] + + class PreventRegistration(OpenEdxFilterException): + """ + Custom class used to stop the registration process. + """ + + @classmethod + def run_filter(cls, form_data): + """ + Execute a filter with the signature specified. + + Arguments: + form_data (QueryDict): contains the request.data submitted by the registration + form. + """ + sensitive_data = cls.extract_sensitive_data(form_data) + data = super().run_pipeline(form_data=form_data) + form_data = data.get("form_data") + form_data.update(sensitive_data) + return form_data + + +class StudentLoginRequested(OpenEdxPublicFilter): + """ + Custom class used to create PreLogin filters. + """ + + filter_type = "org.openedx.learning.student.login.requested.v1" + + class PreventLogin(OpenEdxFilterException): + """ + Custom class used to stop the login process. + """ + + def __init__(self, message, redirect_to=None, error_code="", context=None): + """ + Override init that defines specific arguments used in the login process. + """ + super().__init__(message, redirect_to=redirect_to, error_code=error_code, context=context) + + @classmethod + def run_filter(cls, user): + """ + Execute a filter with the signature specified. + + Arguments: + user (User): is a Django User object. + """ + data = super().run_pipeline(user=user) + return data.get("user") + + +class CourseEnrollmentStarted(OpenEdxPublicFilter): + """ + Custom class used to create PreEnrollment filters. + """ + + filter_type = "org.openedx.learning.course.enrollment.started.v1" + + class PreventEnrollment(OpenEdxFilterException): + """ + Custom class used to stop the enrollment process. + """ + + @classmethod + def run_filter(cls, user, course_key, mode): + """ + Execute a filter with the signature specified. + + Arguments: + user (User): is a Django User object. + course_key (CourseKey): name of the filter. + mode (str): is a string specifying what kind of enrollment. + """ + data = super().run_pipeline( + user=user, course_key=course_key, mode=mode, + ) + return data.get("user"), data.get("course_key"), data.get("mode") diff --git a/openedx_filters/tests/test_tooling.py b/openedx_filters/tests/test_tooling.py index e5076cb2..40238d80 100644 --- a/openedx_filters/tests/test_tooling.py +++ b/openedx_filters/tests/test_tooling.py @@ -21,7 +21,7 @@ class FirstPipelineStep(PipelineStep): Utility function used when getting steps for pipeline. """ - def run(self, **kwargs): + def run_filter(self, **kwargs): pass @@ -30,7 +30,7 @@ class SecondPipelineStep(PipelineStep): Utility class used when getting steps for pipeline. """ - def run(self, **kwargs): + def run_filter(self, **kwargs): pass diff --git a/openedx_filters/utils.py b/openedx_filters/utils.py new file mode 100644 index 00000000..c3a82fc2 --- /dev/null +++ b/openedx_filters/utils.py @@ -0,0 +1,35 @@ +""" +Utilities for Open edX filters usage. +""" + + +class SensitiveDataManagementMixin: + """ + Custom class used manage sensitive data within filter arguments. + """ + + sensitive_form_data = [] + + @classmethod + def extract_sensitive_data(cls, form_data): + """ + Extract sensitive data from its child class input arguments. + + Example usage: + + >> sensitive_form_data = ["password"] # Specified in FilterExample + >> form_data = {"username": "example", "password": "password"} + >> sensitive_data = FilterExample.extract_sensitive_data(form_data) + >> sensitive_data + {"password": "password"} + >> form_data + {"username": "example"} + """ + sensitive_data = {} + base_form_data = form_data.copy() + for key, value in base_form_data.items(): + if key in cls.sensitive_form_data: + form_data.pop(key) + sensitive_data[key] = value + + return sensitive_data