Skip to content

Challenges in Validating LtiConfiguration Data #277

@michaelroytman

Description

@michaelroytman

Context:

In #260, support for database configuration for LTI 1.3 launches was added; these changes were put behind a CourseWaffleFlag. The CourseWaffleFlag is referenced in the LtiConsumerXBlock and in the LtiConfiguration classes. In the LtiConfiguration's clean method, the waffle flag is used to validate or clean incoming data to prevent the creation of an LtiConfiguration with a config_store value of CONFIG_ON_DB if the waffle flag is not enabled.

The problem is that the CourseWaffleFlag.is_enabled(<course_key>) method can only be called with a course_key. For LtiConfigurations with a None value for the location field, this raises an exception, because the location is used to load in the XBlock from the modulestore. The course_key is then retrieved from the XBlock. Without a location, there is no course_key, and CourseWaffleFlag.is_enabled(<course_key>) fails. This is only observed in the Django admin, because Model.full_clean() and Model.clean() are not called after Model.save().

image

I tried to use a WaffleFlag instead, thinking that I would sacrifice the added customizability of the CourseWaffleFlag in exchange for the ability of the flag to work outside the XBlock context. However, WaffleFlag requires there to be an available request object, and there isn't one when executing a model's clean method.

Problem:

Regardless, I believe that this kind of validation does not belong in the model clean method. This kind of business logic validation should occur before the database layer (e.g. in a Form class or in a DRF serializer). But we are not making use of Form classes or DRF serializers here. I would prefer that this check be in a centralized place within the library; however, I cannot easily determine where this should be because of the various ways LtiConfigurations can be created. The two questions I would like to answer are.

  1. How can we use feature flags throughout the codebase in a way that does not assume an XBlock runtime context?

  2. How can we ensure that the same kind of validation is applied in the three different ways LtiConfigurations can be created?

Requirements:

We want to ensure we do not save invalid data to LtiConfiguration in three places:

1. in the XBlock edit menu in Studio
Currently, this is handled by the xblock_studio_view.js Javascript. There is no backend enforcement.
We could add this waffle flag to validate_field_data.

2. in the Django admin
Currently, whatever is in the LtiConfiguration.clean() method will be run.

3. in any other places where we create LtiConfiguration instances, like the Python API
Currently, there is no validation of the data used to create LtiConfiguration instances when the Python API is used. This is because calling Model.clean() does not call Model.full_clean() and Model.clean().

It may happen that we decide to release "CONFIG_ON_DB" platform wide and remove the feature flag before edx-exams even needs it, in which case the only remaining issue is #2 under "Problems".

Other Notes:

  1. In the Python API, there is no clear place to validate the incoming data. Because of the way that data is synced from the XBlock to the LtiConfiguration in _get_lti_config and _get_or_create_local_lti_config, any time a caller intends to fetch an LtiConfiguration, it may be updated as part of the fetch to sync it with the modulestore. It's unexpected for a function like get_lti_1p3_launch_info to have to handle a ValidationError if we were to add validation to _get_or_create_local_lti_config.

  2. Currently, LtiConfiguration instances are created via the _get_or_create_local_lti_config API method, which is used throughout the codebase. Note that Model.full_clean() and Model.clean() are not actually called when Model.save() is called, so the model clean method only runs when making changes through the Django admin. With support for CONFIG_ON_DB, we need to support creating and editing LtiConfigurations from the Django admin while also being able to validate incoming data. It's possible to customize the model admin form to store the request and use a WaffleFlag, but it's hacky.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugReport of or fix for something that isn't working as intended

    Type

    No type

    Projects

    Status

    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions