From f1845b00ee72ccd501bdf499eed9dc92ae76989c Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Tue, 10 Oct 2023 16:21:11 +0200 Subject: [PATCH] add more API tests Signed-off-by: Jens Langhammer --- authentik/core/tests/utils.py | 12 +- authentik/rbac/models.py | 11 ++ authentik/rbac/permissions.py | 2 +- authentik/rbac/tests/test_api.py | 169 +++++++++++++++++++++++++++++ authentik/rbac/tests/test_roles.py | 5 +- 5 files changed, 192 insertions(+), 7 deletions(-) create mode 100644 authentik/rbac/tests/test_api.py diff --git a/authentik/core/tests/utils.py b/authentik/core/tests/utils.py index 59294e6fda60..da4294f420bd 100644 --- a/authentik/core/tests/utils.py +++ b/authentik/core/tests/utils.py @@ -21,10 +21,9 @@ def create_test_flow( ) -def create_test_admin_user(name: Optional[str] = None, **kwargs) -> User: - """Generate a test-admin user""" +def create_test_user(name: Optional[str] = None, **kwargs) -> User: + """Generate a test user""" uid = generate_id(20) if not name else name - group = Group.objects.create(name=uid, is_superuser=True) kwargs.setdefault("email", f"{uid}@goauthentik.io") kwargs.setdefault("username", uid) user: User = User.objects.create( @@ -33,6 +32,13 @@ def create_test_admin_user(name: Optional[str] = None, **kwargs) -> User: ) user.set_password(uid) user.save() + return user + + +def create_test_admin_user(name: Optional[str] = None, **kwargs) -> User: + """Generate a test-admin user""" + user = create_test_user(name, **kwargs) + group = Group.objects.create(name=user.name or name, is_superuser=True) group.users.add(user) return user diff --git a/authentik/rbac/models.py b/authentik/rbac/models.py index b5668694670f..df541bdbdc9a 100644 --- a/authentik/rbac/models.py +++ b/authentik/rbac/models.py @@ -1,8 +1,11 @@ """RBAC models""" +from typing import Optional from uuid import uuid4 from django.db import models +from django.db.transaction import atomic from django.utils.translation import gettext_lazy as _ +from guardian.shortcuts import assign_perm from rest_framework.serializers import BaseSerializer from authentik.lib.models import SerializerModel @@ -27,6 +30,14 @@ class Role(SerializerModel): # name field has the same constraints as the group model name = models.TextField(max_length=150, unique=True) + def assign_permission(self, *perms: str, obj: Optional[models.Model] = None): + """Assign permission to role, can handle multiple permissions, + but when assigning multiple permissions to an object the permissions + must all belong to the object given""" + with atomic(): + for perm in perms: + assign_perm(perm, self.group, obj) + @property def serializer(self) -> type[BaseSerializer]: from authentik.rbac.api.roles import RoleSerializer diff --git a/authentik/rbac/permissions.py b/authentik/rbac/permissions.py index f29b12e41266..db7940f02e6d 100644 --- a/authentik/rbac/permissions.py +++ b/authentik/rbac/permissions.py @@ -1,6 +1,6 @@ """RBAC Permissions""" -from rest_framework.permissions import DjangoObjectPermissions from django.db.models import Model +from rest_framework.permissions import DjangoObjectPermissions from rest_framework.request import Request diff --git a/authentik/rbac/tests/test_api.py b/authentik/rbac/tests/test_api.py new file mode 100644 index 000000000000..f530955f6ba0 --- /dev/null +++ b/authentik/rbac/tests/test_api.py @@ -0,0 +1,169 @@ +"""RBAC role tests""" +from django.urls import reverse +from rest_framework.test import APITestCase +from rest_framework.utils.encoders import JSONEncoder + +from authentik.core.models import Group +from authentik.core.tests.utils import create_test_admin_user, create_test_user +from authentik.lib.generators import generate_id +from authentik.rbac.models import Role +from authentik.stages.invitation.models import Invitation + + +class TestAPIPerms(APITestCase): + """Test API Permission and filtering""" + + def setUp(self) -> None: + self.superuser = create_test_admin_user() + + self.user = create_test_user() + self.role = Role.objects.create(name=generate_id()) + self.group = Group.objects.create(name=generate_id()) + self.group.roles.add(self.role) + self.group.users.add(self.user) + + def test_list_simple(self): + """Test list (single object, role has global permission)""" + self.client.force_login(self.user) + self.role.assign_permission("authentik_stages_invitation.view_invitation") + + Invitation.objects.all().delete() + inv = Invitation.objects.create( + name=generate_id(), + created_by=self.superuser, + ) + res = self.client.get(reverse("authentik_api:invitation-list")) + self.assertEqual(res.status_code, 200) + self.assertJSONEqual( + res.content.decode(), + { + "pagination": { + "next": 0, + "previous": 0, + "count": 1, + "current": 1, + "total_pages": 1, + "start_index": 1, + "end_index": 1, + }, + "results": [ + { + "pk": str(inv.pk), + "name": inv.name, + "expires": JSONEncoder().default(inv.expires), + "fixed_data": {}, + "created_by": { + "pk": self.superuser.pk, + "username": f"{self.superuser.username}", + "name": f"{self.superuser.username}", + "is_active": True, + "last_login": None, + "email": f"{self.superuser.username}@goauthentik.io", + "attributes": {}, + "uid": self.superuser.uid, + }, + "single_use": False, + "flow": None, + "flow_obj": None, + } + ], + }, + ) + + def test_list_object_perm(self): + """Test list""" + self.client.force_login(self.user) + + Invitation.objects.all().delete() + Invitation.objects.create( + name=generate_id(), + created_by=self.superuser, + ) + inv2 = Invitation.objects.create( + name=generate_id(), + created_by=self.superuser, + ) + self.role.assign_permission("authentik_stages_invitation.view_invitation", obj=inv2) + + res = self.client.get(reverse("authentik_api:invitation-list")) + self.assertEqual(res.status_code, 200) + self.assertJSONEqual( + res.content.decode(), + { + "pagination": { + "next": 0, + "previous": 0, + "count": 1, + "current": 1, + "total_pages": 1, + "start_index": 1, + "end_index": 1, + }, + "results": [ + { + "pk": str(inv2.pk), + "name": inv2.name, + "expires": JSONEncoder().default(inv2.expires), + "fixed_data": {}, + "created_by": { + "pk": self.superuser.pk, + "username": f"{self.superuser.username}", + "name": f"{self.superuser.username}", + "is_active": True, + "last_login": None, + "email": f"{self.superuser.username}@goauthentik.io", + "attributes": {}, + "uid": self.superuser.uid, + }, + "single_use": False, + "flow": None, + "flow_obj": None, + } + ], + }, + ) + + def test_list_denied(self): + """Test list without adding permission""" + self.client.force_login(self.user) + + res = self.client.get(reverse("authentik_api:invitation-list")) + self.assertEqual(res.status_code, 200) + self.assertJSONEqual( + res.content.decode(), + { + "pagination": { + "count": 0, + "current": 1, + "end_index": 0, + "next": 0, + "previous": 0, + "start_index": 0, + "total_pages": 1, + }, + "results": [], + }, + ) + + def test_create_simple(self): + """Test create with permission""" + self.client.force_login(self.user) + self.role.assign_permission("authentik_stages_invitation.add_invitation") + res = self.client.post( + reverse("authentik_api:invitation-list"), + data={ + "name": generate_id(), + }, + ) + self.assertEqual(res.status_code, 201) + + def test_create_simple_denied(self): + """Test create without assigning permission""" + self.client.force_login(self.user) + res = self.client.post( + reverse("authentik_api:invitation-list"), + data={ + "name": generate_id(), + }, + ) + self.assertEqual(res.status_code, 403) diff --git a/authentik/rbac/tests/test_roles.py b/authentik/rbac/tests/test_roles.py index 1e83253cd49c..f9cbfdabb84a 100644 --- a/authentik/rbac/tests/test_roles.py +++ b/authentik/rbac/tests/test_roles.py @@ -1,5 +1,4 @@ """RBAC role tests""" -from guardian.shortcuts import assign_perm from rest_framework.test import APITestCase from authentik.core.models import Group @@ -16,7 +15,7 @@ def test_role_create(self): user = create_test_admin_user() group = Group.objects.create(name=generate_id()) role = Role.objects.create(name=generate_id()) - assign_perm("authentik_core.view_application", role.group) + role.assign_permission("authentik_core.view_application") group.roles.add(role) group.users.add(user) self.assertEqual(list(role.group.user_set.all()), [user]) @@ -27,7 +26,7 @@ def test_role_create_remove(self): user = create_test_admin_user() group = Group.objects.create(name=generate_id()) role = Role.objects.create(name=generate_id()) - assign_perm("authentik_core.view_application", role.group) + role.assign_permission("authentik_core.view_application") group.roles.add(role) group.users.add(user) self.assertEqual(list(role.group.user_set.all()), [user])