Skip to content

Commit

Permalink
feat: Roles 5 permission check helper function (#33201)
Browse files Browse the repository at this point in the history
* feat: add permission_check function def

* test: add permission_check base test case

* test: add permission check tests

* feat: add permission check logic

* test: remove allowed field from role permission

* test: update tests

* feat: split permission_check function

* feat: update CourseRolesUserRole model

* feat: update initial migration

* test: rewrite the permission check tests and add new cases

* chore: update unit test shards

* docs: update docstrings
  • Loading branch information
julianpalmerio authored and hsinkoff committed Oct 27, 2023
1 parent 02dbbb1 commit 675006e
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 5 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/unit-test-shards.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
"openedx/core/djangoapps/course_apps/",
"openedx/core/djangoapps/course_date_signals/",
"openedx/core/djangoapps/course_groups/",
"openedx/core/djangoapps/course_roles/",
"openedx/core/djangoapps/courseware_api/",
"openedx/core/djangoapps/crawlers/",
"openedx/core/djangoapps/credentials/",
Expand Down Expand Up @@ -182,6 +183,7 @@
"openedx/core/djangoapps/course_apps/",
"openedx/core/djangoapps/course_date_signals/",
"openedx/core/djangoapps/course_groups/",
"openedx/core/djangoapps/course_roles/",
"openedx/core/djangoapps/courseware_api/",
"openedx/core/djangoapps/crawlers/",
"openedx/core/djangoapps/credentials/",
Expand Down
30 changes: 30 additions & 0 deletions openedx/core/djangoapps/course_roles/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""
Helpers for the course roles app.
"""
from openedx.core.djangoapps.course_roles.models import CourseRolesUserRole
from openedx.core.lib.cache_utils import request_cached


@request_cached()
def course_permission_check(user, permission_name, course_id):
"""
Check if a user has a permission in a course.
"""
return CourseRolesUserRole.objects.filter(
user=user,
role__permissions__name=permission_name,
course=course_id,
).exists()


@request_cached()
def organization_permission_check(user, permission_name, organization_name):
"""
Check if a user has a permission in an organization.
"""
return CourseRolesUserRole.objects.filter(
user=user,
role__permissions__name=permission_name,
course__isnull=True,
org__name=organization_name,
).exists()
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 3.2.20 on 2023-09-12 22:03
# Generated by Django 3.2.20 on 2023-09-13 20:50

from django.conf import settings
from django.db import migrations, models
Expand All @@ -10,9 +10,9 @@ class Migration(migrations.Migration):
initial = True

dependencies = [
('organizations', '0004_auto_20230727_2054'),
('course_overviews', '0029_alter_historicalcourseoverview_options'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('course_overviews', '0029_alter_historicalcourseoverview_options'),
('organizations', '0004_auto_20230727_2054'),
]

operations = [
Expand Down Expand Up @@ -42,7 +42,7 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('course', models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.CASCADE, to='course_overviews.courseoverview')),
('org', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='organizations.organization')),
('org', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='organizations.organization')),
('role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='course_roles.courserolesrole')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
Expand Down
2 changes: 1 addition & 1 deletion openedx/core/djangoapps/course_roles/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class CourseRolesUserRole(models.Model):
on_delete=models.CASCADE,
null=True,
)
org = models.ForeignKey(Organization, on_delete=models.CASCADE, null=False)
org = models.ForeignKey(Organization, on_delete=models.CASCADE, null=True)

class Meta:
unique_together = ('user', 'role', 'course')
Expand Down
120 changes: 120 additions & 0 deletions openedx/core/djangoapps/course_roles/tests/test_helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
"""
Tests of the course_roles.helpers module
"""
from organizations.tests.factories import OrganizationFactory

from common.djangoapps.student.tests.factories import UserFactory
from openedx.core.djangoapps.course_roles.helpers import course_permission_check, organization_permission_check
from openedx.core.djangoapps.course_roles.models import (
CourseRolesPermission,
CourseRolesRole,
CourseRolesService,
CourseRolesUserRole
)
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory


class PermissionCheckTestCase(SharedModuleStoreTestCase):
"""
Tests of the permission check functions in course_roles.helpers module
"""
def setUp(self):
super().setUp()
self.user_1 = UserFactory(username="test_user_1")
self.organization_1 = OrganizationFactory(name="test_organization_1")
self.organization_2 = OrganizationFactory(name="test_organization_2")
self.course_1 = CourseFactory.create(
display_name="test course 1", run="Testing_course_1", org=self.organization_1.name
)
self.course_2 = CourseFactory.create(
display_name="test course 2", run="Testing_course_2", org=self.organization_1.name
)
self.role_1 = CourseRolesRole.objects.create(name="test_role_1")
self.service = CourseRolesService.objects.create(name="test_service")
self.role_1.services.add(self.service)
self.permission_1 = CourseRolesPermission.objects.create(name="test_permission_1")
self.role_1.permissions.add(self.permission_1)

def test_course_permission_check_with_course_level_permission(self):
"""
Test that course_permission_check returns True when the user has the correct permission at the course level
"""
CourseRolesUserRole.objects.create(
user=self.user_1, role=self.role_1, course_id=self.course_1.id, org=self.organization_1
)
assert course_permission_check(self.user_1, self.permission_1.name, self.course_1.id)

def test_course_permission_check_without_course_level_permission(self):
"""
Test that course_permission_check returns False when the user does not have the correct permission at the
course level
"""
assert not course_permission_check(self.user_1, self.permission_1.name, self.course_1.id)

def test_course_permision_check_with_organization_level_permission(self):
"""
Test that course_permission_check returns False when the user has the permission but at the organization
level, and has not been granted the permission at the course level
"""
CourseRolesUserRole.objects.create(user=self.user_1, role=self.role_1, org=self.organization_1)
assert not course_permission_check(self.user_1, self.permission_1.name, self.course_1.id)

def test_course_permission_check_with_instance_level_permission(self):
"""
Test that course_permission_check returns False when the user has the permission but at the instance level,
and has not been granted the permission at the course level
"""
CourseRolesUserRole.objects.create(user=self.user_1, role=self.role_1)
assert not course_permission_check(self.user_1, self.permission_1.name, self.course_1.id)

def test_course_permission_check_with_permission_in_another_course(self):
"""
Test that course_permission_check returns False when the user has the permission but at the course level,
but in another course
"""
CourseRolesUserRole.objects.create(
user=self.user_1, role=self.role_1, course_id=self.course_2.id, org=self.organization_1
)
assert not course_permission_check(self.user_1, self.permission_1.name, self.course_1.id)

def test_organization_permission_check_with_organization_level_permission(self):
"""
Test that organization_permission_check returns True when the user has the correct permission at the
organization level
"""
CourseRolesUserRole.objects.create(user=self.user_1, role=self.role_1, org=self.organization_1)
assert organization_permission_check(self.user_1, self.permission_1.name, self.organization_1.name)

def test_organization_permission_check_without_organization_level_permission(self):
"""
Test that organization_permission_check returns False when the user does not have the correct permission at
the organization level
"""
assert not organization_permission_check(self.user_1, self.permission_1.name, self.organization_1.name)

def test_organization_permission_check_with_course_level_permission(self):
"""
Test that organization_permission_check returns False when the user has the permission but at the course
level, and has not been granted the permission at the organization level
"""
CourseRolesUserRole.objects.create(
user=self.user_1, role=self.role_1, course_id=self.course_1.id, org=self.organization_1
)
assert not organization_permission_check(self.user_1, self.permission_1.name, self.organization_1.name)

def test_organization_permission_check_with_instance_level_permission(self):
"""
Test that organization_permission_check returns False when the user has the permission but at the instance
level, and has not been granted the permission at the organization level
"""
CourseRolesUserRole.objects.create(user=self.user_1, role=self.role_1)
assert not organization_permission_check(self.user_1, self.permission_1.name, self.organization_1.name)

def test_organization_permission_check_with_permission_in_another_organization(self):
"""
Test that organization_permission_check returns False when the user has the permission but at the
organization level, but in another organization
"""
CourseRolesUserRole.objects.create(user=self.user_1, role=self.role_1, org=self.organization_2)
assert not organization_permission_check(self.user_1, self.permission_1.name, self.organization_1.name)

0 comments on commit 675006e

Please sign in to comment.