From 61368a1f93ba21fa1ac957b28a2e8a0655030b86 Mon Sep 17 00:00:00 2001 From: Emily <44536222+emi-hi@users.noreply.github.com> Date: Mon, 22 Apr 2024 09:26:40 -0700 Subject: [PATCH] task: backend tests for user permissions (#279) * chore: adds tests for user permissions * removes extra space --- README.md | 13 ++++ django/api/keycloak_authentication.py | 10 ++- django/api/settings.py | 2 +- django/api/tests/test_user.py | 89 +++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 django/api/tests/test_user.py diff --git a/README.md b/README.md index a1be2b22..7b1a67cf 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,19 @@ The Clean Transportation Data Hub provides an evidence base for the Clean Transp - This is where you can make changes to your package.json - You can technically make changes to your packages without going into your container, but you'll need npm installed into your system + + - To run in testing mode + if you don't have docker-compose-local-dev.yml locally, create a new file and + add the contents from docker-compose plus a line for: + - KEYCLOAK_TESTING=True + in api environment + + to run using this docker file: + docker-compose -f docker-compose-local-dev.yml up + + this ensures that the authentication skips the actual keycloak authentication + and uses the user table to get permissions + # Rebasing Guide - To rebase your branch onto the latest release branch: - ```git fetch upstream``` diff --git a/django/api/keycloak_authentication.py b/django/api/keycloak_authentication.py index 7abd1d86..b532a5b3 100644 --- a/django/api/keycloak_authentication.py +++ b/django/api/keycloak_authentication.py @@ -2,15 +2,21 @@ from django.conf import settings from rest_framework import authentication, exceptions +from api.models.user import User class KeycloakAuthentication(authentication.BaseAuthentication): def authenticate(self, request): auth = request.headers.get("Authorization", None) - + if settings.KEYCLOAK_TESTING: + try: + user = User.objects.get(idir=auth['idir']) + return user.idir, None + except User.DoesNotExist as exc: + # print("Testing User does not exist") + raise User.DoesNotExist(str(exc)) if not auth: raise exceptions.AuthenticationFailed("Authorization token required") - try: scheme, token = auth.split() except ValueError: diff --git a/django/api/settings.py b/django/api/settings.py index 1a6ddbb1..73856b9d 100644 --- a/django/api/settings.py +++ b/django/api/settings.py @@ -167,7 +167,7 @@ KEYCLOAK_CLIENT_ID = os.getenv("KEYCLOAK_CLIENT_ID") KEYCLOAK_REALM = os.getenv("KEYCLOAK_REALM") KEYCLOAK_URL = os.getenv("KEYCLOAK_URL", "http://localhost:8080") - +KEYCLOAK_TESTING = os.getenv("KEYCLOAK_TESTING", False) MINIO_ACCESS_KEY = os.getenv("MINIO_ROOT_USER") MINIO_SECRET_KEY = os.getenv("MINIO_ROOT_PASSWORD") diff --git a/django/api/tests/test_user.py b/django/api/tests/test_user.py new file mode 100644 index 00000000..b4324f4a --- /dev/null +++ b/django/api/tests/test_user.py @@ -0,0 +1,89 @@ +from unittest.mock import MagicMock +from django.test import TestCase +from api.keycloak_authentication import KeycloakAuthentication +from rest_framework.test import APIRequestFactory +from rest_framework.response import Response +from rest_framework import status +from django.http import HttpResponse +from rest_framework import exceptions + +from api.models.permission import Permission +from api.models.user import User +from api.models.user_permission import UserPermission +from api.viewsets.user import UserViewSet +from api.viewsets.upload import UploadViewset +from api.decorators.permission import check_upload_permission +class TestUsers(TestCase): + def setUp(self): + self.factory = APIRequestFactory() + self.userauth = KeycloakAuthentication() + self.view = UserViewSet.as_view({'get': 'list'}) + #user with no permission + self.test_user = User.objects.create(idir='test_user') + + #user with admin permission + self.test_admin_permission = Permission.objects.create(description='admin') + self.test_admin_user = User.objects.create(idir='test_admin_user') + self.admin_user_permission = UserPermission.objects.create(user=self.test_admin_user, permission=self.test_admin_permission) + + #user with upload permission + self.test_upload_permission = Permission.objects.create(description='uploader') + self.test_upload_user = User.objects.create(idir='test_upload_user') + self.upload_user_permission = UserPermission.objects.create(user=self.test_upload_user, permission=self.test_upload_permission) + + + def test_get_user_list(self): + self.assertTrue(User.objects.filter(idir='test_user').exists()) + request = self.factory.get('/api/users/list') + request.META = { + 'HTTP_AUTHORIZATION': { + 'idir': 'test_user' + } + } + response = self.view(request) + self.assertEqual(response.status_code, 403) # Forbidden status code + + self.assertTrue(User.objects.filter(idir='test_upload_user').exists()) + request_uploader = self.factory.get('/api/users/list') + request_uploader.META = { + 'HTTP_AUTHORIZATION': { + 'idir': 'test_upload_user' + } + } + response = self.view(request_uploader) + self.assertEqual(response.status_code, 403) # Forbidden status code + + self.assertTrue(User.objects.filter(idir='test_admin_user').exists()) + request_admin = self.factory.get('/api/users/list') + request_admin.META = { + 'HTTP_AUTHORIZATION': { + 'idir': 'test_admin_user' + } + } + response = self.view(request_admin) + self.assertEqual(response.status_code, 200) # OK status code + def test_not_authenticated_user(self): + request = self.factory.get('/api/users/list') + request.META = { + 'HTTP_AUTHORIZATION': { + 'idir': 'test' + } + } + with self.assertRaises(User.DoesNotExist): + _user, _auth = self.userauth.authenticate(request) + + def test_upload_user_permissions(self): + # test decorator for user with upload permission + @check_upload_permission() + def mock_import_function(request): + return HttpResponse() + request = self.factory.post('/api/users/list') + request.user = 'test_upload_user' + response = mock_import_function(request) + self.assertEqual(response.status_code, 200) # OK status code + + request_admin = self.factory.post('/api/users/list') + request_admin.user = 'test_admin_user' + response = mock_import_function(request_admin) + self.assertEqual(response.status_code, 403) # Forbidden! +