diff --git a/SAS/TMSS/test/t_permissions.py b/SAS/TMSS/test/t_permissions.py
new file mode 100755
index 0000000000000000000000000000000000000000..1ccfe0fd7a3839cd51c169ce7db34ef18eb9d2e5
--- /dev/null
+++ b/SAS/TMSS/test/t_permissions.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2018    ASTRON (Netherlands Institute for Radio Astronomy)
+# P.O. Box 2, 7990 AA Dwingeloo, The Netherlands
+#
+# This file is part of the LOFAR software suite.
+# The LOFAR software suite is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# The LOFAR software suite 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
+
+# $Id:  $
+
+
+# This functional test talks to the API like a regular user would.
+# It is supposed to cover all REST http methods for all ViewSets.
+# I am still a bit under the impression that we re-test Django functionality that we can expect to just work
+# with some of these tests. On the other hand a lot of these provide us a nice basis for differentiating out
+# behavior in a controlled way.
+# We should probably also fully test behavior wrt mandatory and nullable fields.
+
+from datetime import datetime
+import unittest
+import logging
+logger = logging.getLogger(__name__)
+logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', level=logging.INFO)
+
+from lofar.common.test_utils import skip_integration_tests
+if skip_integration_tests():
+    exit(3)
+
+# Do Mandatory setup step:
+# use setup/teardown magic for tmss test database, ldap server and django server
+# (ignore pycharm unused import statement, python unittests does use at RunTime the tmss_test_environment_unittest_setup module)
+from lofar.sas.tmss.test.tmss_test_environment_unittest_setup import *
+from lofar.sas.tmss.test.tmss_test_data_django_models import *
+from lofar.sas.tmss.tmss.tmssapp import models
+from lofar.sas.tmss.test.test_utils import assertUrlList
+
+
+# import and setup test data creator
+from lofar.sas.tmss.test.tmss_test_data_rest import TMSSRESTTestDataCreator
+test_data_creator = TMSSRESTTestDataCreator(BASE_URL, AUTH)
+
+from django.test import TestCase
+
+class ProjectPermissionTestCase(TestCase):
+
+    @classmethod
+    def setUpClass(cls) -> None:
+        super().setUpClass()
+
+        # create projects with magic names for which permission exists (or which have no whitelisted generic name)
+        cls.project_pi_url = test_data_creator.post_data_and_get_url(test_data_creator.Project(name='test_user_is_pi'), '/project/')
+        cls.project_contact_url = test_data_creator.post_data_and_get_url(test_data_creator.Project(name='test_user_is_contact'), '/project/')
+        cls.project_forbidden_url = test_data_creator.post_data_and_get_url(test_data_creator.Project(name='forbidden'), '/project/')
+
+        cls.task_template_url = test_data_creator.post_data_and_get_url(test_data_creator.TaskTemplate(), '/task_template/')
+
+        # user is pi
+        cls.scheduling_set_pi_url = test_data_creator.post_data_and_get_url(test_data_creator.SchedulingSet(project_url=cls.project_pi_url), '/scheduling_set/')
+        cls.scheduling_unit_draft_pi_url = test_data_creator.post_data_and_get_url(test_data_creator.SchedulingUnitDraft(scheduling_set_url=cls.scheduling_set_pi_url), '/scheduling_unit_draft/')
+
+        # user is contact
+        cls.scheduling_set_contact_url = test_data_creator.post_data_and_get_url(test_data_creator.SchedulingSet(project_url=cls.project_contact_url), '/scheduling_set/')
+        cls.scheduling_unit_draft_contact_url = test_data_creator.post_data_and_get_url(test_data_creator.SchedulingUnitDraft(scheduling_set_url=cls.scheduling_set_contact_url), '/scheduling_unit_draft/')
+
+        # user has no role
+        cls.scheduling_set_forbidden_url = test_data_creator.post_data_and_get_url(test_data_creator.SchedulingSet(project_url=cls.project_forbidden_url), '/scheduling_set/')
+        cls.scheduling_unit_draft_forbidden_url = test_data_creator.post_data_and_get_url(test_data_creator.SchedulingUnitDraft(scheduling_set_url=cls.scheduling_set_contact_url), '/scheduling_unit_draft/')
+
+    # TaskDraft
+
+    def test_task_draft_GET_works_if_user_has_permission_for_related_project(self):
+        # create task draft connected to project where we have PI role
+        taskdraft_test_data = test_data_creator.TaskDraft(scheduling_unit_draft_url=self.scheduling_unit_draft_pi_url, template_url=self.task_template_url)
+        taskdraft_url = POST_and_assert_expected_response(self, BASE_URL + '/task_draft/', taskdraft_test_data, 201, taskdraft_test_data)['url']
+
+        # make sure we can access it
+        GET_and_assert_equal_expected_code(self, taskdraft_url, 200)
+
+    def test_task_draft_GET_raises_error_if_user_has_no_permission_for_related_project(self):
+        # create task draft connected to project where we have no role
+        taskdraft_test_data = test_data_creator.TaskDraft(scheduling_unit_draft_url=self.scheduling_unit_draft_forbidden_url, template_url=self.task_template_url)
+        taskdraft_url = POST_and_assert_expected_response(self, BASE_URL + '/task_draft/', taskdraft_test_data, 201, taskdraft_test_data)['url']
+
+        # make sure we cannot access it
+        GET_and_assert_equal_expected_code(self, taskdraft_url, 403)
+
+    def test_task_draft_GET_raises_error_if_user_has_permission_for_related_project_but_with_wrong_role(self):
+        # create task draft connected to project where we have Contact Author role  (Task Draft access requires role 'PI')
+        taskdraft_test_data = test_data_creator.TaskDraft(scheduling_unit_draft_url=self.scheduling_unit_draft_contact_url, template_url=self.task_template_url)
+        taskdraft_url = POST_and_assert_expected_response(self, BASE_URL + '/task_draft/', taskdraft_test_data, 201, taskdraft_test_data)['url']
+
+        # make sure we cannot access it
+        GET_and_assert_equal_expected_code(self, taskdraft_url, 403)
+
+    def test_GET_task_draft_list_returns_filtered_list_reflecting_user_permission_for_related_projects(self):
+        nbr_results = GET_and_assert_equal_expected_code(self, BASE_URL + '/task_draft/', 200)["count"] # note: this does not guarantee the correct number with permission-based filtering: nbr_results = models.TaskDraft.objects.count()
+
+        # create task draft connected to project where we have PI role
+        taskdraft_test_data_pi = test_data_creator.TaskDraft(scheduling_unit_draft_url=self.scheduling_unit_draft_pi_url, template_url=self.task_template_url)
+        POST_and_assert_expected_response(self, BASE_URL + '/task_draft/', taskdraft_test_data_pi, 201, taskdraft_test_data_pi)
+
+        # create task draft connected to project where we have unsufficient contact role
+        taskdraft_test_data_contact = test_data_creator.TaskDraft(scheduling_unit_draft_url=self.scheduling_unit_draft_contact_url, template_url=self.task_template_url)
+        POST_and_assert_expected_response(self, BASE_URL + '/task_draft/', taskdraft_test_data_contact, 201, taskdraft_test_data_contact)
+
+        # create task draft connected to project where we have no role
+        taskdraft_test_data_forbidden = test_data_creator.TaskDraft(scheduling_unit_draft_url=self.scheduling_unit_draft_contact_url, template_url=self.task_template_url)
+        POST_and_assert_expected_response(self, BASE_URL + '/task_draft/', taskdraft_test_data_forbidden, 201, taskdraft_test_data_forbidden)
+
+        # make sure the list contains only the one more item we have permission for
+        GET_and_assert_in_expected_response_result_list(self, BASE_URL + '/task_draft/', taskdraft_test_data_pi, nbr_results + 1)
+
+    # todo: add tests for other models with project permissions
+
+if __name__ == "__main__":
+    logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s',
+                        level=logging.INFO)
+    unittest.main()
+
diff --git a/SAS/TMSS/test/t_permissions.run b/SAS/TMSS/test/t_permissions.run
new file mode 100755
index 0000000000000000000000000000000000000000..4adc6f4186ebd66e1d329c4a174dcbaf05a4754f
--- /dev/null
+++ b/SAS/TMSS/test/t_permissions.run
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+# Run the unit test
+source python-coverage.sh
+python_coverage_test "*tmss*" t_permissions.py
+
diff --git a/SAS/TMSS/test/t_permissions.sh b/SAS/TMSS/test/t_permissions.sh
new file mode 100755
index 0000000000000000000000000000000000000000..c66d4e64d5c2a8d5494146563785bd567baf23c0
--- /dev/null
+++ b/SAS/TMSS/test/t_permissions.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+./runctest.sh t_permissions
\ No newline at end of file