From 9e41e9d77c8b48f0a320cec48df3b0bc5b82a369 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Tue, 13 Nov 2018 20:44:51 +0100 Subject: [PATCH] general: add support for importing permissions (#26924) --- chrono/agendas/models.py | 12 ++++- .../management/commands/import_site.py | 14 ++++-- chrono/manager/utils.py | 2 +- chrono/manager/views.py | 10 +++- tests/test_import_export.py | 49 ++++++++++++++++++- tests/test_manager.py | 11 +++++ 6 files changed, 87 insertions(+), 11 deletions(-) diff --git a/chrono/agendas/models.py b/chrono/agendas/models.py index f414c261..7540d21f 100644 --- a/chrono/agendas/models.py +++ b/chrono/agendas/models.py @@ -53,6 +53,10 @@ class ICSError(Exception): pass +class AgendaImportError(Exception): + pass + + class Agenda(models.Model): label = models.CharField(_('Label'), max_length=150) slug = models.SlugField(_('Identifier'), max_length=160) @@ -130,12 +134,18 @@ class Agenda(models.Model): @classmethod def import_json(cls, data, overwrite=False): data = data.copy() - permissions = data.pop('permissions') + permissions = data.pop('permissions') or {} if data['kind'] == 'events': events = data.pop('events') elif data['kind'] == 'meetings': meetingtypes = data.pop('meetingtypes') desks = data.pop('desks') + for permission in ('view', 'edit'): + if permissions.get(permission): + try: + data[permission + '_role'] = Group.objects.get(name=permissions[permission]) + except Group.DoesNotExist: + raise AgendaImportError(_('Missing "%s" role') % permissions[permission]) agenda, created = cls.objects.get_or_create(slug=data['slug'], defaults=data) if data['kind'] == 'events': if overwrite: diff --git a/chrono/manager/management/commands/import_site.py b/chrono/manager/management/commands/import_site.py index 827954ec..f32a1dd5 100644 --- a/chrono/manager/management/commands/import_site.py +++ b/chrono/manager/management/commands/import_site.py @@ -17,8 +17,9 @@ import json import sys -from django.core.management.base import BaseCommand +from django.core.management.base import BaseCommand, CommandError +from chrono.agendas.models import AgendaImportError from chrono.manager.utils import import_site @@ -43,7 +44,10 @@ class Command(BaseCommand): fd = sys.stdin else: fd = open(filename) - import_site(json.load(fd), - if_empty=options['if_empty'], - clean=options['clean'], - overwrite=options['overwrite']) + try: + import_site(json.load(fd), + if_empty=options['if_empty'], + clean=options['clean'], + overwrite=options['overwrite']) + except AgendaImportError as exc: + raise CommandError(u'%s' % exc) diff --git a/chrono/manager/utils.py b/chrono/manager/utils.py index 32ec50d1..20ad2e04 100644 --- a/chrono/manager/utils.py +++ b/chrono/manager/utils.py @@ -16,7 +16,7 @@ from django.db import transaction -from chrono.agendas.models import Agenda +from chrono.agendas.models import Agenda, AgendaImportError def export_site(): diff --git a/chrono/manager/views.py b/chrono/manager/views.py index 29851f33..e2173494 100644 --- a/chrono/manager/views.py +++ b/chrono/manager/views.py @@ -33,7 +33,8 @@ from django.views.generic import (DetailView, CreateView, UpdateView, MonthArchiveView) from chrono.agendas.models import (Agenda, Event, MeetingType, TimePeriod, - Booking, Desk, TimePeriodException, ICSError) + Booking, Desk, TimePeriodException, + ICSError, AgendaImportError) from .forms import (AgendaAddForm, AgendaEditForm, EventForm, NewMeetingTypeForm, MeetingTypeForm, TimePeriodForm, ImportEventsForm, NewDeskForm, DeskForm, TimePeriodExceptionForm, @@ -95,7 +96,12 @@ class AgendasImportView(FormView): form.add_error('agendas_json', _('File is not in the expected JSON format.')) return self.form_invalid(form) - results = import_site(agendas_json, overwrite=True) + try: + results = import_site(agendas_json, overwrite=True) + except AgendaImportError as exc: + form.add_error('agendas_json', u'%s' % exc) + return self.form_invalid(form) + if results.get('created') == 0 and results.get('updated') == 0: messages.info(self.request, _('No agendas were found.')) else: diff --git a/tests/test_import_export.py b/tests/test_import_export.py index b123923f..cd6a71eb 100644 --- a/tests/test_import_export.py +++ b/tests/test_import_export.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + from __future__ import unicode_literals import datetime @@ -8,12 +10,14 @@ import sys import tempfile import pytest -from django.core.management import call_command +from django.contrib.auth.models import Group +from django.core.management import call_command, CommandError from django.utils.encoding import force_bytes from django.utils.six import StringIO from django.utils.timezone import make_aware -from chrono.agendas.models import (Agenda, Event, TimePeriod, Desk, TimePeriodException) +from chrono.agendas.models import (Agenda, Event, TimePeriod, Desk, + TimePeriodException, AgendaImportError) from chrono.manager.utils import import_site from test_api import some_data, meetings_agenda, time_zone, mock_now @@ -112,3 +116,44 @@ def test_import_export(app, some_data, meetings_agenda): empty_output = get_output_of_command('export_site', output=os.path.join(tempdir, 't.json')) assert os.path.exists(os.path.join(tempdir, 't.json')) shutil.rmtree(tempdir) + + +def test_import_export_permissions(app, some_data, meetings_agenda): + group1 = Group(name=u'gé1') + group1.save() + group2 = Group(name=u'gé2') + group2.save() + + meetings_agenda.view_role = group1 + meetings_agenda.edit_role = group2 + meetings_agenda.save() + output = get_output_of_command('export_site') + assert len(json.loads(output)['agendas']) == 3 + import_site(data={}, clean=True) + assert Agenda.objects.count() == 0 + Group.objects.all().delete() + + with pytest.raises(AgendaImportError) as excinfo: + import_site(json.loads(output), overwrite=True) + assert u'%s' % excinfo.value == u'Missing "gé1" role' + + group1 = Group(name=u'gé1') + group1.save() + with pytest.raises(AgendaImportError) as excinfo: + import_site(json.loads(output), overwrite=True) + assert u'%s' % excinfo.value == u'Missing "gé2" role' + + with tempfile.NamedTemporaryFile() as f: + f.write(force_bytes(output)) + f.flush() + with pytest.raises(CommandError) as excinfo: + call_command('import_site', f.name) + assert u'%s' % excinfo.value == u'Missing "gé2" role' + + group2 = Group(name=u'gé2') + group2.save() + import_site(json.loads(output), overwrite=True) + + agenda = Agenda.objects.get(slug=meetings_agenda.slug) + assert agenda.view_role == group1 + assert agenda.edit_role == group2 diff --git a/tests/test_manager.py b/tests/test_manager.py index 0127253c..ab78e7ca 100644 --- a/tests/test_manager.py +++ b/tests/test_manager.py @@ -1529,3 +1529,14 @@ def test_import_agenda(app, admin_user): resp = resp.form.submit().follow() assert 'An agenda has been created. No agenda updated.' in resp.text assert Agenda.objects.count() == 1 + + # reference to unknown group + agenda_export_dict = json.loads(agenda_export) + agenda_export_dict['agendas'][0]['permissions']['view'] = u'gé1' + agenda_export = json.dumps(agenda_export_dict).encode('utf-8') + Agenda.objects.all().delete() + resp = app.get('/manage/', status=200) + resp = resp.click('Import') + resp.form['agendas_json'] = Upload('export.json', agenda_export, 'application/json') + resp = resp.form.submit() + assert u'Missing "gé1" role' in resp.text