diff --git a/authentik/core/api/applications.py b/authentik/core/api/applications.py index b43d26431..76d8710f8 100644 --- a/authentik/core/api/applications.py +++ b/authentik/core/api/applications.py @@ -91,6 +91,23 @@ class ApplicationViewSet(ModelViewSet): applications.append(application) return applications + @swagger_auto_schema( + responses={ + 204: "Access granted", + 403: "Access denied", + } + ) + @action(detail=True, methods=["GET"]) + # pylint: disable=unused-argument + def check_access(self, request: Request, slug: str) -> Response: + """Check access to a single application by slug""" + application = self.get_object() + engine = PolicyEngine(application, self.request.user, self.request) + engine.build() + if engine.passing: + return Response(status=204) + return Response(status=403) + @swagger_auto_schema( manual_parameters=[ openapi.Parameter( diff --git a/authentik/core/tests/test_applications_api.py b/authentik/core/tests/test_applications_api.py new file mode 100644 index 000000000..40a554e2e --- /dev/null +++ b/authentik/core/tests/test_applications_api.py @@ -0,0 +1,125 @@ +"""Test Applications API""" +from django.urls import reverse +from django.utils.encoding import force_str +from rest_framework.test import APITestCase + +from authentik.core.models import Application, User +from authentik.policies.dummy.models import DummyPolicy +from authentik.policies.models import PolicyBinding + + +class TestApplicationsAPI(APITestCase): + """Test applications API""" + + def setUp(self) -> None: + self.user = User.objects.get(username="akadmin") + self.allowed = Application.objects.create(name="allowed", slug="allowed") + self.denied = Application.objects.create(name="denied", slug="denied") + PolicyBinding.objects.create( + target=self.denied, + policy=DummyPolicy.objects.create( + name="deny", result=False, wait_min=1, wait_max=2 + ), + order=0, + ) + + def test_check_access(self): + """Test check_access operation """ + self.client.force_login(self.user) + response = self.client.get( + reverse( + "authentik_api:application-check-access", + kwargs={"slug": self.allowed.slug}, + ) + ) + self.assertEqual(response.status_code, 204) + response = self.client.get( + reverse( + "authentik_api:application-check-access", + kwargs={"slug": self.denied.slug}, + ) + ) + self.assertEqual(response.status_code, 403) + + def test_list(self): + """Test list operation without superuser_full_list""" + self.client.force_login(self.user) + response = self.client.get(reverse("authentik_api:application-list")) + self.assertJSONEqual( + force_str(response.content), + { + "pagination": { + "next": 0, + "previous": 0, + "count": 2, + "current": 1, + "total_pages": 1, + "start_index": 1, + "end_index": 2, + }, + "results": [ + { + "pk": str(self.allowed.pk), + "name": "allowed", + "slug": "allowed", + "provider": None, + "provider_obj": None, + "launch_url": None, + "meta_launch_url": "", + "meta_icon": None, + "meta_description": "", + "meta_publisher": "", + "policy_engine_mode": "any", + }, + ], + }, + ) + + def test_list_superuser_full_list(self): + """Test list operation with superuser_full_list""" + self.client.force_login(self.user) + response = self.client.get( + reverse("authentik_api:application-list") + "?superuser_full_list=true" + ) + self.assertJSONEqual( + force_str(response.content), + { + "pagination": { + "next": 0, + "previous": 0, + "count": 2, + "current": 1, + "total_pages": 1, + "start_index": 1, + "end_index": 2, + }, + "results": [ + { + "pk": str(self.allowed.pk), + "name": "allowed", + "slug": "allowed", + "provider": None, + "provider_obj": None, + "launch_url": None, + "meta_launch_url": "", + "meta_icon": None, + "meta_description": "", + "meta_publisher": "", + "policy_engine_mode": "any", + }, + { + "launch_url": None, + "meta_description": "", + "meta_icon": None, + "meta_launch_url": "", + "meta_publisher": "", + "name": "denied", + "pk": str(self.denied.pk), + "policy_engine_mode": "any", + "provider": None, + "provider_obj": None, + "slug": "denied", + }, + ], + }, + ) diff --git a/swagger.yaml b/swagger.yaml index a505326de..e74f8e2de 100755 --- a/swagger.yaml +++ b/swagger.yaml @@ -1329,6 +1329,33 @@ paths: type: string format: slug pattern: ^[-a-zA-Z0-9_]+$ + /core/applications/{slug}/check_access/: + get: + operationId: core_applications_check_access + description: Check access to a single application by slug + parameters: [] + responses: + '204': + description: Access granted + '403': + description: Authentication credentials were invalid, absent or insufficient. + schema: + $ref: '#/definitions/GenericError' + '404': + description: Object does not exist or caller has insufficient permissions + to access it. + schema: + $ref: '#/definitions/APIException' + tags: + - core + parameters: + - name: slug + in: path + description: Internal application name, used in URLs. + required: true + type: string + format: slug + pattern: ^[-a-zA-Z0-9_]+$ /core/applications/{slug}/metrics/: get: operationId: core_applications_metrics