diff --git a/SAS/TMSS/src/tmss/settings.py b/SAS/TMSS/src/tmss/settings.py
index 617b4eedbe9fd40273033319d6fa90fe7f14c4ba..5a817ce8d4714fb7b2bedf1cded25d3d5763b630 100644
--- a/SAS/TMSS/src/tmss/settings.py
+++ b/SAS/TMSS/src/tmss/settings.py
@@ -82,7 +82,7 @@ SECRET_KEY = '-&$!kx$_0)u1x#zk9w^^81hfssaover2(8wdq_8n8n3u(8=-9n'       # todo:
 # SECURITY WARNING: don't run with debug turned on in production!
 DEBUG = True
 
-ALLOWED_HOSTS = []
+ALLOWED_HOSTS = ['localhost']
 
 
 # Application definition
@@ -183,7 +183,8 @@ REST_FRAMEWORK = {
         #'rest_framework.permissions.AllowAny',
         #'rest_framework.permissions.IsAdminUser',
         'rest_framework.permissions.IsAuthenticated',
-        #'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
+        #'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly',
+        #'rest_framework.permissions.DjangoModelPermissions'
     ],
     'PAGE_SIZE': 50,
     'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
@@ -234,8 +235,8 @@ OIDC_DRF_AUTH_BACKEND = 'mozilla_django_oidc.auth.OIDCAuthenticationBackend'
 
 
 # For talking to Mozilla Identity Provider:
-OIDC_RP_SCOPES = "openid email groups"
-OIDC_RP_CLIENT_ID = os.environ.get('OIDC_RP_CLIENT_ID', '1')               # Secret, do not put real credentials on Git
+OIDC_RP_SCOPES = "openid email profile"   # todo: groups are not a standard scope, how to handle those?
+OIDC_RP_CLIENT_ID = os.environ.get('OIDC_RP_CLIENT_ID', '2')               # Secret, do not put real credentials on Git
 OIDC_RP_CLIENT_SECRET = os.environ.get('OIDC_RP_CLIENT_SECRET', 'secret')       # Secret, do not put real credentials on Git
 OIDC_ENDPOINT_HOST = os.environ.get('OIDC_ENDPOINT_HOST', 'tmss_test_oidc')
 OIDC_OP_AUTHORIZATION_ENDPOINT="http://%s:8088/openid/authorize" % OIDC_ENDPOINT_HOST
diff --git a/SAS/TMSS/src/tmss/tmssapp/migrations/0002_auto_20200123_1057.py b/SAS/TMSS/src/tmss/tmssapp/migrations/0002_auto_20200204_1117.py
similarity index 99%
rename from SAS/TMSS/src/tmss/tmssapp/migrations/0002_auto_20200123_1057.py
rename to SAS/TMSS/src/tmss/tmssapp/migrations/0002_auto_20200204_1117.py
index 1bccdfe6ac7fbf01c71031f992938c4e25aca38d..095dd64a905103bd7e2aba8752f213d825bd6746 100644
--- a/SAS/TMSS/src/tmss/tmssapp/migrations/0002_auto_20200123_1057.py
+++ b/SAS/TMSS/src/tmss/tmssapp/migrations/0002_auto_20200204_1117.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.0.6 on 2020-01-23 10:57
+# Generated by Django 2.0.6 on 2020-02-04 11:17
 
 import django.contrib.postgres.fields
 import django.contrib.postgres.fields.jsonb
diff --git a/SAS/TMSS/src/tmss/tmssapp/migrations/0003_populate.py b/SAS/TMSS/src/tmss/tmssapp/migrations/0003_populate.py
index 8fcea39a130ebe08b53939653c6465d3343d71d5..ed5e1e345ff88c4492b486390f115588b1ae3cae 100644
--- a/SAS/TMSS/src/tmss/tmssapp/migrations/0003_populate.py
+++ b/SAS/TMSS/src/tmss/tmssapp/migrations/0003_populate.py
@@ -6,7 +6,7 @@ from ..populate import *
 class Migration(migrations.Migration):
 
     dependencies = [
-        ('tmssapp', '0002_auto_20200123_1057'),
+        ('tmssapp', '0002_auto_20200204_1117'),
     ]
 
     operations = [ migrations.RunPython(populate_choices) ]
diff --git a/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py b/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py
index 4c31debc36ccbf9840521a52d310624f58fa7025..c9ab8b5e1b1239eef057365146524c38384c48e7 100644
--- a/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py
+++ b/SAS/TMSS/src/tmss/tmssapp/viewsets/specification.py
@@ -7,6 +7,8 @@ from rest_framework import viewsets
 from .lofar_viewset import LOFARViewSet
 from .. import models, serializers
 from rest_framework.renderers import BrowsableAPIRenderer, TemplateHTMLRenderer
+from rest_framework.decorators import permission_classes
+from rest_framework.permissions import IsAuthenticatedOrReadOnly, DjangoModelPermissions
 
 class TagsViewSet(LOFARViewSet):
     queryset = models.Tags.objects.all()
@@ -78,6 +80,7 @@ class TaskConnectorsViewSet(LOFARViewSet):
     serializer_class = serializers.TaskConnectorsSerializer
 
 
+@permission_classes((DjangoModelPermissions,))   # example override of default permissions per viewset | todo: review for production
 class CycleViewSet(LOFARViewSet):
     queryset = models.Cycle.objects.all()
     serializer_class = serializers.CycleSerializer
diff --git a/SAS/TMSS/test/CMakeLists.txt b/SAS/TMSS/test/CMakeLists.txt
index 3d41eb2be7b75a35e11f57989726d9cca7b3d55b..57e393a0516fe7b634b95c9d8d860b350170ea9d 100644
--- a/SAS/TMSS/test/CMakeLists.txt
+++ b/SAS/TMSS/test/CMakeLists.txt
@@ -26,9 +26,11 @@ endif()
 
 lofar_add_test(t_tmssapp_specification_django)
 lofar_add_test(t_tmssapp_specification_functional)
+lofar_add_test(t_tmssapp_specification_permissions)
 lofar_add_test(t_tmssapp_scheduling_django)
 lofar_add_test(t_tmssapp_scheduling_functional)
 
+
 # copy helper script to bin dir
 configure_file(test_funcs.sh ${CMAKE_CURRENT_BINARY_DIR}/test_funcs.sh COPYONLY)
 
diff --git a/SAS/TMSS/test/t_tmssapp_specification_permissions.py b/SAS/TMSS/test/t_tmssapp_specification_permissions.py
new file mode 100644
index 0000000000000000000000000000000000000000..2954875dd7d8a4bc6d0ae5a62901dab2bc44e42e
--- /dev/null
+++ b/SAS/TMSS/test/t_tmssapp_specification_permissions.py
@@ -0,0 +1,119 @@
+#!/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 unittest
+import rest_framework.test
+from tmss.tmssapp.populate import populate_choices
+from tmss.tmssapp import models
+from django.db.utils import IntegrityError
+from django.contrib.auth.models import User, Group, Permission, ContentType
+from test_utils import assertDataWithUrls, assertUrlList
+import uuid
+import json
+
+from datetime import datetime
+# use this to create timezone-aware datetime objects: from django.utils import timezone
+
+client = rest_framework.test.APIClient()
+
+BASE_URI = '/api'
+
+class CycleTest(rest_framework.test.APITransactionTestCase):
+    reset_sequences = True
+
+    def setUp(self):
+        self.user, _ = User.objects.get_or_create(username='paulus', email='paulus@boskabouter.com')
+
+        self.support_group, _ = Group.objects.get_or_create(name='support')
+        self.support_group.permissions.add(Permission.objects.get(codename='add_cycle'))
+        # for user-specific permission use e.g.:
+        # self.user.user_permissions.add(Permission.objects.get(codename='add_cycle'))
+
+        self.admin_group, _ = Group.objects.get_or_create(name='admin')
+        self.admin_group.permissions.add(Permission.objects.get(codename='delete_cycle'))
+
+        client.force_login(self.user)
+
+        # test data # todo: Jorrit created some factory methods for this, I think, so we should use that after a merge
+        self.test_data_1 = {"name": 'my_cycle' + str(uuid.uuid4()),
+                            "description": "",
+                            "tags": [],
+                            "start": datetime.utcnow().isoformat(),
+                            "stop": datetime.utcnow().isoformat(),
+                            "number": 1,
+                            "standard_hours": 2,
+                            "expert_hours": 3,
+                            "filler_hours": 4}
+
+        # test data
+        self.test_data_2 = {"name": 'my_cycle' + str(uuid.uuid4()),
+                            "description": "This is my other cycle",
+                            "tags": ['othercycle'],
+                            "start": datetime.utcnow().isoformat(),
+                            "stop": datetime.utcnow().isoformat(),
+                            "number": 4,
+                            "standard_hours": 3,
+                            "expert_hours": 2,
+                            "filler_hours": 1}
+
+    def tearDown(self):
+        client.logout()
+
+    def test_Cycle_cannot_be_added_without_group(self):
+
+        self.user.groups.set([])
+        self.assertFalse(self.user.has_perm('tmssapp.add_cycle'))
+        res = client.post('%s/cycle/' % BASE_URI, data=self.test_data_1)
+        self.assertEqual(res.status_code, 403)
+
+    def test_Cycle_can_be_added_by_support(self):
+
+        self.user.groups.set([self.support_group])
+        self.assertTrue(self.user.has_perm('tmssapp.add_cycle'))
+        res = client.post('%s/cycle/' % BASE_URI, data=self.test_data_2)
+        self.assertEqual(res.status_code, 201)
+
+        #self.assertEqual(0, len(models.Cycle.objects.all()))
+        #models.Cycle.objects.create(**self.test_data_1)
+        #self.assertEqual(7, len(models.Cycle.objects.all()))
+
+        #self.user.groups.set([self.support_group])
+        #self.user.save()
+
+
+        #self.assertTrue(self.user.has_perm('tmssapp.change_cycle'))
+        #models.Cycle.objects.create(**self.test_data_1)
+
+    def test_Cycle_can_only_be_deleted_by_admin(self):
+
+        self.user.groups.set([self.admin_group])
+
+        # add
+        count = len(models.Cycle.objects.all())
+        models.Cycle.objects.create(**self.test_data_2)
+        url = '%s/cycle/%s/' % (BASE_URI, self.test_data_2['name'])
+        self.assertEqual(count+1, len(models.Cycle.objects.all()))
+
+        # delete
+        res = client.delete(url)
+        self.assertEqual(res.status_code, 204)
+        new_count = len(models.Cycle.objects.all())
+        self.assertEqual(count, len(models.Cycle.objects.all()))
\ No newline at end of file
diff --git a/SAS/TMSS/test/t_tmssapp_specification_permissions.run b/SAS/TMSS/test/t_tmssapp_specification_permissions.run
new file mode 100755
index 0000000000000000000000000000000000000000..0b21a314c4ec0af7fae98d352687cdf7918f2a0b
--- /dev/null
+++ b/SAS/TMSS/test/t_tmssapp_specification_permissions.run
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+. test_funcs.sh
+
+setup
+run_test "$LOFARROOT/lib*/python*/site-packages/lofar/sas/tmss/manage.py test --pattern=t_tmssapp_specification_permissions.py --testrunner=postgres_testrunner.PostgresqlTestRunner"
+
+
+
+
diff --git a/SAS/TMSS/test/t_tmssapp_specification_permissions.sh b/SAS/TMSS/test/t_tmssapp_specification_permissions.sh
new file mode 100755
index 0000000000000000000000000000000000000000..8689f8e0e9a5ccc08371584254cc450704cd9d9d
--- /dev/null
+++ b/SAS/TMSS/test/t_tmssapp_specification_permissions.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+./runctest.sh t_tmssapp_specification_permissions
\ No newline at end of file