Projet

Général

Profil

0006-derni-res-remarques-to-rebase.patch

Benjamin Dauvergne, 21 juin 2019 13:33

Télécharger (14,3 ko)

Voir les différences:

Subject: [PATCH 6/6] =?UTF-8?q?derni=C3=A8res=20remarques=20(to=20rebase)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

 .../authentic2/manager/user_import.html       |  18 ++--
 .../manager/user_import_report.html           |   2 +-
 .../templates/authentic2/manager/users.html   |   6 +-
 src/authentic2/manager/user_import.py         |   1 +
 src/authentic2/manager/user_views.py          |  18 +++-
 src/authentic2/manager/views.py               |   9 +-
 tests/test_user_manager.py                    | 100 ++++++++++++++++++
 7 files changed, 136 insertions(+), 18 deletions(-)
src/authentic2/manager/templates/authentic2/manager/user_import.html
11 11
{% block breadcrumb %}
12 12
  {{ block.super }}
13 13
  <a href="{% url 'a2-manager-users' %}">{% trans 'Users' %}</a>
14
  <a href="{% url 'a2-manager-users-imports' %}">{% trans 'Import' %}</a>
14
  <a href="{% url 'a2-manager-users-imports' %}">{% trans 'Imports' %}</a>
15 15
  <a href="{% url 'a2-manager-users-import' uuid=user_import.uuid %}">{% trans "User Import" %} {{ user_import.created }}</a>
16 16
{% endblock %}
17 17

  
......
19 19
  <aside id="sidebar">
20 20
    <div>
21 21
      <h3>{% trans "Modify import" %}</h3>
22
      <form method="post">
22
      <form method="post" id="action-form">
23 23
        {% csrf_token %}
24 24
        {{ form|with_template }}
25 25
        <div class="buttons">
26
          <button name="create">{% trans "Modify" %}</button>
26
          <button name="modify">{% trans "Modify" %}</button>
27 27
          <button name="simulate">{% trans "Simulate" %}</button>
28 28
          <button name="execute">{% trans "Execute" %}</button>
29 29
        </div>
......
31 31
    </div>
32 32
    <div>
33 33
      <h3>{% trans "Download" %}</h3>
34
      <form action="download/{{ user_import.filename }}">
34
      <form action="download/{{ user_import.filename }}" id="download-form">
35 35
        <div class="buttons">
36 36
          <button>{% trans "Download" %}</button>
37 37
        </div>
......
55 55
    </thead>
56 56
    <tbody>
57 57
      {% for report in reports %}
58
        <tr>
59
          <td>{% if report.state != 'running' %}<a href="{% url "a2-manager-users-import-report" import_uuid=user_import.uuid report_uuid=report.uuid %}">{{ report.created }}</a>{% else %}{{ report.created }}{% endif %}</td>
60
          <td>{{ report.state }} {% if report.state == 'error' %}"{{ report.exception }}"{% endif %}</td>
61
          <td>{% if not report.simulate %}<span class="icon-check"></span>{% endif %}</td>
62
          <td>{% if report.simulate %}<form method="post">{% csrf_token %}<button name="delete" value="{{ report.uuid }}">{% trans "Delete" %}</button></form>{% endif %}</td>
58
        <tr data-uuid="{{ report.uuid }}">
59
          <td class="created">{% if report.state != 'running' %}<a href="{% url "a2-manager-users-import-report" import_uuid=user_import.uuid report_uuid=report.uuid %}">{{ report.created }}</a>{% else %}{{ report.created }}{% endif %}</td>
60
          <td class="state"><span>{{ report.state }}</span> {% if report.state == 'error' %}"{{ report.exception }}"{% endif %}</td>
61
          <td class="applied">{% if not report.simulate %}<span class="icon-check"></span>{% endif %}</td>
62
          <td class="delete-action">{% if report.simulate %}<form method="post" id="delete-form-{{ report.uuid }}">{% csrf_token %}<button name="delete" value="{{ report.uuid }}">{% trans "Delete" %}</button></form>{% endif %}</td>
63 63
        </tr>
64 64
      {% endfor %}
65 65
    </tbody>
src/authentic2/manager/templates/authentic2/manager/user_import_report.html
11 11
{% block breadcrumb %}
12 12
  {{ block.super }}
13 13
  <a href="{% url 'a2-manager-users' %}">{% trans 'Users' %}</a>
14
  <a href="{% url 'a2-manager-users-imports' %}">{% trans 'Import' %}</a>
14
  <a href="{% url 'a2-manager-users-imports' %}">{% trans 'Imports' %}</a>
15 15
  <a href="{% url 'a2-manager-users-import' uuid=user_import.uuid %}">{% trans "User Import" %} {{ user_import.created }}</a>
16 16
  <a href="{% url 'a2-manager-users-import-report' import_uuid=user_import.uuid report_uuid=report.uuid%}">{{ report_title }} {{ report.created }}</a>
17 17
{% endblock %}
src/authentic2/manager/templates/authentic2/manager/users.html
21 21
         {% trans "Add user" %}
22 22
     </a>
23 23
   {% endif %}
24
   {% if extra_actions %}
24 25
     <ul class="extra-actions-menu">
25
       <li><a href="{% url "a2-manager-users-imports" %}">{% trans 'Import Users' %}</a></li>
26
       {% for extra_action in extra_actions %}
27
         <li><a href="{{ extra_action.url }}">{{ extra_action.label }}</a></li>
28
       {% endfor %}
26 29
     </ul>
30
   {% endif %}
27 31
  </span>
28 32

  
29 33
{% endblock %}
src/authentic2/manager/user_import.py
186 186
                    data['exception'] = exception
187 187
                    data['importer'] = importer
188 188
        t = threading.Thread(target=target)
189
        t.daemon = True
189 190
        with self.data_update as data:
190 191
            data['state'] = 'running'
191 192
        if start:
src/authentic2/manager/user_views.py
42 42
from .views import (BaseTableView, BaseAddView, BaseEditView, ActionMixin,
43 43
                    OtherActionsMixin, Action, ExportMixin, BaseSubTableView,
44 44
                    HideOUColumnMixin, BaseDeleteView, BaseDetailView,
45
                    PermissionMixin)
45
                    PermissionMixin, MediaMixin)
46 46
from .tables import UserTable, UserRolesTable, OuUserRolesTable
47 47
from .forms import (UserSearchForm, UserAddForm, UserEditForm,
48 48
                    UserChangePasswordForm, ChooseUserRoleForm,
......
110 110
            ou = self.search_form.cleaned_data.get('ou')
111 111
        if ou and self.request.user.has_ou_perm('custom_user.add_user', ou):
112 112
            ctx['add_ou'] = ou
113
        extra_actions = ctx['extra_actions'] = []
114
        if self.request.user.has_perm('custom_user.admin_user'):
115
            extra_actions.append({
116
                'url': reverse('a2-manager-users-imports'),
117
                'label': _('Import users'),
118
            })
113 119
        return ctx
114 120

  
115 121

  
......
626 632
user_delete = UserDeleteView.as_view()
627 633

  
628 634

  
629
class UserImportsView(PermissionMixin, FormView):
635
class UserImportsView(MediaMixin, PermissionMixin, FormView):
630 636
    form_class = UserNewImportForm
637
    permissions = ['custom_user.admin_user']
638
    permissions_global = True
631 639
    template_name = 'authentic2/manager/user_imports.html'
632 640

  
633 641
    def post(self, request, *args, **kwargs):
......
656 664
user_imports = UserImportsView.as_view()
657 665

  
658 666

  
659
class UserImportView(PermissionMixin, FormView):
667
class UserImportView(MediaMixin, PermissionMixin, FormView):
660 668
    form_class = UserEditImportForm
661 669
    permissions = ['custom_user.admin_user']
670
    permissions_global = True
662 671
    template_name = 'authentic2/manager/user_import.html'
663 672

  
664 673
    def dispatch(self, request, uuid, **kwargs):
......
715 724
user_import = UserImportView.as_view()
716 725

  
717 726

  
718
class UserImportReportView(PermissionMixin, TemplateView):
727
class UserImportReportView(MediaMixin, PermissionMixin, TemplateView):
719 728
    form_class = UserEditImportForm
720 729
    permissions = ['custom_user.admin_user']
730
    permissions_global = True
721 731
    template_name = 'authentic2/manager/user_import_report.html'
722 732

  
723 733
    def dispatch(self, request, import_uuid, report_uuid):
src/authentic2/manager/views.py
101 101
class PermissionMixin(object):
102 102
    '''Control access to views based on permissions'''
103 103
    permissions = None
104
    permissions_global = False
104 105

  
105 106
    def authorize(self, request, *args, **kwargs):
106 107
        if hasattr(self, 'model'):
......
130 131
                    and not request.user.has_perm_any(self.permissions):
131 132
                raise PermissionDenied
132 133
        else:
133
            if self.permissions \
134
                    and not request.user.has_perm_any(self.permissions):
135
                raise PermissionDenied
134
            if self.permissions:
135
                if self.permissions_global and not request.user.has_perms(self.permissions):
136
                    raise PermissionDenied
137
                if not self.permissions_global and not request.user.has_perm_any(self.permissions):
138
                    raise PermissionDenied
136 139

  
137 140
    def dispatch(self, request, *args, **kwargs):
138 141
        response = self.authorize(request, *args, **kwargs)
tests/test_user_manager.py
1
# -*- coding: utf-8 -*-
1 2
# authentic2 - versatile identity manager
2 3
# Copyright (C) 2010-2019 Entr'ouvert
3 4
#
......
14 15
# You should have received a copy of the GNU Affero General Public License
15 16
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 17
import csv
18
import time
19

  
20
from webtest import Upload
17 21

  
18 22
from django.core.urlresolvers import reverse
19 23

  
......
25 29
from authentic2.custom_user.models import User
26 30
from authentic2.models import Attribute, AttributeValue
27 31
from authentic2.a2_rbac.utils import get_default_ou
32
from authentic2.manager import user_import
28 33

  
29 34

  
30 35
from utils import login, get_link_from_mail, skipif_sqlite
......
210 215

  
211 216
    response = app.get('/manage/users/?search-ou=%s' % ou1.id)
212 217
    assert response.pyquery('td.username')
218

  
219

  
220
def test_user_import(transactional_db, app, admin, ou1, admin_ou1, media):
221
    Attribute.objects.create(name='phone', kind='phone_number', label='Numéro de téléphone')
222

  
223
    user_count = User.objects.count()
224

  
225
    assert Attribute.objects.count() == 3
226

  
227
    response = login(app, admin, '/manage/users/')
228

  
229
    response = response.click('Import users')
230
    response.form.set('import_file',
231
                      Upload(
232
                          'users.csv',
233
                          u'''email key verified,first_name,last_name,phone
234
tnoel@entrouvert.com,Thomas,Noël,1234
235
fpeters@entrouvert.com,Frédéric,Péters,5678
236
x,x,x,x'''.encode('utf-8'),
237
                          'application/octet-stream'))
238
    response.form.set('encoding', 'utf-8')
239
    response.form.set('ou', str(get_default_ou().pk))
240
    response = response.form.submit()
241

  
242
    imports = list(user_import.UserImport.all())
243
    assert len(imports) == 1
244
    _import_uuid = response.location.split('/')[-2]
245
    _import = user_import.UserImport(uuid=_import_uuid)
246
    assert _import.exists()
247

  
248
    response = response.follow()
249
    response = response.forms['action-form'].submit(name='simulate')
250

  
251
    reports = list(_import.reports)
252
    assert len(reports) == 1
253
    uuid = reports[0].uuid
254

  
255
    response = response.follow()
256

  
257
    def assert_timeout(duration, wait_function):
258
        start = time.time()
259
        while True:
260
            result = wait_function()
261
            if result is not None:
262
                return result
263
            assert time.time() - start < duration, '%s timed out after %s seconds' % (wait_function, duration)
264
            time.sleep(0.001)
265

  
266
    def wait_finished():
267
        new_resp = response.click('User Import')
268
        if new_resp.pyquery('tr[data-uuid="%s"] td.state span' % uuid).text() == 'finished':
269
            return new_resp
270

  
271
    simulate = reports[0]
272
    assert simulate.simulate
273

  
274
    response = assert_timeout(20, wait_finished)
275

  
276
    response = response.click(href=simulate.uuid)
277

  
278
    assert len(response.pyquery('table.main tbody tr')) == 3
279
    assert len(response.pyquery('table.main tbody tr.row-valid')) == 2
280
    assert len(response.pyquery('table.main tbody tr.row-invalid')) == 1
281

  
282
    assert User.objects.count() == user_count
283

  
284
    response = response.click('User Import')
285
    response = response.forms['action-form'].submit(name='execute')
286

  
287
    execute = list(report for report in _import.reports if not report.simulate)[0]
288
    uuid = execute.uuid
289

  
290
    response = response.follow()
291
    response = assert_timeout(20, wait_finished)
292

  
293
    assert User.objects.count() == user_count + 2
294
    assert User.objects.filter(
295
        email='tnoel@entrouvert.com',
296
        first_name=u'Thomas',
297
        last_name=u'Noël',
298
        attribute_values__content='1234').count() == 1
299
    assert User.objects.filter(
300
        email='fpeters@entrouvert.com',
301
        first_name=u'Frédéric',
302
        last_name=u'Péters',
303
        attribute_values__content='5678').count() == 1
304

  
305
    # logout
306
    app.session.flush()
307
    response = login(app, admin_ou1, '/manage/users/')
308

  
309
    app.get('/manage/users/import/', status=403)
310
    app.get('/manage/users/import/%s/' % _import.uuid, status=403)
311
    app.get('/manage/users/import/%s/%s/' % (_import.uuid, simulate.uuid), status=403)
312
    app.get('/manage/users/import/%s/%s/' % (_import.uuid, execute.uuid), status=403)
213
-