From f5704ace4ded4c99d5543da8098450da2e167909 Mon Sep 17 00:00:00 2001 From: Emmanuel Cazenave Date: Wed, 2 May 2018 13:01:22 +0200 Subject: [PATCH 1/2] add UI for import/export site (#23210) --- src/authentic2/forms.py | 4 ++ .../authentic2/manager/homepage.html | 8 ++- .../authentic2/manager/site_import.html | 18 ++++++ src/authentic2/manager/urls.py | 4 ++ src/authentic2/manager/views.py | 39 +++++++++++- tests/test_manager.py | 62 +++++++++++++++++++ 6 files changed, 132 insertions(+), 3 deletions(-) create mode 100644 src/authentic2/manager/templates/authentic2/manager/site_import.html diff --git a/src/authentic2/forms.py b/src/authentic2/forms.py index 2f387a93..804481f8 100644 --- a/src/authentic2/forms.py +++ b/src/authentic2/forms.py @@ -191,3 +191,7 @@ class AuthenticationForm(auth_forms.AuthenticationForm): if keys: self.exponential_backoff.success(*keys) return self.cleaned_data + + +class SiteImportForm(forms.Form): + site_json = forms.FileField(label=_('Site Export File')) diff --git a/src/authentic2/manager/templates/authentic2/manager/homepage.html b/src/authentic2/manager/templates/authentic2/manager/homepage.html index 77d37c50..d9635385 100644 --- a/src/authentic2/manager/templates/authentic2/manager/homepage.html +++ b/src/authentic2/manager/templates/authentic2/manager/homepage.html @@ -5,7 +5,13 @@ {% endblock %} {% block appbar %} - {% blocktrans %}Here you can manage objects related to organizational units, users, roles and applications.{% endblocktrans %} +

{% blocktrans %}Here you can manage objects related to organizational units, users, roles and applications.{% endblocktrans %}

+ + + {% endblock %} {% block content %} diff --git a/src/authentic2/manager/templates/authentic2/manager/site_import.html b/src/authentic2/manager/templates/authentic2/manager/site_import.html new file mode 100644 index 00000000..b6afd2b4 --- /dev/null +++ b/src/authentic2/manager/templates/authentic2/manager/site_import.html @@ -0,0 +1,18 @@ +{% extends "authentic2/manager/form.html" %} +{% load i18n %} + +{% block appbar %} +

{% trans "Site Import" %}

+{% endblock %} + +{% block content %} + +
+ {% csrf_token %} + {{ form.as_p }} +
+ + {% trans 'Cancel' %} +
+
+{% endblock %} diff --git a/src/authentic2/manager/urls.py b/src/authentic2/manager/urls.py index bac85aca..a5a21ce8 100644 --- a/src/authentic2/manager/urls.py +++ b/src/authentic2/manager/urls.py @@ -115,6 +115,10 @@ urlpatterns = required( # backoffice menu as json url(r'^menu.json$', views.menu_json), + + # general management + url(r'^site-export/$', views.site_export, name='a2-manager-site-export'), + url(r'^site-import/$', views.site_import, name='a2-manager-site-import'), ) ) diff --git a/src/authentic2/manager/views.py b/src/authentic2/manager/views.py index e55a01ff..bc11804c 100644 --- a/src/authentic2/manager/views.py +++ b/src/authentic2/manager/views.py @@ -5,7 +5,7 @@ from django.core.exceptions import PermissionDenied from django.views.generic.base import ContextMixin from django.views.generic.edit import FormMixinBase from django.views.generic import (FormView, UpdateView, CreateView, DeleteView, TemplateView, - DetailView) + DetailView, View) from django.views.generic.detail import SingleObjectMixin from django.http import HttpResponse, Http404 from django.utils.encoding import force_text @@ -21,7 +21,8 @@ from django_select2.views import AutoResponseView from django_rbac.utils import get_ou_model -from authentic2.forms import modelform_factory +from authentic2.data_transfer import export_site, import_site, DataImportError, ImportContext +from authentic2.forms import modelform_factory, SiteImportForm from authentic2.utils import redirect from authentic2.decorators import json as json_view from authentic2 import hooks @@ -607,3 +608,37 @@ class Select2View(AutoResponseView): return widget select2 = Select2View.as_view() + + +class SiteExport(View): + + def get(self, request, *args, **kwargs): + return HttpResponse( + json.dumps(export_site(), indent=4), content_type='application/json') + + +site_export = SiteExport.as_view() + + +class SiteImportView(FormView): + form_class = SiteImportForm + template_name = 'authentic2/manager/site_import.html' + success_url = reverse_lazy('a2-manager-homepage') + + def form_valid(self, form): + try: + json_site = json.load(self.request.FILES['site_json']) + except ValueError: + form.add_error('site_json', _('File is not in the expected JSON format.')) + return self.form_invalid(form) + + try: + import_site(json_site, ImportContext()) + except DataImportError as e: + form.add_error('site_json', unicode(e)) + return self.form_invalid(form) + + return super(SiteImportView, self).form_valid(form) + + +site_import = SiteImportView.as_view() diff --git a/tests/test_manager.py b/tests/test_manager.py index d664ef99..5e65d494 100644 --- a/tests/test_manager.py +++ b/tests/test_manager.py @@ -1,9 +1,12 @@ import pytest +import json from urlparse import urlparse from django.core.urlresolvers import reverse from django.core import mail +from webtest import Upload + from authentic2.a2_rbac.utils import get_default_ou from django_rbac.utils import get_ou_model, get_role_model @@ -552,3 +555,62 @@ def test_manager_many_ou_auto_admin_role(app, ou1, admin, user_with_auto_admin_r assert set(names) == {u'Auto Admin Role'} test_user_listing_auto_admin_role(user_with_auto_admin_role) + + +def test_manager_site_export(app, superuser_or_admin): + response = login(app, superuser_or_admin, '/manage/site-export/') + assert 'roles' in response.json + assert 'ous' in response.json + + +def test_manager_site_import(app, db, superuser_or_admin): + site_import = login(app, superuser_or_admin, '/manage/site-import/') + form = site_import.form + site_export = { + 'roles': [ + { + "description": "", "service": None, "name": "basic", + "attributes": [], + "ou": { + "slug": "default", + "uuid": "ba60d9e6c2874636883bdd604b23eab2", + "name": "Collectivit\u00e9 par d\u00e9faut" + }, + "external_id": "", + "slug": "basic", + "uuid": "6eb7bbf64bf547119120f925f0e560ac" + }] + } + form['site_json'] = Upload( + 'site_export.json', json.dumps(site_export), 'application/octet-stream') + res = form.submit() + assert res.status_code == 302 + assert get_role_model().objects.get(slug='basic') + + +def test_manager_site_import_error(app, db, superuser_or_admin): + site_import = login(app, superuser_or_admin, '/manage/site-import/') + form = site_import.form + site_export = { + 'roles': [ + { + "description": "", "service": None, "name": "basic", + "attributes": [], + "ou": { + "slug": "unkown-ou", + "uuid": "ba60d9e6c2874636883bdd604b23eab2", + "name": "unkown ou" + }, + "external_id": "", + "slug": "basic", + "uuid": "6eb7bbf64bf547119120f925f0e560ac" + }] + } + form['site_json'] = Upload( + 'site_export.json', json.dumps(site_export), 'application/octet-stream') + res = form.submit() + assert res.status_code == 200 + assert 'missing Organizational Unit' in res.text + Role = get_role_model() + with pytest.raises(Role.DoesNotExist): + Role.objects.get(slug='basic') -- 2.17.0