See #152: added management command to execute one-time migration scripts
This commit is contained in:
parent
a57d975183
commit
ac7db73785
|
@ -0,0 +1,66 @@
|
||||||
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
|
|
||||||
|
from funkwhale_api.common import scripts
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = 'Run a specific script from funkwhale_api/common/scripts/'
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
parser.add_argument('script_name', nargs='?', type=str)
|
||||||
|
parser.add_argument(
|
||||||
|
'--noinput', '--no-input', action='store_false', dest='interactive',
|
||||||
|
help="Do NOT prompt the user for input of any kind.",
|
||||||
|
)
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
name = options['script_name']
|
||||||
|
if not name:
|
||||||
|
self.show_help()
|
||||||
|
|
||||||
|
available_scripts = self.get_scripts()
|
||||||
|
try:
|
||||||
|
script = available_scripts[name]
|
||||||
|
except KeyError:
|
||||||
|
raise CommandError(
|
||||||
|
'{} is not a valid script. Run python manage.py script for a '
|
||||||
|
'list of available scripts'.format(name))
|
||||||
|
|
||||||
|
self.stdout.write('')
|
||||||
|
if options['interactive']:
|
||||||
|
message = (
|
||||||
|
'Are you sure you want to execute the script {}?\n\n'
|
||||||
|
"Type 'yes' to continue, or 'no' to cancel: "
|
||||||
|
).format(name)
|
||||||
|
if input(''.join(message)) != 'yes':
|
||||||
|
raise CommandError("Script cancelled.")
|
||||||
|
script['entrypoint'](self, **options)
|
||||||
|
|
||||||
|
def show_help(self):
|
||||||
|
indentation = 4
|
||||||
|
self.stdout.write('')
|
||||||
|
self.stdout.write('Available scripts:')
|
||||||
|
self.stdout.write('Launch with: python manage.py <script_name>')
|
||||||
|
available_scripts = self.get_scripts()
|
||||||
|
for name, script in sorted(available_scripts.items()):
|
||||||
|
self.stdout.write('')
|
||||||
|
self.stdout.write(self.style.SUCCESS(name))
|
||||||
|
self.stdout.write('')
|
||||||
|
for line in script['help'].splitlines():
|
||||||
|
self.stdout.write(' {}'.format(line))
|
||||||
|
self.stdout.write('')
|
||||||
|
|
||||||
|
def get_scripts(self):
|
||||||
|
available_scripts = [
|
||||||
|
k for k in sorted(scripts.__dict__.keys())
|
||||||
|
if not k.startswith('__')
|
||||||
|
]
|
||||||
|
data = {}
|
||||||
|
for name in available_scripts:
|
||||||
|
module = getattr(scripts, name)
|
||||||
|
data[name] = {
|
||||||
|
'name': name,
|
||||||
|
'help': module.__doc__.strip(),
|
||||||
|
'entrypoint': module.main
|
||||||
|
}
|
||||||
|
return data
|
|
@ -0,0 +1,2 @@
|
||||||
|
from . import django_permissions_to_user_permissions
|
||||||
|
from . import test
|
|
@ -0,0 +1,29 @@
|
||||||
|
"""
|
||||||
|
Convert django permissions to user permissions in the database,
|
||||||
|
following the work done in #152.
|
||||||
|
"""
|
||||||
|
from django.db.models import Q
|
||||||
|
from funkwhale_api.users import models
|
||||||
|
|
||||||
|
from django.contrib.auth.models import Permission
|
||||||
|
|
||||||
|
mapping = {
|
||||||
|
'dynamic_preferences.change_globalpreferencemodel': 'settings',
|
||||||
|
'music.add_importbatch': 'library',
|
||||||
|
'federation.change_library': 'federation',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def main(command, **kwargs):
|
||||||
|
for codename, user_permission in sorted(mapping.items()):
|
||||||
|
app_label, c = codename.split('.')
|
||||||
|
p = Permission.objects.get(
|
||||||
|
content_type__app_label=app_label, codename=c)
|
||||||
|
users = models.User.objects.filter(
|
||||||
|
Q(groups__permissions=p) | Q(user_permissions=p)).distinct()
|
||||||
|
total = users.count()
|
||||||
|
|
||||||
|
command.stdout.write('Updating {} users with {} permission...'.format(
|
||||||
|
total, user_permission
|
||||||
|
))
|
||||||
|
users.update(**{'permission_{}'.format(user_permission): True})
|
|
@ -0,0 +1,8 @@
|
||||||
|
"""
|
||||||
|
This is a test script that does nothing.
|
||||||
|
You can launch it just to check how it works.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def main(command, **kwargs):
|
||||||
|
command.stdout.write('Test script run successfully')
|
|
@ -57,7 +57,18 @@ class UserAdmin(AuthUserAdmin):
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, {'fields': ('username', 'password', 'privacy_level')}),
|
(None, {'fields': ('username', 'password', 'privacy_level')}),
|
||||||
(_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
|
(_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
|
||||||
(_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser',
|
(_('Permissions'), {
|
||||||
'permission_library', 'permission_settings', 'permission_federation')}),
|
'fields': (
|
||||||
|
'is_active',
|
||||||
|
'is_staff',
|
||||||
|
'is_superuser',
|
||||||
|
'permission_library',
|
||||||
|
'permission_settings',
|
||||||
|
'permission_federation')}),
|
||||||
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
|
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
|
||||||
)
|
(_('Useless fields'), {
|
||||||
|
'fields': (
|
||||||
|
'user_permissions',
|
||||||
|
'groups',
|
||||||
|
)})
|
||||||
|
)
|
||||||
|
|
|
@ -1,15 +1,41 @@
|
||||||
import factory
|
import factory
|
||||||
|
|
||||||
from funkwhale_api.factories import registry
|
from funkwhale_api.factories import registry, ManyToManyFromList
|
||||||
from django.contrib.auth.models import Permission
|
from django.contrib.auth.models import Permission
|
||||||
|
|
||||||
|
|
||||||
|
@registry.register
|
||||||
|
class GroupFactory(factory.django.DjangoModelFactory):
|
||||||
|
name = factory.Sequence(lambda n: 'group-{0}'.format(n))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = 'auth.Group'
|
||||||
|
|
||||||
|
@factory.post_generation
|
||||||
|
def perms(self, create, extracted, **kwargs):
|
||||||
|
if not create:
|
||||||
|
# Simple build, do nothing.
|
||||||
|
return
|
||||||
|
|
||||||
|
if extracted:
|
||||||
|
perms = [
|
||||||
|
Permission.objects.get(
|
||||||
|
content_type__app_label=p.split('.')[0],
|
||||||
|
codename=p.split('.')[1],
|
||||||
|
)
|
||||||
|
for p in extracted
|
||||||
|
]
|
||||||
|
# A list of permissions were passed in, use them
|
||||||
|
self.permissions.add(*perms)
|
||||||
|
|
||||||
|
|
||||||
@registry.register
|
@registry.register
|
||||||
class UserFactory(factory.django.DjangoModelFactory):
|
class UserFactory(factory.django.DjangoModelFactory):
|
||||||
username = factory.Sequence(lambda n: 'user-{0}'.format(n))
|
username = factory.Sequence(lambda n: 'user-{0}'.format(n))
|
||||||
email = factory.Sequence(lambda n: 'user-{0}@example.com'.format(n))
|
email = factory.Sequence(lambda n: 'user-{0}@example.com'.format(n))
|
||||||
password = factory.PostGenerationMethodCall('set_password', 'test')
|
password = factory.PostGenerationMethodCall('set_password', 'test')
|
||||||
subsonic_api_token = None
|
subsonic_api_token = None
|
||||||
|
groups = ManyToManyFromList('groups')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = 'users.User'
|
model = 'users.User'
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from funkwhale_api.common.management.commands import script
|
||||||
|
from funkwhale_api.common import scripts
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def command():
|
||||||
|
return script.Command()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('script_name', [
|
||||||
|
'django_permissions_to_user_permissions',
|
||||||
|
'test',
|
||||||
|
])
|
||||||
|
def test_script_command_list(command, script_name, mocker):
|
||||||
|
mocked = mocker.patch(
|
||||||
|
'funkwhale_api.common.scripts.{}.main'.format(script_name))
|
||||||
|
|
||||||
|
command.handle(script_name=script_name, interactive=False)
|
||||||
|
|
||||||
|
mocked.assert_called_once_with(
|
||||||
|
command, script_name=script_name, interactive=False)
|
||||||
|
|
||||||
|
|
||||||
|
def test_django_permissions_to_user_permissions(factories, command):
|
||||||
|
group = factories['auth.Group'](
|
||||||
|
perms=[
|
||||||
|
'federation.change_library'
|
||||||
|
]
|
||||||
|
)
|
||||||
|
user1 = factories['users.User'](
|
||||||
|
perms=[
|
||||||
|
'dynamic_preferences.change_globalpreferencemodel',
|
||||||
|
'music.add_importbatch',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
user2 = factories['users.User'](
|
||||||
|
perms=[
|
||||||
|
'music.add_importbatch',
|
||||||
|
],
|
||||||
|
groups=[group]
|
||||||
|
)
|
||||||
|
|
||||||
|
scripts.django_permissions_to_user_permissions.main(command)
|
||||||
|
|
||||||
|
user1.refresh_from_db()
|
||||||
|
user2.refresh_from_db()
|
||||||
|
|
||||||
|
assert user1.permission_settings is True
|
||||||
|
assert user1.permission_library is True
|
||||||
|
assert user1.permission_federation is False
|
||||||
|
|
||||||
|
assert user2.permission_settings is False
|
||||||
|
assert user2.permission_library is True
|
||||||
|
assert user2.permission_federation is True
|
Loading…
Reference in New Issue