authentic/tests_rbac/test_rbac.py

253 lines
11 KiB
Python

# authentic2 - versatile identity manager
# Copyright (C) 2010-2020 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 time
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.test.utils import CaptureQueriesContext
from django.db import connection
from django_rbac import utils, models, backends
import pytest
def test_role_parenting(db):
OrganizationalUnit = utils.get_ou_model()
Role = utils.get_role_model()
RoleParenting = utils.get_role_parenting_model()
ou = OrganizationalUnit.objects.create(name='ou')
roles = []
for i in range(10):
roles.append(Role.objects.create(name='r%d' % i, ou=ou))
assert Role.objects.count() == 10
assert RoleParenting.objects.count() == 0
for i in range(1, 3):
RoleParenting.objects.create(
parent=roles[i - 1],
child=roles[i])
assert RoleParenting.objects.filter(direct=True).count() == 2
assert RoleParenting.objects.filter(direct=False).count() == 1
for i, role in enumerate(roles[:3]):
assert role.children().count() == 3 - i
assert role.parents().count() == i + 1
assert role.children(False).count() == 3 - i - 1
assert role.parents(False).count() == i
for i in range(4, 6):
RoleParenting.objects.create(
parent=roles[i - 1],
child=roles[i])
assert RoleParenting.objects.filter(direct=True).count() == 4
assert RoleParenting.objects.filter(direct=False).count() == 2
for i, role in enumerate(roles[3:6]):
assert role.children().count() == 3 - i
assert role.parents().count() == i + 1
assert role.children(False).count() == 3 - i - 1
assert role.parents(False).count() == i
RoleParenting.objects.create(parent=roles[2], child=roles[3])
assert RoleParenting.objects.filter(direct=True).count() == 5
assert RoleParenting.objects.filter(direct=False).count() == 10
for i in range(6):
assert roles[i].parents().distinct().count() == i + 1
for i, role in enumerate(roles[:6]):
assert role.children().count() == 6 - i
assert role.parents().count() == i + 1
assert role.children(False).count() == 6 - i - 1
assert role.parents(False).count() == i
RoleParenting.objects.filter(parent=roles[2], child=roles[3],
direct=True).delete()
assert RoleParenting.objects.filter(direct=True).count() == 4
assert RoleParenting.objects.filter(direct=False).count() == 2
# test that it works with cycles
RoleParenting.objects.create(parent=roles[2], child=roles[3])
RoleParenting.objects.create(parent=roles[5], child=roles[0])
for role in roles[:6]:
assert role.children().count() == 6
assert role.parents().count() == 6
SIZE = 1000
SPAN = 50
@pytest.mark.slow
def test_massive_role_parenting(db):
User = get_user_model()
Role = utils.get_role_model()
RoleParenting = utils.get_role_parenting_model()
Permission = utils.get_permission_model()
user = User.objects.create(username='user')
roles = []
# Try a depth=10 tree of roles
for i in range(0, SIZE):
name = 'role%s' % i
roles.append(
Role(pk=i + 1, name=name, slug=name))
Role.objects.bulk_create(roles)
relations = []
for i in range(0, SIZE):
if not i:
continue
relations.append(
RoleParenting(parent=roles[i], child=roles[(i - 1) // SPAN]))
RoleParenting.objects.bulk_create(relations)
RoleParenting.objects.update_transitive_closure()
operation, created = models.Operation.objects.get_or_create(
slug='admin', defaults={'name': 'Administration'})
perm, created = Permission.objects.get_or_create(
operation=operation,
target_ct=ContentType.objects.get_for_model(ContentType),
target_id=ContentType.objects.get_for_model(User).id)
roles[0].members.add(user)
Role.objects.get(pk=roles[-1].pk).permissions.add(perm)
b = time.time()
for i in range(1000):
assert models.Operation.objects.has_perm(user, 'admin', User)
t = time.time() - b
assert float(t) / 1000 < 0.01
b = time.time()
for i in range(1000):
assert (list(Role.objects.for_user(user).order_by('pk')) ==
list(Role.objects.order_by('pk')))
t = time.time() - b
assert float(t) / 1000 < 0.1
b = time.time()
def test_rbac_backend(db):
Permission = utils.get_permission_model()
User = get_user_model()
OU = utils.get_ou_model()
ou1 = OU.objects.create(name=u'ou1', slug=u'ou1')
ou2 = OU.objects.create(name=u'ou2', slug=u'ou2')
user1 = User.objects.create(username='john.doe')
Role = utils.get_role_model()
ct_ct = ContentType.objects.get_for_model(ContentType)
role_ct = ContentType.objects.get_for_model(Role)
change_op = models.Operation.objects.get(slug='change')
view_op = models.Operation.objects.get(slug='view')
delete_op = models.Operation.objects.get(slug='delete')
add_op = models.Operation.objects.get(slug='add')
admin_op = models.Operation.objects.get(slug='admin')
perm1 = Permission.objects.create(operation=change_op, target_ct=ct_ct,
target_id=role_ct.pk)
perm2 = Permission.objects.create(operation=view_op, target_ct=ct_ct,
target_id=role_ct.pk)
role1 = Role.objects.create(name='role1')
role2 = Role.objects.create(name='role2', ou=ou1)
role1.permissions.add(perm1)
role2.permissions.add(perm2)
role1.add_child(role2)
role2.members.add(user1)
perm3 = Permission.objects.create(
operation=delete_op,
target_ct=role_ct,
target_id=role1.pk)
perm4 = Permission.objects.create(
operation=add_op,
ou=ou1,
target_ct=ct_ct,
target_id=role_ct.pk)
role1.permissions.add(perm3)
role1.permissions.add(perm4)
rbac_backend = backends.DjangoRBACBackend()
ctx = CaptureQueriesContext(connection)
with ctx:
assert rbac_backend.get_all_permissions(user1) == set(['django_rbac.change_role',
'django_rbac.search_role',
'django_rbac.view_role'])
assert rbac_backend.get_all_permissions(user1, obj=role1) == set(['django_rbac.delete_role',
'django_rbac.change_role',
'django_rbac.search_role',
'django_rbac.view_role'])
assert rbac_backend.get_all_permissions(user1, obj=role2) == set(['django_rbac.change_role',
'django_rbac.view_role',
'django_rbac.search_role',
'django_rbac.add_role'])
assert not rbac_backend.has_perm(user1, 'django_rbac.delete_role', obj=role2)
assert rbac_backend.has_perm(user1, 'django_rbac.delete_role', obj=role1)
assert rbac_backend.has_perms(user1, ['django_rbac.delete_role', 'django_rbac.change_role',
'django_rbac.view_role'],
obj=role1)
assert rbac_backend.has_module_perms(user1, 'django_rbac')
assert not rbac_backend.has_module_perms(user1, 'contenttypes')
assert len(ctx.captured_queries) == 1
assert (set(rbac_backend.filter_by_perm(user1, 'django_rbac.add_role', Role.objects.all())) ==
set([role2]))
assert (set(rbac_backend.filter_by_perm(user1, 'django_rbac.delete_role', Role.objects.all()))
== set([role1]))
assert set(rbac_backend.filter_by_perm(user1, ['django_rbac.delete_role',
'django_rbac.add_role'],
Role.objects.all())) == set([role1, role2])
assert (set(rbac_backend.filter_by_perm(user1, 'django_rbac.view_role', Role.objects.all())) ==
set([role1, role2]))
assert (set(rbac_backend.filter_by_perm(user1, 'django_rbac.change_role', Role.objects.all()))
== set([role1, role2]))
# Test admin op as a generalization of other ops
user2 = User.objects.create(username='donald.knuth')
role3 = Role.objects.create(name='role3')
role3.members.add(user2)
perm5 = Permission.objects.create(
operation=admin_op,
target_ct=ct_ct,
target_id=role_ct.pk)
role3.permissions.add(perm5)
assert rbac_backend.get_all_permissions(user2) == set(['django_rbac.add_role',
'django_rbac.change_role',
'django_rbac.search_role',
'django_rbac.admin_role',
'django_rbac.view_role',
'django_rbac.delete_role'])
# test ous_with_perm
assert set(rbac_backend.ous_with_perm(user1, 'django_rbac.add_role')) == set([ou1])
assert set(rbac_backend.ous_with_perm(user1, 'django_rbac.view_role')) == set([ou1, ou2])
assert set(rbac_backend.ous_with_perm(user1, 'django_rbac.delete_role')) == set([])
def test_all_members(db):
User = get_user_model()
u1 = User.objects.create(username='john.doe')
u2 = User.objects.create(username='donald.knuth')
u3 = User.objects.create(username='alan.turing')
Role = utils.get_role_model()
r1 = Role.objects.create(name='r1')
r1.members.add(u1)
r1.members.add(u3)
r2 = Role.objects.create(name='r2')
r2.members.add(u3)
r3 = Role.objects.create(name='r3')
r3.members.add(u2)
r3.members.add(u3)
r3.add_parent(r2)
r2.add_parent(r1)
for member in r1.all_members():
if member == u1 or member == u3:
assert member.direct == [r1]
if member == u2:
assert member.direct == []
for member in Role.objects.filter(id=r1.id).all_members():
if member == u1 or member == u3:
assert member.direct == [r1]
if member == u2:
assert member.direct == []