fix: allow_to_create_new_org checks org autocreate [FC-0076] (#36094)

Updates the StudioHome API's allow_to_create_new_org to require both organization-creation permissions and ORGANIZATION_AUTOCREATE to be enabled. It also adds the list of "allowed organizations for libraries" to the Studio Home API so that the Authoring MFE can use it.
This commit is contained in:
Jillian
2025-01-16 04:02:37 +10:30
committed by GitHub
parent fa0ead4f4f
commit b96a3bf249
5 changed files with 95 additions and 2 deletions

View File

@@ -50,6 +50,10 @@ class StudioHomeSerializer(serializers.Serializer):
child=serializers.CharField(),
allow_empty=True
)
allowed_organizations_for_libraries = serializers.ListSerializer(
child=serializers.CharField(),
allow_empty=True
)
archived_courses = CourseCommonSerializer(required=False, many=True)
can_access_advanced_settings = serializers.BooleanField()
can_create_organizations = serializers.BooleanField()

View File

@@ -5,6 +5,7 @@ from django.conf import settings
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.views import APIView
from organizations import api as org_api
from openedx.core.lib.api.view_utils import view_auth_classes
from ....utils import get_home_context, get_course_context, get_library_context
@@ -51,6 +52,7 @@ class HomePageView(APIView):
"allow_to_create_new_org": true,
"allow_unicode_course_id": false,
"allowed_organizations": [],
"allowed_organizations_for_libraries": [],
"archived_courses": [],
"can_access_advanced_settings": true,
"can_create_organizations": true,
@@ -79,7 +81,12 @@ class HomePageView(APIView):
home_context = get_home_context(request, True)
home_context.update({
'allow_to_create_new_org': settings.FEATURES.get('ENABLE_CREATOR_GROUP', True) and request.user.is_staff,
# 'allow_to_create_new_org' is actually about auto-creating organizations
# (e.g. when creating a course or library), so we add an additional test.
'allow_to_create_new_org': (
home_context['can_create_organizations'] and
org_api.is_autocreate_enabled()
),
'studio_name': settings.STUDIO_NAME,
'studio_short_name': settings.STUDIO_SHORT_NAME,
'studio_request_email': settings.FEATURES.get('STUDIO_REQUEST_EMAIL', ''),

View File

@@ -32,9 +32,10 @@ class HomePageViewTest(CourseTestCase):
self.url = reverse("cms.djangoapps.contentstore:v1:home")
self.expected_response = {
"allow_course_reruns": True,
"allow_to_create_new_org": False,
"allow_to_create_new_org": True,
"allow_unicode_course_id": False,
"allowed_organizations": [],
"allowed_organizations_for_libraries": [],
"archived_courses": [],
"can_access_advanced_settings": True,
"can_create_organizations": True,
@@ -78,6 +79,17 @@ class HomePageViewTest(CourseTestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertDictEqual(expected_response, response.data)
@override_settings(ORGANIZATIONS_AUTOCREATE=False)
def test_home_page_studio_with_org_autocreate_disabled(self):
"""Check response content when Organization autocreate is disabled"""
response = self.client.get(self.url)
expected_response = self.expected_response
expected_response["allow_to_create_new_org"] = False
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertDictEqual(expected_response, response.data)
def test_taxonomy_list_link(self):
response = self.client.get(self.url)
self.assertTrue(response.data['taxonomies_enabled'])

View File

@@ -8,6 +8,7 @@ from uuid import uuid4
import ddt
from django.contrib.auth.models import Group
from django.test import override_settings
from django.test.client import Client
from freezegun import freeze_time
from opaque_keys.edx.locator import LibraryLocatorV2, LibraryUsageLocatorV2
@@ -139,6 +140,63 @@ class ContentLibrariesTestCase(ContentLibrariesRestApiTest, OpenEdxEventsTestMix
'slug': ['Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or hyphens.'],
}
def test_library_org_validation(self):
"""
Staff users can create libraries in any existing or auto-created organization.
"""
assert Organization.objects.filter(short_name='auto-created-org').count() == 0
self._create_library(slug="auto-created-org-1", title="Library in an auto-created org", org='auto-created-org')
assert Organization.objects.filter(short_name='auto-created-org').count() == 1
self._create_library(slug="existing-org-1", title="Library in an existing org", org="CL-TEST")
@patch(
"openedx.core.djangoapps.content_libraries.views.user_can_create_organizations",
)
@patch(
"openedx.core.djangoapps.content_libraries.views.get_allowed_organizations_for_libraries",
)
@override_settings(ORGANIZATIONS_AUTOCREATE=False)
def test_library_org_no_autocreate(self, mock_get_allowed_organizations, mock_can_create_organizations):
"""
When org auto-creation is disabled, user must use one of their allowed orgs.
"""
mock_can_create_organizations.return_value = False
mock_get_allowed_organizations.return_value = ["CL-TEST"]
assert Organization.objects.filter(short_name='auto-created-org').count() == 0
response = self._create_library(
slug="auto-created-org-2",
org="auto-created-org",
title="Library in an auto-created org",
expect_response=400,
)
assert response == {
'org': "No such organization 'auto-created-org' found.",
}
Organization.objects.get_or_create(
short_name="not-allowed-org",
defaults={"name": "Content Libraries Test Org Membership"},
)
response = self._create_library(
slug="not-allowed-org",
org="not-allowed-org",
title="Library in an not-allowed org",
expect_response=400,
)
assert response == {
'org': "User not allowed to create libraries in 'not-allowed-org'.",
}
assert mock_can_create_organizations.call_count == 1
assert mock_get_allowed_organizations.call_count == 1
self._create_library(
slug="allowed-org-2",
org="CL-TEST",
title="Library in an allowed org",
)
assert mock_can_create_organizations.call_count == 2
assert mock_get_allowed_organizations.call_count == 2
@skip("This endpoint shouldn't support num_blocks and has_unpublished_*.")
@patch("openedx.core.djangoapps.content_libraries.views.LibraryRootView.pagination_class.page_size", new=2)
def test_list_library(self):

View File

@@ -99,6 +99,10 @@ from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.viewsets import GenericViewSet
from cms.djangoapps.contentstore.views.course import (
get_allowed_organizations_for_libraries,
user_can_create_organizations,
)
from openedx.core.djangoapps.content_libraries import api, permissions
from openedx.core.djangoapps.content_libraries.serializers import (
ContentLibraryBlockImportTaskCreateSerializer,
@@ -269,6 +273,14 @@ class LibraryRootView(GenericAPIView):
raise ValidationError( # lint-amnesty, pylint: disable=raise-missing-from
detail={"org": f"No such organization '{org_name}' found."}
)
# Ensure the user is allowed to create libraries under this org
if not (
user_can_create_organizations(request.user) or
org_name in get_allowed_organizations_for_libraries(request.user)
):
raise ValidationError( # lint-amnesty, pylint: disable=raise-missing-from
detail={"org": f"User not allowed to create libraries in '{org_name}'."}
)
org = Organization.objects.get(short_name=org_name)
try: