feat: AuthZ for course authoring compatibility layer (#38013)

This commit is contained in:
Rodrigo Mendez
2026-03-06 10:35:17 -06:00
committed by GitHub
parent 0c5e96d566
commit 12a46e6463
26 changed files with 628 additions and 174 deletions

View File

@@ -514,12 +514,12 @@ class TestTaxonomyListCreateViewSet(TestTaxonomyObjectsMixin, APITestCase):
@ddt.data(
('staff', 11),
("content_creatorA", 22),
("library_staffA", 22),
("library_userA", 22),
("instructorA", 22),
("course_instructorA", 22),
("course_staffA", 22),
("content_creatorA", 23),
("library_staffA", 23),
("library_userA", 23),
("instructorA", 23),
("course_instructorA", 23),
("course_staffA", 23),
)
@ddt.unpack
def test_list_taxonomy_query_count(self, user_attr: str, expected_queries: int):
@@ -1947,16 +1947,16 @@ class TestObjectTagViewSet(TestObjectTagMixin, APITestCase):
('staff', 'courseA', 8),
('staff', 'libraryA', 8),
('staff', 'collection_key', 8),
("content_creatorA", 'courseA', 17, False),
("content_creatorA", 'libraryA', 17, False),
("content_creatorA", 'collection_key', 17, False),
("library_staffA", 'libraryA', 17, False), # Library users can only view objecttags, not change them?
("library_staffA", 'collection_key', 17, False),
("library_userA", 'libraryA', 17, False),
("library_userA", 'collection_key', 17, False),
("instructorA", 'courseA', 17),
("course_instructorA", 'courseA', 17),
("course_staffA", 'courseA', 17),
("content_creatorA", 'courseA', 18, False),
("content_creatorA", 'libraryA', 18, False),
("content_creatorA", 'collection_key', 18, False),
("library_staffA", 'libraryA', 18, False), # Library users can only view objecttags, not change them?
("library_staffA", 'collection_key', 18, False),
("library_userA", 'libraryA', 18, False),
("library_userA", 'collection_key', 18, False),
("instructorA", 'courseA', 18),
("course_instructorA", 'courseA', 18),
("course_staffA", 'courseA', 18),
)
@ddt.unpack
def test_object_tags_query_count(

View File

@@ -19,7 +19,8 @@ from django.test.utils import override_settings
from xmodule.modulestore.tests.factories import CourseFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, mixed_store_config
from openedx.core.djangolib.testing.utils import skip_unless_lms
from openedx.core.djangolib.testing.utils import AUTHZ_TABLES, skip_unless_lms
from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES
from common.djangoapps.student.tests.factories import UserFactory
from common.djangoapps.student.roles import (
GlobalStaff, CourseRole, OrgRole,
@@ -35,6 +36,7 @@ from ..models import (
from .. import api as embargo_api
from ..exceptions import InvalidAccessPoint
QUERY_COUNT_TABLE_IGNORELIST = WAFFLE_TABLES + AUTHZ_TABLES
MODULESTORE_CONFIG = mixed_store_config(settings.COMMON_TEST_DATA_ROOT, {})
@@ -175,10 +177,10 @@ class EmbargoCheckAccessApiTests(ModuleStoreTestCase):
# (restricted course, but pass all the checks)
# This is the worst case, so it will hit all of the
# caching code.
with self.assertNumQueries(3):
with self.assertNumQueries(5, table_ignorelist=QUERY_COUNT_TABLE_IGNORELIST):
embargo_api.check_course_access(self.course.id, user=self.user, ip_addresses=['0.0.0.0'])
with self.assertNumQueries(0):
with self.assertNumQueries(0, table_ignorelist=QUERY_COUNT_TABLE_IGNORELIST):
embargo_api.check_course_access(self.course.id, user=self.user, ip_addresses=['0.0.0.0'])
def test_caching_no_restricted_courses(self):

View File

@@ -12,6 +12,7 @@ from zoneinfo import ZoneInfo
from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.course_modes.tests.factories import CourseModeFactory
from common.djangoapps.student.roles import AuthzCompatCourseAccessRole
from openedx.core.djangoapps.enrollments import data
from openedx.core.djangoapps.enrollments.errors import (
CourseEnrollmentClosedError,
@@ -387,8 +388,15 @@ class EnrollmentDataTest(ModuleStoreTestCase):
expected_role = CourseAccessRoleFactory.create(
course_id=self.course.id, user=self.user, role="SuperCoolTestRole",
)
expected_role_compat = AuthzCompatCourseAccessRole(
user_id=expected_role.user.id,
username=expected_role.user.username,
org=expected_role.org,
course_id=expected_role.course_id,
role=expected_role.role,
)
roles = data.get_user_roles(self.user.username)
assert roles == {expected_role}
assert roles == {expected_role_compat}
def test_get_roles_no_roles(self):
"""Get roles for a user who has no roles"""

View File

@@ -34,8 +34,11 @@ from openedx.core.djangoapps.schedules.resolvers import (
)
from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFactory
from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory, SiteFactory
from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES
from openedx.core.djangolib.testing.utils import CacheIsolationMixin, skip_unless_lms
QUERY_COUNT_TABLE_IGNORELIST = WAFFLE_TABLES
class SchedulesResolverTestMixin(CacheIsolationMixin):
"""
@@ -276,7 +279,7 @@ class TestCourseNextSectionUpdateResolver(SchedulesResolverTestMixin, ModuleStor
def test_schedule_context(self):
resolver = self.create_resolver()
# using this to make sure the select_related stays intact
with self.assertNumQueries(26):
with self.assertNumQueries(22, table_ignorelist=QUERY_COUNT_TABLE_IGNORELIST):
sc = resolver.get_schedules()
schedules = list(sc)
apple_logo_url = 'http://email-media.s3.amazonaws.com/edX/2021/store_apple_229x78.jpg'