예제 #1
0
    def setUpClass(cls):
        super(SharedAuthTest, cls).setUpClass()
        settings.SHARED_APPS = ('django_tenants',
                                'django.contrib.auth',
                                'django.contrib.contenttypes', )
        settings.TENANT_APPS = ('dts_test_app', )
        settings.INSTALLED_APPS = settings.SHARED_APPS + settings.TENANT_APPS
        cls.sync_shared()
        Tenant(domain_urls=['test.com'], schema_name=get_public_schema_name()).save()

        # Create a tenant
        cls.tenant = Tenant(domain_urls=['tenant.test.com'], schema_name='tenant')
        cls.tenant.save()

        # Create some users
        with schema_context(get_public_schema_name()):  # this could actually also be executed inside a tenant
            cls.user1 = User(username='******', email="*****@*****.**")
            cls.user1.save()
            cls.user2 = User(username='******', email="*****@*****.**")
            cls.user2.save()

        # Create instances on the tenant that point to the users on public
        with tenant_context(cls.tenant):
            cls.d1 = ModelWithFkToPublicUser(user=cls.user1)
            cls.d1.save()
            cls.d2 = ModelWithFkToPublicUser(user=cls.user2)
            cls.d2.save()
예제 #2
0
    def ready(self):
        # Test for configuration recommendations. These are best practices,
        # they avoid hard to find bugs and unexpected behaviour.
        if not hasattr(settings, 'TENANT_APPS'):
            raise ImproperlyConfigured('TENANT_APPS setting not set')

        if not settings.TENANT_APPS:
            raise ImproperlyConfigured("TENANT_APPS is empty. "
                                       "Maybe you don't need this app?")

        if not hasattr(settings, 'TENANT_MODEL'):
            raise ImproperlyConfigured('TENANT_MODEL setting not set')

        if 'django_tenants.routers.TenantSyncRouter' not in settings.DATABASE_ROUTERS:
            raise ImproperlyConfigured(
                "DATABASE_ROUTERS setting must contain "
                "'django_tenants.routers.TenantSyncRouter'.")

        if hasattr(settings, 'PG_EXTRA_SEARCH_PATHS'):
            if get_public_schema_name() in settings.PG_EXTRA_SEARCH_PATHS:
                raise ImproperlyConfigured(
                    "%s can not be included on PG_EXTRA_SEARCH_PATHS." %
                    get_public_schema_name())

            # make sure no tenant schema is in settings.PG_EXTRA_SEARCH_PATHS
            invalid_schemas = set(settings.PG_EXTRA_SEARCH_PATHS).intersection(
                get_tenant_model().objects.all().values_list('schema_name',
                                                             flat=True))
            if invalid_schemas:
                raise ImproperlyConfigured(
                    "Do not include tenant schemas (%s) on PG_EXTRA_SEARCH_PATHS."
                    % list(invalid_schemas))
예제 #3
0
    def ready(self):
        # Test for configuration recommendations. These are best practices,
        # they avoid hard to find bugs and unexpected behaviour.
        if not hasattr(settings, "TENANT_APPS"):
            raise ImproperlyConfigured("TENANT_APPS setting not set")

        if not settings.TENANT_APPS:
            raise ImproperlyConfigured("TENANT_APPS is empty. " "Maybe you don't need this app?")

        if not hasattr(settings, "TENANT_MODEL"):
            raise ImproperlyConfigured("TENANT_MODEL setting not set")

        if "django_tenants.routers.TenantSyncRouter" not in settings.DATABASE_ROUTERS:
            raise ImproperlyConfigured(
                "DATABASE_ROUTERS setting must contain " "'django_tenants.routers.TenantSyncRouter'."
            )

        if hasattr(settings, "PG_EXTRA_SEARCH_PATHS"):
            if get_public_schema_name() in settings.PG_EXTRA_SEARCH_PATHS:
                raise ImproperlyConfigured(
                    "%s can not be included on PG_EXTRA_SEARCH_PATHS." % get_public_schema_name()
                )

            # make sure no tenant schema is in settings.PG_EXTRA_SEARCH_PATHS
            invalid_schemas = set(settings.PG_EXTRA_SEARCH_PATHS).intersection(
                get_tenant_model().objects.all().values_list("schema_name", flat=True)
            )
            if invalid_schemas:
                raise ImproperlyConfigured(
                    "Do not include tenant schemas (%s) on PG_EXTRA_SEARCH_PATHS." % list(invalid_schemas)
                )
예제 #4
0
    def setUpClass(cls):
        super(SharedAuthTest, cls).setUpClass()
        settings.SHARED_APPS = (
            'django_tenants',
            'django.contrib.auth',
            'django.contrib.contenttypes',
        )
        settings.TENANT_APPS = ('dts_test_app', )
        settings.INSTALLED_APPS = settings.SHARED_APPS + settings.TENANT_APPS
        cls.sync_shared()
        Tenant(domain_urls=['test.com'],
               schema_name=get_public_schema_name()).save()

        # Create a tenant
        cls.tenant = Tenant(domain_urls=['tenant.test.com'],
                            schema_name='tenant')
        cls.tenant.save()

        # Create some users
        with schema_context(get_public_schema_name(
        )):  # this could actually also be executed inside a tenant
            cls.user1 = User(username='******', email="*****@*****.**")
            cls.user1.save()
            cls.user2 = User(username='******', email="*****@*****.**")
            cls.user2.save()

        # Create instances on the tenant that point to the users on public
        with tenant_context(cls.tenant):
            cls.d1 = ModelWithFkToPublicUser(user=cls.user1)
            cls.d1.save()
            cls.d2 = ModelWithFkToPublicUser(user=cls.user2)
            cls.d2.save()
예제 #5
0
    def test_signal_on_migrate_schemas(self):
        """
        Check signals are sent on running of migrate_schemas.
        """
        executor = get_executor()
        tenant = get_tenant_model()(schema_name='test')
        tenant.save()
        domain = get_tenant_domain_model()(tenant=tenant,
                                           domain='something.test.com')
        domain.save()

        # test the signal gets called when running migrate
        with catch_signal(schema_migrated) as handler:
            call_command('migrate_schemas', interactive=False, verbosity=0)

        if executor == 'simple':
            handler.assert_has_calls([
                mock.call(
                    schema_name=get_public_schema_name(),
                    sender=mock.ANY,
                    signal=schema_migrated,
                ),
                mock.call(schema_name='test',
                          sender=mock.ANY,
                          signal=schema_migrated)
            ])
        elif executor == 'multiprocessing':
            # public schema gets migrated in the current process, always
            handler.assert_called_once_with(
                schema_name=get_public_schema_name(),
                sender=mock.ANY,
                signal=schema_migrated,
            )
예제 #6
0
    def delete_tenant(self):
        """
        We don't actually delete the tenant out of the database, but we associate them
        with a the public schema user and change their url to reflect their delete
        datetime and previous owner.

        The caller should verify that the user deleting the tenant owns the tenant.
        """
        # Prevent public tenant schema from being deleted
        if self.schema_name == get_public_schema_name():
            raise ValueError("Cannot delete public tenant schema")

        for user_obj in self.users.all():
            self.remove_user(user_obj)

        # Seconds since epoch, time() returns a float, so we convert to
        # an int first to truncate the decimal portion
        time_string = str(int(time.time()))
        new_url = "{}-{}-{}".format(time_string, str(self.owner.id),
                                    self.domain_url)
        self.domain_url = new_url
        # The schema generated each time (even with same url slug) will be unique.
        # So we do not have to worry about a conflict with that

        # Set the owner to the system user (public schema owner)
        public_tenant = get_tenant_model().objects.get(
            schema_name=get_public_schema_name())

        # Transfer ownership to system
        self.transfer_ownership(public_tenant.owner)
예제 #7
0
    def setUp(self):
        super().setUp()

        settings.SHARED_APPS = ('django_tenants',
                                'customers',
                                'django.contrib.auth',
                                'django.contrib.contenttypes', )
        settings.TENANT_APPS = ('dts_test_app', )
        settings.INSTALLED_APPS = settings.SHARED_APPS + settings.TENANT_APPS
        self.sync_shared()
        self.public_tenant = get_tenant_model()(schema_name=get_public_schema_name())
        self.public_tenant.save()
        self.public_domain = get_tenant_domain_model()(tenant=self.public_tenant, domain='test.com')
        self.public_domain.save()

        # Create a tenant
        self.tenant = get_tenant_model()(schema_name='tenant')
        self.tenant.save()
        self.domain = get_tenant_domain_model()(tenant=self.tenant, domain='tenant.test.com')
        self.domain.save()

        # Create some users
        with schema_context(get_public_schema_name()):  # this could actually also be executed inside a tenant
            self.user1 = User(username='******', email="*****@*****.**")
            self.user1.save()
            self.user2 = User(username='******', email="*****@*****.**")
            self.user2.save()

        # Create instances on the tenant that point to the users on public
        with tenant_context(self.tenant):
            self.d1 = ModelWithFkToPublicUser(user=self.user1)
            self.d1.save()
            self.d2 = ModelWithFkToPublicUser(user=self.user2)
            self.d2.save()
예제 #8
0
    def setUp(self):
        super(SharedAuthTest, self).setUp()

        settings.SHARED_APPS = ('django_tenants',
                                'customers',
                                'django.contrib.auth',
                                'django.contrib.contenttypes', )
        settings.TENANT_APPS = ('dts_test_app', )
        settings.INSTALLED_APPS = settings.SHARED_APPS + settings.TENANT_APPS
        self.sync_shared()
        self.public_tenant = get_tenant_model()(schema_name=get_public_schema_name())
        self.public_tenant.save()
        self.public_domain = get_tenant_domain_model()(tenant=self.public_tenant, domain='test.com')
        self.public_domain.save()

        # Create a tenant
        self.tenant = get_tenant_model()(schema_name='tenant')
        self.tenant.save()
        self.domain = get_tenant_domain_model()(tenant=self.tenant, domain='tenant.test.com')
        self.domain.save()

        # Create some users
        with schema_context(get_public_schema_name()):  # this could actually also be executed inside a tenant
            self.user1 = User(username='******', email="*****@*****.**")
            self.user1.save()
            self.user2 = User(username='******', email="*****@*****.**")
            self.user2.save()

        # Create instances on the tenant that point to the users on public
        with tenant_context(self.tenant):
            self.d1 = ModelWithFkToPublicUser(user=self.user1)
            self.d1.save()
            self.d2 = ModelWithFkToPublicUser(user=self.user2)
            self.d2.save()
예제 #9
0
 def set_schema_to_public(self):
     """
     Instructs to stay in the common 'public' schema.
     """
     self.tenant = FakeTenant(schema_name=get_public_schema_name())
     self.schema_name = get_public_schema_name()
     self.set_settings_schema(self.schema_name)
     self.search_path_set = False
예제 #10
0
 def set_schema_to_public(self):
     """
     Instructs to stay in the common 'public' schema.
     """
     self.tenant = FakeTenant(schema_name=get_public_schema_name())
     self.schema_name = get_public_schema_name()
     self.set_settings_schema(self.schema_name)
     self.search_path_set = False
예제 #11
0
    def setUp(self):
        super().setUp()
        # Django calls syncdb by default for the test database, but we want
        # a blank public schema for this set of tests.
        connection.set_schema_to_public()
        with connection.cursor() as cursor:
            cursor.execute('DROP SCHEMA %s CASCADE; CREATE SCHEMA %s;'
                           % (get_public_schema_name(), get_public_schema_name(), ))

        self.sync_shared()
예제 #12
0
    def setUp(self):
        super(BaseSyncTest, self).setUp()
        # Django calls syncdb by default for the test database, but we want
        # a blank public schema for this set of tests.
        connection.set_schema_to_public()
        with connection.cursor() as cursor:
            cursor.execute('DROP SCHEMA %s CASCADE; CREATE SCHEMA %s;'
                           % (get_public_schema_name(), get_public_schema_name(), ))

        self.sync_shared()
예제 #13
0
    def process_request(self, request):
        # Short circuit if tenant is already set by another middleware.
        # This allows for multiple tenant-resolving middleware chained together.
        if hasattr(request, "tenant"):
            return

        connection.set_schema_to_public()

        tenant = None
        urlconf = None

        TenantModel = get_tenant_model()
        hostname = self.hostname_from_request(request)
        subfolder_prefix_path = "/{}/".format(get_subfolder_prefix())

        # We are in the public tenant
        if not request.path.startswith(subfolder_prefix_path):
            try:
                tenant = TenantModel.objects.get(
                    schema_name=get_public_schema_name())
            except TenantModel.DoesNotExist:
                raise self.TENANT_NOT_FOUND_EXCEPTION(
                    "Unable to find public tenant")

            # Do we have a public-specific urlconf?
            if (hasattr(settings, "PUBLIC_SCHEMA_URLCONF")
                    and tenant.schema_name == get_public_schema_name()):
                urlconf = settings.PUBLIC_SCHEMA_URLCONF

        # We are in a specific tenant
        else:
            path_chunks = request.path[len(subfolder_prefix_path):].split("/")
            tenant_subfolder = path_chunks[0]

            try:
                tenant = TenantModel.objects.get(
                    domains__domain=tenant_subfolder)
            except TenantModel.DoesNotExist:
                raise self.TENANT_NOT_FOUND_EXCEPTION(
                    'No tenant for subfolder "%s"' % (tenant_subfolder or ""))

            tenant.domain_subfolder = tenant_subfolder
            urlconf = get_subfolder_urlconf(tenant)

        tenant.domain_url = hostname
        request.tenant = tenant

        connection.set_tenant(request.tenant)
        clear_url_caches(
        )  # Required to remove previous tenant prefix from cache, if present

        if urlconf:
            request.urlconf = urlconf
            set_urlconf(urlconf)
예제 #14
0
    def ready(self):
        from django.db import connections

        # Test for configuration recommendations. These are best practices,
        # they avoid hard to find bugs and unexpected behaviour.
        if not hasattr(settings, 'TENANT_APPS'):
            raise ImproperlyConfigured('TENANT_APPS setting not set')

        if not settings.TENANT_APPS:
            raise ImproperlyConfigured("TENANT_APPS is empty. "
                                       "Maybe you don't need this app?")

        if not hasattr(settings, 'TENANT_MODEL'):
            raise ImproperlyConfigured('TENANT_MODEL setting not set')

        if not hasattr(settings, 'TENANT_SESSION_KEY'):
            setattr(settings, 'TENANT_SESSION_KEY', 'tenant_schema')

        if not hasattr(settings, 'TENANT_SELECTION_METHOD'):
            setattr(settings, 'TENANT_SELECTION_METHOD', 'domain')

        if not hasattr(settings, 'TENANT_DATABASE'):
            setattr(settings, 'TENANT_DATABASE', 'default')

        if 'django_tenants.routers.TenantSyncRouter' not in settings.DATABASE_ROUTERS:
            raise ImproperlyConfigured(
                "DATABASE_ROUTERS setting must contain "
                "'django_tenants.routers.TenantSyncRouter'.")

        if hasattr(settings, 'PG_EXTRA_SEARCH_PATHS'):
            if get_public_schema_name() in settings.PG_EXTRA_SEARCH_PATHS:
                raise ImproperlyConfigured(
                    "%s can not be included on PG_EXTRA_SEARCH_PATHS." %
                    get_public_schema_name())

            # make sure no tenant schema is in settings.PG_EXTRA_SEARCH_PATHS

            # first check that the model table is created
            model = get_tenant_model()
            c = connections[DEFAULT_DB_ALIAS].cursor()
            c.execute(
                'SELECT 1 FROM information_schema.tables WHERE table_name = %s;',
                [model._meta.db_table])
            if c.fetchone():
                invalid_schemas = set(
                    settings.PG_EXTRA_SEARCH_PATHS).intersection(
                        model.objects.all().values_list('schema_name',
                                                        flat=True))
                if invalid_schemas:
                    raise ImproperlyConfigured(
                        "Do not include tenant schemas (%s) on PG_EXTRA_SEARCH_PATHS."
                        % list(invalid_schemas))
예제 #15
0
    def setUpClass(cls):
        settings.TENANT_MODEL = "django_tenants.Tenant"
        settings.SHARED_APPS = ("django_tenants",)
        settings.TENANT_APPS = ("dts_test_app", "django.contrib.contenttypes", "django.contrib.auth")
        settings.INSTALLED_APPS = settings.SHARED_APPS + settings.TENANT_APPS

        # Django calls syncdb by default for the test database, but we want
        # a blank public schema for this set of tests.
        connection.set_schema_to_public()
        cursor = connection.cursor()
        cursor.execute(
            "DROP SCHEMA %s CASCADE; CREATE SCHEMA %s;" % (get_public_schema_name(), get_public_schema_name())
        )
        super(BaseTestCase, cls).setUpClass()
예제 #16
0
    def ready(self):
        from django.db import connections

        # Test for configuration recommendations. These are best practices,
        # they avoid hard to find bugs and unexpected behaviour.
        if not hasattr(settings, 'TENANT_APPS'):
            raise ImproperlyConfigured('TENANT_APPS setting not set')

        if not settings.TENANT_APPS:
            raise ImproperlyConfigured("TENANT_APPS is empty. "
                                       "Maybe you don't need this app?")

        if not hasattr(settings, 'TENANT_MODEL'):
            raise ImproperlyConfigured('TENANT_MODEL setting not set')

        if not hasattr(settings, 'TENANT_SESSION_KEY'):
            setattr(settings, 'TENANT_SESSION_KEY', 'tenant_schema')

        if not hasattr(settings, 'TENANT_SELECTION_METHOD'):
            setattr(settings, 'TENANT_SELECTION_METHOD', 'domain')

        if not hasattr(settings, 'TENANT_DATABASE'):
            setattr(settings, 'TENANT_DATABASE', 'default')

        if 'django_tenants.routers.TenantSyncRouter' not in settings.DATABASE_ROUTERS:
            raise ImproperlyConfigured("DATABASE_ROUTERS setting must contain "
                                       "'django_tenants.routers.TenantSyncRouter'.")

        if hasattr(settings, 'PG_EXTRA_SEARCH_PATHS'):
            if get_public_schema_name() in settings.PG_EXTRA_SEARCH_PATHS:
                raise ImproperlyConfigured(
                    "%s can not be included on PG_EXTRA_SEARCH_PATHS."
                    % get_public_schema_name())

            # make sure no tenant schema is in settings.PG_EXTRA_SEARCH_PATHS

            # first check that the model table is created
            model = get_tenant_model()
            c = connections[DEFAULT_DB_ALIAS].cursor()
            c.execute(
                'SELECT 1 FROM information_schema.tables WHERE table_name = %s;',
                [model._meta.db_table]
            )
            if c.fetchone():
                invalid_schemas = set(settings.PG_EXTRA_SEARCH_PATHS).intersection(
                    model.objects.all().values_list('schema_name', flat=True))
                if invalid_schemas:
                    raise ImproperlyConfigured(
                        "Do not include tenant schemas (%s) on PG_EXTRA_SEARCH_PATHS."
                        % list(invalid_schemas))
예제 #17
0
파일: tenants.py 프로젝트: kzailac/poem-2
    def get(self, request, name=None):
        results = []
        if name:
            if name == 'SuperPOEM_Tenant':
                tenants = Tenant.objects.filter(
                    schema_name=get_public_schema_name())
            else:
                tenants = Tenant.objects.filter(name=name)

            if len(tenants) == 0:
                return Response({'detail': 'Tenant not found.'},
                                status=status.HTTP_404_NOT_FOUND)

        else:
            tenants = Tenant.objects.all()

        for tenant in tenants:
            if tenant.schema_name == get_public_schema_name():
                tenant_name = 'SuperPOEM Tenant'
                metric_key = 'metric_templates'
            else:
                tenant_name = tenant.name
                metric_key = 'metrics'

            try:
                domain = get_tenant_domain_model().objects.get(tenant=tenant)

                data = get_tenant_resources(tenant.schema_name)
                results.append(
                    dict(name=tenant_name,
                         schema_name=tenant.schema_name,
                         domain_url=domain.domain,
                         created_on=datetime.date.strftime(
                             tenant.created_on, '%Y-%m-%d'),
                         nr_metrics=data[metric_key],
                         nr_probes=data['probes']))

            except get_tenant_domain_model().DoesNotExist:
                return error_response(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail='Domain for tenant {} not found.'.format(
                        tenant_name))

        if name:
            results = results[0]

        else:
            results = sorted(results, key=lambda k: k['name'].lower())

        return Response(results)
예제 #18
0
    def ready(self):
        from django.db import connection

        # Test for configuration recommendations. These are best practices,
        # they avoid hard to find bugs and unexpected behaviour.

        if hasattr(settings, 'HAS_MULTI_TYPE_TENANTS') and settings.HAS_MULTI_TYPE_TENANTS:
            if not hasattr(settings, 'TENANT_TYPES'):
                raise ImproperlyConfigured('Using multi type you must setup TENANT_TYPES setting')
            if get_public_schema_name() not in settings.TENANT_TYPES:
                raise ImproperlyConfigured('get_public_schema_name() value not found as a key in TENANTS')
            if not hasattr(settings, 'MULTI_TYPE_DATABASE_FIELD'):
                raise ImproperlyConfigured('Using multi type you must setup MULTI_TYPE_DATABASE_FIELD setting')
        else:
            if not hasattr(settings, 'TENANT_APPS'):
                raise ImproperlyConfigured('TENANT_APPS setting not set')

            if not settings.TENANT_APPS:
                raise ImproperlyConfigured("TENANT_APPS is empty. "
                                           "Maybe you don't need this app?")

        if not hasattr(settings, 'TENANT_MODEL'):
            raise ImproperlyConfigured('TENANT_MODEL setting not set')

        tenant_sync_router = getattr(settings, 'TENANT_SYNC_ROUTER', 'django_tenants.routers.TenantSyncRouter')
        if tenant_sync_router not in settings.DATABASE_ROUTERS:
            raise ImproperlyConfigured("DATABASE_ROUTERS setting must contain "
                                       "'%s'." % tenant_sync_router)

        validate_extra_extensions()
예제 #19
0
    def connect_user(self, user_obj, tenant_user):
        """
        Connect a public user to a tenant user.
        """
        if self.schema_name == get_public_schema_name():
            raise SchemaError(
                "It's not allowed to connect a public user to a public user."
                "Make sure the current tenant {} is not the "
                "public tenant.".format(self))

        self._check_user_exists(user_obj)

        # The tenant user already linked to a public user
        if tenant_user.supervisor is not None:
            raise ExistsError(
                "The tenant user already linked to public user: {}".format(
                    tenant_user.supervisor))

        tenant_user.supervisor = user_obj
        tenant_user.save(update_fields=['supervisor'])

        # Link user to tenant
        fields = self.__class__.users.field.remote_field.through_fields + (
            'organization_user', )
        self.__class__.users.through._default_manager.create(
            **dict(zip(fields, (self, user_obj, tenant_user.pk))))

        tenant_user_connected.send(sender=self.__class__,
                                   user=user_obj,
                                   tenant=self,
                                   tenant_user=tenant_user)
예제 #20
0
def is_shared_app(app):
    if has_multi_type_tenants():
        _apps = get_tenant_types()[get_public_schema_name()]['APPS']
    else:
        _apps = settings.SHARED_APPS

    return app["app_label"] in [get_app_label(_app) for _app in _apps]
예제 #21
0
def create_public_tenant(domain_url, owner_username, owner_email,
                         **owner_extra):
    UserModel = get_user_model()
    TenantModel = get_tenant_model()
    public_schema_name = get_public_schema_name()

    if TenantModel.objects.filter(schema_name=public_schema_name).first():
        raise ExistsError("Public tenant already exists.")

    # Create public tenant user. This user doesn't go through object manager
    # create_user function because public tenant does not exist yet
    profile = UserModel.objects.create(username=owner_username,
                                       email=owner_email,
                                       is_active=True,
                                       **owner_extra)
    profile.set_unusable_password()
    profile.save()

    # Create public tenant
    public_tenant = TenantModel.objects.create(schema_name=public_schema_name,
                                               name='Public Tenant',
                                               owner=profile)

    # Add one or more domains for the tenant
    get_tenant_domain_model().objects.create(domain=domain_url,
                                             tenant=public_tenant,
                                             is_primary=True)
예제 #22
0
파일: tenants.py 프로젝트: kzailac/poem-2
    def delete(self, request, name=None):
        if name:
            if request.tenant.schema_name == get_public_schema_name():
                if request.user.is_superuser:
                    try:
                        tenant = Tenant.objects.get(name=name)
                        tenant.delete()

                        return Response(status=status.HTTP_204_NO_CONTENT)

                    except Tenant.DoesNotExist:
                        return error_response(
                            status_code=status.HTTP_404_NOT_FOUND,
                            detail='Tenant not found.')

                else:
                    return error_response(
                        status_code=status.HTTP_401_UNAUTHORIZED,
                        detail='You do not have permission to delete tenants.')

            else:
                return error_response(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail='Cannot delete tenant outside public schema.')

        else:
            return error_response(status_code=status.HTTP_400_BAD_REQUEST,
                                  detail='Tenant name should be specified.')
예제 #23
0
    def test_tenant_apps_and_shared_apps_can_have_the_same_apps(self):
        """
        Tests that both SHARED_APPS and TENANT_APPS can have apps in common.
        In this case they should get synced to both tenant and public schemas.
        """
        settings.SHARED_APPS = ('django_tenants',  # 2 tables
                                'customers',
                                'django.contrib.auth',  # 6 tables
                                'django.contrib.contenttypes',  # 1 table
                                'django.contrib.sessions', )  # 1 table
        settings.TENANT_APPS = ('django.contrib.sessions', )  # 1 table
        settings.INSTALLED_APPS = settings.SHARED_APPS + settings.TENANT_APPS
        self.sync_shared()
        tenant = get_tenant_model()(schema_name='test')
        tenant.save()

        domain = get_tenant_domain_model()(tenant=tenant, domain='arbitrary.test.com')
        domain.save()

        shared_tables = self.get_tables_list_in_schema(get_public_schema_name())
        tenant_tables = self.get_tables_list_in_schema(tenant.schema_name)
        self.assertEqual(2+6+1+1+self.MIGRATION_TABLE_SIZE, len(shared_tables))
        self.assertIn('django_session', shared_tables)
        self.assertEqual(1+self.MIGRATION_TABLE_SIZE, len(tenant_tables))
        self.assertIn('django_session', tenant_tables)
예제 #24
0
파일: admin.py 프로젝트: azizur77/etools
 def execute_sync(country_pk, synchronizer):
     country = Country.objects.get(pk=country_pk)
     if country.schema_name == get_public_schema_name():
         vision_sync_task(synchronizers=[synchronizer, ])
     else:
         sync_handler.delay(country.name, synchronizer)
     return HttpResponseRedirect(reverse('admin:users_country_change', args=[country.pk]))
예제 #25
0
    def test_tenant_apps_and_shared_apps_can_have_the_same_apps(self):
        """
        Tests that both SHARED_APPS and TENANT_APPS can have apps in common.
        In this case they should get synced to both tenant and public schemas.
        """
        settings.SHARED_APPS = (
            'django_tenants',  # 2 tables
            'django.contrib.auth',  # 6 tables
            'django.contrib.contenttypes',  # 1 table
            'django.contrib.sessions',
        )  # 1 table
        settings.TENANT_APPS = ('django.contrib.sessions', )  # 1 table
        settings.INSTALLED_APPS = settings.SHARED_APPS + settings.TENANT_APPS
        self.sync_shared()
        tenant = Tenant(domain_urls=['arbitrary.test.com'], schema_name='test')
        tenant.save()

        shared_tables = self.get_tables_list_in_schema(
            get_public_schema_name())
        tenant_tables = self.get_tables_list_in_schema(tenant.schema_name)
        self.assertEqual(2 + 6 + 1 + 1 + self.MIGRATION_TABLE_SIZE,
                         len(shared_tables))
        self.assertIn('django_session', shared_tables)
        self.assertEqual(1 + self.MIGRATION_TABLE_SIZE, len(tenant_tables))
        self.assertIn('django_session', tenant_tables)
예제 #26
0
    def delete(self, request, name):
        if request.tenant.schema_name == get_public_schema_name() and \
                request.user.is_superuser:
            try:

                mts = admin_models.MetricTemplate.objects.filter(
                    tags__name=name)

                tag = admin_models.MetricTags.objects.get(name=name)

                for mt in mts:
                    mt.tags.remove(tag)
                    update_metrics(mt, mt.name, mt.probekey)

                tag.delete()

                return Response(status=status.HTTP_204_NO_CONTENT)

            except admin_models.MetricTags.DoesNotExist:
                return error_response(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail="The requested metric tag does not exist.")

        else:
            return error_response(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="You do not have permission to delete metric tags.")
예제 #27
0
    def process_request(self, request):
        # Connection needs first to be at the public schema, as this is where
        # the tenant metadata is stored.
        connection.set_schema_to_public()
        tenant_model = get_tenant_model()
        user = getattr(request, 'user', None)

        try:
            tenant = self.get_tenant(tenant_model, user)
        except tenant_model.DoesNotExist:
            tenant = tenant_model.objects.get(schema_name=get_public_schema_name())

        request.tenant = tenant

        connection.set_tenant(request.tenant)

        # Content type can no longer be cached as public and tenant schemas
        # have different models. If someone wants to change this, the cache
        # needs to be separated between public and shared schemas. If this
        # cache isn't cleared, this can cause permission problems. For example,
        # on public, a particular model has id 14, but on the tenants it has
        # the id 15. if 14 is cached instead of 15, the permissions for the
        # wrong model will be fetched.
        ContentType.objects.clear_cache()

        # Do we have a public-specific urlconf?
        if hasattr(settings, 'PUBLIC_SCHEMA_URLCONF') and request.tenant.schema_name == get_public_schema_name():
            request.urlconf = settings.PUBLIC_SCHEMA_URLCONF
예제 #28
0
    def delete(self, request, name=None, tag=None):
        if request.tenant.schema_name == get_public_schema_name() and \
                request.user.is_superuser:
            if name and tag:
                if tag == 'centos6':
                    ostag = admin_models.OSTag.objects.get(name='CentOS 6')

                elif tag == 'centos7':
                    ostag = admin_models.OSTag.objects.get(name='CentOS 7')

                else:
                    raise NotFound(status=404, detail='OS tag does not exist.')

                try:
                    admin_models.YumRepo.objects.get(name=name,
                                                     tag=ostag).delete()

                    return Response(status=status.HTTP_204_NO_CONTENT)

                except admin_models.YumRepo.DoesNotExist:
                    raise NotFound(status=404,
                                   detail='YUM repo does not exist.')

            else:
                return error_response(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail='YUM repo name and/or tag should be specified.')

        else:
            return error_response(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail='You do not have permission to delete YUM repos.')
예제 #29
0
    def post(self, request):
        if request.tenant.schema_name == get_public_schema_name() and \
                request.user.is_superuser:
            try:
                admin_models.YumRepo.objects.create(
                    name=request.data['name'],
                    tag=admin_models.OSTag.objects.get(
                        name=request.data['tag']),
                    content=request.data['content'],
                    description=request.data['description'])

                return Response(status=status.HTTP_201_CREATED)

            except IntegrityError:
                return error_response(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail='YUM repo with this name and tag already exists.')

            except admin_models.OSTag.DoesNotExist:
                return error_response(status_code=status.HTTP_404_NOT_FOUND,
                                      detail='OS tag does not exist.')

            except KeyError as e:
                return error_response(status_code=status.HTTP_400_BAD_REQUEST,
                                      detail='Missing data key: {}'.format(
                                          e.args[0]))

        else:
            return error_response(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail='You do not have permission to add YUM repos.')
예제 #30
0
    def handle(self, *args, **options):
        super(MigrateSchemasCommand, self).handle(*args, **options)
        self.PUBLIC_SCHEMA_NAME = get_public_schema_name()

        if self.sync_public and not self.schema_name:
            self.schema_name = self.PUBLIC_SCHEMA_NAME

        executor = get_executor(codename=self.executor)(self.args, self.options)

        if self.sync_public:
            executor.run_migrations(tenants=[self.PUBLIC_SCHEMA_NAME])
        if self.sync_tenant:
            if self.schema_name and self.schema_name != self.PUBLIC_SCHEMA_NAME:
                if not schema_exists(self.schema_name):
                    raise RuntimeError('Schema "{}" does not exist'.format(
                        self.schema_name))
                else:
                    tenants = [self.schema_name]
            else:
                tenants = get_tenant_model().objects.only(
                    'schema_name').exclude(
                    schema_name=self.PUBLIC_SCHEMA_NAME).values_list(
                    'schema_name', flat=True)

            executor.run_migrations(tenants=tenants)
예제 #31
0
    def add_user(self, user_obj, is_superuser=False, is_staff=False):
        """
        Create a user inside the tenant and set its supervisor to the public user.
        """
        if self.schema_name == get_public_schema_name():
            raise SchemaError(
                "It's not allowed to add a public user to the public tenant."
                "Make sure the current tenant {} is not the "
                "public tenant.".format(self))

        self._check_user_exists(user_obj)

        # Create a user in the tenant with generated username and email
        # And link it to the public user
        time_string = str(int(time.time()))
        tenant_user = get_tenant_user_model().objects.create(
            email='{}_{}'.format(user_obj.email, time_string),
            username='******'.format(user_obj.username, time_string),
            supervisor=user_obj,
            is_superuser=is_superuser,
            is_staff=is_staff,
            is_verified=True)

        # Link user to tenant
        self._link_to_tenant_user(user_obj, tenant_user)

        tenant_user_added.send(sender=self.__class__,
                               user=user_obj,
                               tenant=self)
예제 #32
0
def is_shared_app(app):
    if has_multi_type_tenants():
        _apps = get_tenant_types()[get_public_schema_name()]['APPS']
    else:
        _apps = settings.SHARED_APPS

    return app_in_list(app['app_label'], _apps)
예제 #33
0
 def test_PG_EXTRA_SEARCH_PATHS(self):
     del apps.all_models['django_tenants']
     c = connection.cursor()
     c.execute('DROP SCHEMA {0} CASCADE; CREATE SCHEMA {0};'.format(
         get_public_schema_name()
     ))
     apps.set_installed_apps(['customers', 'django_tenants'])
예제 #34
0
    def handle(self, *args, **options):
        self.sync_tenant = options.get('tenant')
        self.sync_public = options.get('shared')
        self.schema_name = options.get('schema_name')
        self.executor = options.get('executor')
        self.installed_apps = settings.INSTALLED_APPS
        self.args = args
        self.options = options

        if self.schema_name:
            if self.sync_public:
                raise CommandError("schema should only be used with the --tenant switch.")
            elif self.schema_name == get_public_schema_name():
                self.sync_public = True
            else:
                self.sync_tenant = True
        elif not self.sync_public and not self.sync_tenant:
            # no options set, sync both
            self.sync_tenant = True
            self.sync_public = True

        if hasattr(settings, 'TENANT_APPS'):
            self.tenant_apps = settings.TENANT_APPS
        if hasattr(settings, 'SHARED_APPS'):
            self.shared_apps = settings.SHARED_APPS
예제 #35
0
    def allow_migrate(self, db, app_label, model_name=None, **hints):
        # the imports below need to be done here else django <1.5 goes crazy
        # https://code.djangoproject.com/ticket/20704
        from django.db import connections
        from django_tenants.utils import get_public_schema_name, get_tenant_database_alias

        if db != get_tenant_database_alias():
            return False

        connection = connections[db]
        public_schema_name = get_public_schema_name()
        if has_multi_type_tenants():
            tenant_types = get_tenant_types()
            if connection.schema_name == public_schema_name:
                installed_apps = tenant_types[public_schema_name]['APPS']
            else:
                installed_apps = tenant_types[
                    connection.tenant.tenant_type]['APPS']
        else:
            if connection.schema_name == public_schema_name:
                installed_apps = settings.SHARED_APPS
            else:
                installed_apps = settings.TENANT_APPS
        if not self.app_in_list(app_label, installed_apps):
            return False
        return None
예제 #36
0
    def handle(self, *args, **options):
        super(MigrateSchemasCommand, self).handle(*args, **options)
        self.PUBLIC_SCHEMA_NAME = get_public_schema_name()

        if self.sync_public and not self.schema_name:
            self.schema_name = self.PUBLIC_SCHEMA_NAME

        executor = get_executor(codename=self.executor)(self.args, self.options)

        if self.sync_public:
            executor.run_migrations(tenants=[self.PUBLIC_SCHEMA_NAME])
        if self.sync_tenant:
            if self.schema_name and self.schema_name != self.PUBLIC_SCHEMA_NAME:
                if not schema_exists(self.schema_name):
                    raise RuntimeError('Schema "{}" does not exist'.format(
                        self.schema_name))
                else:
                    tenants = [self.schema_name]
            else:
                tenants = get_tenant_model().objects.only(
                    'schema_name').exclude(
                    schema_name=self.PUBLIC_SCHEMA_NAME).values_list(
                    'schema_name', flat=True)

            executor.run_migrations(tenants=tenants)
            tenant_objects = get_tenant_model().objects.filter(schema_name__in=tenants)

            for tenant in tenant_objects:
                if not isinstance(tenant, TenantMixin):
                    continue
                post_schema_migrate.send(sender=TenantMixin, tenant=tenant.serializable_fields())
예제 #37
0
    def handle(self, *args, **options):
        super(MigrateSchemasCommand, self).handle(*args, **options)
        self.PUBLIC_SCHEMA_NAME = get_public_schema_name()

        if self.sync_public and not self.schema_name:
            self.schema_name = self.PUBLIC_SCHEMA_NAME

        executor = get_executor(codename=self.executor)(self.args,
                                                        self.options)

        if self.sync_public:
            executor.run_migrations(tenants=[self.PUBLIC_SCHEMA_NAME])
        if self.sync_tenant:
            if self.schema_name and self.schema_name != self.PUBLIC_SCHEMA_NAME:
                if not schema_exists(self.schema_name):
                    raise RuntimeError('Schema "{}" does not exist'.format(
                        self.schema_name))
                else:
                    tenants = [self.schema_name]
            else:
                tenants = get_tenant_model().objects.only(
                    'schema_name').exclude(
                        schema_name=self.PUBLIC_SCHEMA_NAME).values_list(
                            'schema_name', flat=True)

            executor.run_migrations(tenants=tenants)
예제 #38
0
def get_user(request):
    if not hasattr(request, '_cached_user'):
        if request.tenant.schema_name == get_public_schema_name():
            request._cached_user = auth.get_user(request)
        else:
            request._cached_user = get_tenant_user(request)
    return request._cached_user
예제 #39
0
    def process_request(self, request):
        connection.set_schema_to_public()
        hostname_without_port = remove_www_and_dev(
            request.get_host().split(':')[0])

        try:
            domain = get_tenant_domain_model().objects.select_related(
                'tenant').get(domain=hostname_without_port)
            request.tenant = domain.tenant
        except utils.DatabaseError:
            request.urlconf = settings.PUBLIC_SCHEMA_URLCONF
            return
        except get_tenant_domain_model().DoesNotExist:
            if hostname_without_port in ("127.0.0.1", "localhost"):
                request.urlconf = settings.PUBLIC_SCHEMA_URLCONF
                return
            else:
                raise Http404

        connection.set_tenant(request.tenant)
        ContentType.objects.clear_cache()

        if hasattr(
                settings, 'PUBLIC_SCHEMA_URLCONF'
        ) and request.tenant.schema_name == get_public_schema_name():
            request.urlconf = settings.PUBLIC_SCHEMA_URLCONF
예제 #40
0
    def handle(self, *args, **options):
        self.sync_tenant = options.get('tenant')
        self.sync_public = options.get('shared')
        self.schema_name = options.get('schema_name')
        self.executor = options.get('executor')
        self.installed_apps = settings.INSTALLED_APPS
        self.args = args
        self.options = options

        if self.schema_name:
            if self.sync_public:
                raise CommandError(
                    "schema should only be used with the --tenant switch.")
            elif self.schema_name == get_public_schema_name():
                self.sync_public = True
            else:
                self.sync_tenant = True
        elif not self.sync_public and not self.sync_tenant:
            # no options set, sync both
            self.sync_tenant = True
            self.sync_public = True

        if hasattr(settings, 'TENANT_APPS'):
            self.tenant_apps = settings.TENANT_APPS
        if hasattr(settings, 'SHARED_APPS'):
            self.shared_apps = settings.SHARED_APPS
예제 #41
0
def update_metric_history(sender, instance, created, **kwargs):
    if not created:
        schemas = list(
            Tenant.objects.all().values_list('schema_name', flat=True)
        )
        schemas.remove(get_public_schema_name())
        probes = admin_models.ProbeHistory.objects.filter(
            package=instance
        )
        for schema in schemas:
            with schema_context(schema):
                for probe in probes:
                    metrics = Metric.objects.filter(probekey=probe)
                    for metric in metrics:
                        vers = TenantHistory.objects.filter(
                            object_id=metric.id
                        ).order_by('-date_created')

                        for ver in vers:
                            serialized_data = json.loads(ver.serialized_data)
                            serialized_data[0]['fields']['probekey'] = [
                                probe.name, probe.package.version
                            ]
                            ver.serialized_data = json.dumps(serialized_data)
                            ver.save()
예제 #42
0
 def test_shared_apps_does_not_sync_tenant_apps(self):
     """
     Tests that if an app is in SHARED_APPS, it does not get synced to
     the a tenant schema.
     """
     shared_tables = self.get_tables_list_in_schema(get_public_schema_name())
     self.assertEqual(2+6+1+self.MIGRATION_TABLE_SIZE, len(shared_tables))
     self.assertNotIn('django_session', shared_tables)
예제 #43
0
 def setUpClass(cls):
     super(TenantDataAndSettingsTest, cls).setUpClass()
     settings.SHARED_APPS = ('django_tenants', )
     settings.TENANT_APPS = ('dts_test_app',
                             'django.contrib.contenttypes',
                             'django.contrib.auth', )
     settings.INSTALLED_APPS = settings.SHARED_APPS + settings.TENANT_APPS
     cls.sync_shared()
     Tenant(domain_urls=['test.com'], schema_name=get_public_schema_name()).save()
예제 #44
0
 def get_tenant(self, domain_model, hostname):
     try:
         return super().get_tenant(domain_model, hostname)
     except domain_model.DoesNotExist:
         schema_name = self.DEFAULT_SCHEMA_NAME
         if not schema_name:
             schema_name = get_public_schema_name()
         tenant_model = get_tenant_model()
         return tenant_model.objects.get(schema_name=schema_name)
예제 #45
0
    def _cursor(self, name=None):
        """
        Here it happens. We hope every Django db operation using PostgreSQL
        must go through this to get the cursor handle. We change the path.
        """
        if name:
            # Only supported and required by Django 1.11 (server-side cursor)
            cursor = super(DatabaseWrapper, self)._cursor(name=name)
        else:
            cursor = super(DatabaseWrapper, self)._cursor()

        # optionally limit the number of executions - under load, the execution
        # of `set search_path` can be quite time consuming
        if (not get_limit_set_calls()) or not self.search_path_set or self._previous_cursor != cursor:
            # Store the cursor pointer to check if it has changed since we 
            # last validated.
            self._previous_cursor = cursor
            # Actual search_path modification for the cursor. Database will
            # search schemata from left to right when looking for the object
            # (table, index, sequence, etc.).
            if not self.schema_name:
                raise ImproperlyConfigured("Database schema not set. Did you forget "
                                           "to call set_schema() or set_tenant()?")
            _check_schema_name(self.schema_name)
            public_schema_name = get_public_schema_name()
            search_paths = []

            if self.schema_name == public_schema_name:
                search_paths = [public_schema_name]
            elif self.include_public_schema:
                search_paths = [self.schema_name, public_schema_name]
            else:
                search_paths = [self.schema_name]

            search_paths.extend(EXTRA_SEARCH_PATHS)

            if name:
                # Named cursor can only be used once
                cursor_for_search_path = self.connection.cursor()
            else:
                # Reuse
                cursor_for_search_path = cursor

            # In the event that an error already happened in this transaction and we are going
            # to rollback we should just ignore database error when setting the search_path
            # if the next instruction is not a rollback it will just fail also, so
            # we do not have to worry that it's not the good one
            try:
                cursor_for_search_path.execute('SET search_path = {0}'.format(','.join(search_paths)))
            except (django.db.utils.DatabaseError, psycopg2.InternalError):
                self.search_path_set = False
            else:
                self.search_path_set = True
            if name:
                cursor_for_search_path.close()
        return cursor
예제 #46
0
    def handle(self, *args, **options):
        super(MigrateSchemasCommand, self).handle(*args, **options)
        self.PUBLIC_SCHEMA_NAME = get_public_schema_name()

        if self.sync_public and not self.schema_name:
            self.schema_name = self.PUBLIC_SCHEMA_NAME

        if self.sync_public:
            self.run_migrations(self.schema_name, settings.SHARED_APPS, options)
        if self.sync_tenant:
            if self.schema_name and self.schema_name != self.PUBLIC_SCHEMA_NAME:
                if not schema_exists(self.schema_name):
                    raise RuntimeError('Schema "{}" does not exist'.format(
                        self.schema_name))
                else:
                    self.run_migrations(self.schema_name, settings.TENANT_APPS, options)
            else:
                all_tenants = get_tenant_model().objects.exclude(schema_name=get_public_schema_name())
                for tenant in all_tenants:
                    self.run_migrations(tenant.schema_name, settings.TENANT_APPS, options)
예제 #47
0
def restore_schema(task, **kwargs):
    """ Switches the schema back to the one from before running the task. """
    from django_tenants.utils import get_public_schema_name

    schema_name, include_public = getattr(task,
                                          '_old_schema',
                                          (get_public_schema_name(), True))

    # If the schema names match, don't do anything.
    if connection.schema_name == schema_name:
        return

    connection.set_schema(schema_name, include_public=include_public)
예제 #48
0
 def setUpClass(cls):
     super(RoutesTestCase, cls).setUpClass()
     settings.SHARED_APPS = ('django_tenants',
                             'customers')
     settings.TENANT_APPS = ('dts_test_app',
                             'django.contrib.contenttypes',
                             'django.contrib.auth', )
     settings.INSTALLED_APPS = settings.SHARED_APPS + settings.TENANT_APPS
     cls.sync_shared()
     cls.public_tenant = get_tenant_model()(schema_name=get_public_schema_name())
     cls.public_tenant.save()
     cls.public_domain = get_tenant_domain_model()(domain='test.com', tenant=cls.public_tenant)
     cls.public_domain.save()
예제 #49
0
 def handle(self, *args, **options):
     """
     Iterates a command over all registered schemata.
     """
     if options['schema_name']:
         # only run on a particular schema
         connection.set_schema_to_public()
         self.execute_command(get_tenant_model().objects.get(schema_name=options['schema_name']), self.COMMAND_NAME,
                              *args, **options)
     else:
         for tenant in get_tenant_model().objects.all():
             if not (options['skip_public'] and tenant.schema_name == get_public_schema_name()):
                 self.execute_command(tenant, self.COMMAND_NAME, *args, **options)
예제 #50
0
    def verify_need_change_tenant_dir(self):
        if connection.schema_name != get_public_schema_name() and \
                self.current_tenant_dir_name != self.get_tenant_dir_name():
            print "TenantFileSystemFinder change config $$$$$$$$$$$$$$$$$$$$ ==============="
            print "self.current_tenant_dir_name: {0}".format(self.current_tenant_dir_name)
            print "tenant_dir_name: {0}".format(self.get_tenant_dir_name())

            # print "TenantSiteFileSystemFinder change config $$$$$$$$$$$$$$$$$$$$ ==============="
            # print "self.current_tenant_dir_name: {}".format(self.current_tenant_dir_name)
            # print "self.site_dir: {}".format(self.site_dir)
            # print "connection.schema_name: {}".format(connection.schema_name)
            # print "bucket_static_name: {}".format(self.get_bucket_static_name())
            self.set_tenant_dir_name()
            self.config_by_tenant_dir()
예제 #51
0
    def test_restoring_schema_name(self):
        update_task.apply_async(
            args=(self.dummy1.pk, 'updated-name'),
            kwargs={'_schema_name': self.tenant1.schema_name}
        )
        self.assertEqual(connection.schema_name, get_public_schema_name())

        connection.set_tenant(self.tenant1)
        update_task.apply_async(
            args=(self.dummy2.pk, 'updated-name'),
            kwargs={'_schema_name': self.tenant2.schema_name}
        )
        self.assertEqual(connection.schema_name, self.tenant1.schema_name)

        connection.set_tenant(self.tenant2)
        # The model does not exist in the public schema.
        with self.assertRaises(DummyModel.DoesNotExist):
            update_task.apply_async(
                args=(self.dummy2.pk, 'updated-name'),
                kwargs={'_schema_name': get_public_schema_name()}
            )

        self.assertEqual(connection.schema_name, self.tenant2.schema_name)
예제 #52
0
    def setUpClass(cls):
        super(TenantDataAndSettingsTest, cls).setUpClass()
        settings.SHARED_APPS = ('django_tenants',
                                'customers')
        settings.TENANT_APPS = ('dts_test_app',
                                'django.contrib.contenttypes',
                                'django.contrib.auth', )
        settings.INSTALLED_APPS = settings.SHARED_APPS + settings.TENANT_APPS
        cls.sync_shared()

        tenant = get_tenant_model()(schema_name=get_public_schema_name())
        tenant.save()
        domain = get_tenant_domain_model()(tenant=tenant, domain='test.com')
        domain.save()
예제 #53
0
    def allow_migrate(self, db, app_label, model_name=None, **hints):
        # the imports below need to be done here else django <1.5 goes crazy
        # https://code.djangoproject.com/ticket/20704
        from django.db import connection
        from django_tenants.utils import get_public_schema_name

        if connection.schema_name == get_public_schema_name():
            if not self.app_in_list(app_label, settings.SHARED_APPS):
                return False
        else:
            if not self.app_in_list(app_label, settings.TENANT_APPS):
                return False

        return None
예제 #54
0
def switch_schema(task, kwargs, **kw):
    """ Switches schema of the task, before it has been run. """
    # Lazily load needed functions, as they import django model functions which
    # in turn load modules that need settings to be loaded and we can't
    # guarantee this module was loaded when the settings were ready.
    from django_tenants.utils import get_public_schema_name, get_tenant_model

    old_schema = (connection.schema_name, connection.include_public_schema)
    setattr(task, '_old_schema', old_schema)

    # Pop it from the kwargs since tasks don't except the additional kwarg.
    # This change is transparent to the system.
    schema = kwargs.pop('_schema_name', get_public_schema_name())

    # If the schema has not changed, don't do anything.
    if connection.schema_name == schema:
        return

    if connection.schema_name != get_public_schema_name():
        connection.set_schema_to_public()

    tenant = get_tenant_model().objects.get(schema_name=schema)
    connection.set_tenant(tenant, include_public=True)
예제 #55
0
    def delete(self, force_drop=False, *args, **kwargs):
        """
        Deletes this row. Drops the tenant's schema if the attribute
        auto_drop_schema set to True.
        """
        if connection.schema_name not in (self.schema_name, get_public_schema_name()):
            raise Exception("Can't delete tenant outside it's own schema or "
                            "the public schema. Current schema is %s."
                            % connection.schema_name)

        if schema_exists(self.schema_name) and (self.auto_drop_schema or force_drop):
            cursor = connection.cursor()
            cursor.execute('DROP SCHEMA %s CASCADE' % self.schema_name)

        super(TenantMixin, self).delete(*args, **kwargs)
예제 #56
0
    def save(self, verbosity=1, *args, **kwargs):
        is_new = self.pk is None

        if is_new and connection.schema_name != get_public_schema_name():
            raise Exception("Can't create tenant outside the public schema. "
                            "Current schema is %s." % connection.schema_name)
        elif not is_new and connection.schema_name not in (self.schema_name, get_public_schema_name()):
            raise Exception("Can't update tenant outside it's own schema or "
                            "the public schema. Current schema is %s."
                            % connection.schema_name)

        super(TenantMixin, self).save(*args, **kwargs)

        if is_new and self.auto_create_schema:
            try:
                self.create_schema(check_if_exists=True, verbosity=verbosity)
                post_schema_sync.send(sender=TenantMixin, tenant=self)
            except:
                # We failed creating the tenant, delete what we created and
                # re-raise the exception
                self.delete(force_drop=True)
                raise
        elif is_new:
            schema_needs_to_be_sync.send(sender=TenantMixin, tenant=self)
예제 #57
0
    def test_shared_apps_does_not_sync_tenant_apps(self):
        """
        Tests that if an app is in SHARED_APPS, it does not get synced to
        the a tenant schema.
        """
        settings.SHARED_APPS = ('django_tenants',  # 2 tables
                                'django.contrib.auth',  # 6 tables
                                'django.contrib.contenttypes', )  # 1 table
        settings.TENANT_APPS = ('django.contrib.sessions', )
        settings.INSTALLED_APPS = settings.SHARED_APPS + settings.TENANT_APPS
        self.sync_shared()

        shared_tables = self.get_tables_list_in_schema(get_public_schema_name())
        self.assertEqual(2+6+1+self.MIGRATION_TABLE_SIZE, len(shared_tables))
        self.assertNotIn('django_session', shared_tables)
예제 #58
0
    def test_content_types_is_not_mandatory(self):
        """
        Tests that even if content types is in SHARED_APPS, it's
        not required in TENANT_APPS.
        """
        tenant = get_tenant_model()(schema_name='test')
        tenant.save()
        domain = get_tenant_domain_model()(tenant=tenant, domain='something.test.com')
        domain.save()

        shared_tables = self.get_tables_list_in_schema(get_public_schema_name())
        tenant_tables = self.get_tables_list_in_schema(tenant.schema_name)
        self.assertEqual(2+1 + self.MIGRATION_TABLE_SIZE, len(shared_tables))
        self.assertIn('django_session', tenant_tables)
        self.assertEqual(1+self.MIGRATION_TABLE_SIZE, len(tenant_tables))
        self.assertIn('django_session', tenant_tables)
예제 #59
0
    def test_tenant_apps_and_shared_apps_can_have_the_same_apps(self):
        """
        Tests that both SHARED_APPS and TENANT_APPS can have apps in common.
        In this case they should get synced to both tenant and public schemas.
        """
        tenant = get_tenant_model()(schema_name='test')
        tenant.save()

        domain = get_tenant_domain_model()(tenant=tenant, domain='arbitrary.test.com')
        domain.save()

        shared_tables = self.get_tables_list_in_schema(get_public_schema_name())
        tenant_tables = self.get_tables_list_in_schema(tenant.schema_name)
        self.assertEqual(2+6+1+1+self.MIGRATION_TABLE_SIZE, len(shared_tables))
        self.assertIn('django_session', shared_tables)
        self.assertEqual(1+self.MIGRATION_TABLE_SIZE, len(tenant_tables))
        self.assertIn('django_session', tenant_tables)
예제 #60
0
    def allow_migrate(self, db, app_label, model_name=None, **hints):
        # the imports below need to be done here else django <1.5 goes crazy
        # https://code.djangoproject.com/ticket/20704
        from django.db import connection
        from django_tenants.utils import get_public_schema_name

        # for INSTALLED_APPS we need a name
        from django.apps import apps
        app_name = apps.get_app_config(app_label).name

        if connection.schema_name == get_public_schema_name():
            if app_name not in settings.SHARED_APPS:
                return False
        else:
            if app_name not in settings.TENANT_APPS:
                return False

        return None