Skip to content
Snippets Groups Projects
Commit 394eed9c authored by Muthukrishnanmatriot's avatar Muthukrishnanmatriot
Browse files

Merge branch 'master' into TMSS-936

parents 2ad73573 9241ebb9
No related branches found
No related tags found
2 merge requests!634WIP: COBALT commissioning delta,!545Resolve TMSS-936
...@@ -48,7 +48,7 @@ def get_users_by_role_in_project(role, project): ...@@ -48,7 +48,7 @@ def get_users_by_role_in_project(role, project):
if project in project_persons: if project in project_persons:
return project_persons[project][role] return project_persons[project][role]
else: else:
[] return []
@cachetools.func.ttl_cache(ttl=600) @cachetools.func.ttl_cache(ttl=600)
......
...@@ -28,7 +28,8 @@ def get_project_roles_for_user(user): ...@@ -28,7 +28,8 @@ def get_project_roles_for_user(user):
try: try:
if user == models.User.objects.get(username='paulus'): if user == models.User.objects.get(username='paulus'):
return ({'project': 'test_user_is_shared_support', 'role': 'shared_support'}, return ({'project': 'test_user_is_shared_support', 'role': 'shared_support'},
{'project': 'test_user_is_contact', 'role': 'contact'}) {'project': 'test_user_is_contact', 'role': 'contact'},
{'project': 'test_user_is_friend', 'role': 'friend_of_project'})
except: except:
pass pass
......
...@@ -27,6 +27,7 @@ from lofar.sas.tmss.tmss.tmssapp.viewsets.lofar_viewset import LOFARViewSet, LOF ...@@ -27,6 +27,7 @@ from lofar.sas.tmss.tmss.tmssapp.viewsets.lofar_viewset import LOFARViewSet, LOF
from lofar.sas.tmss.tmss.tmssapp import models from lofar.sas.tmss.tmss.tmssapp import models
from lofar.sas.tmss.tmss.tmssapp import serializers from lofar.sas.tmss.tmss.tmssapp import serializers
from lofar.sas.tmss.tmss.tmssapp.adapters.reports import create_cycle_report, create_project_report from lofar.sas.tmss.tmss.tmssapp.adapters.reports import create_cycle_report, create_project_report
from lofar.sas.tmss.tmss.tmssapp.adapters.keycloak import get_users_by_role_in_project
from django.http import JsonResponse from django.http import JsonResponse
from datetime import datetime from datetime import datetime
...@@ -34,7 +35,7 @@ from lofar.common.json_utils import get_default_json_object_for_schema ...@@ -34,7 +35,7 @@ from lofar.common.json_utils import get_default_json_object_for_schema
from lofar.common.datetimeutils import formatDatetime from lofar.common.datetimeutils import formatDatetime
from lofar.sas.tmss.tmss.tmssapp.tasks import * from lofar.sas.tmss.tmss.tmssapp.tasks import *
from lofar.sas.tmss.tmss.tmssapp.subtasks import * from lofar.sas.tmss.tmss.tmssapp.subtasks import *
from lofar.sas.tmss.tmss.tmssapp.viewsets.permissions import TMSSDjangoModelPermissions from lofar.sas.tmss.tmss.tmssapp.viewsets.permissions import TMSSDjangoModelPermissions, get_project_roles_for_user
from django.urls import resolve, get_script_prefix,Resolver404 from django.urls import resolve, get_script_prefix,Resolver404
from rest_framework.filters import OrderingFilter from rest_framework.filters import OrderingFilter
...@@ -434,6 +435,28 @@ class ProjectViewSet(LOFARViewSet): ...@@ -434,6 +435,28 @@ class ProjectViewSet(LOFARViewSet):
return Response('Error: workflowapp is not running. It is needed to retrieve some reporting information.', status=status.HTTP_503_SERVICE_UNAVAILABLE) return Response('Error: workflowapp is not running. It is needed to retrieve some reporting information.', status=status.HTTP_503_SERVICE_UNAVAILABLE)
return Response(result, status=status.HTTP_200_OK) return Response(result, status=status.HTTP_200_OK)
@swagger_auto_schema(responses={200: 'List of users that have the "friend of project" role for this project',
403: 'forbidden'},
operation_description="Get the list of users that have the 'friend of project' role for this project")
@action(methods=['get'], detail=True, url_name="friend", name="Friend(s) of this project")
def friend(self, request, pk=None):
project = get_object_or_404(models.Project, pk=pk)
result = get_users_by_role_in_project(models.ProjectRole.Choices.FRIEND_OF_PROJECT.value, project.name)
return Response(result, status=status.HTTP_200_OK)
@swagger_auto_schema(responses={200: 'List of roles that the requesting user has in this project',
403: 'forbidden'},
operation_description="Get the list of roles that the requesting user has in this project")
@action(methods=['get'], detail=True, url_name="my_roles", name="My roles in this project")
def my_roles(self, request, pk=None):
project = get_object_or_404(models.Project, pk=pk)
project_roles = get_project_roles_for_user(request.user)
result = []
for project_role in project_roles:
if project.name == project_role['project']:
result.append(project_role['role'])
return Response(result, status=status.HTTP_200_OK)
class ProjectNestedViewSet(LOFARNestedViewSet): class ProjectNestedViewSet(LOFARNestedViewSet):
queryset = models.Project.objects.all() queryset = models.Project.objects.all()
......
...@@ -51,6 +51,13 @@ from lofar.sas.tmss.test.tmss_test_data_rest import TMSSRESTTestDataCreator ...@@ -51,6 +51,13 @@ from lofar.sas.tmss.test.tmss_test_data_rest import TMSSRESTTestDataCreator
from django.test import TestCase from django.test import TestCase
from django.contrib.auth import get_user_model
User = get_user_model()
from django.conf import settings
import json
class ProjectPermissionTestCase(TestCase): class ProjectPermissionTestCase(TestCase):
# This tests that the project permissions are enforced in light of the project roles that are externally provided # This tests that the project permissions are enforced in light of the project roles that are externally provided
# for the user through the user admin. This test does not rely on the project permissions as defined in the system, # for the user through the user admin. This test does not rely on the project permissions as defined in the system,
...@@ -66,7 +73,8 @@ class ProjectPermissionTestCase(TestCase): ...@@ -66,7 +73,8 @@ class ProjectPermissionTestCase(TestCase):
cls.project_permission_patcher = mock.patch('lofar.sas.tmss.tmss.tmssapp.viewsets.get_project_roles_for_user') # todo: fix namespace so we get the get_project_roles that gets actually used cls.project_permission_patcher = mock.patch('lofar.sas.tmss.tmss.tmssapp.viewsets.get_project_roles_for_user') # todo: fix namespace so we get the get_project_roles that gets actually used
cls.project_permission_mock = cls.project_permission_patcher.start() cls.project_permission_mock = cls.project_permission_patcher.start()
cls.project_permission_mock.return_value = ({'project': 'test_user_is_shared_support', 'role': 'shared_support'}, cls.project_permission_mock.return_value = ({'project': 'test_user_is_shared_support', 'role': 'shared_support'},
{'project': 'test_user_is_contact', 'role': 'contact_author'}) {'project': 'test_user_is_contact', 'role': 'contact_author'},
{'project': 'test_user_is_friend', 'role': 'friend_of_project'})
# create some stuff as the standard super user, as setup for the tests below # create some stuff as the standard super user, as setup for the tests below
cls.test_data_creator = TMSSRESTTestDataCreator(BASE_URL, AUTH) cls.test_data_creator = TMSSRESTTestDataCreator(BASE_URL, AUTH)
...@@ -75,8 +83,9 @@ class ProjectPermissionTestCase(TestCase): ...@@ -75,8 +83,9 @@ class ProjectPermissionTestCase(TestCase):
# create projects with magic names for which permission exists (or which have no whitelisted generic name) # create projects with magic names for which permission exists (or which have no whitelisted generic name)
cls.project_shared_support_url = cls.test_data_creator.post_data_and_get_url(cls.test_data_creator.Project(name='test_user_is_shared_support', cycle_urls=[cycle_url]), '/project/') cls.project_shared_support_url = cls.test_data_creator.post_data_and_get_url(cls.test_data_creator.Project(name='test_user_is_shared_support', cycle_urls=[cycle_url]), '/project/')
cls.project_contact_url = cls.test_data_creator.post_data_and_get_url(cls.test_data_creator.Project(name='test_user_is_contact', cycle_urls=[cycle_url]), '/project/') cls.project_contact_url = cls.test_data_creator.post_data_and_get_url(cls.test_data_creator.Project(name='test_user_is_contact', cycle_urls=[cycle_url]), '/project/')
cls.project_friend_url = cls.test_data_creator.post_data_and_get_url(cls.test_data_creator.Project(name='test_user_is_friend', cycle_urls=[cycle_url]), '/project/')
cls.project_forbidden_url = cls.test_data_creator.post_data_and_get_url(cls.test_data_creator.Project(name='forbidden', cycle_urls=[cycle_url]), '/project/') cls.project_forbidden_url = cls.test_data_creator.post_data_and_get_url(cls.test_data_creator.Project(name='forbidden', cycle_urls=[cycle_url]), '/project/')
cls.project_keycloak_url = cls.test_data_creator.post_data_and_get_url(cls.test_data_creator.Project(name="2014LOFAROBS", cycle_urls=[cycle_url]), '/project/') # project is known to Keycloak
# user is shared_support # user is shared_support
cls.scheduling_set_shared_support_url = cls.test_data_creator.post_data_and_get_url(cls.test_data_creator.SchedulingSet(project_url=cls.project_shared_support_url), '/scheduling_set/') cls.scheduling_set_shared_support_url = cls.test_data_creator.post_data_and_get_url(cls.test_data_creator.SchedulingSet(project_url=cls.project_shared_support_url), '/scheduling_set/')
...@@ -92,8 +101,10 @@ class ProjectPermissionTestCase(TestCase): ...@@ -92,8 +101,10 @@ class ProjectPermissionTestCase(TestCase):
# create the required permission entries to control what endpoint action requires which project role # create the required permission entries to control what endpoint action requires which project role
shared_support_role_url = BASE_URL + '/project_role/shared_support/' shared_support_role_url = BASE_URL + '/project_role/shared_support/'
friend_of_project_role_url = BASE_URL + '/project_role/friend_of_project/'
cls.test_data_creator.post_data_and_get_url(cls.test_data_creator.ProjectPermission(name='taskdraft', GET=[shared_support_role_url], POST=[shared_support_role_url]), '/project_permission/') cls.test_data_creator.post_data_and_get_url(cls.test_data_creator.ProjectPermission(name='taskdraft', GET=[shared_support_role_url], POST=[shared_support_role_url]), '/project_permission/')
cls.test_data_creator.post_data_and_get_url(cls.test_data_creator.ProjectPermission(name='taskdraft-create_task_blueprint', POST=[shared_support_role_url]), '/project_permission/') cls.test_data_creator.post_data_and_get_url(cls.test_data_creator.ProjectPermission(name='taskdraft-create_task_blueprint', POST=[shared_support_role_url]), '/project_permission/')
cls.test_data_creator.post_data_and_get_url(cls.test_data_creator.ProjectPermission(name='project-my_roles', GET=[shared_support_role_url, friend_of_project_role_url]), '/project_permission/')
cls.task_template_url = cls.test_data_creator.post_data_and_get_url(cls.test_data_creator.TaskTemplate(), '/task_template/') cls.task_template_url = cls.test_data_creator.post_data_and_get_url(cls.test_data_creator.TaskTemplate(), '/task_template/')
...@@ -228,7 +239,44 @@ class ProjectPermissionTestCase(TestCase): ...@@ -228,7 +239,44 @@ class ProjectPermissionTestCase(TestCase):
self.assertEqual(r.status_code, 403) self.assertEqual(r.status_code, 403)
self.assertNotIn('Access-Control-Allow-Methods', r.headers) self.assertNotIn('Access-Control-Allow-Methods', r.headers)
# todo: add tests for other models with project permissions def test_project_get_friend_returns_correct_user(self):
"""
Note: This test relies on real data from Keycloak.
"""
with requests.Session() as session:
session.verify = False
session.auth = (AUTH.username, AUTH.password)
r = session.get(self.project_keycloak_url + '/friend/')
if 'Invalid user credentials' in str(r.content) or \
'Service Unavailable' in str(r.content):
self.skipTest('skipping test_project_get_friend_returns_correct_user because the test environment has'
'no valid admin credentials configured, or Keycloak is not working correctly.')
self.assertEqual(r.status_code, 200)
content = json.loads(r.content.decode('utf-8'))
self.assertEqual(len(content), 2)
for friend in content:
# Todo: find a way to mock the Keycloak response so we can assert more strictly.
self.assertTrue(friend.endswith('@astron.nl')) # redacted expected full email due to GDPR
def test_project_get_friend_returns_403_if_no_permission_for_project(self):
r = GET_and_assert_equal_expected_code(self, self.project_forbidden_url + '/friend/', 403, auth=self.auth)
self.assertIn('permission', str(r))
def test_project_get_my_roles_returns_correct_roles(self):
r = GET_and_assert_equal_expected_code(self, self.project_shared_support_url + '/my_roles/', 200, auth=self.auth)
expected_reply = ['shared_support']
self.assertEqual(expected_reply, r)
r = GET_and_assert_equal_expected_code(self, self.project_friend_url + '/my_roles/', 200, auth=self.auth)
expected_reply = ['friend_of_project']
self.assertEqual(expected_reply, r)
def test_project_get_my_roles_returns_403_if_no_permission_for_project(self):
r = GET_and_assert_equal_expected_code(self, self.project_forbidden_url + '/my_roles/', 403, auth=self.auth)
self.assertIn('permission', str(r))
if __name__ == "__main__": if __name__ == "__main__":
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment