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:
@@ -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()
|
||||
|
||||
@@ -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', ''),
|
||||
|
||||
@@ -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'])
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user