# Pluggable access control both viewing and enrolling in a course

**URL:** <https://discuss.openedx.org/t/pluggable-access-control-both-viewing-and-enrolling-in-a-course/803>\
**Category:** Architecture\
**Tags:** api\
**Created:** [November 5, 2019, 11:36am UTC](https://discuss.openedx.org/t/pluggable-access-control-both-viewing-and-enrolling-in-a-course/803 "2019-11-05T11:36:50Z")\
**Posts on this page:** 1\
**Showing post:** 1

<div class="post-metadata">

**Author:** ![omar](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.openedx.org/omar/32/7275_2.png) [@omar](https://discuss.openedx.org/u/omar)\
**Post date:** [November 5, 2019, 11:36am UTC](https://discuss.openedx.org/t/pluggable-access-control-both-viewing-and-enrolling-in-a-course/803/1 "2019-11-05T11:36:51Z")

</div>

## Summary

I’m proposing a method to customize the access control in Open edX via plugins. This proposal is concerned about augmenting the course `load` and `enroll` permissions by providing django app and registering it using the Django settings.

## Access Control in Open edX

Enrollment access control is pretty much hard-coded in Open edX. The course view and enrollment are not an exception.

Often times, when the access control logic need to be changed, the modifications are also hardcoded  
which adds to the tech-debt that fork maintainers needs to carry on.

One example is the ability to make a Course Access Group feature which is something that  
a couple of Open edX providers have expressed interest in and also being requested by our customers.

## Pluggable Access Control

I’m trying to collect some architecture feedback before going and making such change in Open edX,  
but mostly I’m interested in stealing from the [LTI Consumer XBlock extensions](https://github.com/edx/xblock-lti-consumer#custom-lti-parameters) architecture.

You can also check [original pull request](https://github.com/edx/xblock-lti-consumer/pull/44) and how an [LTI Plugin](https://github.com/appsembler/tahoe-lti) looks like.

If you’re really into it check other related work in Open edX:

- [The registration extension form](https://edx.readthedocs.io/projects/edx-installing-configuring-and-running/en/latest/configuration/customize_registration_page.html#add-custom-fields-to-the-registration-page).
- The original hard-coded [Course Access Group](https://github.com/appsembler/edx-platform/compare/024e7aaa57a120097b7468b9cf97ec7cdca271db...metalogix/feature/courseAccessRestriction) that we developed for a customer in 2015.  
Focus on [`can_enroll()`](https://github.com/appsembler/edx-platform/compare/024e7aaa57a120097b7468b9cf97ec7cdca271db...metalogix/feature/courseAccessRestriction#diff-be0481503e8fd3f1d02e60ef92bc39f4R249) changes.

## Proposal

There’s a couple of ways to design the extension, I’m going for the most simple one that can work focusing on the [`_has_access_course`](https://github.com/edx/edx-platform/blob/c1274ba765e9911f2145c1415e3c7894d5e9bee0/lms/djangoapps/courseware/access.py#L287-L307) function.

Hypothetically, if we’d like to make the enrollment in the `course-v1:Uni+Demo+2020` course  
to be exclusive for users with`@example.edu` email we’d use the following configuration:

```auto
# my-configs/server-vars.yml
ACCESS_CONTROL_BACKENDS:
    course_enroll:
      NAME: 'my_access_plugins.backends.domain_based_course_enrollment'
      OPTIONS:
        enforce_domain_name_check: true
    course_load:
      NAME: 'my_access_plugins.backends.domain_based_course_enrollment'

```

And the backend would look like this:

```python
# my_access_plugins/backends.py
def domain_based_course_enrollment(user, courselike, default_access, options):
    if courselike.id == 'course-v1:Uni+Demo+2020' and not user.email.endswith('@example.edu'):
        return False

    if options.get('enforce_domain_name_check', False):
       # Check the DNS records for such email.
       pass

    return default_access

```

### Some Implementation Example

While the actual implementation will be different, here’s an approximate idea of what would it look like:

The settings would follow the same patten like other Open edX settings.

```python
# lms/envs/aws.py
ACCESS_CONTROL_BACKENDS = ENV_TOKENS.get('ACCESS_CONTROL_BACKENDS', {})

```

We also need a helper to handle the common logic for invoking the backends:

```python
# somewhere/helpers.py
def use_acl_backend(backend_name, user, resource, default_access):
    # Not doing too much error handling at the moment
    backend_path = ACCESS_CONTROL_BACKENDS.get(backend_name).get('NAME')
    backend_options = ACCESS_CONTROL_BACKENDS.get(backend_name).get('OPTIONS', {})

    if backend_path:
        backend = import_module(module_path)
        return backend(user, resource, default_access=default_access, options=backend_options)

    return default_access

```

This would be the actual use of the backends, it needs to put it in other places in the platform.

```python
# lms/djangoapps/courseware/access.py
def can_enroll(user, course):
    # Over-simplified function, since the actual `access.py` is pretty complex.
    # ...
    can_enroll = through_some_other_logic()
    # ...
    #
    return use_acl_backend('course_enroll', user, resource=course, default_access=can_enroll)

```

Let me know if you’d like me to expand with more details.

## Questions

- Tell me what do you think?
- How can we get this into Open edX?
- Do you see any issues with the proposal above?
- Any better alternative do you have in mind?
- Is this change has enough impact to require an OEP?

---

_[View the full topic](https://discuss.openedx.org/t/pluggable-access-control-both-viewing-and-enrolling-in-a-course/803)._
