Unverified Kaydet (Commit) 98296f86 authored tarafından Carlton Gibson's avatar Carlton Gibson Kaydeden (comit) Mariusz Felisiak

Fixed #30351 -- Handled pre-existing permissions in proxy model permissions data migration.

Regression in 181fb601.
üst 08a4ee06
from django.db import migrations import sys
from django.core.management.color import color_style
from django.db import migrations, transaction
from django.db.models import Q from django.db.models import Q
from django.db.utils import IntegrityError
WARNING = """
A problem arose migrating proxy model permissions for {old} to {new}.
Permission(s) for {new} already existed.
Codenames Q: {query}
Ensure to audit ALL permissions for {old} and {new}.
"""
def update_proxy_model_permissions(apps, schema_editor, reverse=False): def update_proxy_model_permissions(apps, schema_editor, reverse=False):
...@@ -7,6 +20,7 @@ def update_proxy_model_permissions(apps, schema_editor, reverse=False): ...@@ -7,6 +20,7 @@ def update_proxy_model_permissions(apps, schema_editor, reverse=False):
Update the content_type of proxy model permissions to use the ContentType Update the content_type of proxy model permissions to use the ContentType
of the proxy model. of the proxy model.
""" """
style = color_style()
Permission = apps.get_model('auth', 'Permission') Permission = apps.get_model('auth', 'Permission')
ContentType = apps.get_model('contenttypes', 'ContentType') ContentType = apps.get_model('contenttypes', 'ContentType')
for Model in apps.get_models(): for Model in apps.get_models():
...@@ -24,10 +38,16 @@ def update_proxy_model_permissions(apps, schema_editor, reverse=False): ...@@ -24,10 +38,16 @@ def update_proxy_model_permissions(apps, schema_editor, reverse=False):
proxy_content_type = ContentType.objects.get_for_model(Model, for_concrete_model=False) proxy_content_type = ContentType.objects.get_for_model(Model, for_concrete_model=False)
old_content_type = proxy_content_type if reverse else concrete_content_type old_content_type = proxy_content_type if reverse else concrete_content_type
new_content_type = concrete_content_type if reverse else proxy_content_type new_content_type = concrete_content_type if reverse else proxy_content_type
Permission.objects.filter( try:
permissions_query, with transaction.atomic():
content_type=old_content_type, Permission.objects.filter(
).update(content_type=new_content_type) permissions_query,
content_type=old_content_type,
).update(content_type=new_content_type)
except IntegrityError:
old = '{}_{}'.format(old_content_type.app_label, old_content_type.model)
new = '{}_{}'.format(new_content_type.app_label, new_content_type.model)
sys.stdout.write(style.WARNING(WARNING.format(old=old, new=new, query=permissions_query)))
def revert_proxy_model_permissions(apps, schema_editor): def revert_proxy_model_permissions(apps, schema_editor):
......
...@@ -59,3 +59,8 @@ Bugfixes ...@@ -59,3 +59,8 @@ Bugfixes
* Increased the default timeout when using ``Watchman`` to 5 seconds to prevent * Increased the default timeout when using ``Watchman`` to 5 seconds to prevent
falling back to ``StatReloader`` on larger projects and made it customizable falling back to ``StatReloader`` on larger projects and made it customizable
via the ``DJANGO_WATCHMAN_TIMEOUT`` environment variable (:ticket:`30361`). via the ``DJANGO_WATCHMAN_TIMEOUT`` environment variable (:ticket:`30361`).
* Fixed a regression in Django 2.2 that caused a crash when migrating
permissions for proxy models if the target permissions already existed. For
example, when a permission had been created manually or a model had been
migrated from concrete to proxy (:ticket:`30351`).
...@@ -4,6 +4,7 @@ from django.apps import apps ...@@ -4,6 +4,7 @@ from django.apps import apps
from django.contrib.auth.models import Permission, User from django.contrib.auth.models import Permission, User
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.test import TestCase from django.test import TestCase
from django.test.utils import captured_stdout
from .models import Proxy, UserProxy from .models import Proxy, UserProxy
...@@ -152,3 +153,27 @@ class ProxyModelWithSameAppLabelTests(TestCase): ...@@ -152,3 +153,27 @@ class ProxyModelWithSameAppLabelTests(TestCase):
user = User._default_manager.get(pk=user.pk) user = User._default_manager.get(pk=user.pk)
for permission in [self.default_permission, self.custom_permission]: for permission in [self.default_permission, self.custom_permission]:
self.assertTrue(user.has_perm('auth_tests.' + permission.codename)) self.assertTrue(user.has_perm('auth_tests.' + permission.codename))
def test_migrate_with_existing_target_permission(self):
"""
Permissions may already exist:
- Old workaround was to manually create permissions for proxy models.
- Model may have been concrete and then converted to proxy.
Output a reminder to audit relevant permissions.
"""
proxy_model_content_type = ContentType.objects.get_for_model(Proxy, for_concrete_model=False)
Permission.objects.create(
content_type=proxy_model_content_type,
codename='add_proxy',
name='Can add proxy',
)
Permission.objects.create(
content_type=proxy_model_content_type,
codename='display_proxys',
name='May display proxys information',
)
with captured_stdout() as stdout:
update_proxy_permissions.update_proxy_model_permissions(apps, None)
self.assertIn('A problem arose migrating proxy model permissions', stdout.getvalue())
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment