339 lines
12 KiB
Python
339 lines
12 KiB
Python
# authentic2 - versatile identity manager
|
|
# Copyright (C) 2010-2019 Entr'ouvert
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify it
|
|
# under the terms of the GNU Affero General Public License as published
|
|
# by the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import json
|
|
|
|
from django.utils.encoding import force_bytes, force_text
|
|
from webtest import Upload
|
|
|
|
from authentic2.a2_rbac.models import OrganizationalUnit, Role
|
|
from authentic2.a2_rbac.utils import get_default_ou
|
|
from authentic2.custom_user.models import User
|
|
|
|
from .utils import login, text_content
|
|
|
|
|
|
def test_manager_role_export(app, admin, ou1, role_ou1, ou2, role_ou2):
|
|
import csv
|
|
|
|
response = login(app, admin, 'a2-manager-roles')
|
|
|
|
export_response = response.click('Export')
|
|
export = export_response.json
|
|
|
|
assert list(export.keys()) == ['roles']
|
|
assert len(export['roles']) == 2
|
|
assert set([role['slug'] for role in export['roles']]) == set(['role_ou1', 'role_ou2'])
|
|
|
|
export_response = response.click('CSV', href='/export/')
|
|
reader = csv.reader(
|
|
[force_text(line) for line in export_response.body.split(force_bytes('\r\n'))], delimiter=','
|
|
)
|
|
rows = [row for row in reader]
|
|
|
|
assert rows[0] == ['name', 'slug', 'members', 'ou']
|
|
assert len(rows) - 2 == 2 # csv header and last EOL
|
|
assert set([row[1] for row in rows[1:3]]) == set(['role_ou1', 'role_ou2'])
|
|
assert set([row[3] for row in rows[1:3]]) == set(['OU1', 'OU2'])
|
|
|
|
response.form.set('search-text', 'role_ou1')
|
|
search_response = response.form.submit()
|
|
|
|
export_response = search_response.click('Export')
|
|
export = export_response.json
|
|
|
|
assert list(export.keys()) == ['roles']
|
|
assert len(export['roles']) == 1
|
|
assert export['roles'][0]['slug'] == 'role_ou1'
|
|
|
|
export_response = search_response.click('CSV', href='/export/')
|
|
reader = csv.reader(
|
|
[force_text(line) for line in export_response.body.split(force_bytes('\r\n'))], delimiter=','
|
|
)
|
|
rows = [row for row in reader]
|
|
|
|
assert rows[0] == ['name', 'slug', 'members', 'ou']
|
|
assert len(rows) - 2 == 1 # csv header and last EOL
|
|
assert rows[1][1] == 'role_ou1'
|
|
|
|
|
|
def test_manager_role_name_uniqueness_single_ou(app, admin):
|
|
response = login(app, admin, 'a2-manager-roles')
|
|
|
|
response = response.click('Add')
|
|
response.form.set('name', 'Role1')
|
|
response = response.form.submit('Save').follow()
|
|
response = response.click('Roles')
|
|
assert response.pyquery('td.name').text() == 'Role1'
|
|
|
|
response = response.click('Add')
|
|
response.form.set('name', 'Role1')
|
|
response = response.form.submit('Save')
|
|
assert response.pyquery('.error').text() == 'Name already used'
|
|
|
|
|
|
def test_manager_role_name_uniqueness_multiple_ou(app, admin, ou1):
|
|
response = login(app, admin, 'a2-manager-roles')
|
|
|
|
response = response.click('Add')
|
|
response.form.set('ou', str(ou1.id))
|
|
response.form.set('name', 'Role1')
|
|
response = response.form.submit('Save').follow()
|
|
response = response.click('Roles')
|
|
assert response.pyquery('td.name').text() == 'Role1'
|
|
|
|
response = response.click('Add')
|
|
response.form.set('ou', str(ou1.id))
|
|
response.form.set('name', 'Role1')
|
|
response = response.form.submit('Save')
|
|
assert response.pyquery('.error').text() == 'Name already used'
|
|
|
|
|
|
def test_role_members_via(app, admin):
|
|
user1 = User.objects.create(username='user1')
|
|
user2 = User.objects.create(username='user2')
|
|
role1 = Role.objects.create(name='role1')
|
|
role2 = Role.objects.create(name='role2')
|
|
|
|
role1.add_child(role2)
|
|
user1.roles.add(role1)
|
|
user2.roles.add(role2)
|
|
|
|
response = login(app, admin, '/manage/roles/%s/' % role1.id)
|
|
rows = list(
|
|
zip(
|
|
[text_content(el) for el in response.pyquery('tr td.username')],
|
|
[text_content(el) for el in response.pyquery('tr td.direct')],
|
|
[text_content(el) for el in response.pyquery('tr td.via')],
|
|
)
|
|
)
|
|
assert rows == [
|
|
('user1', '✔', ''),
|
|
('user2', '✘', 'role2'),
|
|
]
|
|
|
|
|
|
def test_manager_role_import(app, admin, ou1, role_ou1, ou2, role_ou2):
|
|
response = login(app, admin, 'a2-manager-roles')
|
|
|
|
export_response = response.click('Export')
|
|
export = export_response.json
|
|
|
|
assert len(export['roles']) == 2
|
|
assert not 'ous' in export
|
|
Role.objects.filter(ou__in=[ou1, ou2]).delete()
|
|
|
|
resp = app.get('/manage/roles/')
|
|
resp = resp.click('Import')
|
|
resp.form['site_json'] = Upload('export.json', json.dumps(export).encode(), 'application/json')
|
|
resp = resp.form.submit().follow()
|
|
|
|
assert Role.objects.filter(name=role_ou1.name, ou=get_default_ou()).exists()
|
|
assert Role.objects.filter(name=role_ou2.name, ou=get_default_ou()).exists()
|
|
|
|
response.form.set('search-text', 'role_ou1')
|
|
search_response = response.form.submit()
|
|
|
|
export_response = response.click('Export')
|
|
new_export = export_response.json
|
|
assert len(export['roles']) == 2
|
|
assert new_export['roles'][0]['uuid'] == export['roles'][0]['uuid']
|
|
assert new_export['roles'][1]['uuid'] == export['roles'][1]['uuid']
|
|
|
|
resp = app.get('/manage/roles/')
|
|
resp = resp.click('Import')
|
|
resp.form['site_json'] = Upload('export.json', json.dumps(export).encode(), 'application/json')
|
|
resp.form['ou'] = ou1.pk
|
|
resp = resp.form.submit().follow()
|
|
|
|
assert Role.objects.filter(name=role_ou1.name, ou=get_default_ou()).exists()
|
|
assert Role.objects.filter(name=role_ou2.name, ou=get_default_ou()).exists()
|
|
assert Role.objects.filter(ou=ou1).count() == 4
|
|
|
|
# in case ous are present in export file, they must not be imported
|
|
export['ous'] = [
|
|
{
|
|
"uuid": "27255f404cb140df9a577da76b59f285",
|
|
"slug": "should_not_exist",
|
|
"name": "should_not_exist",
|
|
}
|
|
]
|
|
resp = app.get('/manage/roles/') # unselect ou1
|
|
resp = resp.click('Import')
|
|
resp.form['site_json'] = Upload('export.json', json.dumps(export).encode(), 'application/json')
|
|
resp = resp.form.submit().follow()
|
|
|
|
assert not OrganizationalUnit.objects.filter(slug="should_not_exist").exists()
|
|
|
|
|
|
def test_manager_role_import_single_ou(app, admin, simple_role):
|
|
assert OrganizationalUnit.objects.count() == 1
|
|
response = login(app, admin, 'a2-manager-roles')
|
|
|
|
export_response = response.click('Export')
|
|
export = export_response.json
|
|
|
|
assert len(export['roles']) == 1
|
|
simple_role.delete()
|
|
|
|
resp = app.get('/manage/roles/')
|
|
resp = resp.click('Import')
|
|
assert not 'Organizational unit' in resp.text
|
|
assert resp.form['ou'].attrs['type'] == 'hidden'
|
|
|
|
resp.form['site_json'] = Upload('export.json', json.dumps(export).encode(), 'application/json')
|
|
resp = resp.form.submit().follow()
|
|
|
|
imported_role = Role.objects.get(slug=simple_role.slug)
|
|
assert imported_role.ou == simple_role.ou
|
|
|
|
|
|
def test_manager_role_import_selected_ou(app, admin, ou1, ou2):
|
|
response = login(app, admin, 'a2-manager-roles')
|
|
response.form.set('search-ou', ou2.pk)
|
|
response = response.form.submit()
|
|
response = response.click('Import')
|
|
assert response.pyquery.find('select#id_ou option[selected]')[0].text == 'OU2'
|
|
|
|
|
|
def test_manager_role_add_selected_ou(app, admin, ou1, ou2):
|
|
response = login(app, admin, 'a2-manager-roles')
|
|
response.form.set('search-ou', ou2.pk)
|
|
response = response.form.submit()
|
|
response = response.click('Add role')
|
|
assert response.pyquery.find('select#id_ou option[selected]')[0].text == 'OU2'
|
|
|
|
|
|
def test_roles_displayed_fields(app, admin, ou1, ou2):
|
|
login(app, admin)
|
|
role1 = Role.objects.create(name='role1')
|
|
role2 = Role.objects.create(name='role2')
|
|
|
|
user1 = User.objects.create(username='user1')
|
|
user2 = User.objects.create(username='user2')
|
|
user1.roles.add(role1)
|
|
user2.roles.add(role1)
|
|
role2.add_child(role1) # indirect members
|
|
|
|
assert role1.can_manage_members
|
|
role2.can_manage_members = False # user syncronized from LDAP
|
|
role2.save()
|
|
|
|
# check OUTable
|
|
response = app.get('/manage/roles/')
|
|
rows = list(
|
|
zip(
|
|
[text_content(el) for el in response.pyquery('tr td.name')],
|
|
[text_content(el) for el in response.pyquery('tr td.member_count')],
|
|
)
|
|
)
|
|
assert rows == [
|
|
('role1', '2'),
|
|
('role2 (LDAP)', '0'),
|
|
]
|
|
|
|
# check UserRolesTable
|
|
response = app.get('/manage/users/%s/roles/?search-ou=all' % user2.pk)
|
|
rows = list(
|
|
zip(
|
|
[text_content(el) for el in response.pyquery('tr td.name')],
|
|
[text_content(el) for el in response.pyquery('tr td.via')],
|
|
)
|
|
)
|
|
assert rows == [
|
|
('role1', ''),
|
|
('role2 (LDAP)', 'role1 '),
|
|
]
|
|
|
|
# check OuUserRolesTable
|
|
response = app.get('/manage/users/%s/roles/?search-ou=' % user2.pk)
|
|
rows = list(
|
|
zip(
|
|
[text_content(el) for el in response.pyquery('tr td.name')],
|
|
[text_content(el) for el in response.pyquery('tr td.via')],
|
|
[el.attrib.get('checked') for el in response.pyquery('tr td.member input')],
|
|
[el.attrib.get('disabled') for el in response.pyquery('tr td.member input')],
|
|
)
|
|
)
|
|
assert rows == [
|
|
('role1', '', 'checked', None),
|
|
('role2 (LDAP)', 'role1 ', None, 'disabled'),
|
|
]
|
|
|
|
|
|
def test_manager_role_csv_import(app, admin, ou1, ou2):
|
|
roles_count = Role.objects.count()
|
|
resp = login(app, admin, '/manage/roles/')
|
|
|
|
resp = resp.click('CSV import')
|
|
csv_header = b'name,slug,ou\n'
|
|
csv_content = 'Role Name,role_slug,%s' % ou1.slug
|
|
resp.form['import_file'] = Upload('t.csv', csv_header + csv_content.encode(), 'text/csv')
|
|
resp.form.submit(status=302)
|
|
assert Role.objects.get(name='Role Name', slug='role_slug', ou=ou1)
|
|
assert Role.objects.count() == roles_count + 1
|
|
|
|
csv_content = 'Role 2,role2,\nRole 3,,%s' % ou2.slug
|
|
resp.form['import_file'] = Upload('t.csv', csv_header + csv_content.encode(), 'text/csv')
|
|
resp.form.submit(status=302)
|
|
assert Role.objects.get(name='Role 2', slug='role2', ou=get_default_ou())
|
|
assert Role.objects.get(name='Role 3', slug='role-3', ou=ou2)
|
|
assert Role.objects.count() == roles_count + 3
|
|
|
|
# slug can be updated using name, name can be updated using slug
|
|
csv_content = 'Role two,role2,\nRole 3,role-three,%s' % ou2.slug
|
|
resp.form['import_file'] = Upload('t.csv', csv_header + csv_content.encode(), 'text/csv')
|
|
resp.form.submit(status=302)
|
|
assert Role.objects.get(name='Role two', slug='role2', ou=get_default_ou())
|
|
assert Role.objects.get(name='Role 3', slug='role-three', ou=ou2)
|
|
assert Role.objects.count() == roles_count + 3
|
|
|
|
# conflict in auto-generated slug is handled
|
|
csv_header = b'name\n'
|
|
csv_content = 'Role!2'
|
|
resp.form['import_file'] = Upload('t.csv', csv_header + csv_content.encode(), 'text/csv')
|
|
resp.form.submit(status=302)
|
|
assert Role.objects.get(name='Role!2', slug='role2-1', ou=get_default_ou())
|
|
assert Role.objects.count() == roles_count + 4
|
|
|
|
# Identical roles are created only once
|
|
csv_content = 'Role 4,role-4,\nRole 4,,\nRole 4,role-4,'
|
|
resp.form['import_file'] = Upload('t.csv', csv_header + csv_content.encode(), 'text/csv')
|
|
resp.form.submit(status=302)
|
|
assert Role.objects.get(name='Role 4', slug='role-4', ou=get_default_ou())
|
|
assert Role.objects.count() == roles_count + 5
|
|
|
|
csv_content = 'xx\0xx,,'
|
|
resp.form['import_file'] = Upload('t.csv', csv_header + csv_content.encode(), 'text/csv')
|
|
resp = resp.form.submit()
|
|
assert 'Invalid file format.' in resp.text
|
|
|
|
wrong_header = b'a,b,c\n'
|
|
resp.form['import_file'] = Upload('t.csv', wrong_header, 'text/csv')
|
|
resp = resp.form.submit()
|
|
assert 'Invalid file header' in resp.text
|
|
|
|
csv_content = ',slug-but-no-name,\nRole,,unknown-ou'
|
|
resp = app.get('/manage/roles/csv-import/')
|
|
resp.form['import_file'] = Upload('t.csv', csv_header + csv_content.encode(), 'text/csv')
|
|
resp = resp.form.submit()
|
|
assert 'Name is required. (line 2)' in resp.text
|
|
assert 'Organizational Unit unknown-ou does not exist. (line 3)' in resp.text
|
|
|
|
resp = app.get('/manage/roles/csv-import/')
|
|
resp = resp.click('Download sample')
|
|
assert 'name,slug,ou' in resp.text
|