diff --git a/openedx/core/djangoapps/appsembler/api/sites.py b/openedx/core/djangoapps/appsembler/api/sites.py index a5931e0a92d0..df8177ab268c 100644 --- a/openedx/core/djangoapps/appsembler/api/sites.py +++ b/openedx/core/djangoapps/appsembler/api/sites.py @@ -9,6 +9,7 @@ Organization, OrganizationCourse, ) +from tahoe_sites.api import get_site_by_organization from openedx.core.djangoapps.content.course_overviews.models import CourseOverview @@ -66,13 +67,7 @@ def get_site_for_course(course_id): # Keep until this assumption analyzed msg = 'Multiple orgs found for course: {}' assert org_courses.count() == 1, msg.format(course_id) - first_org = org_courses.first().organization - if hasattr(first_org, 'sites'): - msg = 'Must have one and only one site. Org is "{}"' - assert first_org.sites.count() == 1, msg.format(first_org.name) - site = first_org.sites.first() - else: - site = None + site = get_site_by_organization(organization=org_courses.get().organization) else: # We don't want to make assumptions of who our consumers are # TODO: handle no organizations found for the course diff --git a/openedx/core/djangoapps/appsembler/settings/helpers.py b/openedx/core/djangoapps/appsembler/settings/helpers.py index 76d90dab83c7..ff1bff3f9231 100644 --- a/openedx/core/djangoapps/appsembler/settings/helpers.py +++ b/openedx/core/djangoapps/appsembler/settings/helpers.py @@ -4,6 +4,8 @@ from os import path +from tahoe_sites.api import get_tahoe_sites_auth_backends + def get_tahoe_theme_static_dirs(settings): """ @@ -74,9 +76,10 @@ def get_tahoe_multitenant_auth_backends(settings): upstream_backend_index = authentication_backends.index(upstream_user_model_backend) # Use multi-tenant Tahoe backends instead of the upstream EdxRateLimitedAllowAllUsersModelBackend backend. - authentication_backends = settings.AUTHENTICATION_BACKENDS[:upstream_backend_index] + [ - 'organizations.backends.DefaultSiteBackend', - 'organizations.backends.OrganizationMemberBackend', - ] + settings.AUTHENTICATION_BACKENDS[upstream_backend_index + 1:] + authentication_backends = ( + settings.AUTHENTICATION_BACKENDS[:upstream_backend_index] + + get_tahoe_sites_auth_backends() + + settings.AUTHENTICATION_BACKENDS[upstream_backend_index + 1:] + ) return authentication_backends diff --git a/openedx/core/djangoapps/appsembler/settings/tests/test_tahoe_auth_backends_bridgekeeper.py b/openedx/core/djangoapps/appsembler/settings/tests/test_tahoe_auth_backends_bridgekeeper.py index 0a6bed8d86cc..349de4138a64 100644 --- a/openedx/core/djangoapps/appsembler/settings/tests/test_tahoe_auth_backends_bridgekeeper.py +++ b/openedx/core/djangoapps/appsembler/settings/tests/test_tahoe_auth_backends_bridgekeeper.py @@ -36,8 +36,8 @@ def test_tahoe_backends(self): """ Ensure get_tahoe_multitenant_auth_backends adds Tahoe backends to AUTHENTICATION_BACKENDS. """ - assert 'organizations.backends.DefaultSiteBackend' in settings.AUTHENTICATION_BACKENDS - assert 'organizations.backends.OrganizationMemberBackend' in settings.AUTHENTICATION_BACKENDS + assert 'tahoe_sites.backends.DefaultSiteBackend' in settings.AUTHENTICATION_BACKENDS + assert 'tahoe_sites.backends.OrganizationMemberBackend' in settings.AUTHENTICATION_BACKENDS def test_instructors_has_access(self): """ diff --git a/openedx/core/djangoapps/appsembler/sites/management/commands/create_devstack_site.py b/openedx/core/djangoapps/appsembler/sites/management/commands/create_devstack_site.py index 5c787dca0960..75c338454da9 100644 --- a/openedx/core/djangoapps/appsembler/sites/management/commands/create_devstack_site.py +++ b/openedx/core/djangoapps/appsembler/sites/management/commands/create_devstack_site.py @@ -105,7 +105,7 @@ def _handle_with_atmoic(self, *args, **options): 'organization': { 'name': name, 'short_name': name, - 'edx_uuid': uuid.uuid4(), + 'edx_uuid': uuid.uuid4(), # TODO: RED-2845 Remove this line when AMC is migrated }, 'initial_values': { 'SITE_NAME': site_name, diff --git a/openedx/core/djangoapps/appsembler/sites/management/commands/offboard.py b/openedx/core/djangoapps/appsembler/sites/management/commands/offboard.py index 1f8b0f53a3e1..1e6dba97672a 100644 --- a/openedx/core/djangoapps/appsembler/sites/management/commands/offboard.py +++ b/openedx/core/djangoapps/appsembler/sites/management/commands/offboard.py @@ -11,7 +11,7 @@ from django.core.management.base import BaseCommand, CommandError from django.core.serializers.json import DjangoJSONEncoder from django.db.models import ForeignKey -from tahoe_sites.api import get_organization_by_site +from tahoe_sites.api import get_organization_by_site, get_uuid_by_organization from opaque_keys.edx.keys import CourseKey from openedx.core.djangoapps.content.course_overviews.models import CourseOverview @@ -119,7 +119,7 @@ def generate_objects(self, site): objects = { 'site': self.process_site(site), 'organizations': [ - self.process_organization(org) for org in [organization] + self.process_organization(organization), ], 'courses': self.process_courses(organization), 'configurations': self.process_site_configurations(site), @@ -412,7 +412,7 @@ def process_organization(self, organization): 'description': organization.description, 'logo': organization.logo.url if organization.logo else '', 'active': organization.active, - 'UUID': organization.edx_uuid, + 'UUID': get_uuid_by_organization(organization=organization), 'created': organization.created, 'users': self.process_organization_users(organization) } diff --git a/openedx/core/djangoapps/appsembler/sites/serializers.py b/openedx/core/djangoapps/appsembler/sites/serializers.py index 448215788a6d..f0313669a3a1 100644 --- a/openedx/core/djangoapps/appsembler/sites/serializers.py +++ b/openedx/core/djangoapps/appsembler/sites/serializers.py @@ -3,6 +3,7 @@ from rest_framework import serializers from organizations import api as organizations_api from organizations.models import Organization +from tahoe_sites.zd_helpers import should_site_use_org_models from openedx.core.djangoapps.user_authn.views.registration_form import validate_username from openedx.core.djangoapps.site_configuration.models import SiteConfiguration @@ -87,7 +88,7 @@ def custom_domain_status(self, obj): class OrganizationSerializer(serializers.ModelSerializer): class Meta: model = Organization - fields = ('id', 'name', 'short_name', 'edx_uuid') + fields = ('id', 'name', 'short_name') + ('edx_uuid',) if should_site_use_org_models() else () @beeline.traced(name="OrganizationSerializer.create") def create(self, validated_data): diff --git a/openedx/core/djangoapps/appsembler/sites/tests/test_commands.py b/openedx/core/djangoapps/appsembler/sites/tests/test_commands.py index 76d605c7d37e..5e35d4a5d5a2 100644 --- a/openedx/core/djangoapps/appsembler/sites/tests/test_commands.py +++ b/openedx/core/djangoapps/appsembler/sites/tests/test_commands.py @@ -13,6 +13,7 @@ create_tahoe_site_by_link, get_organization_for_user, get_users_of_organization, + get_uuid_by_organization, ) from tahoe_sites.tests.utils import create_organization_mapping @@ -308,7 +309,9 @@ def test_process_site(self): @patch('openedx.core.djangoapps.appsembler.sites.management.commands.offboard.Command.process_organization_users', return_value=['user1', 'user2']) def test_process_organization(self, mock_process_organization_users): + site = SiteFactory(domain='test') organization = OrganizationFactory.create(name='test') + create_tahoe_site_by_link(organization=organization, site=site) data = self.command.process_organization(organization) assert data == { 'name': organization.name, @@ -316,7 +319,7 @@ def test_process_organization(self, mock_process_organization_users): 'description': organization.description, 'logo': '', 'active': organization.active, - 'UUID': organization.edx_uuid, + 'UUID': get_uuid_by_organization(organization=organization), 'created': organization.created, 'users': ['user1', 'user2'] } diff --git a/openedx/core/djangoapps/appsembler/sites/tests/test_site_config_client.py b/openedx/core/djangoapps/appsembler/sites/tests/test_site_config_client.py index d7231ad2191b..3e0bd5c59dc4 100644 --- a/openedx/core/djangoapps/appsembler/sites/tests/test_site_config_client.py +++ b/openedx/core/djangoapps/appsembler/sites/tests/test_site_config_client.py @@ -10,8 +10,8 @@ from django.utils import timezone from mock import Mock -from organizations.tests.factories import OrganizationFactory from site_config_client.exceptions import SiteConfigurationError +from tahoe_sites.api import create_tahoe_site, get_uuid_by_organization from lms.djangoapps.courseware.access_utils import in_preview_mode from openedx.core.djangoapps.appsembler.sites import ( @@ -29,11 +29,8 @@ @pytest.fixture def site_with_org(): - org = OrganizationFactory.create() - assert org.edx_uuid, 'Should have valid uuid' - site = Site.objects.create(domain='fake-site') - site.organizations.add(org) - return site, org + site_info = create_tahoe_site(domain='fake-site', short_name='FS') + return site_info['site'], site_info['organization'] @pytest.mark.django_db @@ -45,7 +42,8 @@ def test_is_enabled_for_site(monkeypatch, site_with_org): is_enabled = client_helpers.is_enabled_for_site(site) assert is_enabled, 'Enabled if client is installed' - helper.assert_called_with(org.edx_uuid) + uuid = get_uuid_by_organization(organization=org) + helper.assert_called_with(uuid) @pytest.mark.django_db @@ -75,7 +73,7 @@ def test_get_configuration_adapter(site_with_org): with patch('crum.get_current_request', return_value=request): adapter = client_helpers.init_site_configuration_adapter(site) assert adapter, 'Should return if client package is installed' - assert adapter.site_uuid == org.edx_uuid, 'Should set the correct ID' + assert adapter.site_uuid == get_uuid_by_organization(organization=org), 'Should set the correct ID' assert adapter.status == 'draft', 'can be set to draft based on current request parameters' @@ -85,7 +83,7 @@ def test_get_configuration_adapter(site_with_org): adapter = client_helpers.init_site_configuration_adapter(site) assert adapter, 'Should return if client package is installed' - assert adapter.site_uuid == org.edx_uuid, 'Should set the correct ID' + assert adapter.site_uuid == get_uuid_by_organization(organization=org), 'Should set the correct ID' assert adapter.status == 'live', 'by default should be live status' diff --git a/openedx/core/djangoapps/appsembler/sites/tests/test_utils.py b/openedx/core/djangoapps/appsembler/sites/tests/test_utils.py index 8823775c0ab3..92e104a535c4 100644 --- a/openedx/core/djangoapps/appsembler/sites/tests/test_utils.py +++ b/openedx/core/djangoapps/appsembler/sites/tests/test_utils.py @@ -102,17 +102,12 @@ def test_get_active_sites_ordering(self): class OrganizationByRequestTestCase(TestCase): def setUp(self): super(OrganizationByRequestTestCase, self).setUp() - self.siteFoo = SiteFactory.create(domain='foo.dev', name='foo.dev') - self.siteBar = SiteFactory.create(domain='bar.dev', name='bar.dev') self.siteBaz = SiteFactory.create(domain='baz.dev', name='baz.dev') - self.organizationA = OrganizationFactory(linked_site=self.siteFoo) - self.organizationB = OrganizationFactory(linked_site=self.siteFoo) - self.organizationC = OrganizationFactory(linked_site=self.siteBar) self.request = RequestFactory().post('dummy_url') self.request.session = {} for patch_req in ( 'openedx.core.djangoapps.appsembler.sites.utils.get_current_request', - 'openedx.core.djangoapps.theming.helpers.get_current_request' + 'openedx.core.djangoapps.theming.helpers.get_current_request', ): patcher = patch(patch_req) patched_req = patcher.start() @@ -124,40 +119,11 @@ def test_amc_admin_user_no_org_in_request(self): # TODO: would be good to test pass - @patch.dict('django.conf.settings.FEATURES', {'TAHOE_ENABLE_MULTI_ORGS_PER_SITE': False}) - def test_single_organization_multiorg_feature_off(self): - self.request.site = self.siteBar - current_org = get_current_organization() - self.assertEqual(current_org, self.organizationC) - - @patch.dict('django.conf.settings.FEATURES', {'TAHOE_ENABLE_MULTI_ORGS_PER_SITE': False}) - def test_multiple_organization_multiorg_feature_off(self): - self.request.site = self.siteFoo - # fail raising exception if more than one org found for site when feature not enabled - with self.assertRaises(MultipleObjectsReturned): - get_current_organization() - - @patch.dict('django.conf.settings.FEATURES', {'TAHOE_ENABLE_MULTI_ORGS_PER_SITE': True}) - def test_multiple_organizations_multiorg_feature_on(self): - self.request.site = self.siteFoo - # return one org from Site's org relations - current_org = get_current_organization() - self.assertIn(current_org, (self.organizationA, self.organizationB)) - def test_no_org_for_site(self): self.request.site = self.siteBaz with self.assertRaises(Organization.DoesNotExist): get_current_organization() - @patch.dict('django.conf.settings.FEATURES', { - 'TAHOE_ENABLE_MULTI_ORGS_PER_SITE': True, - 'APPSEMBLER_MULTI_TENANT_EMAILS': True - }) - def test_raises_if_multiorg_feature_and_multitenant_email_feature_on(self): - self.request.site = self.siteFoo - with self.assertRaises(ImproperlyConfigured): - get_current_organization() - class LMSLinkByCourseOrgTestCase(TestCase): """ diff --git a/openedx/core/djangoapps/appsembler/sites/utils.py b/openedx/core/djangoapps/appsembler/sites/utils.py index 1a5db30c9d27..e85ca237e2e6 100644 --- a/openedx/core/djangoapps/appsembler/sites/utils.py +++ b/openedx/core/djangoapps/appsembler/sites/utils.py @@ -23,7 +23,6 @@ from django.conf import settings from django.contrib.auth.models import User from django.contrib.sites.models import Site -from django.core.exceptions import ImproperlyConfigured from oauth2_provider.models import AccessToken, RefreshToken, Application from oauth2_provider.generators import generate_client_id @@ -38,7 +37,10 @@ from tahoe_sites.api import ( add_user_to_organization, create_tahoe_site_by_link, + get_organization_by_site, get_organization_for_user, + get_organizations_from_uuids, + get_sites_from_organizations, update_admin_role_in_organization, ) @@ -93,10 +95,7 @@ def get_active_organizations(): """ active_tiers_uuids = get_active_organizations_uuids() - # Now back to the LMS MySQL database - return Organization.objects.filter( - edx_uuid__in=[str(edx_uuid) for edx_uuid in active_tiers_uuids], - ) + return get_organizations_from_uuids(uuids=active_tiers_uuids) def get_active_sites(order_by='domain'): @@ -108,9 +107,7 @@ def get_active_sites(order_by='domain'): TODO: This helper should live in a future Tahoe Sites package. """ - return Site.objects.filter( - organizations__in=get_active_organizations() - ).order_by(order_by) + return get_sites_from_organizations(organizations=get_active_organizations()).order_by(order_by) @beeline.traced(name="get_amc_oauth_app") @@ -308,22 +305,8 @@ def _get_current_organization(failure_return_none=False): ) else: try: - if settings.FEATURES.get('TAHOE_ENABLE_MULTI_ORGS_PER_SITE', False): - if settings.FEATURES.get('APPSEMBLER_MULTI_TENANT_EMAILS', False): - raise ImproperlyConfigured( - 'TAHOE_ENABLE_MULTI_ORGS_PER_SITE and ' - 'APPSEMBLER_MULTI_TENANT_EMAILS are incompatible as ' - 'we are not able to determine the exact Org when more than one ' - 'is associated with a Site.') - current_org = current_site.organizations.first() - if not current_org: - raise Organization.DoesNotExist( - 'TAHOE_ENABLE_MULTI_ORGS_PER_SITE: Could not find current ' - 'organization for site `{}`'.format(repr(current_site)) - ) - else: - current_org = current_site.organizations.get() - except (Organization.DoesNotExist, ImproperlyConfigured): + current_org = get_organization_by_site(site=current_site) + except Organization.DoesNotExist: if not failure_return_none: raise # Re-raise the exception else: @@ -481,7 +464,7 @@ def bootstrap_site(site, org_data=None, username=None): organization_data = org_api.add_organization({ 'name': organization_slug, 'short_name': organization_slug, - 'edx_uuid': org_data.get('edx_uuid') + 'edx_uuid': org_data.get('edx_uuid') # TODO: RED-2845 Remove this line when AMC is migrated }) organization = org_models.Organization.objects.get(id=organization_data.get('id')) create_tahoe_site_by_link(organization=organization, site=site) diff --git a/openedx/core/djangoapps/appsembler/tahoe_tiers/legacy_amc_helpers.py b/openedx/core/djangoapps/appsembler/tahoe_tiers/legacy_amc_helpers.py index cc5d400348b7..3a68c1ba68db 100644 --- a/openedx/core/djangoapps/appsembler/tahoe_tiers/legacy_amc_helpers.py +++ b/openedx/core/djangoapps/appsembler/tahoe_tiers/legacy_amc_helpers.py @@ -1,7 +1,7 @@ """ A module for deprecated AMC tier utilities. -TODO: Remove this module once AMC is shut down. +TODO: Remove this module once AMC is shut down. Related to RED-2845 too """ import logging