Projet

Général

Profil

0001-admin-turn-permissions-panel-into-a-matrix-of-roles-.patch

Frédéric Péters, 14 septembre 2015 15:50

Télécharger (9,04 ko)

Voir les différences:

Subject: [PATCH] admin: turn permissions panel into a matrix of roles/accesses
 (#8239)

This also adds "backoffice access" to this panel, so it's no longer required to
go into individual roles to set that one.
 tests/test_admin_pages.py           | 51 +++++++++++++++++++++++++++++++++
 wcs/admin/settings.py               | 57 ++++++++++++++++++++++++++++---------
 wcs/qommon/form.py                  | 10 ++++++-
 wcs/qommon/static/css/dc2/admin.css | 19 +++++++++++++
 4 files changed, 123 insertions(+), 14 deletions(-)
tests/test_admin_pages.py
2285 2285
    assert resp.location == 'http://example.net/backoffice/settings/data-sources/'
2286 2286
    resp = resp.follow()
2287 2287
    assert NamedDataSource.count() == 0
2288

  
2289
def test_settings_permissions():
2290
    create_superuser()
2291
    role1 = create_role()
2292
    role1.name = 'foobar1'
2293
    role1.store()
2294
    role2 = Role(name='foobar2')
2295
    role2.store()
2296
    role3 = Role(name='foobar3')
2297
    role3.store()
2298

  
2299
    app = login(get_app(pub))
2300
    resp = app.get('/backoffice/settings/admin-permissions')
2301
    # assert all first checkboxes are checked
2302
    assert resp.forms[0]['permissions$c-0-0'].checked
2303
    assert resp.forms[0]['permissions$c-1-0'].checked
2304
    assert resp.forms[0]['permissions$c-2-0'].checked
2305

  
2306
    role2.allows_backoffice_access = False
2307
    role2.store()
2308
    resp = app.get('/backoffice/settings/admin-permissions')
2309
    assert resp.forms[0]['permissions$c-0-0'].checked
2310
    assert not resp.forms[0]['permissions$c-1-0'].checked
2311
    assert resp.forms[0]['permissions$c-2-0'].checked
2312

  
2313
    resp.forms[0]['permissions$c-0-0'].checked = False
2314
    resp.forms[0]['permissions$c-1-0'].checked = True
2315
    resp = resp.forms[0].submit()
2316
    assert Role.get(role1.id).allows_backoffice_access is False
2317
    assert Role.get(role2.id).allows_backoffice_access is True
2318

  
2319
    # give some roles access to the forms workshop (2nd checkbox) and to the
2320
    # workflows workshop (3rd)
2321
    resp = app.get('/backoffice/settings/admin-permissions')
2322
    resp.forms[0]['permissions$c-1-1'].checked = True
2323
    resp.forms[0]['permissions$c-2-1'].checked = True
2324
    resp.forms[0]['permissions$c-2-2'].checked = True
2325
    resp = resp.forms[0].submit()
2326
    pub.reload_cfg()
2327
    assert set(pub.cfg['admin-permissions']['forms']) == set([role2.id, role3.id])
2328
    assert set(pub.cfg['admin-permissions']['workflows']) == set([role3.id])
2329

  
2330
    # remove accesses
2331
    resp = app.get('/backoffice/settings/admin-permissions')
2332
    resp.forms[0]['permissions$c-1-1'].checked = False
2333
    resp.forms[0]['permissions$c-2-1'].checked = False
2334
    resp.forms[0]['permissions$c-2-2'].checked = False
2335
    resp = resp.forms[0].submit()
2336
    pub.reload_cfg()
2337
    assert pub.cfg['admin-permissions']['forms'] == []
2338
    assert pub.cfg['admin-permissions']['workflows'] == []
wcs/admin/settings.py
16 16

  
17 17
import copy
18 18
import cStringIO
19
import hashlib
19 20
import mimetypes
20 21
import random
21 22
import os
......
503 504
        r += htmltext('</div>')
504 505
        return r.getvalue()
505 506

  
506
    def add_roles_widget(self, form, permissions_cfg, key, label):
507
        roles = list(Role.select())
508
        form.add(WidgetList, key, title=label, element_type=SingleSelectWidget,
509
                    value=permissions_cfg.get(key, None),
510
                    add_element_label=_('Add Role'),
511
                    element_kwargs = {
512
                        'render_br': False,
513
                        'options': [(None, str('---'))] + [(x.id, x.name) for x in roles]})
514

  
515 507
    def admin_permissions(self):
516 508
        permissions_cfg = get_cfg('admin-permissions', {})
517 509
        form = Form(enctype='multipart/form-data')
518 510

  
519
        keys = []
511
        permissions = [_('Backoffice')]
512

  
513
        permission_keys = []
520 514
        for k, v in get_publisher().get_admin_root().menu_items:
521 515
            if not k.endswith(str('/')):
522 516
                continue
523 517
            k = k.strip(str('/'))
524 518
            if not k:
525 519
                continue
526
            self.add_roles_widget(form, permissions_cfg, k, _(v))
527
            keys.append(k)
520
            permissions.append(_(v))
521
            permission_keys.append(k)
522

  
523
        rows = []
524
        value = []
525
        roles = list(Role.select(order_by='name'))
526
        for role in roles:
527
            rows.append(role.name)
528
            value.append([role.allows_backoffice_access])
529
            for k in permission_keys:
530
                authorised_roles = [str(x) for x in permissions_cfg.get(k) or []]
531
                value[-1].append(bool(str(role.id) in authorised_roles))
532
        colrows_hash = hashlib.md5('%r-%r' % (rows, permissions)).hexdigest()
533

  
534
        form.add_hidden('hash', colrows_hash)
535
        form.add(CheckboxesTableWidget, 'permissions', rows=rows, columns=permissions)
536
        form.get_widget('permissions').set_value(value)
528 537

  
529 538
        form.add_submit('submit', _('Submit'))
530 539
        form.add_submit('cancel', _('Cancel'))
......
532 541
        if form.get_widget('cancel').parse():
533 542
            return redirect('.')
534 543

  
544
        if form.get_widget('hash').parse() != colrows_hash:
545
            # The columns and rows are made of indices; permissions could be
546
            # wrongly assigned if there were some changes to the columns and
547
            # rows between the form being displayed and submitted.
548
            form.get_widget('permissions').set_error(
549
                    _('Changes were made to roles or permissions while the table was displayed.'))
550

  
535 551
        if not form.is_submitted() or form.has_errors():
536 552
            get_response().breadcrumb.append(('admin-permissions', _('Admin Permissions')))
537 553
            html_top('settings', title = _('Admin Permissions'))
538 554
            r = TemplateIO(html=True)
555
            r += htmltext('<div class="admin-permissions">')
539 556
            r += htmltext('<h2>%s</h2>') % _('Admin Permissions')
540 557
            r += form.render()
558
            r += htmltext('</div>')
541 559
            return r.getvalue()
542 560
        else:
543
            cfg_submit(form, 'admin-permissions', keys)
561
            value = form.get_widget('permissions').parse()
562
            permissions = {}
563
            for key in permission_keys:
564
                permissions[key] = []
565
            for i, role in enumerate(roles):
566
                permission_row = value[i]
567
                if role.allows_backoffice_access != permission_row[0]:
568
                    role.allows_backoffice_access = permission_row[0]
569
                    role.store()
570
                for j, key in enumerate(permission_keys):
571
                    if permission_row[j+1]:
572
                        permissions[key].append(role.id)
573
            get_publisher().cfg['admin-permissions'] = permissions
574
            get_publisher().write_cfg()
544 575
            return redirect('.')
545 576

  
546 577
    def themes(self):
wcs/qommon/form.py
1266 1266
        r = TemplateIO(html=True)
1267 1267
        r += htmltext('<table><thead><tr><td></td>')
1268 1268
        for column in self.columns:
1269
            r += htmltext('<th>%s</th>') % column
1269
            r += htmltext('<th><span>%s</span></th>') % column
1270 1270
        r += htmltext('</tr></thead><tbody>')
1271 1271
        for i, row in enumerate(self.rows):
1272 1272
            r += htmltext('<tr><th>%s</th>') % row
......
1328 1328
        return self.add(SingleSelectWidget, 'c-%s-%s' % (i, j), **widget_kwargs)
1329 1329

  
1330 1330

  
1331
class CheckboxesTableWidget(TableWidget):
1332
    def add_widget(self, kwargs, i, j):
1333
        widget_kwargs = {'options': kwargs.get('options')}
1334
        if kwargs.has_key('readonly') and kwargs.get('readonly'):
1335
            widget_kwargs['readonly'] = 'readonly'
1336
        return self.add(CheckboxWidget, 'c-%s-%s' % (i, j), **widget_kwargs)
1337

  
1338

  
1331 1339
class SingleSelectHintWidget(SingleSelectWidget):
1332 1340

  
1333 1341
    def separate_hint(self):
wcs/qommon/static/css/dc2/admin.css
1049 1049
	width: calc(100% - 1em);
1050 1050
}
1051 1051

  
1052
div.admin-permissions thead th {
1053
	transform: rotate(-45deg);
1054
	transform-origin: 10% 0;
1055
}
1056

  
1057
div.admin-permissions thead th span {
1058
	width: 3em;
1059
	display: inline-block;
1060
	white-space: nowrap;
1061
}
1062

  
1063
div.admin-permissions tbody th {
1064
	text-align: left;
1065
	padding-right: 1ex;
1066
}
1067

  
1068
div.admin-permissions tbody tr:nth-child(even) {
1069
	background: #eee;
1070
}
1052 1071

  
1053 1072
@media print {
1054 1073
	div#sidebar {
1055
-