diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0002_populate.py b/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0002_populate.py
index 023594b67ad9d5f700bb0a6976b5151bacd4fd49..724aa45accf73a8abe4bfd69fd902038a10dc9fa 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0002_populate.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/migrations/0002_populate.py
@@ -31,4 +31,7 @@ class Migration(migrations.Migration):
                    migrations.RunPython(populate_misc),
                    migrations.RunPython(populate_resources),
                    migrations.RunPython(populate_cycles),
-                   migrations.RunPython(populate_projects) ]
+                   migrations.RunPython(populate_projects)]#,
+                   #migrations.RunPython(populate_system_permissions),
+                   #migrations.RunPython(populate_system_roles),
+                   #migrations.RunPython(populate_system_test_users) ]
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/models/CMakeLists.txt b/SAS/TMSS/backend/src/tmss/tmssapp/models/CMakeLists.txt
index 64b472d0532f38dcee337722ff16d68881b85099..3496efd57358ab186b665fe2dc3bd40264d4deaa 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/models/CMakeLists.txt
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/models/CMakeLists.txt
@@ -3,6 +3,7 @@ include(PythonInstall)
 
 set(_py_files
     __init__.py
+    permissions.py
     specification.py
     scheduling.py
     common.py
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/models/permissions.py b/SAS/TMSS/backend/src/tmss/tmssapp/models/permissions.py
index 9ff0dd5a806e427b40cf485bf700181440d16b75..8cdbf52b01eb27e1c3e8467cbba34e4494c37b3d 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/models/permissions.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/models/permissions.py
@@ -9,6 +9,9 @@ from .common import NamedCommonPK, AbstractChoice
 from django.db.models import ManyToManyField
 from enum import Enum
 
+from rest_framework.permissions import DjangoModelPermissions
+
+
 #
 # Project Permissions
 #
@@ -31,3 +34,20 @@ class ProjectPermission(NamedCommonPK):
      POST = ManyToManyField('ProjectRole', related_name='can_POST', blank=True)
      PATCH = ManyToManyField('ProjectRole', related_name='can_PATCH', blank=True)
      DELETE = ManyToManyField('ProjectRole', related_name='can_DELETE', blank=True)
+
+
+# todo: move to viewsets / merge with TMSSDjangoModelPermissions class
+class TMSSBasePermissions(DjangoModelPermissions):
+    # This enforces permissions as "deny any" by default.
+    view_permissions = ['%(app_label)s.view_%(model_name)s']
+
+    perms_map = {
+        'GET': view_permissions,
+        'OPTIONS': view_permissions,
+        'HEAD': view_permissions,
+        'POST': DjangoModelPermissions.perms_map['POST'],
+        'PUT': DjangoModelPermissions.perms_map['PUT'],
+        'PATCH': DjangoModelPermissions.perms_map['PATCH'],
+        'DELETE': DjangoModelPermissions.perms_map['DELETE'],
+    }
+
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/populate.py b/SAS/TMSS/backend/src/tmss/tmssapp/populate.py
index 6eae000487adfd897cdfcc6a9491f9f343cf4d54..4aa9fe38cc2774eb3f5f72075e8ff3e567656fbe 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/populate.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/populate.py
@@ -255,3 +255,102 @@ def populate_permissions():
     scientist_group = Group.objects.create(name='Scientist')
     scientist_group.permissions.add(Permission.objects.get(codename='view_cycle'))
     scientist_group.permissions.add(Permission.objects.get(codename='view_project'))
+
+
+# TODO: Merge with project permissions
+def populate_system_permissions(apps, schema_editor):
+    import inspect
+    from django.contrib.auth.models import Permission
+    from django.contrib.contenttypes.models import ContentType
+    from lofar.sas.tmss.tmss.tmssapp import viewsets
+
+    # For each viewset create custom permissions for extra actions.
+    for name, obj in inspect.getmembers(viewsets):
+        if inspect.isclass(obj):
+            try:
+                ct = ContentType.objects.get_for_model(obj.serializer_class.Meta.model)
+                extra_actions = obj.get_extra_actions()
+                if extra_actions:
+                    for action in extra_actions:
+                        codename = ("%s_%s" % (action.__name__, obj.serializer_class.Meta.model.__name__)).lower()
+                        name = f'Can {action.__name__} {obj.serializer_class.Meta.model.__name__.lower()}'
+                        Permission.objects.create(codename=codename, name=name, content_type=ct)
+            except:
+                pass
+
+
+def populate_system_roles(apps, schema_editor):
+    from django.contrib.contenttypes.models import ContentType
+    from django.contrib.auth.models import Group, Permission
+    to_observer_group = Group.objects.create(name='TO observer')
+    # TODO: Define permissions and add to the proper roles. Refactoring.
+
+    # Subtask permissions
+    ct = ContentType.objects.get(model='subtask')
+    perm, created = Permission.objects.get_or_create(codename='view_subtask', name='Can view subtask', content_type=ct)
+    to_observer_group.permissions.add(perm)
+    to_observer_group.permissions.add(Permission.objects.get(codename='schedule_subtask'))
+
+    # Template permissions
+    import inspect
+    from lofar.sas.tmss.tmss.tmssapp import models
+    import re
+    # For each template add permissions for TO observer role.
+    for name, obj in inspect.getmembers(models):
+        if inspect.isclass(obj) and issubclass(obj, Template) and obj is not Template:
+            ct = ContentType.objects.get(model=name.lower())
+            perm_name = ''.join('_'.join(re.findall(r'[A-Z](?:[a-z]+|[A-Z]*(?=[A-Z]|$))', name)).lower())
+            perm, created = Permission.objects.get_or_create(codename=f'add_{name.lower()}',
+                                                             name=f'Can add {perm_name}', content_type=ct)
+            to_observer_group.permissions.add(perm)
+            perm, created = Permission.objects.get_or_create(codename=f'view_{name.lower()}',
+                                                             name=f'Can view {perm_name}', content_type=ct)
+            to_observer_group.permissions.add(perm)
+            perm, created = Permission.objects.get_or_create(codename=f'change_{name.lower()}',
+                                                             name=f'Can change {perm_name}', content_type=ct)
+            to_observer_group.permissions.add(perm)
+
+    # SchedulingUnit permissions
+    for template in ['schedulingunitdraft', 'schedulingunitblueprint']:
+        ct = ContentType.objects.get(model=template)
+        perm, created = Permission.objects.get_or_create(codename=f'add_{template}', name=f'Can add {template}',
+                                                         content_type=ct)
+        to_observer_group.permissions.add(perm)
+        perm, created = Permission.objects.get_or_create(codename=f'view_{template}', name=f'Can view {template}',
+                                                         content_type=ct)
+        to_observer_group.permissions.add(perm)
+
+    sdco_support_group = Group.objects.create(name='SDCO support')
+    tmss_maintainer_group = Group.objects.create(name='TMSS Maintainer')
+    tmss_admin_group = Group.objects.create(name='TMSS Admin')
+    to_maintenance_group = Group.objects.create(name='TO maintenance')
+    scientist_group = Group.objects.create(name='Scientist')
+    e_scientist_group = Group.objects.create(name='Scientist (Expert)')
+    guest_group = Group.objects.create(name='Guest')
+    lta_user_group = Group.objects.create(name='LTA User')
+
+
+def populate_system_test_users(apps, schema_editor):
+    from django.contrib.auth.models import User, Group
+
+    # TODO: Superuser only for testing purposes, remove it when finished.
+    User.objects.create_superuser('admin', 'admin@example.com', 'admin')
+
+    to_observer_user = User.objects.create(username='to_observer', password='to_observer')
+    to_observer_user.groups.add(Group.objects.get(name='TO observer'))
+    sdco_support_user = User.objects.create(username='sdco_support', password='sdco_support')
+    sdco_support_user.groups.add(Group.objects.get(name='SDCO support'))
+    tmss_maintainer_user = User.objects.create(username='tmss_maintainer', password='tmss_maintainer')
+    tmss_maintainer_user.groups.add(Group.objects.get(name='TMSS Maintainer'))
+    tmss_admin_user = User.objects.create(username='tmss_admin', password='tmss_admin')
+    tmss_admin_user.groups.add(Group.objects.get(name='TMSS Admin'))
+    to_maintenance_user = User.objects.create(username='to_maintenance', password='to_maintenance')
+    to_maintenance_user.groups.add(Group.objects.get(name='TO maintenance'))
+    scientist_user = User.objects.create(username='scientist', password='scientist')
+    scientist_user.groups.add(Group.objects.get(name='Scientist'))
+    e_scientist_user = User.objects.create(username='e_scientist', password='e_scientist')
+    e_scientist_user.groups.add(Group.objects.get(name='Scientist (Expert)'))
+    guest_user = User.objects.create(username='guest', password='guest')
+    guest_user.groups.add(Group.objects.get(name='Guest'))
+    lta_user = User.objects.create(username='lta_user', password='lta_user')
+    lta_user.groups.add(Group.objects.get(name='LTA User'))
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/lofar_viewset.py b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/lofar_viewset.py
index 6e8807ec170b99044936ac30b9ac7c4509564143..abe9383eb92c077fb0ca4b452d9c8b1fd2575675 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/lofar_viewset.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/lofar_viewset.py
@@ -18,15 +18,56 @@ from django.urls import reverse as revese_url
 from rest_framework.decorators import action
 from lofar.common import json_utils
 from lofar.sas.tmss.tmss.tmssapp.viewsets.permissions import TMSSPermissions, IsProjectMemberFilterBackend
-
-class LOFARViewSet(viewsets.ModelViewSet):
+from lofar.sas.tmss.tmss.tmssapp.models import permissions
+
+
+class TMSSPermissionsMixin:
+
+    def __init__(self, *args, **kwargs):
+        self.permission_classes = (TMSSPermissions,)
+        self.filter_backends = (IsProjectMemberFilterBackend,)
+        self.extra_action_permission_classes = []
+        self._create_extra_action_permission_classes()
+        super(TMSSPermissionsMixin, self).__init__(*args, **kwargs)
+
+    # TODO: Cache this method to avoid redundancy and overhead.
+    def _create_extra_action_permission_classes(self):
+        extra_actions = [a.__name__ for a in self.get_extra_actions()]
+        for ea in extra_actions:  # Create permission classes
+            permission_name = f'{ea}_{self.serializer_class.Meta.model.__name__.lower()}'
+            permission_class_name = f'Can {ea} {self.serializer_class.Meta.model.__name__.lower()}'
+            new_permission_class = type(f'{permission_class_name}', (permissions.TMSSBasePermissions,), {
+                # TODO: Is it necessary to have both permissions and object permissions?
+                # TODO: Find a way to use the "%(app_label)s." syntax.
+                'permission_name': permission_name,
+                'has_permission': lambda self, request, view: request.user.has_perm(f'tmssapp.{self.permission_name}'),
+                'has_object_permission': lambda self, request, view, obj: request.user.has_perm(f'tmssapp.{self.permission_name}'),
+            })
+            new_permission_class.__setattr__(self, 'permission_name', permission_name)
+            self.extra_action_permission_classes.append({ea: new_permission_class},)
+
+    # TODO: Refactoring.
+    def get_model_permissions(self):
+        extra_actions = [a.__name__ for a in self.get_extra_actions()]
+        if self.action in extra_actions:
+            for ea_permission_class in self.extra_action_permission_classes:
+                if ea_permission_class.get(self.action):
+                    return [permissions.TMSSBasePermissions, ea_permission_class.get(self.action),]
+                else:
+                    return [permissions.TMSSBasePermissions,]
+        else:
+            return [permissions.TMSSBasePermissions, ]
+
+    #def get_permissions(self):
+    #    self.get_extra_action_permission_classes()
+    #    return super(TMSSPermissionsMixin, self).get_permissions()
+
+
+class LOFARViewSet(TMSSPermissionsMixin, viewsets.ModelViewSet):
     """
     If you're using format suffixes, make sure to also include
     the `format=None` keyword argument for each action.
     """
-    permission_classes = (TMSSPermissions,)
-    filter_backends = (IsProjectMemberFilterBackend,)
-
 
     @swagger_auto_schema(responses={403: 'forbidden'})
     def list(self, request, **kwargs):
@@ -57,9 +98,6 @@ class LOFARNestedViewSet(mixins.CreateModelMixin,
                                 #mixins.RetrieveModelMixin,
                                 viewsets.GenericViewSet):
 
-    permission_classes = (TMSSPermissions,)
-    filter_backends = (IsProjectMemberFilterBackend,)
-
     @swagger_auto_schema(responses={403: 'forbidden'})
     def list(self, request, **kwargs):
         return super(LOFARNestedViewSet, self).list(request, **kwargs)
@@ -78,9 +116,6 @@ class LOFARCopyViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet):
         return super(LOFARCopyViewSet, self).list(request, **kwargs)
     """
 
-    permission_classes = (TMSSPermissions,)
-    filter_backends = (IsProjectMemberFilterBackend,)
-
     @swagger_auto_schema(responses={400: 'invalid specification', 403: 'forbidden'})
     def create(self, request, **kwargs):
         return super(LOFARCopyViewSet, self).create(request, **kwargs)
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/permissions.py b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/permissions.py
index dd702ea2c2bcc2483f181181c3b9633f951556ef..30e8d1c255c66eb7bbd6aa3cca0579b25ef4bdba 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/permissions.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/permissions.py
@@ -180,15 +180,14 @@ class TMSSPermissions(drf_permissions.DjangoObjectPermissions):
         Create custom permission class
         Note: required because the composition using & and | in the permission_classes does not seem to work as it should.
         """
-        model_permissions = TMSSDjangoModelPermissions()
         project_permissions = IsProjectMember()
 
         def has_permission(self, request, view):
-            return (self.model_permissions.has_permission(request, view) or
+            return (all(model_permission().has_permission(request, view) for model_permission in view.get_model_permissions()) or
                     self.project_permissions.has_permission(request, view)) and request.user.is_authenticated
 
         def has_object_permission(self, request, view, obj):
-            return IsProjectMember().has_object_permission(request, view, obj) and request.user.is_authenticated
+            return self.project_permissions.has_object_permission(request, view, obj) and request.user.is_authenticated
 
 
 #
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/scheduling.py b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/scheduling.py
index 42fc0210cbabfc8e86b05c51f2b0657aaca6ea53..b6045d85cd416eb6ed0a0e3036182a66a0a2d6a7 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/scheduling.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/scheduling.py
@@ -17,6 +17,7 @@ from drf_yasg.inspectors import SwaggerAutoSchema
 from drf_yasg.openapi import Parameter
 
 from rest_framework.decorators import action
+from rest_framework.decorators import permission_classes
 from django.http import HttpResponse, JsonResponse, HttpResponseRedirect, HttpResponseNotFound
 from rest_framework.response import Response as RestResponse
 
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py
index e77738e5735924f1f0a8301ce9b514209b0ef734..ee92b5de210d8952f551362dd8f34b36fb8174b9 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/viewsets/specification.py
@@ -735,8 +735,6 @@ class SchedulingUnitBlueprintNestedViewSet(LOFARNestedViewSet):
 class TaskDraftViewSet(LOFARViewSet):
     queryset = models.TaskDraft.objects.all()
     serializer_class = serializers.TaskDraftSerializer
-    #permission_classes = (TMSSPermissions,) # todo: move to LOFARViewSet eventually?  # Note: this should work the same, but something funny is going on: [IsProjectMember | TMSSDjangoModelPermissions]
-    #filter_backends = (IsProjectMemberFilterBackend,) # todo: move to LOFARViewSet eventually?
 
     # prefetch all reverse related references from other models on their related_name to avoid a ton of duplicate queries
     queryset = queryset.prefetch_related('first_scheduling_relation') \
diff --git a/SAS/TMSS/test/t_tmssapp_permissions_system_roles.py b/SAS/TMSS/test/t_tmssapp_permissions_system_roles.py
new file mode 100644
index 0000000000000000000000000000000000000000..56573ae527a2bd3be7e0c22d22352185b8f86b54
--- /dev/null
+++ b/SAS/TMSS/test/t_tmssapp_permissions_system_roles.py
@@ -0,0 +1,95 @@
+#!/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:  $
+
+import logging
+logger = logging.getLogger(__name__)
+logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s', level=logging.INFO)
+
+# 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 *
+
+# import and setup test data creator
+from lofar.sas.tmss.test.tmss_test_data_rest import TMSSRESTTestDataCreator
+
+from lofar.sas.tmss.tmss.tmssapp import models
+
+from django.contrib.auth.models import User, Group, Permission
+from datetime import datetime
+import unittest
+import requests
+
+
+class SystemRolesTestCase(unittest.TestCase):
+    @classmethod
+    def setUpClass(cls):
+        # cls.test_data_creator = TMSSRESTTestDataCreator(BASE_URL, requests.auth.HTTPBasicAuth('paulus', 'pauluspass'))
+        # response = requests.get(cls.test_data_creator.django_api_url + '/', auth=cls.test_data_creator.auth)
+
+        # System roles creation. More at https://support.astron.nl/confluence/display/TMSS/User+roles
+        cls.to_observer_group = Group.objects.create(name='TO observer')
+        # TODO: Define permissions and add to the proper roles.
+        # cls.to_observer_group.permissions.add(Permission.objects.get(codename='add_user'))
+        # cls.to_observer_group.permissions.add(Permission.objects.get(codename='add_group'))
+
+        cls.sdco_support_group = Group.objects.create(name='SDCO support')
+        cls.tmss_maintainer_group = Group.objects.create(name='TMSS Maintainer')
+        cls.tmss_admin_group = Group.objects.create(name='TMSS Admin')
+        cls.to_maintenance_group = Group.objects.create(name='TO maintenance')
+        cls.scientist_group = Group.objects.create(name='Scientist')
+        cls.e_scientist_group = Group.objects.create(name='Scientist (Expert)')
+        cls.guest_group = Group.objects.create(name='Guest')
+        cls.lta_user_group = Group.objects.create(name='LTA User')
+
+    def test(self):
+        logger.info('Created new group: %s', self.to_observer_group)
+
+
+class SystemUserTestCase(unittest.TestCase):
+    @classmethod
+    def setUpClass(cls):
+        # Create test users and assign them the proper system role.
+        cls.to_observer_user = User.objects.create(username='to_observer', password='to_observer')
+        cls.to_observer_user.groups.add(Group.objects.get(name='TO observer'))
+        cls.sdco_support_user = User.objects.create(username='sdco_support', password='sdco_support')
+        cls.sdco_support_user.groups.add(Group.objects.get(name='SDCO support'))
+        cls.tmss_maintainer_user = User.objects.create(username='tmss_maintainer', password='tmss_maintainer')
+        cls.tmss_maintainer_user.groups.add(Group.objects.get(name='TMSS Maintainer'))
+        cls.tmss_admin_user = User.objects.create(username='tmss_admin', password='tmss_admin')
+        cls.tmss_admin_user.groups.add(Group.objects.get(name='TMSS Admin'))
+        cls.to_maintenance_user = User.objects.create(username='to_maintenance', password='to_maintenance')
+        cls.to_maintenance_user.groups.add(Group.objects.get(name='TO maintenance'))
+        cls.scientist_user = User.objects.create(username='scientist', password='scientist')
+        cls.scientist_user.groups.add(Group.objects.get(name='Scientist'))
+        cls.e_scientist_user = User.objects.create(username='e_scientist', password='e_scientist')
+        cls.e_scientist_user.groups.add(Group.objects.get(name='Scientist (Expert)'))
+        cls.guest_user = User.objects.create(username='guest', password='guest')
+        cls.guest_user.groups.add(Group.objects.get(name='Guest'))
+        cls.lta_user = User.objects.create(username='lta_user', password='lta_user')
+        cls.lta_user.groups.add(Group.objects.get(name='LTA User'))
+
+    def test(self):
+        logger.info('Created new user: %s', self.to_observer_user)
+
+
+if __name__ == "__main__":
+    unittest.main()