+
{% trans %}Documentation is disabled!{% endtrans %}
-
-
{% trans %}Authors{% endtrans %}
-
- Simon Brière, ing., M.Sc.A. (Simon.Briere@USherbrooke.ca)
- Dominic Létourneau, ing., M.Sc.A (Dominic.Letourneau@USherbrooke.ca)
- François Michaud, ing., Ph.D (Francois.Michaud@USherbrooke.ca)
- Michel Tousignant, Pht, Ph.D (Michel.Tousignant@USherbrooke.ca)
-
-
-
-
-
{% trans %}Contributors{% endtrans %}
-
- Philippe Arsenault
- Cédric Godin
- Marc-Antoine Maheux
- Cynthia Vilanova
-
-
diff --git a/teraserver/python/templates/login.html b/teraserver/python/templates/login.html
new file mode 100644
index 000000000..95aef0984
--- /dev/null
+++ b/teraserver/python/templates/login.html
@@ -0,0 +1,148 @@
+
+
+
+
+
{{ gettext('OpenTera Login Page') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% if show_logo %}
+
+
+
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/teraserver/python/templates/login_change_password.html b/teraserver/python/templates/login_change_password.html
new file mode 100644
index 000000000..83d2cf208
--- /dev/null
+++ b/teraserver/python/templates/login_change_password.html
@@ -0,0 +1,120 @@
+
+
+
+
+
{{ gettext("OpenTera - Change Password") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ gettext('Password successfully changed!') }}
+ {{ gettext('Redirecting to login screen...') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/teraserver/python/templates/login_setup_2fa.html b/teraserver/python/templates/login_setup_2fa.html
new file mode 100644
index 000000000..f498ef982
--- /dev/null
+++ b/teraserver/python/templates/login_setup_2fa.html
@@ -0,0 +1,157 @@
+
+
+
+
+
OpenTera Setup 2FA
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ gettext('You need to setup multi-factor authentication before continuing.') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/teraserver/python/templates/login_validate_2fa.html b/teraserver/python/templates/login_validate_2fa.html
new file mode 100644
index 000000000..5eab13465
--- /dev/null
+++ b/teraserver/python/templates/login_validate_2fa.html
@@ -0,0 +1,174 @@
+
+
+
+
+
OpenTera Login Page
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ gettext('Successfully authenticated') }}
+
+
+
+
+
+
+
+
+
+
diff --git a/teraserver/python/tests/__init__.py b/teraserver/python/tests/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/teraserver/python/tests/modules/DatabaseModule/__init__.py b/teraserver/python/tests/modules/DatabaseModule/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/teraserver/python/tests/modules/DatabaseModule/test_DBManagerTeraDeviceAccess.py b/teraserver/python/tests/modules/DatabaseModule/test_DBManagerTeraDeviceAccess.py
new file mode 100644
index 000000000..2c02b0ac1
--- /dev/null
+++ b/teraserver/python/tests/modules/DatabaseModule/test_DBManagerTeraDeviceAccess.py
@@ -0,0 +1,90 @@
+from modules.DatabaseModule.DBManager import DBManager
+from modules.DatabaseModule.DBManagerTeraDeviceAccess import DBManagerTeraDeviceAccess
+from opentera.db.models.TeraUser import TeraUser
+from opentera.db.models.TeraParticipant import TeraParticipant
+from opentera.db.models.TeraParticipantGroup import TeraParticipantGroup
+from opentera.db.models.TeraService import TeraService
+from opentera.db.models.TeraDevice import TeraDevice
+from opentera.db.models.TeraProject import TeraProject
+from opentera.db.models.TeraSite import TeraSite
+from opentera.db.models.TeraSession import TeraSession
+from tests.opentera.db.models.BaseModelsTest import BaseModelsTest
+
+
+class DBManagerTeraDeviceAccessTest(BaseModelsTest):
+ """
+ Tests for the DBManagerTeraDeviceAccess class.
+ """
+
+ def test_device_query_sesion(self):
+ """
+ Query session for a device with id.
+ """
+ with self._flask_app.app_context():
+ devices = TeraDevice.query.all()
+ for device in devices:
+ device_access : DBManagerTeraDeviceAccess = DBManager.deviceAccess(device)
+ # Query invalid session
+ session = device_access.query_session(session_id=0)
+ self.assertIsNone(session)
+
+ def test_device_query_existing_session(self):
+ """
+ Query session for a device with existing session.
+ """
+ with self._flask_app.app_context():
+ devices = TeraDevice.query.all()
+ for device in devices:
+ device_access : DBManagerTeraDeviceAccess = DBManager.deviceAccess(device)
+ pass
+
+
+ def test_device_get_accessible_sessions_ids(self):
+ """
+ Get accessible sessions for a device.
+ """
+ with self._flask_app.app_context():
+ devices = TeraDevice.query.all()
+ for device in devices:
+ device_access : DBManagerTeraDeviceAccess = DBManager.deviceAccess(device)
+ pass
+
+ def test_device_get_accessible_session_types_ids(self):
+ """
+ Get accessible session types for a device.
+ """
+ with self._flask_app.app_context():
+ devices = TeraDevice.query.all()
+ for device in devices:
+ device_access : DBManagerTeraDeviceAccess = DBManager.deviceAccess(device)
+ pass
+
+ def test_device_get_accessible_assets(self):
+ """
+ Get accessible assets for a device.
+ """
+ with self._flask_app.app_context():
+ devices = TeraDevice.query.all()
+ for device in devices:
+ device_access : DBManagerTeraDeviceAccess = DBManager.deviceAccess(device)
+ pass
+
+ def test_device_get_accessible_services(self):
+ """
+ Get accessible services for a device.
+ """
+ with self._flask_app.app_context():
+ devices = TeraDevice.query.all()
+ for device in devices:
+ device_access : DBManagerTeraDeviceAccess = DBManager.deviceAccess(device)
+ pass
+
+ def test_device_get_accessible_tests_types_ids(self):
+ """
+ Get accessible tests types for a device.
+ """
+ with self._flask_app.app_context():
+ devices = TeraDevice.query.all()
+ for device in devices:
+ device_access : DBManagerTeraDeviceAccess = DBManager.deviceAccess(device)
+ pass
diff --git a/teraserver/python/tests/modules/DatabaseModule/test_DBManagerTeraParticipantAccess.py b/teraserver/python/tests/modules/DatabaseModule/test_DBManagerTeraParticipantAccess.py
new file mode 100644
index 000000000..053aae505
--- /dev/null
+++ b/teraserver/python/tests/modules/DatabaseModule/test_DBManagerTeraParticipantAccess.py
@@ -0,0 +1,155 @@
+from modules.DatabaseModule.DBManager import DBManager
+from modules.DatabaseModule.DBManagerTeraParticipantAccess import DBManagerTeraParticipantAccess
+from opentera.db.models.TeraParticipant import TeraParticipant
+from opentera.db.models.TeraDeviceParticipant import TeraDeviceParticipant
+from opentera.db.models.TeraParticipantGroup import TeraParticipantGroup
+from opentera.db.models.TeraAsset import TeraAsset
+from opentera.db.models.TeraSession import TeraSession
+from opentera.db.models.TeraSessionType import TeraSessionType
+from opentera.db.models.TeraSessionTypeProject import TeraSessionTypeProject
+from opentera.db.models.TeraTestType import TeraTestType
+from opentera.db.models.TeraTestTypeProject import TeraTestTypeProject
+from opentera.db.models.TeraService import TeraService
+from opentera.db.models.TeraServiceProject import TeraServiceProject
+# from opentera.db.models.TeraSessionParticipants import TeraSessionParticipants
+from tests.opentera.db.models.BaseModelsTest import BaseModelsTest
+
+
+class DBManagerTeraParticipantAccessTest(BaseModelsTest):
+
+ def test_participant_query_device(self):
+ """
+ Query devices for a participant
+ """
+ with self._flask_app.app_context():
+
+ participants = TeraParticipant.query.all()
+ for participant in participants:
+ participant_access : DBManagerTeraParticipantAccess = DBManager.participantAccess(participant)
+ devices = participant_access.query_device({})
+
+ # Make a query to get all devices for this participant
+ queried_devices = TeraDeviceParticipant.query.filter_by(id_participant=participant.id_participant).all()
+ self.assertEqual(len(devices), len(queried_devices))
+ # self.assertEqual(devices, queried_devices)
+
+
+ # Make a query with a filter with invalid device id
+ queried_devices = participant_access.query_device({'id_device': 0})
+ self.assertEqual(len(queried_devices), 0)
+
+ def test_participant_get_accessible_assets(self):
+ """
+ Get accessible assets for a participant
+ """
+ with self._flask_app.app_context():
+
+ participants = TeraParticipant.query.all()
+ for participant in participants:
+ participant_access : DBManagerTeraParticipantAccess = DBManager.participantAccess(participant)
+ assets = participant_access.get_accessible_assets()
+
+ # Verify valid filters
+ for asset in assets:
+ test_query = participant_access.get_accessible_assets(id_asset=asset.id_asset)
+ self.assertEqual(len(test_query), 1)
+
+ test_query = participant_access.get_accessible_assets(uuid_asset=asset.asset_uuid)
+ self.assertEqual(len(test_query), 1)
+
+ test_query = participant_access.get_accessible_assets(session_id=asset.id_session)
+ self.assertEqual(len(test_query), 1)
+
+ # Verify that we get the same result from db queries
+ # This query returns all assets for sessions where the participant is in
+ # get_accessible_assets() is more restrictive and only returns assets where the participant is assigned
+ # all_assets = TeraAsset.query.join(TeraSessionParticipants, TeraSessionParticipants.id_session == TeraAsset.id_session).filter(
+ # TeraSessionParticipants.id_participant == participant.id_participant).distinct().all()
+
+ queried_assets = TeraAsset.query.filter(TeraAsset.id_participant == participant.id_participant).all()
+ self.assertEqual(len(assets), len(queried_assets))
+ self.assertEqual(assets, queried_assets)
+
+ # Make a query with a filter with invalid asset id
+ queried_assets = participant_access.get_accessible_assets(id_asset=1000)
+ self.assertEqual(len(queried_assets), 0)
+
+ # Make a query with a filter with invalid asset uuid
+ queried_assets = participant_access.get_accessible_assets(uuid_asset='invalid_uuid')
+ self.assertEqual(len(queried_assets), 0)
+
+ # Make a query with a filter with invalid session id
+ queried_assets = participant_access.get_accessible_assets(session_id=1000)
+ self.assertEqual(len(queried_assets), 0)
+
+ def test_participant_get_accessible_services(self):
+ """
+ Get accessible services for a participant
+ """
+ with self._flask_app.app_context():
+
+ participants = TeraParticipant.query.all()
+ for participant in participants:
+ participant_access : DBManagerTeraParticipantAccess = DBManager.participantAccess(participant)
+ services = participant_access.get_accessible_services()
+
+ queried_services = TeraService.query.join(TeraServiceProject, TeraServiceProject.id_service == TeraService.id_service).\
+ filter(TeraServiceProject.id_project == participant.id_project).all()
+
+ self.assertEqual(len(services), len(queried_services))
+ self.assertEqual(services, queried_services)
+
+ def test_participant_get_accessible_session_types_ids(self):
+ """
+ Get accessible session types for a participant, with ids, will also test get_accessible_session_types()
+ """
+ with self._flask_app.app_context():
+
+ participants = TeraParticipant.query.all()
+ for participant in participants:
+ participant_access : DBManagerTeraParticipantAccess = DBManager.participantAccess(participant)
+ session_types_ids = participant_access.get_accessible_session_types_ids()
+
+ queried_session_types = TeraSessionType.query.join(
+ TeraSessionTypeProject, TeraSessionTypeProject.id_session_type == TeraSessionType.id_session_type).filter(
+ TeraSessionTypeProject.id_project == participant.id_project).all()
+ queried_session_types_ids = [session_type.id_session_type for session_type in queried_session_types]
+ self.assertEqual(len(session_types_ids), len(queried_session_types_ids))
+ self.assertEqual(session_types_ids, queried_session_types_ids)
+
+
+ def test_participant_query_existing_sessions_ids(self):
+ """
+ Query existing session for a participant. Will also test get_accessible_sessions
+ """
+ with self._flask_app.app_context():
+
+ participants = TeraParticipant.query.all()
+ for participant in participants:
+ participant_access : DBManagerTeraParticipantAccess = DBManager.participantAccess(participant)
+
+ sessions_ids = participant_access.get_accessible_sessions_ids()
+
+ queried_sessions = TeraSession.query.filter_by(id_creator_participant=participant.id_participant).all()
+ queried_sessions_ids = [session.id_session for session in queried_sessions]
+ self.assertEqual(len(sessions_ids), len(queried_sessions_ids))
+ self.assertEqual(sessions_ids, queried_sessions_ids)
+
+
+ def test_participant_get_accessible_tests_types_ids(self):
+ """
+ Get accessible test types for a participant, with ids, will also test get_accessible_tests_types()
+ """
+ with self._flask_app.app_context():
+
+ participants = TeraParticipant.query.all()
+ for participant in participants:
+ participant_access : DBManagerTeraParticipantAccess = DBManager.participantAccess(participant)
+ test_types_ids = participant_access.get_accessible_tests_types_ids()
+
+ queried_test_types = TeraTestType.query.join(
+ TeraTestTypeProject, TeraTestTypeProject.id_test_type == TeraTestType.id_test_type).filter(
+ TeraTestTypeProject.id_project == participant.id_project).all()
+ queried_test_types_ids = [test_type.id_test_type for test_type in queried_test_types]
+ self.assertEqual(len(test_types_ids), len(queried_test_types_ids))
+ self.assertEqual(test_types_ids, queried_test_types_ids)
diff --git a/teraserver/python/tests/modules/DatabaseModule/test_DBManagerTeraServiceAccess.py b/teraserver/python/tests/modules/DatabaseModule/test_DBManagerTeraServiceAccess.py
new file mode 100644
index 000000000..5d2684a2d
--- /dev/null
+++ b/teraserver/python/tests/modules/DatabaseModule/test_DBManagerTeraServiceAccess.py
@@ -0,0 +1,736 @@
+from datetime import datetime, timedelta
+
+from modules.DatabaseModule.DBManager import DBManager
+from modules.DatabaseModule.DBManagerTeraServiceAccess import DBManagerTeraServiceAccess
+from opentera.db.models.TeraUser import TeraUser
+from opentera.db.models.TeraUserGroup import TeraUserGroup
+from opentera.db.models.TeraUserUserGroup import TeraUserUserGroup
+from opentera.db.models.TeraParticipant import TeraParticipant
+from opentera.db.models.TeraParticipantGroup import TeraParticipantGroup
+from opentera.db.models.TeraService import TeraService
+from opentera.db.models.TeraDevice import TeraDevice
+from opentera.db.models.TeraProject import TeraProject
+from opentera.db.models.TeraSite import TeraSite
+from opentera.db.models.TeraSession import TeraSession, TeraSessionStatus
+from opentera.db.models.TeraSessionType import TeraSessionType
+from opentera.db.models.TeraTestType import TeraTestType
+from opentera.db.models.TeraTestInvitation import TeraTestInvitation
+from opentera.db.models.TeraServiceAccess import TeraServiceAccess
+from opentera.db.models.TeraServiceRole import TeraServiceRole
+from opentera.db.models.TeraAsset import TeraAsset
+from tests.opentera.db.models.BaseModelsTest import BaseModelsTest
+
+
+class DBManagerTeraServiceAccessTest(BaseModelsTest):
+
+ def _create_service_with_no_access(self) -> TeraService:
+
+ # Since service keys are unique, if service already exists, return it
+ service = TeraService.get_service_by_key('NoAccess')
+ if service:
+ return service
+
+ service = TeraService()
+ # service.id_service = 0 # This is generated by the DB
+ # service.service_uuid = '00000000-0000-0000-0000-000000000000' # This is generated by the DB
+ service.service_name = 'No Access'
+ service.service_key = 'NoAccess'
+ service.service_hostname = 'localhost'
+ service.service_port = 8080
+ service.service_endpoint = '/noaccess'
+ service.service_clientendpoint = '/noaccess'
+ service.service_endpoint_user = '/noaccess'
+ service.service_endpoint_participant = '/noaccess'
+ service.service_endpoint_device = '/noaccess'
+ service.service_enabled = True
+ service.service_system = False
+ service.service_editable_config = False
+ service.service_default_config = '{}'
+ service.service_has_assets = False
+ service.service_has_tests = False
+ TeraService.insert(service)
+ return service
+
+ def test_service_get_accessible_devices_ids_and_uuids(self):
+ """
+ This will test at the same time get_accessible_devices and get_accessible_devices_ids/uuids.
+ """
+ with self._flask_app.app_context():
+ service = TeraService.get_service_by_key('VideoRehabService')
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ devices_ids : set[int] = set(service_access.get_accessible_devices_ids())
+ devices_uuids : set[str] = set(service_access.get_accessible_devices_uuids())
+
+ all_devices = TeraDevice.query.all()
+ accessible_devices = set()
+ for device in all_devices:
+ for project in device.device_projects:
+ if project.id_project in service_access.get_accessible_projects_ids():
+ accessible_devices.add(device.id_device)
+
+ self.assertEqual(len(devices_ids), len(accessible_devices))
+ self.assertEqual(len(devices_uuids), len(devices_ids))
+ self.assertEqual(devices_ids, accessible_devices)
+
+ service = self._create_service_with_no_access()
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ self.assertEqual(len(service_access.get_accessible_devices_ids()), 0)
+
+ def test_service_get_accessible_projects_ids(self):
+ """
+ This will test at the same time get_accessible_projects and get_accessible_projects_ids.
+ """
+ with self._flask_app.app_context():
+ service = TeraService.get_service_by_key('VideoRehabService')
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ projects_ids = set(service_access.get_accessible_projects_ids())
+
+ all_projects: list[TeraProject] = TeraProject.query.all()
+ accessible_projects = set()
+ for project in all_projects:
+ for project_service in project.project_services:
+ if project_service.id_service == service.id_service:
+ accessible_projects.add(project.id_project)
+ self.assertEqual(len(projects_ids), len(accessible_projects))
+ self.assertEqual(projects_ids, accessible_projects)
+
+ service = self._create_service_with_no_access()
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ self.assertEqual(len(service_access.get_accessible_projects_ids()), 0)
+
+ def test_service_get_accessible_sessions_ids(self):
+ """
+ This will test at the same time get_accessible_sessions and get_accessible_sessions_ids.
+ """
+ with self._flask_app.app_context():
+ service = TeraService.get_service_by_key('VideoRehabService')
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ sessions_ids = set(service_access.get_accessible_sessions_ids())
+
+ all_sessions = TeraSession.query.all()
+ accessible_sessions = set()
+
+ for session in all_sessions:
+ # Creator
+ if session.id_creator_service == service.id_service:
+ accessible_sessions.add(session.id_session)
+
+ session_type : TeraSessionType = TeraSessionType.query.get(session.id_session_type)
+
+ # Same service ?
+ if session_type.id_service == service.id_service:
+ accessible_sessions.add(session.id_session)
+ continue
+
+ # Secondary services
+ for secondary_service in session_type.session_type_secondary_services:
+ if secondary_service.id_service == service.id_service:
+ accessible_sessions.add(session.id_session)
+ break
+
+ self.assertEqual(len(sessions_ids), len(accessible_sessions))
+ self.assertEqual(sessions_ids, accessible_sessions)
+
+ service = self._create_service_with_no_access()
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ self.assertEqual(len(service_access.get_accessible_sessions_ids()), 0)
+
+ def test_service_get_accessible_sites_id(self):
+ """
+ This will test at the same time get_accessible_sites and get_accessible_sites_ids.
+ """
+ with self._flask_app.app_context():
+ service = TeraService.get_service_by_key('VideoRehabService')
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ sites_ids = set(service_access.get_accessible_sites_ids())
+
+ all_sites = TeraSite.query.all()
+ accessible_sites = set()
+ for site in all_sites:
+ for project in site.site_projects:
+ if project.id_project in service_access.get_accessible_projects_ids():
+ accessible_sites.add(site.id_site)
+ self.assertEqual(len(sites_ids), len(accessible_sites))
+ self.assertEqual(sites_ids, accessible_sites)
+
+ service = self._create_service_with_no_access()
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ self.assertEqual(len(service_access.get_accessible_sites_ids()), 0)
+
+ def test_service_get_accessible_participants_id(self):
+ """
+ This will test at the same time get_accessible_participants and get_accessible_participants_ids.
+ """
+ with self._flask_app.app_context():
+ service = TeraService.get_service_by_key('VideoRehabService')
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ participants_ids = set(service_access.get_accessible_participants_ids())
+
+ all_participants = TeraParticipant.query.all()
+ accessible_participants = set()
+ for participant in all_participants:
+ if participant.id_project in service_access.get_accessible_projects_ids():
+ accessible_participants.add(participant.id_participant)
+ self.assertEqual(len(participants_ids), len(accessible_participants))
+ self.assertEqual(participants_ids, accessible_participants)
+
+ service = self._create_service_with_no_access()
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ self.assertEqual(len(service_access.get_accessible_participants_ids()), 0)
+
+ def test_service_get_accessible_participant_groups_id(self):
+ """
+ This will test at the same time get_accessible_participant_groups and get_accessible_participant_groups_ids.
+ """
+ with self._flask_app.app_context():
+ service = TeraService.get_service_by_key('VideoRehabService')
+ self.assertIsNotNone(service)
+ service_access: DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ participant_groups_ids = set(service_access.get_accessible_participants_groups_ids())
+
+ all_participant_groups = TeraParticipantGroup.query.all()
+ accessible_participant_groups = set()
+ for participant_group in all_participant_groups:
+ if participant_group.id_project in service_access.get_accessible_projects_ids():
+ accessible_participant_groups.add(participant_group.id_participant_group)
+ self.assertEqual(len(participant_groups_ids), len(accessible_participant_groups))
+ self.assertEqual(participant_groups_ids, accessible_participant_groups)
+
+ service = self._create_service_with_no_access()
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ self.assertEqual(len(service_access.get_accessible_participants_groups_ids()), 0)
+
+ def test_service_get_accessible_users_ids(self):
+ """
+ This will test at the same time get_accessible_users and get_accessible_users_ids.
+ """
+ with self._flask_app.app_context():
+ service = TeraService.get_service_by_key('VideoRehabService')
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ users_ids = set(service_access.get_accessible_users_ids())
+
+ all_users = TeraUser.query.all()
+ accessible_users = set()
+
+ service_projects_ids = [project.id_project for project in service.service_projects]
+ service_site_ids = [site.id_site for site in service.service_sites]
+
+ for user in all_users:
+ if user.user_superadmin:
+ accessible_users.add(user.id_user)
+ continue
+
+ teraserver_service = TeraService.get_openteraserver_service()
+
+ for user_group in user.user_user_groups:
+ for service_role in user_group.user_group_services_roles:
+ if service_role.id_service == teraserver_service.id_service:
+ # Add users that have a role in a project accessible by the service
+ if service_role.id_project in service_projects_ids :
+ accessible_users.add(user.id_user)
+ # Add users that have a role in a site accessible by the service
+ if service_role.id_site in service_site_ids :
+ accessible_users.add(user.id_user)
+
+ self.assertEqual(len(users_ids), len(accessible_users))
+ self.assertEqual(users_ids, accessible_users)
+
+ # Test with a service with no access
+ service = self._create_service_with_no_access()
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ self.assertEqual(len(service_access.get_accessible_users_ids()), 0)
+
+ def test_service_get_accessible_usergroup_ids(self):
+ """
+ This will test at the same time get_accessible_usergroups and get_accessible_usergroups_ids.
+ """
+ with self._flask_app.app_context():
+ service = TeraService.get_service_by_key('VideoRehabService')
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ usergroups_ids = set(service_access.get_accessible_usergroups_ids())
+
+ all_usergroups = TeraUserGroup.query.all()
+ accessible_usergroups = set()
+
+ service_projects_ids = [project.id_project for project in service.service_projects]
+ service_site_ids = [site.id_site for site in service.service_sites]
+
+ teraserver_service = TeraService.get_openteraserver_service()
+
+ for usergroup in all_usergroups:
+ for service_role in usergroup.user_group_services_roles:
+ # TODO More permissive ?
+ #if service_role.id_service == teraserver_service.id_service or \
+ # service_role.id_service == service.id_service:
+ if service_role.id_service == service.id_service:
+ # Add usergroups that have a role in a project accessible by the service
+ if service_role.id_project in service_projects_ids:
+ accessible_usergroups.add(usergroup.id_user_group)
+ # Add usergroups that have a role in a site accessible by the service
+ if service_role.id_site in service_site_ids:
+ accessible_usergroups.add(usergroup.id_user_group)
+
+ self.assertEqual(len(usergroups_ids), len(accessible_usergroups))
+ self.assertEqual(usergroups_ids, accessible_usergroups)
+
+ # Test with a service with no access
+ service = self._create_service_with_no_access()
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ self.assertEqual(len(service_access.get_accessible_usergroups_ids()), 0)
+
+ def test_service_get_accessible_sessions_types_ids(self):
+ """
+ This will test at the same time get_accessible_session_types and get_accessible_session_types_ids.
+ """
+ with self._flask_app.app_context():
+ service = TeraService.get_service_by_key('VideoRehabService')
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ session_types_ids = set(service_access.get_accessible_sessions_types_ids())
+
+ all_session_types = TeraSessionType.query.all()
+ accessible_session_types = set()
+ for session_type in all_session_types:
+ if session_type.id_service == service.id_service:
+ accessible_session_types.add(session_type.id_session_type)
+ self.assertEqual(len(session_types_ids), len(accessible_session_types))
+ self.assertEqual(session_types_ids, accessible_session_types)
+
+ # Test with a service with no access
+ service = self._create_service_with_no_access()
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ self.assertEqual(len(service_access.get_accessible_sessions_types_ids()), 0)
+
+ def test_service_get_accessible_tests_types_ids(self):
+ """
+ This will test at the same time get_accessible_tests_types and get_accessible_tests_types_ids.
+ """
+ with self._flask_app.app_context():
+ service = TeraService.get_service_by_key('VideoRehabService')
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ test_types_ids = set(service_access.get_accessible_tests_types_ids())
+
+ all_test_types = TeraTestType.query.all()
+ accessible_test_types = set()
+ for test_type in all_test_types:
+ if test_type.id_service == service.id_service:
+ accessible_test_types.add(test_type.id_test_type)
+ self.assertEqual(len(test_types_ids), len(accessible_test_types))
+ self.assertEqual(test_types_ids, accessible_test_types)
+
+ # Test with a service with no access
+ service = self._create_service_with_no_access()
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ self.assertEqual(len(service_access.get_accessible_tests_types_ids()), 0)
+
+
+ def test_service_get_accessible_tests_types_ids_for_device(self):
+ """
+ This will test at the same time get_accessible_tests_types_for_device and get_accessible_tests_types_ids_for_device.
+ """
+ with self._flask_app.app_context():
+
+ all_devices = TeraDevice.query.all()
+ for device in all_devices:
+ service = TeraService.get_service_by_key('VideoRehabService')
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+
+ test_types_service_ids = service_access.get_accessible_tests_types_ids()
+ test_types_ids = set(service_access.get_accessible_tests_types_ids_for_device(device.id_device))
+
+ for id_test_type in test_types_ids:
+ self.assertTrue(id_test_type in test_types_service_ids)
+
+ # Test with a service with no access
+ service = self._create_service_with_no_access()
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ self.assertEqual(len(service_access.get_accessible_tests_types_ids_for_device(device.id_device)), 0)
+
+ def test_service_get_accessible_tests_types_ids_for_participant(self):
+ """
+ This will test at the same time get_accessible_tests_types_for_participant and get_accessible_tests_types_ids_for_participant.
+ """
+ with self._flask_app.app_context():
+
+ all_participants = TeraParticipant.query.all()
+ for participant in all_participants:
+ service = TeraService.get_service_by_key('VideoRehabService')
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+
+ test_types_service_ids = service_access.get_accessible_tests_types_ids()
+ test_types_ids = set(service_access.get_accessible_tests_types_ids_for_participant(participant.id_participant))
+
+ for id_test_type in test_types_ids:
+ self.assertTrue(id_test_type in test_types_service_ids)
+
+ # Test with a service with no access
+ service = self._create_service_with_no_access()
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ self.assertEqual(len(service_access.get_accessible_tests_types_ids_for_participant(participant.id_participant)), 0)
+
+ def test_service_get_accesible_tests_types_ids_for_user(self):
+ """
+ This will test at the same time get_accessible_tests_types_for_user and get_accessible_tests_types_ids_for_user.
+ """
+ with self._flask_app.app_context():
+
+ all_users = TeraUser.query.all()
+ for user in all_users:
+ service = TeraService.get_service_by_key('VideoRehabService')
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+
+ test_types_service_ids = service_access.get_accessible_tests_types_ids()
+ test_types_ids = set(service_access.get_accessible_tests_types_ids_for_user(user.id_user))
+
+ for id_test_type in test_types_ids:
+ self.assertTrue(id_test_type in test_types_service_ids)
+
+ # Test with a service with no access
+ service = self._create_service_with_no_access()
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ self.assertEqual(len(service_access.get_accessible_tests_types_ids_for_user(user.id_user)), 0)
+
+ def test_service_get_site_role(self):
+ """
+ This will test get_site_role.
+ """
+ with self._flask_app.app_context():
+
+ all_users = TeraUser.query.all()
+ all_sites = TeraSite.query.all()
+
+ for user in all_users:
+
+ for site in all_sites:
+ service = TeraService.get_service_by_key('VideoRehabService')
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+
+ site_role = service_access.get_site_role(site_id = site.id_site, uuid_user = user.user_uuid)
+
+ teraserver_service = TeraService.get_openteraserver_service()
+
+ if site.id_site in service_access.get_accessible_sites_ids():
+ if user.user_superadmin:
+ self.assertEqual(site_role, 'admin')
+ continue
+
+ queried_role = TeraServiceAccess.query.join(TeraUserUserGroup, TeraServiceAccess.id_user_group == TeraUserUserGroup.id_user_group) \
+ .join(TeraServiceRole, TeraServiceAccess.id_service_role == TeraServiceRole.id_service_role) \
+ .join(TeraSite, TeraServiceRole.id_site == TeraSite.id_site) \
+ .filter(TeraUserUserGroup.id_user == user.id_user) \
+ .filter(TeraSite.id_site == site.id_site) \
+ .filter(TeraServiceRole.id_service == teraserver_service.id_service) \
+ .with_entities(TeraServiceRole).first()
+
+
+ # Query role for project if not found for site, if users have access to a project, they need to be automatically granted access to the site
+ if not queried_role:
+ queried_roles = TeraServiceAccess.query.join(TeraUserUserGroup, TeraServiceAccess.id_user_group == TeraUserUserGroup.id_user_group) \
+ .join(TeraServiceRole, TeraServiceAccess.id_service_role == TeraServiceRole.id_service_role) \
+ .join(TeraProject, TeraServiceRole.id_project == TeraProject.id_project) \
+ .filter(TeraUserUserGroup.id_user == user.id_user) \
+ .filter(TeraProject.id_site == site.id_site) \
+ .filter(TeraServiceRole.id_service == teraserver_service.id_service) \
+ .with_entities(TeraServiceRole).all()
+
+ if len(queried_roles) > 0:
+ # Inherited has always the user role
+ queried_role = TeraServiceRole()
+ queried_role.id_service = teraserver_service.id_service
+ queried_role.service_role_name='user'
+ else:
+ queried_role = None
+
+ queried_role_name = queried_role.service_role_name if queried_role else None
+
+ self.assertEqual(site_role, queried_role_name)
+
+ def test_service_get_project_role(self):
+ """
+ This will test get_project_role.
+ """
+ with self._flask_app.app_context():
+
+ all_users = TeraUser.query.all()
+ all_projects = TeraProject.query.all()
+
+ for user in all_users:
+ for project in all_projects:
+ service = TeraService.get_service_by_key('VideoRehabService')
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+
+ project_role = service_access.get_project_role(project_id = project.id_project, uuid_user = user.user_uuid)
+
+ teraserver_service = TeraService.get_openteraserver_service()
+ queried_role = None
+ if project.id_project in service_access.get_accessible_projects_ids():
+ if user.user_superadmin:
+ self.assertEqual(project_role, 'admin')
+ continue
+
+ queried_roles = TeraServiceAccess.query.join(TeraUserUserGroup, TeraServiceAccess.id_user_group == TeraUserUserGroup.id_user_group) \
+ .join(TeraServiceRole, TeraServiceAccess.id_service_role == TeraServiceRole.id_service_role) \
+ .join(TeraProject, TeraServiceRole.id_project == TeraProject.id_project) \
+ .filter(TeraUserUserGroup.id_user == user.id_user) \
+ .filter(TeraProject.id_project == project.id_project) \
+ .filter(TeraServiceRole.id_service == teraserver_service.id_service) \
+ .with_entities(TeraServiceRole).all()
+
+ # Keep admin as priority
+ for role in queried_roles:
+ if role.service_role_name == 'admin':
+ queried_role = role
+ break
+ elif not queried_role:
+ queried_role = role
+
+ # Query role for site might override project role
+ queried_roles = TeraServiceAccess.query.join(TeraUserUserGroup, TeraServiceAccess.id_user_group == TeraUserUserGroup.id_user_group) \
+ .join(TeraServiceRole, TeraServiceAccess.id_service_role == TeraServiceRole.id_service_role) \
+ .join(TeraSite, TeraServiceRole.id_site == TeraSite.id_site) \
+ .filter(TeraUserUserGroup.id_user == user.id_user) \
+ .filter(TeraSite.id_site == project.id_site) \
+ .filter(TeraServiceRole.id_service == teraserver_service.id_service) \
+ .with_entities(TeraServiceRole).all()
+ # Keep admin as priority
+ for role in queried_roles:
+ if role.service_role_name == 'admin' :
+ queried_role = role
+ break
+ elif not queried_role:
+ queried_role = role
+ else:
+ queried_role = None
+
+
+ queried_role_name = queried_role.service_role_name if queried_role else None
+
+ self.assertEqual(project_role, queried_role_name)
+
+ def test_service_get_user_with_uuid(self):
+ """
+ This will test get_user_with_uuid.
+ """
+ with self._flask_app.app_context():
+
+ all_users = TeraUser.query.all()
+ for user in all_users:
+ service = TeraService.get_service_by_key('VideoRehabService')
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ my_user = service_access.get_user_with_uuid(uuid_user = user.user_uuid)
+ # if (user.user_superadmin):
+ # self.assertIsNone(my_user)
+ # else:
+ if user.id_user in service_access.get_accessible_users_ids():
+ self.assertEqual(my_user.id_user, user.id_user)
+ else:
+ self.assertIsNone(my_user)
+
+ def test_service_query_sites_for_user(self):
+ """
+ This will test query_sites_ids_for_user.
+ """
+ with self._flask_app.app_context():
+
+ all_users = TeraUser.query.all()
+ for user in all_users:
+ service = TeraService.get_service_by_key('VideoRehabService')
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ sites = set(service_access.query_sites_for_user(user_id = user.id_user))
+
+ if user.user_superadmin:
+ self.assertEqual(len(sites), 1)
+ else:
+ teraserver_service = TeraService.get_openteraserver_service()
+ queried_sites = TeraServiceAccess.query.join(TeraUserUserGroup, TeraServiceAccess.id_user_group == TeraUserUserGroup.id_user_group) \
+ .join(TeraServiceRole, TeraServiceAccess.id_service_role == TeraServiceRole.id_service_role) \
+ .join(TeraSite, TeraServiceRole.id_site == TeraSite.id_site) \
+ .filter(TeraUserUserGroup.id_user == user.id_user) \
+ .filter(TeraServiceRole.id_service == teraserver_service.id_service) \
+ .with_entities(TeraSite).all()
+
+ queried_projects = TeraServiceAccess.query.join(TeraUserUserGroup, TeraServiceAccess.id_user_group == TeraUserUserGroup.id_user_group) \
+ .join(TeraServiceRole, TeraServiceAccess.id_service_role == TeraServiceRole.id_service_role) \
+ .join(TeraProject, TeraServiceRole.id_project == TeraProject.id_project) \
+ .filter(TeraUserUserGroup.id_user == user.id_user) \
+ .filter(TeraServiceRole.id_service == teraserver_service.id_service) \
+ .with_entities(TeraProject).all()
+
+ for project in queried_projects:
+ queried_sites.extend([project.project_site])
+
+
+ self.assertEqual(len(set(sites)), len(set(queried_sites)))
+
+ def test_service_query_asset(self):
+ """
+ This will test query_asset.
+ """
+ with self._flask_app.app_context():
+
+ all_assets = TeraAsset.query.all()
+ for asset in all_assets:
+ service = TeraService.get_service_by_key('VideoRehabService')
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ my_assets = service_access.query_asset(asset_id = asset.id_asset)
+
+ # TODO Can only access assets produce by the service ?
+ # TODO Or all available assets in the project ?
+ # TODO Association to project is weak and depends on participants in the session
+
+ def test_service_query_session(self):
+ """
+ This will test query_session.
+ """
+ with self._flask_app.app_context():
+
+ all_sessions = TeraSession.query.all()
+ for session in all_sessions:
+ service = TeraService.get_service_by_key('VideoRehabService')
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ my_sessions = service_access.query_session(session_id = session.id_session)
+
+ # TODO Can only access sessions produce by the service ?
+ # TODO Or all available sessions in the project ?
+ # TODO Association to project is weak and depends on participants in the session
+
+ def test_service_query_usergroups_for_site(self):
+ """
+ This will test query_usergroups_for_site.
+ """
+ with self._flask_app.app_context():
+
+ all_sites = TeraSite.query.all()
+ for site in all_sites:
+ service = TeraService.get_service_by_key('VideoRehabService')
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ usergroups = set(service_access.query_usergroups_for_site(site_id = site.id_site))
+
+ # TODO
+ # queried_usergroups = TeraServiceAccess.query.join(TeraUserUserGroup, TeraServiceAccess.id_user_group == TeraUserUserGroup.id_user_group) \
+ # .join(TeraServiceRole, TeraServiceAccess.id_service_role == TeraServiceRole.id_service_role) \
+ # .join(TeraSite, TeraServiceRole.id_site == TeraSite.id_site) \
+ # .filter(TeraSite.id_site == site.id_site) \
+ # .filter(TeraServiceRole.id_service == service.id_service) \
+ # .with_entities(TeraUserGroup).all()
+
+ # self.assertEqual(len(usergroups), len(queried_usergroups))
+
+ def test_service_query_usergroups_for_project(self):
+ """
+ This will test query_usergroups_for_project.
+ """
+ with self._flask_app.app_context():
+
+ all_projects = TeraProject.query.all()
+ for project in all_projects:
+ service = TeraService.get_service_by_key('VideoRehabService')
+ self.assertIsNotNone(service)
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ usergroups = set(service_access.query_usergroups_for_project(project_id = project.id_project))
+
+ # TODO
+ # queried_usergroups = TeraServiceAccess.query.join(TeraUserUserGroup, TeraServiceAccess.id_user_group == TeraUserUserGroup.id_user_group) \
+ # .join(TeraServiceRole, TeraServiceAccess.id_service_role == TeraServiceRole.id_service_role) \
+ # .join(TeraProject, TeraServiceRole.id_project == TeraProject.id_project) \
+ # .filter(TeraProject.id_project == project.id_project) \
+ # .filter(TeraServiceRole.id_service == service.id_service) \
+ # .with_entities(TeraUserGroup).all()
+
+ # self.assertEqual(len(usergroups), len(queried_usergroups))
+
+ def test_service_get_accessible_tests_invitations_ids(self):
+ """
+ This will test get_accessible_tests_invitations_ids.
+ """
+ with self._flask_app.app_context():
+ service : TeraService = TeraService.get_service_by_key('VideoRehabService')
+ supp_service : TeraService = TeraService.get_service_by_key('LoggingService')
+ self.assertIsNotNone(service)
+ self.assertIsNotNone(supp_service)
+
+ # Create a test type with this service
+ test_type : TeraTestType = TeraTestType()
+ test_type.test_type_name = 'TestType'
+ test_type.test_type_key = 'TestType'
+ test_type.test_type_description = 'TestType'
+ test_type.id_service = service.id_service
+ TeraTestType.insert(test_type)
+
+
+ # Create a session type with this service
+ session_type : TeraSessionType = TeraSessionType()
+ session_type.session_type_name = 'SessionType'
+ session_type.session_type_online = False
+ session_type.session_type_config = str()
+ session_type.session_type_color = '#000000'
+ session_type.session_type_category = TeraSessionType.SessionCategoryEnum.SERVICE.value
+
+ session_type.id_service = service.id_service
+ session_type.session_type_secondary_services = [supp_service]
+
+ TeraSessionType.insert(session_type)
+
+ # Create a session with this session type
+ session : TeraSession = TeraSession()
+ session.id_session_type = session_type.id_session_type
+ session.id_creator_service = service.id_service
+ session.session_name = 'Session'
+ session.session_status = TeraSessionStatus.STATUS_NOTSTARTED.value
+ session.session_start_datetime = datetime.now()
+ session.session_duration = 0
+ session.session_participants = []
+ session.session_users = []
+ session.session_devices = []
+
+ TeraSession.insert(session)
+
+ # Create an invitation for this test type
+ invitation : TeraTestInvitation = TeraTestInvitation()
+ invitation.id_test_type = test_type.id_test_type
+ invitation.id_session = session.id_session
+ invitation.id_user = 1
+ invitation.id_project = 1
+ invitation.test_invitation_creation_date = datetime.now()
+ invitation.test_invitation_expiration_date = datetime.now() + timedelta(days=1)
+
+ TeraTestInvitation.insert(invitation)
+
+ # Service should have access to invitation
+ service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(service)
+ self.assertTrue(invitation.id_test_invitation in service_access.get_accessible_tests_invitations_ids())
+
+ # Supp service should also have access to invitation
+ # service_access : DBManagerTeraServiceAccess = DBManager.serviceAccess(supp_service)
+ # self.assertTrue(invitation.id_test_invitation in service_access.get_accessible_tests_invitations_ids())
diff --git a/teraserver/python/tests/modules/DatabaseModule/test_DBManagerTeraUserAccess.py b/teraserver/python/tests/modules/DatabaseModule/test_DBManagerTeraUserAccess.py
new file mode 100644
index 000000000..09244d50c
--- /dev/null
+++ b/teraserver/python/tests/modules/DatabaseModule/test_DBManagerTeraUserAccess.py
@@ -0,0 +1,127 @@
+from datetime import datetime, timedelta
+from modules.DatabaseModule.DBManager import DBManager
+from opentera.db.models.TeraUser import TeraUser
+from opentera.db.models.TeraProject import TeraProject
+from opentera.db.models.TeraSite import TeraSite
+from opentera.db.models.TeraService import TeraService
+from opentera.db.models.TeraServiceAccess import TeraServiceAccess
+from opentera.db.models.TeraUserGroup import TeraUserGroup
+from opentera.db.models.TeraTestInvitation import TeraTestInvitation
+from tests.opentera.db.models.BaseModelsTest import BaseModelsTest
+from modules.DatabaseModule.DBManagerTeraUserAccess import DBManagerTeraUserAccess
+
+class DBManagerTeraUserAccessTest(BaseModelsTest):
+
+ def test_admin_get_accessible_users(self):
+ with self._flask_app.app_context():
+ admin_user = TeraUser.get_user_by_username('admin')
+ items = DBManager.userAccess(admin_user).get_accessible_users()
+ total_count = TeraUser.get_count()
+ self.assertEqual(len(items), total_count)
+
+ def test_no_access_get_accessible_users(self):
+ with self._flask_app.app_context():
+ user = TeraUser.get_user_by_username('user4')
+ items = DBManager.userAccess(user).get_accessible_users()
+ self.assertEqual(len(items), 1) # Access to self only
+ self.assertEqual(items[0].id_user, user.id_user)
+
+ def test_user_get_accessible_users(self):
+ with self._flask_app.app_context():
+ user = TeraUser.get_user_by_username('user')
+ items = DBManager.userAccess(user).get_accessible_users()
+ total_count = len(TeraProject.get_project_by_id(1).get_users_in_project())
+ self.assertEqual(len(items), total_count)
+
+ def test_user_get_accessible_users_with_site(self):
+ with self._flask_app.app_context():
+ user = TeraUser.get_user_by_username('user')
+ items = DBManager.userAccess(user).get_accessible_users(include_site_access=True)
+ users = [user.id_user for user in TeraProject.get_project_by_id(1).get_users_in_project()]
+ site_access = TeraServiceAccess.get_service_access(
+ id_service=TeraService.get_openteraserver_service().id_service, id_site=1)
+ for access in site_access:
+ users.extend([user.id_user for user in access.service_access_user_group.user_group_users])
+ total_count = len(set(users))
+ self.assertEqual(len(items), total_count)
+
+ def test_admin_get_accessible_usergroups(self):
+ with self._flask_app.app_context():
+ admin_user = TeraUser.get_user_by_username('admin')
+ items = DBManager.userAccess(admin_user).get_accessible_users_groups()
+ total_count = TeraUserGroup.get_count()
+ self.assertEqual(len(items), total_count)
+
+ def test_no_access_get_accessible_usergroups(self):
+ with self._flask_app.app_context():
+ user = TeraUser.get_user_by_username('user4')
+ items = DBManager.userAccess(user).get_accessible_users_groups()
+ self.assertEqual(len(items), 0)
+
+ # def test_user_get_accessible_usergroups(self):
+ # with self._flask_app.app_context():
+ # user = TeraUser.get_user_by_username('user')
+ # items = DBManager.userAccess(user).get_accessible_users_groups()
+ # total_count = len(TeraProject.get_project_by_id(1).get_users_in_project())
+ # self.assertEqual(len(items), total_count)
+
+ def test_admin_accessible_sites(self):
+ with self._flask_app.app_context():
+ admin_user = TeraUser.get_user_by_username('admin')
+ sites = DBManager.userAccess(admin_user).get_accessible_sites()
+ self.assertEqual(len(sites), 2)
+
+ def test_user_accessible_sites(self):
+ with self._flask_app.app_context():
+ test_user = TeraUser.get_user_by_username('user')
+ sites = DBManager.userAccess(test_user).get_accessible_sites()
+ self.assertEqual(len(sites), 1)
+
+
+
+ def test_get_accessible_tests_invitations_ids(self):
+ """
+ Testing get_accessible_tests_invitations_ids in different scenarios.
+ """
+ with self._flask_app.app_context():
+ def create_invitation(id_test_type : int,
+ id_session :int = None,
+ id_user = None,
+ id_participant: int = None,
+ id_device : int = None,
+ id_project : int = None):
+ invitation : TeraTestInvitation = TeraTestInvitation()
+ invitation.id_test_type = id_test_type
+ invitation.id_session = id_session
+ invitation.id_user = id_user
+ invitation.id_project = id_project
+ invitation.id_participant = id_participant
+ invitation.id_device = id_device
+ invitation.test_invitation_max_count=1
+ invitation.test_invitation_count=0
+ invitation.test_invitation_expiration_date = datetime.now() + timedelta(days=1)
+ TeraTestInvitation.insert(invitation)
+ return invitation
+
+ user = TeraUser.get_user_by_username('admin')
+
+ # Default, there is no invitation in the system
+ items = DBManager.userAccess(user).get_accessible_tests_invitations_ids()
+ self.assertEqual(len(items), 0)
+
+ # Create invitation for user 1
+ invitation1 = create_invitation(1, id_user=1, id_project=1)
+ invitation2 = create_invitation(1, id_participant=1, id_project=1)
+ invitation3 = create_invitation(1, id_device=1, id_project=1)
+ self.assertIsNotNone(invitation1)
+ self.assertIsNotNone(invitation2)
+ self.assertIsNotNone(invitation3)
+
+ # Admin should see the invitation
+ items = DBManager.userAccess(user).get_accessible_tests_invitations_ids()
+ self.assertEqual(len(items), 3)
+
+ # Standard user should not see the invitation
+ user = TeraUser.get_user_by_username('user4')
+ items = DBManager.userAccess(user).get_accessible_tests_invitations_ids()
+ self.assertEqual(len(items), 0)
diff --git a/teraserver/python/tests/modules/FlaskModule/API/__init__.py b/teraserver/python/tests/modules/FlaskModule/API/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/teraserver/python/tests/modules/FlaskModule/API/device/__init__.py b/teraserver/python/tests/modules/FlaskModule/API/device/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceLogin.py b/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceLogin.py
index 84f95952c..609a6c04f 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceLogin.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceLogin.py
@@ -1,4 +1,4 @@
-from BaseDeviceAPITest import BaseDeviceAPITest
+from tests.modules.FlaskModule.API.device.BaseDeviceAPITest import BaseDeviceAPITest
from opentera.db.models.TeraDevice import TeraDevice
diff --git a/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceLogout.py b/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceLogout.py
index ee2f24d18..bee36e8ce 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceLogout.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceLogout.py
@@ -1,4 +1,4 @@
-from BaseDeviceAPITest import BaseDeviceAPITest
+from tests.modules.FlaskModule.API.device.BaseDeviceAPITest import BaseDeviceAPITest
from opentera.db.models.TeraDevice import TeraDevice
diff --git a/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceQueryAssets.py b/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceQueryAssets.py
index 429b303f3..3513240bf 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceQueryAssets.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceQueryAssets.py
@@ -1,5 +1,5 @@
from typing import List
-from BaseDeviceAPITest import BaseDeviceAPITest
+from tests.modules.FlaskModule.API.device.BaseDeviceAPITest import BaseDeviceAPITest
from opentera.db.models.TeraDevice import TeraDevice
from opentera.db.models.TeraAsset import TeraAsset
from opentera.db.models.TeraSession import TeraSession
diff --git a/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceQueryDevices.py b/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceQueryDevices.py
index 9363b2bac..3e3e24a4d 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceQueryDevices.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceQueryDevices.py
@@ -1,4 +1,4 @@
-from BaseDeviceAPITest import BaseDeviceAPITest
+from tests.modules.FlaskModule.API.device.BaseDeviceAPITest import BaseDeviceAPITest
from opentera.db.models.TeraDevice import TeraDevice
diff --git a/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceQueryParticipants.py b/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceQueryParticipants.py
index ec71c8712..6869a1730 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceQueryParticipants.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceQueryParticipants.py
@@ -1,4 +1,4 @@
-from BaseDeviceAPITest import BaseDeviceAPITest
+from tests.modules.FlaskModule.API.device.BaseDeviceAPITest import BaseDeviceAPITest
from opentera.db.models.TeraDevice import TeraDevice
diff --git a/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceQuerySessionEvents.py b/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceQuerySessionEvents.py
index c2f7ade64..66e928596 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceQuerySessionEvents.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceQuerySessionEvents.py
@@ -1,4 +1,4 @@
-from BaseDeviceAPITest import BaseDeviceAPITest
+from tests.modules.FlaskModule.API.device.BaseDeviceAPITest import BaseDeviceAPITest
from opentera.db.models.TeraDevice import TeraDevice
from opentera.db.models.TeraSession import TeraSession
from opentera.db.models.TeraSessionEvent import TeraSessionEvent
diff --git a/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceQuerySessions.py b/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceQuerySessions.py
index 358839fa3..9f0de6e2c 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceQuerySessions.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceQuerySessions.py
@@ -1,4 +1,4 @@
-from BaseDeviceAPITest import BaseDeviceAPITest
+from tests.modules.FlaskModule.API.device.BaseDeviceAPITest import BaseDeviceAPITest
from opentera.db.models.TeraDevice import TeraDevice
from opentera.db.models.TeraSession import TeraSession
from modules.DatabaseModule.DBManagerTeraDeviceAccess import DBManagerTeraDeviceAccess
diff --git a/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceQueryStatus.py b/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceQueryStatus.py
index 26b3053f6..78fbdb3c7 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceQueryStatus.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceQueryStatus.py
@@ -1,4 +1,4 @@
-from BaseDeviceAPITest import BaseDeviceAPITest
+from tests.modules.FlaskModule.API.device.BaseDeviceAPITest import BaseDeviceAPITest
from opentera.db.models.TeraDevice import TeraDevice
from opentera.db.models.TeraSession import TeraSession
from modules.DatabaseModule.DBManagerTeraDeviceAccess import DBManagerTeraDeviceAccess
diff --git a/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceRegister.py b/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceRegister.py
index c347e1038..cab8355f0 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceRegister.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/device/test_DeviceRegister.py
@@ -1,4 +1,4 @@
-from BaseDeviceAPITest import BaseDeviceAPITest
+from tests.modules.FlaskModule.API.device.BaseDeviceAPITest import BaseDeviceAPITest
from opentera.db.models.TeraServerSettings import TeraServerSettings
from opentera.db.models.TeraDevice import TeraDevice
from opentera.db.models.TeraDeviceType import TeraDeviceType
diff --git a/teraserver/python/tests/modules/FlaskModule/API/participant/__init__.py b/teraserver/python/tests/modules/FlaskModule/API/participant/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantLogin.py b/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantLogin.py
index 2a772cddd..d35aaeda5 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantLogin.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantLogin.py
@@ -1,5 +1,6 @@
-from BaseParticipantAPITest import BaseParticipantAPITest
+from tests.modules.FlaskModule.API.participant.BaseParticipantAPITest import BaseParticipantAPITest
from opentera.db.models.TeraParticipant import TeraParticipant
+from opentera.db.models.TeraSessionType import TeraSessionType
class ParticipantLoginTest(BaseParticipantAPITest):
@@ -29,12 +30,21 @@ def test_get_endpoint_login_valid_participant_http_auth(self):
self.assertTrue('websocket_url' in response.json)
self.assertTrue('participant_uuid' in response.json)
self.assertTrue('participant_token' in response.json)
+ self.assertTrue('session_types_info' in response.json)
self.assertTrue('base_token' in response.json)
self.assertGreater(len(response.json['websocket_url']), 0)
self.assertGreater(len(response.json['participant_uuid']), 0)
self.assertGreater(len(response.json['participant_token']), 0)
self.assertGreater(len(response.json['base_token']), 0)
+ participant = TeraParticipant.get_participant_by_username('participant1')
+ session_types = TeraSessionType.query.join(TeraSessionType.session_type_projects) \
+ .filter_by(id_project=participant.id_project).all()
+ self.assertEqual(len(response.json['session_types_info']), len(session_types))
+ received_ids = [st['id_session_type'] for st in response.json['session_types_info']]
+ for st in session_types:
+ self.assertTrue(st.id_session_type in received_ids)
+
def test_get_endpoint_login_invalid_participant_httpauth(self):
with self._flask_app.app_context():
# Using default participant information
@@ -56,12 +66,20 @@ def test_get_endpoint_login_valid_token_auth(self):
self.assertTrue('websocket_url' in response.json)
self.assertTrue('participant_uuid' in response.json)
self.assertTrue('participant_name' in response.json)
+ self.assertTrue('session_types_info' in response.json)
self.assertTrue('base_token' in response.json)
self.assertGreater(len(response.json['websocket_url']), 0)
self.assertGreater(len(response.json['participant_uuid']), 0)
self.assertGreater(len(response.json['participant_name']), 0)
self.assertGreater(len(response.json['base_token']), 0)
+ session_types = TeraSessionType.query.join(TeraSessionType.session_type_projects) \
+ .filter_by(id_project=participant.id_project).all()
+ self.assertEqual(len(response.json['session_types_info']), len(session_types))
+ received_ids = [st['id_session_type'] for st in response.json['session_types_info']]
+ for st in session_types:
+ self.assertTrue(st.id_session_type in received_ids)
+
def test_get_endpoint_login_base_token_auth(self):
# Get the token from http auth
with self._flask_app.app_context():
diff --git a/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantLogout.py b/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantLogout.py
index cd1fcff4e..633e1c579 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantLogout.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantLogout.py
@@ -1,4 +1,4 @@
-from BaseParticipantAPITest import BaseParticipantAPITest
+from tests.modules.FlaskModule.API.participant.BaseParticipantAPITest import BaseParticipantAPITest
from opentera.db.models.TeraParticipant import TeraParticipant
diff --git a/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantQueryAssets.py b/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantQueryAssets.py
index 44e973f35..83fce2da2 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantQueryAssets.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantQueryAssets.py
@@ -1,5 +1,8 @@
-from BaseParticipantAPITest import BaseParticipantAPITest
+from typing import List
+from tests.modules.FlaskModule.API.participant.BaseParticipantAPITest import BaseParticipantAPITest
from opentera.db.models.TeraAsset import TeraAsset
+from opentera.db.models.TeraParticipant import TeraParticipant
+from opentera.db.models.TeraSession import TeraSession
class ParticipantQueryAssetsTest(BaseParticipantAPITest):
@@ -35,11 +38,19 @@ def test_query_invalid_token_auth(self):
def test_static_token(self):
with self._flask_app.app_context():
- params = {'id_asset': 1, 'with_urls': True}
+ params = {'with_urls': True}
response = self._get_with_participant_token_auth(self.test_client,
token=self.participant_static_token, params=params)
self.assertEqual(403, response.status_code)
+ def test_static_token_forbidden_id(self):
+ with self._flask_app.app_context():
+ params = {'id_asset': 10, 'with_urls': True}
+ response = self._get_with_participant_token_auth(self.test_client,
+ token=self.participant_static_token, params=params)
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+
def test_query_assets_get_id(self):
with self._flask_app.app_context():
params = {'id_asset': 1, 'with_urls': True}
@@ -106,6 +117,78 @@ def test_query_assets_all_token_only(self):
self.assertTrue(data_item.__contains__("asset_uuid"))
self.assertTrue(data_item.__contains__("access_token"))
+ def test_get_endpoint_with_token_auth_with_forbidden_id_session(self):
+ with self._flask_app.app_context():
+ participants: List[TeraParticipant] = TeraParticipant.query.all()
+
+ for participant in participants:
+ for session in TeraSession.query.all():
+ params = {
+ 'id_session': session.id_session,
+ 'with_urls': True
+ }
+
+ if participant.participant_token:
+ if session.id_creator_participant != participant.id_participant:
+ response = self._get_with_participant_token_auth(self.test_client,
+ token=participant.participant_token,
+ params=params)
+ self.assertEqual(403, response.status_code)
+
+ def test_get_endpoint_with_token_auth_with_session_id(self):
+ with self._flask_app.app_context():
+ participants: List[TeraParticipant] = TeraParticipant.query.all()
+
+ for participant in participants:
+ for session in TeraSession.query.all():
+ params = {
+ 'id_session': session.id_session,
+ 'with_urls': True
+ }
+
+ if participant.participant_token:
+ if session.id_creator_participant == participant.id_participant:
+ response = self._get_with_participant_token_auth(self.test_client,
+ token=participant.participant_token,
+ params=params)
+ self.assertEqual(200, response.status_code)
+
+ assets_ids = [asset.id_asset for asset in
+ TeraAsset.get_assets_for_session(session.id_session)
+ if asset.id_participant == participant.id_participant]
+ for asset_json in response.json:
+ self.assertTrue(asset_json['id_asset'] in assets_ids)
+ self._checkJson(asset_json, minimal=True) # Participant with token = never return url
+ assets_ids.remove(asset_json['id_asset'])
+ self.assertFalse(assets_ids)
+
+ def test_get_endpoint_with_login_with_session_id(self):
+ with self._flask_app.app_context():
+ participants: List[TeraParticipant] = TeraParticipant.query.all()
+
+ for participant in participants:
+ for session in TeraSession.query.all():
+ params = {
+ 'id_session': session.id_session,
+ 'with_urls': True
+ }
+
+ if participant.participant_login_enabled:
+ if session.id_creator_participant == participant.id_participant:
+ response = self._get_with_participant_http_auth(self.test_client,
+ username=participant.participant_username,
+ password='opentera', params=params)
+ self.assertEqual(200, response.status_code)
+
+ assets_ids = [asset.id_asset for asset in
+ TeraAsset.get_assets_for_session(session.id_session)
+ if asset.id_participant == participant.id_participant]
+ for asset_json in response.json:
+ self.assertTrue(asset_json['id_asset'] in assets_ids)
+ self._checkJson(asset_json, minimal=False) # Full infos
+ assets_ids.remove(asset_json['id_asset'])
+ self.assertFalse(assets_ids)
+
def _checkJson(self, json_data, minimal=False):
self.assertGreater(len(json_data), 0)
self.assertTrue(json_data.__contains__('id_asset'))
@@ -123,3 +206,7 @@ def _checkJson(self, json_data, minimal=False):
self.assertTrue(json_data.__contains__('asset_infos_url'))
self.assertTrue(json_data.__contains__('asset_url'))
self.assertTrue(json_data.__contains__('access_token'))
+ else:
+ self.assertFalse(json_data.__contains__('asset_infos_url'))
+ self.assertFalse(json_data.__contains__('asset_url'))
+ self.assertFalse(json_data.__contains__('access_token'))
diff --git a/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantQueryDevices.py b/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantQueryDevices.py
index 1f7aaaf23..0aee1f29a 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantQueryDevices.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantQueryDevices.py
@@ -1,4 +1,4 @@
-from BaseParticipantAPITest import BaseParticipantAPITest
+from tests.modules.FlaskModule.API.participant.BaseParticipantAPITest import BaseParticipantAPITest
class ParticipantQueryDevicesTest(BaseParticipantAPITest):
diff --git a/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantQueryParticipants.py b/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantQueryParticipants.py
index 7a33afd78..e1ade5e55 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantQueryParticipants.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantQueryParticipants.py
@@ -1,4 +1,4 @@
-from BaseParticipantAPITest import BaseParticipantAPITest
+from tests.modules.FlaskModule.API.participant.BaseParticipantAPITest import BaseParticipantAPITest
class ParticipantQueryParticipantsTest(BaseParticipantAPITest):
diff --git a/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantQuerySessions.py b/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantQuerySessions.py
index a3a9dce1f..2daf8a411 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantQuerySessions.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantQuerySessions.py
@@ -1,5 +1,9 @@
from datetime import datetime, timedelta
-from BaseParticipantAPITest import BaseParticipantAPITest
+from tests.modules.FlaskModule.API.participant.BaseParticipantAPITest import BaseParticipantAPITest
+from modules.DatabaseModule.DBManagerTeraParticipantAccess import DBManagerTeraParticipantAccess
+from opentera.db.models.TeraParticipant import TeraParticipant
+from opentera.db.models.TeraSession import TeraSession
+import uuid
class ParticipantQuerySessionsTest(BaseParticipantAPITest):
@@ -121,7 +125,12 @@ def test_query_with_limit_and_offset(self):
password='opentera', params={'limit': 2, 'offset': 27})
self.assertEqual(200, response.status_code)
self.assertEqual('application/json', response.headers['Content-Type'])
- self.assertEqual(1, len(response.json))
+
+ count = len(TeraSession.get_sessions_for_participant(
+ part_id=TeraParticipant.get_participant_by_username('participant1').id_participant,
+ limit=2, offset=27))
+
+ self.assertEqual(count, len(response.json))
def test_query_with_status(self):
with self._flask_app.app_context():
@@ -129,7 +138,12 @@ def test_query_with_status(self):
password='opentera', params={'status': 0})
self.assertEqual(200, response.status_code)
self.assertEqual('application/json', response.headers['Content-Type'])
- self.assertEqual(12, len(response.json))
+
+ count = len(TeraSession.get_sessions_for_participant(
+ part_id=TeraParticipant.get_participant_by_username('participant1').id_participant,
+ status=0))
+
+ self.assertEqual(count, len(response.json))
for data_item in response.json:
self.assertEqual(0, data_item['session_status'])
@@ -141,7 +155,10 @@ def test_query_with_limit_and_offset_and_status_and_list(self):
params={'list': 1, 'limit': 2, 'offset': 11, 'status': 0})
self.assertEqual(200, response.status_code)
self.assertEqual('application/json', response.headers['Content-Type'])
- self.assertEqual(1, len(response.json))
+ count = len(TeraSession.get_sessions_for_participant(
+ part_id=TeraParticipant.get_participant_by_username('participant1').id_participant,
+ limit=2, offset=11, status=0))
+ self.assertEqual(count, len(response.json))
for data_item in response.json:
self.assertEqual(0, data_item['session_status'])
@@ -155,7 +172,10 @@ def test_query_with_start_date_and_end_date(self):
params={'start_date': start_date, 'end_date': end_date})
self.assertEqual(200, response.status_code)
self.assertEqual('application/json', response.headers['Content-Type'])
- self.assertEqual(6, len(response.json))
+ count = len(TeraSession.get_sessions_for_participant(
+ part_id=TeraParticipant.get_participant_by_username('participant1').id_participant,
+ start_date=datetime.now() - timedelta(days=6), end_date=datetime.now() - timedelta(days=4)))
+ self.assertEqual(count, len(response.json))
def test_query_with_start_date(self):
with self._flask_app.app_context():
@@ -164,7 +184,10 @@ def test_query_with_start_date(self):
password='opentera', params={'start_date': start_date})
self.assertEqual(200, response.status_code)
self.assertEqual('application/json', response.headers['Content-Type'])
- self.assertEqual(12, len(response.json))
+ count = len(TeraSession.get_sessions_for_participant(
+ part_id=TeraParticipant.get_participant_by_username('participant1').id_participant,
+ start_date=datetime.now() - timedelta(days=3)))
+ self.assertEqual(count, len(response.json))
def test_query_with_end_date(self):
with self._flask_app.app_context():
@@ -174,4 +197,144 @@ def test_query_with_end_date(self):
params={'end_date': end_date})
self.assertEqual(200, response.status_code)
self.assertEqual('application/json', response.headers['Content-Type'])
- self.assertEqual(9, len(response.json))
+ count = len(TeraSession.get_sessions_for_participant(
+ part_id=TeraParticipant.get_participant_by_username('participant1').id_participant,
+ end_date=end_date))
+ self.assertEqual(count, len(response.json))
+
+ def test_post_endpoint_with_valid_token_but_empty_schema(self):
+ with self._flask_app.app_context():
+ for participant in TeraParticipant.query.all():
+ if not participant.participant_token:
+ continue
+ response = self._post_with_participant_token_auth(self.test_client,
+ token=participant.participant_token, json={})
+ self.assertEqual(response.status_code, 400)
+
+ def test_post_endpoint_with_valid_token_but_invalid_session(self):
+ with self._flask_app.app_context():
+ for participant in TeraParticipant.query.all():
+ if not participant.participant_token:
+ continue
+ # Get all participant ids
+ participants_uuids = [participant.participant_uuid]
+
+ # Invalid session schema
+ session = {'session': {'id_session': 0, 'session_participants': participants_uuids}}
+ response = self._post_with_participant_token_auth(self.test_client,
+ token=participant.participant_token, json=session)
+ self.assertEqual(response.status_code, 400)
+
+ def test_post_endpoint_with_valid_token_valid_participants_valid_session_type_and_new_session(self):
+ with self._flask_app.app_context():
+ for participant in TeraParticipant.query.all():
+ if not participant.participant_token:
+ continue
+ participants_uuids = [participant.participant_uuid]
+
+ access = DBManagerTeraParticipantAccess(participant)
+
+ # Get all the session types available
+ session_types = access.get_accessible_session_types_ids()
+
+ for session_type in session_types:
+ session = {'session': {
+ 'id_session': 0,
+ 'session_participants': participants_uuids,
+ 'id_session_type': session_type,
+ 'session_name': 'TEST',
+ 'session_status': 0,
+ 'session_start_datetime': str(datetime.now())}}
+
+ response = self._post_with_participant_token_auth(self.test_client,
+ token=participant.participant_token, json=session)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertTrue('id_session' in response.json)
+ self.assertGreater(response.json['id_session'], 0)
+ self.assertEqual(response.json['id_creator_participant'], participant.id_participant)
+
+ def test_post_endpoint_with_valid_token_invalid_participants_valid_session_type_and_new_session(self):
+ with self._flask_app.app_context():
+ for participant in TeraParticipant.query.all():
+ if not participant.participant_token:
+ continue
+ # Generate invalid participants
+ participants_uuids = [str(uuid.uuid4()), str(uuid.uuid4()), str(uuid.uuid4())]
+
+ access = DBManagerTeraParticipantAccess(participant)
+
+ # Get all the session types available
+ session_types = access.get_accessible_session_types_ids()
+
+ for session_type in session_types:
+ session = {'session': {
+ 'id_session': 0,
+ 'session_participants': participants_uuids,
+ 'id_session_type': session_type,
+ 'session_name': 'TEST',
+ 'session_status': 0,
+ 'session_start_datetime': str(datetime.now())}}
+
+ response = self._post_with_participant_token_auth(self.test_client,
+ token=participant.participant_token, json=session)
+ self.assertEqual(response.status_code, 400)
+
+ def test_post_endpoint_with_valid_token_valid_participants_invalid_session_type_and_new_session(self):
+ with self._flask_app.app_context():
+ for participant in TeraParticipant.query.all():
+ if not participant.participant_token:
+ continue
+ # Get all participant ids
+ participants_uuids = [participant.participant_uuid]
+
+ access = DBManagerTeraParticipantAccess(participant)
+
+ session_types = [5000, 0]
+
+ for session_type in session_types:
+ session = {'session': {
+ 'id_session': 0,
+ 'session_participants': participants_uuids,
+ 'id_session_type': session_type,
+ 'session_name': 'TEST',
+ 'session_status': 0,
+ 'session_start_datetime': str(datetime.now())}}
+
+ response = self._post_with_participant_token_auth(self.test_client,
+ token=participant.participant_token, json=session)
+
+ self.assertEqual(response.status_code, 403)
+
+ def test_post_endpoint_with_valid_token_valid_participants_valid_session_type_and_update_session(self):
+ with self._flask_app.app_context():
+ for participant in TeraParticipant.query.all():
+ if not participant.participant_token:
+ continue
+ access = DBManagerTeraParticipantAccess(participant)
+
+ # Get all available sessions
+ sessions = access.get_accessible_sessions_ids()
+ participants_uuids = [participant.participant_uuid]
+
+ for id_session in sessions:
+ db_session = TeraSession.get_session_by_id(id_session)
+ session = {'session': {
+ 'id_session': id_session,
+ 'session_participants': participants_uuids,
+ 'id_session_type': db_session.id_session_type,
+ 'session_name': 'TEST-UPDATE',
+ 'session_status': 0,
+ 'session_start_datetime': str(datetime.now())}}
+
+ response = self._post_with_participant_token_auth(self.test_client,
+ token=participant.participant_token, json=session)
+
+ if db_session.id_creator_participant is not participant.id_participant:
+ self.assertEqual(response.status_code, 403)
+ continue
+
+ self.assertEqual(response.status_code, 200)
+ self.assertTrue('id_session' in response.json)
+ self.assertGreater(response.json['id_session'], 0)
+ self.assertEqual(response.json['id_creator_participant'], participant.id_participant)
diff --git a/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantRefreshToken.py b/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantRefreshToken.py
index f9fefe4a2..c8d64d7d1 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantRefreshToken.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/participant/test_ParticipantRefreshToken.py
@@ -1,4 +1,4 @@
-from BaseParticipantAPITest import BaseParticipantAPITest
+from tests.modules.FlaskModule.API.participant.BaseParticipantAPITest import BaseParticipantAPITest
class ParticipantRefreshTokenTest(BaseParticipantAPITest):
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/BaseServiceAPITest.py b/teraserver/python/tests/modules/FlaskModule/API/service/BaseServiceAPITest.py
index 85466577b..517b03b05 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/service/BaseServiceAPITest.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/service/BaseServiceAPITest.py
@@ -1,17 +1,21 @@
import unittest
+import uuid
+
+import redis
+
+from flask.testing import FlaskClient
+from flask import Flask
+from flask_babel import Babel
+
from modules.DatabaseModule.DBManager import DBManager
from modules.LoginModule.LoginModule import LoginModule
+from modules.FlaskModule.FlaskModule import FlaskModule, CustomAPI
+import modules.Globals as Globals
+
from opentera.config.ConfigManager import ConfigManager
from opentera.modules.BaseModule import BaseModule, ModuleNames
-from flask.testing import FlaskClient
from opentera.redis.RedisVars import RedisVars
from opentera.db.models.TeraService import TeraService
-from modules.FlaskModule.FlaskModule import FlaskModule, CustomAPI
-import redis
-import uuid
-from flask import Flask
-from flask_babel import Babel
-import modules.Globals as Globals
class FakeFlaskModule(BaseModule):
@@ -119,7 +123,8 @@ def setup_service_token(self):
self.service_uuid = service.service_uuid
self.id_service = service.id_service
- def _get_with_service_token_auth(self, client: FlaskClient, token, params={}, endpoint=None):
+ def _get_with_service_token_auth(self, client: FlaskClient, token: str,
+ params : dict = None, endpoint : str =None):
if params is None:
params = {}
if endpoint is None:
@@ -127,7 +132,7 @@ def _get_with_service_token_auth(self, client: FlaskClient, token, params={}, en
headers = {'Authorization': 'OpenTera ' + token}
return client.get(endpoint, headers=headers, query_string=params)
- def _post_with_service_token_auth(self, client: FlaskClient, token: str = '', json: dict = None,
+ def _post_with_service_token_auth(self, client: FlaskClient, token: str, json: dict = None,
params: dict = None, endpoint: str = None):
if params is None:
params = {}
@@ -136,7 +141,7 @@ def _post_with_service_token_auth(self, client: FlaskClient, token: str = '', js
headers = {'Authorization': 'OpenTera ' + token}
return client.post(endpoint, headers=headers, query_string=params, json=json)
- def _delete_with_service_token_auth(self, client: FlaskClient, token: str = '',
+ def _delete_with_service_token_auth(self, client: FlaskClient, token: str,
params: dict = None, endpoint: str = None):
if params is None:
params = {}
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/__init__.py b/teraserver/python/tests/modules/FlaskModule/API/service/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryAccess.py b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryAccess.py
index c2c368e32..f2d7b5723 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryAccess.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryAccess.py
@@ -1,4 +1,4 @@
-from BaseServiceAPITest import BaseServiceAPITest
+from tests.modules.FlaskModule.API.service.BaseServiceAPITest import BaseServiceAPITest
from opentera.db.models.TeraDevice import TeraDevice
from opentera.db.models.TeraParticipant import TeraParticipant
from opentera.db.models.TeraUser import TeraUser
@@ -214,4 +214,3 @@ def test_get_endpoint_with_token_auth_all_params_admin_and_not_admin_for_device_
# TODO complete tests with devices
access = DBManagerTeraDeviceAccess(device)
-
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryAssets.py b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryAssets.py
index 45367e339..44d817d68 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryAssets.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryAssets.py
@@ -1,5 +1,6 @@
-from BaseServiceAPITest import BaseServiceAPITest
-from modules.FlaskModule.FlaskModule import flask_app
+from tests.modules.FlaskModule.API.service.BaseServiceAPITest import BaseServiceAPITest
+from opentera.db.models.TeraAsset import TeraAsset
+from opentera.db.models.TeraUser import TeraUser
from datetime import datetime
@@ -131,13 +132,32 @@ def test_get_endpoint_query_participant_assets_no_access(self):
params=params, endpoint=self.test_endpoint)
self.assertEqual(response.status_code, 403)
+ def test_get_endpoint_query_user_assets_forbidden(self):
+ with self._flask_app.app_context():
+ user = TeraUser.get_user_by_username('user4')
+ params = {'id_user': user.id_user, 'with_urls': True}
+ response = self._get_with_service_token_auth(client=self.test_client, token=self.service_token,
+ params=params, endpoint=self.test_endpoint)
+ self.assertEqual(response.status_code, 403)
+
def test_get_endpoint_query_user_assets(self):
with self._flask_app.app_context():
- params = {'id_user': 1, 'with_urls': True}
+ params = {'id_user': 2, 'with_urls': True}
response = self._get_with_service_token_auth(client=self.test_client, token=self.service_token,
params=params, endpoint=self.test_endpoint)
self.assertEqual(response.status_code, 200)
- self.assertEqual(len(response.json), 4)
+ target_count = 0
+ id_sessions = []
+ user = TeraUser.get_user_by_id(2)
+ for session in user.user_sessions:
+ target_count += len(session.session_assets)
+ if session.id_session not in id_sessions:
+ id_sessions.append(session.id_session)
+ # Also add assets created but not in session we are part of
+ for asset in user.user_assets:
+ if asset.id_session not in id_sessions:
+ target_count += 1
+ self.assertEqual(len(response.json), target_count)
for data_item in response.json:
self._checkJson(json_data=data_item)
@@ -179,9 +199,17 @@ def test_get_endpoint_query_assets_created_by_service(self):
for data_item in response.json:
self._checkJson(json_data=data_item)
+ def test_get_endpoint_query_assets_created_by_user_forbidden(self):
+ with self._flask_app.app_context():
+ user = TeraUser.get_user_by_username('user4')
+ params = {'id_creator_user': user.id_user, 'with_urls': True}
+ response = self._get_with_service_token_auth(client=self.test_client, token=self.service_token,
+ params=params, endpoint=self.test_endpoint)
+ self.assertEqual(response.status_code, 403)
+
def test_get_endpoint_query_assets_created_by_user(self):
with self._flask_app.app_context():
- params = {'id_creator_user': 1, 'with_urls': True}
+ params = {'id_creator_user': 2, 'with_urls': True}
response = self._get_with_service_token_auth(client=self.test_client, token=self.service_token,
params=params, endpoint=self.test_endpoint)
self.assertEqual(response.status_code, 200)
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryAuthCode.py b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryAuthCode.py
new file mode 100644
index 000000000..ba6b54754
--- /dev/null
+++ b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryAuthCode.py
@@ -0,0 +1,30 @@
+from tests.modules.FlaskModule.API.service.BaseServiceAPITest import BaseServiceAPITest
+
+class ServiceQueryAuthCodeTest(BaseServiceAPITest):
+ test_endpoint = '/api/service/auth/code'
+
+ def test_get_endpoint_no_auth(self):
+ with self._flask_app.app_context():
+ response = self.test_client.get(self.test_endpoint)
+ self.assertEqual(401, response.status_code)
+
+ def test_get_endpoint_with_token_auth_no_params(self):
+ with self._flask_app.app_context():
+ response = self._get_with_service_token_auth(client=self.test_client, token=self.service_token,
+ params=None, endpoint=self.test_endpoint)
+ self.assertEqual(400, response.status_code)
+
+ def test_get_endpoint_with_token_auth_with_wrong_params(self):
+ with self._flask_app.app_context():
+ params = {'invalid_param': 'invalid'}
+ response = self._get_with_service_token_auth(client=self.test_client, token=self.service_token,
+ params=params, endpoint=self.test_endpoint)
+ self.assertEqual(400, response.status_code)
+
+ def test_get_endpoint_with_token_auth_with_endpoint_url_param(self):
+ with self._flask_app.app_context():
+ params = {'endpoint_url': '/testendpoint'}
+ response = self._get_with_service_token_auth(client=self.test_client, token=self.service_token,
+ params=params, endpoint=self.test_endpoint)
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('auth_code' in response.json)
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryDevices.py b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryDevices.py
index 24ed98adf..39fe9fed0 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryDevices.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryDevices.py
@@ -1,10 +1,12 @@
from typing import List
-from BaseServiceAPITest import BaseServiceAPITest
-from modules.FlaskModule.FlaskModule import flask_app
+from tests.modules.FlaskModule.API.service.BaseServiceAPITest import BaseServiceAPITest
from opentera.db.models.TeraDevice import TeraDevice
from opentera.db.models.TeraDeviceSubType import TeraDeviceSubType
from opentera.db.models.TeraDeviceType import TeraDeviceType
+from opentera.db.models.TeraService import TeraService
+
+from modules.DatabaseModule.DBManagerTeraServiceAccess import DBManagerTeraServiceAccess
class ServiceQueryDevicesTest(BaseServiceAPITest):
@@ -32,8 +34,7 @@ def test_get_endpoint_with_token_auth_with_wrong_params(self):
# Get all devices from DB
devices: List[TeraDevice] = TeraDevice.query.all()
for device in devices:
- device_uuid: str = device.device_uuid
- params = {'device_uuid_wrong': device_uuid}
+ params = {'device_uuid_wrong': device.device_uuid}
response = self._get_with_service_token_auth(client=self.test_client, token=self.service_token,
params=params, endpoint=self.test_endpoint)
self.assertEqual(400, response.status_code)
@@ -42,79 +43,128 @@ def test_get_endpoint_with_token_auth_with_device_uuid(self):
with self._flask_app.app_context():
# Get all devices from DB
devices: List[TeraDevice] = TeraDevice.query.all()
+ service: TeraService = TeraService.get_service_by_key('VideoRehabService')
+ service_access : DBManagerTeraServiceAccess = DBManagerTeraServiceAccess(service)
+
for device in devices:
- device_uuid: str = device.device_uuid
- params = {'device_uuid': device_uuid}
+ params = {'device_uuid': device.device_uuid}
response = self._get_with_service_token_auth(client=self.test_client, token=self.service_token,
params=params, endpoint=self.test_endpoint)
- self.assertEqual(200, response.status_code)
- self.assertFalse('device_type' in response.json)
- self.assertFalse('device_subtype' in response.json)
- self.assertFalse('device_assets' in response.json)
- device_json = device.to_json()
- self.assertEqual(device_json, response.json)
+ if device.id_device in service_access.get_accessible_devices_ids():
+ self.assertEqual(200, response.status_code)
+ self.assertFalse('device_type' in response.json)
+ self.assertFalse('device_subtype' in response.json)
+ self.assertFalse('device_assets' in response.json)
+ device_json = device.to_json()
+ self.assertEqual(device_json, response.json)
+ else:
+ self.assertEqual(403, response.status_code)
- def test_get_endpoint_with_token_auth_with_device_uuid_and_device_type(self):
+ def test_get_endpoint_with_token_auth_with_id_device(self):
with self._flask_app.app_context():
# Get all devices from DB
devices: List[TeraDevice] = TeraDevice.query.all()
+ service: TeraService = TeraService.get_service_by_key('VideoRehabService')
+ service_access : DBManagerTeraServiceAccess = DBManagerTeraServiceAccess(service)
+
for device in devices:
- device_uuid: str = device.device_uuid
- params = {'device_uuid': device_uuid, 'with_device_type': True}
+ params = {'id_device': device.id_device}
response = self._get_with_service_token_auth(client=self.test_client, token=self.service_token,
params=params, endpoint=self.test_endpoint)
- self.assertEqual(200, response.status_code)
- self.assertTrue('device_type' in response.json)
- self.assertFalse('device_subtype' in response.json)
- self.assertFalse('device_assets' in response.json)
- device_json = device.to_json()
- device_json['device_type'] = TeraDeviceType.get_device_type_by_id(device.id_device_type).\
- to_json(minimal=True)
- self.assertEqual(device_json, response.json)
+ if device.id_device in service_access.get_accessible_devices_ids():
+ self.assertEqual(200, response.status_code)
+ self.assertFalse('device_type' in response.json)
+ self.assertFalse('device_subtype' in response.json)
+ self.assertFalse('device_assets' in response.json)
+ device_json = device.to_json()
+ self.assertEqual(device_json, response.json)
+ else:
+ self.assertEqual(403, response.status_code)
+
+ def test_get_endpoint_with_token_auth_with_device_uuid_and_device_type(self):
+ with self._flask_app.app_context():
+ # Get all devices from DB
+ devices: List[TeraDevice] = TeraDevice.query.all()
+
+ service: TeraService = TeraService.get_service_by_key('VideoRehabService')
+ service_access : DBManagerTeraServiceAccess = DBManagerTeraServiceAccess(service)
+
+ for device in devices:
+ params = {'device_uuid': device.device_uuid, 'with_device_type': True}
+ response = self._get_with_service_token_auth(client=self.test_client, token=self.service_token,
+ params=params, endpoint=self.test_endpoint)
+
+ if device.id_device in service_access.get_accessible_devices_ids():
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('device_type' in response.json)
+ self.assertFalse('device_subtype' in response.json)
+ self.assertFalse('device_assets' in response.json)
+ device_json = device.to_json()
+ device_json['device_type'] = TeraDeviceType.get_device_type_by_id(device.id_device_type).\
+ to_json(minimal=True)
+ self.assertEqual(device_json, response.json)
+ else:
+ self.assertEqual(403, response.status_code)
+
def test_get_endpoint_with_token_auth_with_device_uuid_and_device_subtype(self):
with self._flask_app.app_context():
# Get all devices from DB
devices: List[TeraDevice] = TeraDevice.query.all()
+
+ service: TeraService = TeraService.get_service_by_key('VideoRehabService')
+ service_access : DBManagerTeraServiceAccess = DBManagerTeraServiceAccess(service)
+
for device in devices:
device_uuid: str = device.device_uuid
params = {'device_uuid': device_uuid, 'with_device_subtype': True}
response = self._get_with_service_token_auth(client=self.test_client, token=self.service_token,
params=params, endpoint=self.test_endpoint)
- self.assertEqual(200, response.status_code)
- self.assertFalse('device_type' in response.json)
- self.assertTrue('device_subtype' in response.json)
- self.assertFalse('device_assets' in response.json)
- device_json = device.to_json()
+ if device.id_device in service_access.get_accessible_devices_ids():
+ self.assertEqual(200, response.status_code)
+ self.assertFalse('device_type' in response.json)
+ self.assertTrue('device_subtype' in response.json)
+ self.assertFalse('device_assets' in response.json)
+
+ device_json = device.to_json()
- if device.id_device_subtype is not None:
- device_subtype = TeraDeviceSubType.get_device_subtype_by_id(device.id_device_subtype)
- device_json['device_subtype'] = device_subtype.to_json(minimal=True)
+ if device.id_device_subtype is not None:
+ device_subtype = TeraDeviceSubType.get_device_subtype_by_id(device.id_device_subtype)
+ device_json['device_subtype'] = device_subtype.to_json(minimal=True)
+ else:
+ device_json['device_subtype'] = None
+ self.assertEqual(device_json, response.json)
else:
- device_json['device_subtype'] = None
- self.assertEqual(device_json, response.json)
+ self.assertEqual(403, response.status_code)
def test_get_endpoint_with_token_auth_with_device_uuid_and_device_assets(self):
with self._flask_app.app_context():
# Get all devices from DB
devices: List[TeraDevice] = TeraDevice.query.all()
+
+ service: TeraService = TeraService.get_service_by_key('VideoRehabService')
+ service_access : DBManagerTeraServiceAccess = DBManagerTeraServiceAccess(service)
+
for device in devices:
- device_uuid: str = device.device_uuid
- params = {'device_uuid': device_uuid, 'with_device_assets': True}
+ params = {'device_uuid': device.device_uuid, 'with_device_assets': True}
response = self._get_with_service_token_auth(client=self.test_client, token=self.service_token,
params=params, endpoint=self.test_endpoint)
- self.assertEqual(200, response.status_code)
- self.assertFalse('device_type' in response.json)
- self.assertFalse('device_subtype' in response.json)
- self.assertTrue('device_assets' in response.json)
- device_json = device.to_json()
- device_json['device_assets'] = []
- for asset in device.device_assets:
- device_json['device_assets'].append(asset.to_json(minimal=True))
+ if device.id_device in service_access.get_accessible_devices_ids():
+ self.assertEqual(200, response.status_code)
+ self.assertFalse('device_type' in response.json)
+ self.assertFalse('device_subtype' in response.json)
+ self.assertTrue('device_assets' in response.json)
+ device_json = device.to_json()
+ device_json['device_assets'] = []
+
+ for asset in device.device_assets:
+ device_json['device_assets'].append(asset.to_json(minimal=True))
- self.assertEqual(device_json, response.json)
+ self.assertEqual(device_json, response.json)
+ else:
+ self.assertEqual(403, response.status_code)
def test_post_endpoint_without_token_auth(self):
with self._flask_app.app_context():
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryDisconnect.py b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryDisconnect.py
index e0511aef6..ced6bd053 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryDisconnect.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryDisconnect.py
@@ -1,6 +1,6 @@
from typing import List
-from BaseServiceAPITest import BaseServiceAPITest
+from tests.modules.FlaskModule.API.service.BaseServiceAPITest import BaseServiceAPITest
from opentera.db.models.TeraDevice import TeraDevice
from opentera.db.models.TeraUser import TeraUser
from opentera.db.models.TeraParticipant import TeraParticipant
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryParticipantGroups.py b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryParticipantGroups.py
index 4138754d5..651954ab2 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryParticipantGroups.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryParticipantGroups.py
@@ -1,4 +1,4 @@
-from BaseServiceAPITest import BaseServiceAPITest
+from tests.modules.FlaskModule.API.service.BaseServiceAPITest import BaseServiceAPITest
from opentera.db.models import TeraParticipantGroup
@@ -213,4 +213,4 @@ def test_post_and_delete_endpoint_with_token(self):
# Test case: Delete with no problem
response = self._delete_with_service_token_auth(self.test_client, token=self.service_token,
params={'id': 3})
- self.assertEqual(200, response.status_code, msg="Delete OK")
\ No newline at end of file
+ self.assertEqual(200, response.status_code, msg="Delete OK")
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryParticipants.py b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryParticipants.py
index 25e1789c6..2cb9473d6 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryParticipants.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryParticipants.py
@@ -1,6 +1,6 @@
from typing import List
-from BaseServiceAPITest import BaseServiceAPITest
+from tests.modules.FlaskModule.API.service.BaseServiceAPITest import BaseServiceAPITest
from opentera.db.models.TeraParticipant import TeraParticipant
from opentera.db.models.TeraProject import TeraProject
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryProjects.py b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryProjects.py
index 235b1400e..d64653743 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryProjects.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryProjects.py
@@ -1,4 +1,4 @@
-from BaseServiceAPITest import BaseServiceAPITest
+from tests.modules.FlaskModule.API.service.BaseServiceAPITest import BaseServiceAPITest
from opentera.db.models.TeraProject import TeraProject
from opentera.db.models.TeraSite import TeraSite
from opentera.db.models.TeraServiceProject import TeraServiceProject
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryRoles.py b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryRoles.py
index 621d09b29..6be36e9ae 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryRoles.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryRoles.py
@@ -1,4 +1,4 @@
-from BaseServiceAPITest import BaseServiceAPITest
+from tests.modules.FlaskModule.API.service.BaseServiceAPITest import BaseServiceAPITest
from opentera.db.models.TeraServiceRole import TeraServiceRole
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryServiceAccess.py b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryServiceAccess.py
index c02583912..b7d1c7807 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryServiceAccess.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryServiceAccess.py
@@ -1,4 +1,4 @@
-from BaseServiceAPITest import BaseServiceAPITest
+from tests.modules.FlaskModule.API.service.BaseServiceAPITest import BaseServiceAPITest
from opentera.db.models.TeraServiceAccess import TeraServiceAccess
from opentera.db.models.TeraServiceRole import TeraServiceRole
@@ -151,4 +151,4 @@ def _checkJson(self, json_data, minimal=False):
self.assertFalse(json_data.__contains__('service_name'))
self.assertFalse(json_data.__contains__('service_key'))
self.assertFalse(json_data.__contains__('id_service'))
- self.assertFalse(json_data.__contains__('service_access_role_name'))
\ No newline at end of file
+ self.assertFalse(json_data.__contains__('service_access_role_name'))
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryServices.py b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryServices.py
index dc799a0aa..1fedd5d57 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryServices.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryServices.py
@@ -1,6 +1,6 @@
from typing import List
-from BaseServiceAPITest import BaseServiceAPITest
+from tests.modules.FlaskModule.API.service.BaseServiceAPITest import BaseServiceAPITest
from modules.FlaskModule.FlaskModule import flask_app
from opentera.db.models.TeraService import TeraService
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQuerySessionEvents.py b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQuerySessionEvents.py
index a9e5f533e..5243f0aba 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQuerySessionEvents.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQuerySessionEvents.py
@@ -1,4 +1,4 @@
-from BaseServiceAPITest import BaseServiceAPITest
+from tests.modules.FlaskModule.API.service.BaseServiceAPITest import BaseServiceAPITest
from modules.FlaskModule.FlaskModule import flask_app
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQuerySessionTypeProjects.py b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQuerySessionTypeProjects.py
new file mode 100644
index 000000000..e7f518e8f
--- /dev/null
+++ b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQuerySessionTypeProjects.py
@@ -0,0 +1,377 @@
+import datetime
+
+from tests.modules.FlaskModule.API.service.BaseServiceAPITest import BaseServiceAPITest
+
+from opentera.db.models.TeraSessionTypeProject import TeraSessionTypeProject
+from opentera.db.models.TeraTest import TeraTest
+from opentera.db.models.TeraSession import TeraSession
+from opentera.db.models.TeraSessionType import TeraSessionType
+from opentera.db.models.TeraSessionParticipants import TeraSessionParticipants
+from opentera.db.models.TeraProject import TeraProject
+from opentera.db.models.TeraParticipant import TeraParticipant
+from opentera.db.models.TeraTestType import TeraTestType
+from opentera.db.models.TeraSessionTypeSite import TeraSessionTypeSite
+from opentera.db.models.TeraServiceProject import TeraServiceProject
+
+
+
+class ServiceQuerySessionTypeProjectsTest(BaseServiceAPITest):
+ test_endpoint = '/api/service/sessiontypes/projects'
+
+ def test_get_endpoint_no_auth(self):
+ with self._flask_app.app_context():
+ response = self.test_client.get(self.test_endpoint)
+ self.assertEqual(401, response.status_code)
+
+ def test_post_endpoint_no_auth(self):
+ with self._flask_app.app_context():
+ response = self.test_client.post(self.test_endpoint, query_string=None, headers=None, json={})
+ self.assertEqual(401, response.status_code)
+
+ def test_delete_no_auth(self):
+ with self._flask_app.app_context():
+ response = self.test_client.delete(self.test_endpoint)
+ self.assertEqual(401, response.status_code)
+
+ def test_get_endpoint_invalid_token_auth(self):
+ with self._flask_app.app_context():
+ response = self._get_with_service_token_auth(self.test_client, token='invalid')
+ self.assertEqual(401, response.status_code)
+
+ def test_post_endpoint_invalid_token_auth(self):
+ with self._flask_app.app_context():
+ response = self._post_with_service_token_auth(self.test_client, token='invalid')
+ self.assertEqual(401, response.status_code)
+
+ def test_query_with_no_params(self):
+ with self._flask_app.app_context():
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token)
+ self.assertEqual(400, response.status_code)
+
+ def test_query_project(self):
+ with self._flask_app.app_context():
+ params = {'id_project': 10}
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token, params=params)
+ self.assertEqual(200, response.status_code)
+ self.assertTrue(response.is_json)
+ self.assertEqual(0, len(response.json), 0)
+
+ params = {'id_project': 1}
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token, params=params)
+ self.assertEqual(200, response.status_code)
+ self.assertTrue(response.is_json)
+ self.assertEqual(4, len(response.json))
+
+ for data_item in response.json:
+ self._checkJson(json_data=data_item)
+
+ def test_query_session_type(self):
+ with self._flask_app.app_context():
+ params = {'id_session_type': 30} # Invalid
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token, params=params)
+ self.assertEqual(200, response.status_code)
+ self.assertTrue(response.is_json)
+ self.assertEqual(0, len(response.json))
+
+ params = {'id_session_type': 1}
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token, params=params)
+ self.assertEqual(200, response.status_code)
+ self.assertTrue(response.is_json)
+ self.assertEqual(1, len(response.json))
+
+ for data_item in response.json:
+ self._checkJson(json_data=data_item)
+
+ def test_query_session_type_with_sites(self):
+ with self._flask_app.app_context():
+ params = {'id_session_type': 1, 'with_sites': 1}
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token, params=params)
+ self.assertEqual(200, response.status_code)
+ self.assertTrue(response.is_json)
+ self.assertEqual(1, len(response.json))
+
+ for data_item in response.json:
+ self._checkJson(json_data=data_item)
+ self.assertTrue('id_site' in data_item)
+ self.assertTrue('site_name' in data_item)
+
+ def test_post_session_type(self):
+ with self._flask_app.app_context():
+ new_st : TeraSessionType = TeraSessionType()
+ new_st.session_type_category = TeraSessionType.SessionCategoryEnum.SERVICE.value
+ new_st.session_type_name = "Session Type Test"
+ new_st.id_service = self.id_service
+ new_st.session_type_color = "#FF0000"
+ new_st.session_type_online = True
+ new_st.session_type_config = "config"
+ new_st.id_service = self.id_service
+ TeraSessionType.insert(new_st)
+
+ # New with minimal infos
+ json_data = {}
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token, json=json_data)
+ self.assertEqual(400, response.status_code, msg="Missing everything") # Missing
+
+ json_data = {'session_type': {}}
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token, json=json_data)
+ self.assertEqual(400, response.status_code, msg="Missing id_session_type")
+
+ json_data = {'session_type': {'id_session_type': 1}}
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token, json=json_data)
+ self.assertEqual(400, response.status_code, msg="Missing projects")
+
+ json_data = {'session_type': {'id_session_type': 33, 'projects': []}}
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token, json=json_data)
+ self.assertEqual(403, response.status_code, msg="No access to session type")
+
+ session_type: TeraSessionType = TeraSessionType.get_session_type_by_id(new_st.id_session_type)
+ self.assertEqual(0, len(session_type.session_type_projects))
+
+ json_data = {'session_type': {'id_session_type': new_st.id_session_type, 'projects': [{'id_project': 1},
+ {'id_project': 2},
+ {'id_project': 3}]}}
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token, json=json_data)
+ self.assertEqual(403, response.status_code, msg="One project not part of site")
+
+ json_data = {'session_type': {'id_session_type': new_st.id_session_type, 'projects': [{'id_project': 3}]}}
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token, json=json_data)
+ self.assertEqual(403, response.status_code, msg="No access to project")
+
+ json_data = {'session_type': {'id_session_type': new_st.id_session_type, 'projects': [{'id_project': 1},
+ {'id_project': 2}]}}
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token, json=json_data)
+ self.assertEqual(403, response.status_code, msg="At least one not associated to site")
+
+ sts : TeraSessionTypeSite = TeraSessionTypeSite()
+ sts.id_session_type = new_st.id_session_type
+ sts.id_site = 1
+ TeraSessionTypeSite.insert(sts)
+
+ json_data = {'session_type': {'id_session_type': new_st.id_session_type, 'projects': [{'id_project': 1}]}}
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token, json=json_data)
+ self.assertEqual(200, response.status_code, msg="OK now")
+
+ session_type: TeraSessionType = TeraSessionType.get_session_type_by_id(new_st.id_session_type)
+ self.assertEqual(1, len(session_type.session_type_projects))
+
+ json_data = {'session_type': {'id_session_type': new_st.id_session_type, 'projects': []}}
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token, json=json_data)
+ self.assertEqual(200, response.status_code, msg="Remove from all projects OK")
+
+ json_data = {'session_type': {'id_session_type': new_st.id_session_type, 'projects': [{'id_project': 1}]}}
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token, json=json_data)
+ self.assertEqual(200, response.status_code, msg="Remove one project")
+
+ self.assertIsNone(TeraSessionTypeProject.get_session_type_project_for_session_type_project(project_id=3,
+ session_type_id=
+ new_st.id_session_type))
+ self.assertIsNone(TeraSessionTypeProject.get_session_type_project_for_session_type_project(project_id=2,
+ session_type_id=
+ new_st.id_session_type))
+ self.assertIsNotNone(TeraSessionTypeProject.get_session_type_project_for_session_type_project(project_id=1,
+ session_type_id=
+ new_st.id_session_type))
+
+ TeraSessionType.delete(new_st.id_session_type)
+
+ def test_post_project(self):
+ with self._flask_app.app_context():
+ # Create test types and associate in the db for this test
+ json_session_type = {
+ 'session_type_name': 'Test Type',
+ 'id_service': 1,
+ 'session_type_color': '#FF0000',
+ 'session_type_online': True,
+ 'session_type_config': 'config',
+ 'session_type_category': TeraSessionType.SessionCategoryEnum.SERVICE.value
+ }
+
+ st1 = TeraSessionType()
+ st1.from_json(json_session_type)
+ TeraTestType.insert(st1)
+
+ st2 = TeraSessionType()
+ st2.from_json(json_session_type)
+ TeraSessionType.insert(st2)
+
+ sts1 = TeraSessionTypeSite()
+ sts1.id_session_type = st1.id_session_type
+ sts1.id_site = 1
+ TeraSessionTypeSite.insert(sts1)
+
+ sts2 = TeraSessionTypeSite()
+ sts2.id_session_type = st2.id_session_type
+ sts2.id_site = 1
+ TeraSessionTypeSite.insert(sts2)
+
+ stp1 = TeraSessionTypeProject()
+ stp1.id_session_type = st1.id_session_type
+ stp1.id_project = 2
+ TeraSessionTypeProject.insert(stp1)
+
+ stp2 = TeraSessionTypeProject()
+ stp2.id_session_type = st2.id_session_type
+ stp2.id_project = 2
+ TeraSessionTypeProject.insert(stp2)
+
+ # Project update
+ json_data = {}
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token, json=json_data)
+ self.assertEqual(400, response.status_code, msg="Missing project")
+
+ json_data = {'project': {}}
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token, json=json_data)
+ self.assertEqual(400, response.status_code, msg="Missing id_project")
+
+ json_data = {'project': {'id_project': 1}}
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token, json=json_data)
+ self.assertEqual(400, response.status_code, msg="Missing session types")
+
+ json_data = {'project': {'id_project': 2, 'sessions_types': []}}
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token, json=json_data)
+ self.assertEqual(403, response.status_code, msg="No access to project")
+
+ service_project: TeraServiceProject = TeraServiceProject()
+ service_project.id_service = self.id_service
+ service_project.id_project = 2
+ TeraServiceProject.insert(service_project)
+
+ json_data = {'project': {'id_project': 2, 'sessions_types': []}}
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token, json=json_data)
+ self.assertEqual(200, response.status_code, msg="Remove all test types OK")
+
+ project: TeraProject = TeraProject.get_project_by_id(2)
+ self.assertEqual(0, len(project.project_tests_types)) # Everything was deleted!
+
+ json_data = {'project': {'id_project': 2, 'sessions_types': [{'id_session_type': st1.id_session_type},
+ {'id_session_type': st2.id_session_type},
+ {'id_session_type': 3}]}}
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token, json=json_data)
+ self.assertEqual(200, response.status_code, msg="Test type not in site but added")
+
+ json_data = {'project': {'id_project': 2, 'sessions_types': [{'id_session_type': st1.id_session_type},
+ {'id_session_type': st2.id_session_type}]}}
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token, json=json_data)
+ self.assertEqual(200, response.status_code, msg="New test type association OK")
+
+ project: TeraProject = TeraProject.get_project_by_id(2)
+ self.assertEqual(2, len(project.project_session_types))
+
+ json_data = {'project': {'id_project': 2, 'sessions_types': [{'id_session_type': st1.id_session_type}]}}
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token, json=json_data)
+ self.assertEqual(200, response.status_code, msg="Remove 1 type")
+ self.assertIsNotNone(TeraSessionTypeProject.
+ get_session_type_project_for_session_type_project(project_id=2,
+ session_type_id=st1.id_session_type))
+ self.assertIsNone(TeraSessionTypeProject.
+ get_session_type_project_for_session_type_project(project_id=2,
+ session_type_id=st2.id_session_type))
+
+ project: TeraProject = TeraProject.get_project_by_id(2)
+ self.assertEqual(1, len(project.project_session_types))
+
+ # Delete all created for that test
+ TeraSessionTypeSite.delete(sts1.id_session_type_site)
+ TeraSessionTypeSite.delete(sts2.id_session_type_site)
+ TeraSessionType.delete(st1.id_session_type)
+ TeraSessionType.delete(st2.id_session_type)
+ TeraServiceProject.delete(service_project.id_service_project)
+
+ def test_post_session_type_project_and_delete(self):
+ with self._flask_app.app_context():
+ # TestType-Project update
+ json_data = {'sessions_types_projects': {}}
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token, json=json_data)
+ self.assertEqual(400, response.status_code, msg="Badly formatted request")
+
+ json_data = {'sessions_types_projects': {'id_project': 1}}
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token, json=json_data)
+ self.assertEqual(400, response.status_code, msg="Badly formatted request")
+
+ json_data = {'sessions_types_projects': {'id_project': 2, 'id_session_type': 1}}
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token, json=json_data)
+ self.assertEqual(403, response.status_code, msg="No access to project")
+
+ json_data = {'sessions_types_projects': {'id_project': 2, 'id_session_type': 1}}
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token, json=json_data)
+ self.assertEqual(403, response.status_code, msg="Service no access to project")
+
+ service_project: TeraServiceProject = TeraServiceProject()
+ service_project.id_service = self.id_service
+ service_project.id_project = 2
+ TeraServiceProject.insert(service_project)
+
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token, json=json_data)
+ self.assertEqual(200, response.status_code, msg="Add new association OK")
+
+ params = {'id_project': 2}
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token, params=params)
+ self.assertEqual(200, response.status_code)
+
+ current_id = None
+ for dp in response.json:
+ if dp['id_session_type'] == 1:
+ current_id = dp['id_session_type_project']
+ break
+ self.assertFalse(current_id is None)
+
+ # Create a test of that type for a session
+ project = TeraProject.get_project_by_id(2)
+ json_data = {
+ 'id_participant': 0,
+ 'id_project': project.id_project,
+ 'participant_name': 'Test Participant'
+ }
+
+ participant = TeraParticipant()
+ participant.from_json(json_data)
+ TeraParticipant.insert(participant)
+
+ json_session = {'id_session_type': 1,
+ 'session_name': 'Session',
+ 'session_start_datetime': datetime.datetime.now(),
+ 'session_status': 0,
+ 'id_creator_participant': participant.id_participant
+ }
+ session = TeraSession()
+ session.from_json(json_session)
+ TeraSession.insert(session)
+
+
+ ses_participant = TeraSessionParticipants()
+ ses_participant.id_participant = participant.id_participant
+ ses_participant.id_session = session.id_session
+ TeraSessionParticipants.insert(ses_participant)
+
+ response = self._delete_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'id': current_id})
+ self.assertEqual(500, response.status_code, msg="Has sessions of that type, can't delete")
+
+ TeraSession.delete(session.id_session)
+
+
+ response = self._delete_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'id': current_id})
+ self.assertEqual(200, response.status_code, msg="Delete OK")
+
+ TeraParticipant.delete(participant.id_participant)
+
+ params = {'id_project': 2}
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token, params=params)
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(TeraSessionTypeProject.query.filter_by(id_project=2).count(), len(response.json))
+
+ TeraServiceProject.delete(service_project.id_service_project)
+
+ def _checkJson(self, json_data, minimal=False):
+ self.assertGreater(len(json_data), 0)
+ self.assertTrue('id_session_type_project' in json_data)
+ self.assertTrue('id_session_type' in json_data)
+ self.assertTrue('id_project' in json_data)
+
+ if not minimal:
+ self.assertTrue('session_type_name' in json_data)
+ self.assertTrue('project_name' in json_data)
+ else:
+ self.assertFalse('session_type_name' in json_data)
+ self.assertFalse('project_name' in json_data)
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQuerySessionTypes.py b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQuerySessionTypes.py
index bf8725322..d8c50c74e 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQuerySessionTypes.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQuerySessionTypes.py
@@ -1,14 +1,18 @@
from typing import List
-from BaseServiceAPITest import BaseServiceAPITest
-from modules.FlaskModule.FlaskModule import flask_app
+from tests.modules.FlaskModule.API.service.BaseServiceAPITest import BaseServiceAPITest
from opentera.db.models.TeraSessionType import TeraSessionType
from opentera.db.models.TeraSessionTypeSite import TeraSessionTypeSite
from opentera.db.models.TeraSessionTypeProject import TeraSessionTypeProject
+from opentera.db.models.TeraSessionTypeServices import TeraSessionTypeServices
from opentera.db.models.TeraService import TeraService
from opentera.db.models.TeraParticipant import TeraParticipant
class ServiceQuerySessionTypesTest(BaseServiceAPITest):
+ """
+ Test Session Types for service API. We are testing with requests with a service token
+ related to the VideoRehabService as implemented in BaseServiceAPITest.
+ """
test_endpoint = '/api/service/sessiontypes'
def setUp(self):
@@ -88,7 +92,7 @@ def test_get_for_participant(self):
target_count = len(TeraSessionTypeProject.get_sessions_types_for_project(participant.id_project))
self.assertEqual(target_count, len(response.json))
- def test_get_specific(self):
+ def test_get_specific_forbidden(self):
with self._flask_app.app_context():
params = {'id_session_type': 4}
response = self._get_with_service_token_auth(client=self.test_client, token=self.service_token,
@@ -96,8 +100,224 @@ def test_get_specific(self):
self.assertEqual(200, response.status_code, msg='No access to session type')
self.assertEqual(0, len(response.json))
+ def test_get_specific_allowed_because_creator(self):
+ with self._flask_app.app_context():
params = {'id_session_type': 1}
response = self._get_with_service_token_auth(client=self.test_client, token=self.service_token,
params=params, endpoint=self.test_endpoint)
self.assertEqual(200, response.status_code, msg='Get OK')
self.assertEqual(1, len(response.json))
+
+ def test_get_specific_allowed_because_secondary(self):
+ with self._flask_app.app_context():
+ service: TeraService = TeraService.get_service_by_key('FileTransferService')
+ token = service.get_token(self.service_key)
+ for sts in TeraSessionTypeServices.get_sessions_types_for_service(service.id_service):
+ params = {'id_session_type': sts.id_session_type}
+ response = self._get_with_service_token_auth(client=self.test_client, token=token,
+ params=params, endpoint=self.test_endpoint)
+ self.assertEqual(200, response.status_code, msg='Get OK')
+ self.assertEqual(1, len(response.json))
+
+ def test_post_endpoint_no_auth(self):
+ with self._flask_app.app_context():
+ response = self.test_client.post(self.test_endpoint)
+ self.assertEqual(401, response.status_code)
+
+ def test_post_endpoint_with_token_auth_no_json(self):
+ with self._flask_app.app_context():
+ response = self._post_with_service_token_auth(client=self.test_client, token=self.service_token,
+ json=None, params=None, endpoint=self.test_endpoint)
+ self.assertEqual(415, response.status_code)
+
+ def test_post_endpoint_with_token_auth_invalid_schema(self):
+ with self._flask_app.app_context():
+ response = self._post_with_service_token_auth(client=self.test_client, token=self.service_token,
+ json={}, params=None, endpoint=self.test_endpoint)
+ self.assertEqual(400, response.status_code)
+
+ def test_post_endpoint_with_invalid_schema_without_session_type(self):
+ with self._flask_app.app_context():
+
+ json_data = {'service_session_type': {}} # Missing session_type
+ response = self._post_with_service_token_auth(client=self.test_client, token=self.service_token,
+ json=json_data, params=None, endpoint=self.test_endpoint)
+ self.assertEqual(400, response.status_code)
+
+ def test_post_endpoint_with_invalid_schema_without_session_type_id_session_type(self):
+ with self._flask_app.app_context():
+
+ json_data = {'service_session_type': {'session_type': {}}} # Missing id_session_type
+ response = self._post_with_service_token_auth(client=self.test_client, token=self.service_token,
+ json=json_data, params=None, endpoint=self.test_endpoint)
+ self.assertEqual(400, response.status_code)
+
+ def test_post_endpoint_with_invalid_schema_with_invalid_keys(self):
+ with self._flask_app.app_context():
+
+ json_data = {'service_session_type': {'session_type': {'id_session_type': 0,
+ 'invalid_key': 'invalid'}}}
+ response = self._post_with_service_token_auth(client=self.test_client, token=self.service_token,
+ json=json_data, params=None, endpoint=self.test_endpoint)
+ self.assertEqual(400, response.status_code)
+
+ def test_post_endpoint_with_valid_schema_create_session_type_missing_fields(self):
+ with self._flask_app.app_context():
+ # session_type = TeraSessionType.get_session_type_by_id(1)
+ json_data = {'service_session_type': {'session_type': {'id_session_type': 0}}}
+ response = self._post_with_service_token_auth(client=self.test_client, token=self.service_token,
+ json=json_data, params=None, endpoint=self.test_endpoint)
+ self.assertEqual(400, response.status_code)
+
+
+ def test_post_endpoint_with_valid_schema_create_session_type_with_inaccessible_sites(self):
+ with self._flask_app.app_context():
+
+ json_data = {'service_session_type': {
+ 'id_sites': [10],
+ 'session_type': {
+ 'id_session_type': 0,
+ 'session_type_name': 'Test Session Type',
+ 'session_type_online': True,
+ 'session_type_config': '',
+ 'session_type_color': '#FF0000',
+ 'session_type_category': 1, #Service
+ }
+ }
+ }
+ response = self._post_with_service_token_auth(client=self.test_client, token=self.service_token,
+ json=json_data, params=None, endpoint=self.test_endpoint)
+ self.assertEqual(403, response.status_code)
+
+ def test_post_endpoint_with_valid_schema_create_session_type_with_inaccessible_projects(self):
+ with self._flask_app.app_context():
+
+ json_data = {'service_session_type': {
+ 'id_projects': [10],
+ 'session_type': {
+ 'id_session_type': 0,
+ 'session_type_name': 'Test Session Type',
+ 'session_type_online': True,
+ 'session_type_config': '',
+ 'session_type_color': '#FF0000',
+ 'session_type_category': 1, #Service
+ }
+ }
+ }
+ response = self._post_with_service_token_auth(client=self.test_client, token=self.service_token,
+ json=json_data, params=None, endpoint=self.test_endpoint)
+ self.assertEqual(403, response.status_code)
+
+ def test_post_endpoint_with_valid_schema_create_session_type_with_inaccessible_participants(self):
+ with self._flask_app.app_context():
+
+ json_data = {'service_session_type': {
+ 'id_participants': [10],
+ 'session_type': {
+ 'id_session_type': 0,
+ 'session_type_name': 'Test Session Type',
+ 'session_type_online': True,
+ 'session_type_config': '',
+ 'session_type_color': '#FF0000',
+ 'session_type_category': 1, #Service
+ }
+ }
+ }
+ response = self._post_with_service_token_auth(client=self.test_client, token=self.service_token,
+ json=json_data, params=None, endpoint=self.test_endpoint)
+ self.assertEqual(403, response.status_code)
+
+ def test_post_endpoint_with_valid_schema_new_session_and_valid_sites(self):
+ with self._flask_app.app_context():
+
+ json_data = {'service_session_type': {
+ 'id_sites': [1],
+ 'session_type': {
+ 'id_session_type': 0,
+ 'session_type_name': 'Test Session Type',
+ 'session_type_online': True,
+ 'session_type_config': '',
+ 'session_type_color': '#FF0000',
+ 'session_type_category': 1, #Service
+ }
+ }
+ }
+ response = self._post_with_service_token_auth(client=self.test_client, token=self.service_token,
+ json=json_data, params=None, endpoint=self.test_endpoint)
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('id_session_type' in response.json)
+ id_session_type = response.json['id_session_type']
+
+ # Check if session type was created
+ session_type = TeraSessionType.get_session_type_by_id(id_session_type)
+ self.assertIsNotNone(session_type)
+
+ # Check if session type was linked to site
+ sites = TeraSessionTypeSite.get_sites_for_session_type(id_session_type)
+ self.assertIsNotNone(sites)
+ self.assertEqual(1, len(sites))
+ self.assertEqual(sites[0].id_site, 1)
+
+
+ def test_post_endpoint_with_valid_schema_new_session_and_valid_projects(self):
+ with self._flask_app.app_context():
+
+ json_data = {'service_session_type': {
+ 'id_projects': [1],
+ 'session_type': {
+ 'id_session_type': 0,
+ 'session_type_name': 'Test Session Type',
+ 'session_type_online': True,
+ 'session_type_config': '',
+ 'session_type_color': '#FF0000',
+ 'session_type_category': 1, #Service
+ }
+ }
+ }
+ response = self._post_with_service_token_auth(client=self.test_client, token=self.service_token,
+ json=json_data, params=None, endpoint=self.test_endpoint)
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('id_session_type' in response.json)
+ id_session_type = response.json['id_session_type']
+
+ # Check if session type was created
+ session_type = TeraSessionType.get_session_type_by_id(id_session_type)
+ self.assertIsNotNone(session_type)
+
+ # Check if session type was linked to project
+ projects = TeraSessionTypeProject.get_projects_for_session_type(id_session_type)
+ self.assertIsNotNone(projects)
+ self.assertEqual(1, len(projects))
+ self.assertEqual(projects[0].id_project, 1)
+
+ def test_post_endpoint_with_valid_schema_new_session_and_valid_participants(self):
+ with self._flask_app.app_context():
+
+ json_data = {'service_session_type': {
+ 'id_participants': [1],
+ 'session_type': {
+ 'id_session_type': 0,
+ 'session_type_name': 'Test Session Type',
+ 'session_type_online': True,
+ 'session_type_config': '',
+ 'session_type_color': '#FF0000',
+ 'session_type_category': 1, #Service
+ }
+ }
+ }
+ response = self._post_with_service_token_auth(client=self.test_client, token=self.service_token,
+ json=json_data, params=None, endpoint=self.test_endpoint)
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('id_session_type' in response.json)
+ id_session_type = response.json['id_session_type']
+
+ # Check if session type was created
+ session_type = TeraSessionType.get_session_type_by_id(id_session_type)
+ self.assertIsNotNone(session_type)
+
+ # Check if session type was linked to project
+ participant = TeraParticipant.get_participant_by_id(1)
+ projects = TeraSessionTypeProject.get_projects_for_session_type(participant.id_project)
+ self.assertIsNotNone(projects)
+ self.assertEqual(1, len(projects))
+ self.assertEqual(projects[0].id_project, participant.id_project)
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQuerySessions.py b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQuerySessions.py
index 567d36753..2287f5703 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQuerySessions.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQuerySessions.py
@@ -1,4 +1,4 @@
-from BaseServiceAPITest import BaseServiceAPITest
+from tests.modules.FlaskModule.API.service.BaseServiceAPITest import BaseServiceAPITest
from modules.FlaskModule.FlaskModule import flask_app
from datetime import datetime, timedelta
from opentera.db.models.TeraSession import TeraSession
@@ -38,7 +38,7 @@ def test_get_endpoint_query_no_params(self):
def test_get_endpoint_query_list(self):
with self._flask_app.app_context():
- params = {'id_session': 1, 'list': 1}
+ params = {'id_session': 2, 'list': 1}
response = self._get_with_service_token_auth(client=self.test_client, token=self.service_token,
params=params, endpoint=self.test_endpoint)
@@ -51,7 +51,7 @@ def test_get_endpoint_query_list(self):
def test_get_endpoint_query_specific(self):
with self._flask_app.app_context():
- params = {'id_session': 1}
+ params = {'id_session': 2}
response = self._get_with_service_token_auth(client=self.test_client, token=self.service_token,
params=params, endpoint=self.test_endpoint)
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQuerySiteProjectAccessRoles.py b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQuerySiteProjectAccessRoles.py
index 642b6dabe..878dd73fa 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQuerySiteProjectAccessRoles.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQuerySiteProjectAccessRoles.py
@@ -1,11 +1,11 @@
from typing import List
-from BaseServiceAPITest import BaseServiceAPITest
-from modules.FlaskModule.FlaskModule import flask_app
+from tests.modules.FlaskModule.API.service.BaseServiceAPITest import BaseServiceAPITest
from opentera.db.models.TeraUser import TeraUser
from opentera.db.models.TeraSite import TeraSite
from opentera.db.models.TeraProject import TeraProject
-
+from opentera.db.models.TeraServiceProject import TeraServiceProject
+from opentera.db.models.TeraServiceSite import TeraServiceSite
class ServiceQuerySiteProjectAccessRolesTest(BaseServiceAPITest):
test_endpoint = '/api/service/users/access'
@@ -50,7 +50,8 @@ def test_get_endpoint_with_token_auth_and_site_admin(self):
with self._flask_app.app_context():
user: TeraUser = TeraUser.get_user_by_username('admin')
self.assertIsNotNone(user)
- sites: List[TeraSite] = TeraSite.query.all()
+ #sites: List[TeraSite] = TeraSite.query.all()
+ sites: List[TeraSite] = TeraServiceSite.get_sites_for_service(self.id_service)
for site in sites:
params = {
'uuid_user': user.user_uuid,
@@ -65,7 +66,8 @@ def test_get_endpoint_with_token_auth_and_project_admin(self):
with self._flask_app.app_context():
user: TeraUser = TeraUser.get_user_by_username('admin')
self.assertIsNotNone(user)
- projects: List[TeraProject] = TeraProject.query.all()
+ # projects: List[TeraProject] = TeraProject.query.all()
+ projects: List[TeraProject] = TeraServiceProject.get_projects_for_service(self.id_service)
for project in projects:
params = {
'uuid_user': user.user_uuid,
@@ -76,6 +78,31 @@ def test_get_endpoint_with_token_auth_and_project_admin(self):
self.assertEqual(200, response.status_code)
self.assertEqual({'project_role': 'admin'}, response.json)
+ # def test_get_endpoint_with_token_auth_and_project_admin(self):
+ # with self._flask_app.app_context():
+ # # projects: List[TeraProject] = TeraProject.query.all()
+ # projects: List[TeraProject] = TeraServiceProject.get_projects_for_service(self.id_service)
+ # self.assertIsNotNone(projects)
+ # user: TeraUser = TeraUser.get_user_by_username('user')
+ # self.assertIsNotNone(user)
+ # for project in projects:
+ # users = project.service_project_project.get_users_in_project()
+ # if user.id_user not in [p_user.id_user for p_user in users]:
+ # continue
+ # all_roles = user.get_projects_roles(TeraService.get_openteraserver_service().id_service)
+ # roles = [project_role.project_role for project_role in user.get_projects_roles(self.id_service)
+ # if project_role.key == project.id_project]
+ # if not roles:
+ # continue
+ # params = {
+ # 'uuid_user': user.user_uuid,
+ # 'id_project': project.id_project
+ # }
+ # response = self._get_with_service_token_auth(client=self.test_client, token=self.service_token,
+ # params=params, endpoint=self.test_endpoint)
+ # self.assertEqual(200, response.status_code)
+ # self.assertEqual({'project_role': roles[0]}, response.json)
+
def test_get_endpoint_with_token_auth_and_site_siteadmin(self):
with self._flask_app.app_context():
user: TeraUser = TeraUser.get_user_by_username('siteadmin')
@@ -102,7 +129,8 @@ def test_get_endpoint_with_token_auth_and_project_siteadmin(self):
with self._flask_app.app_context():
user: TeraUser = TeraUser.get_user_by_username('siteadmin')
self.assertIsNotNone(user)
- projects: List[TeraProject] = TeraProject.query.all()
+ # projects: List[TeraProject] = TeraProject.query.all()
+ projects: List[TeraProject] = TeraServiceProject.get_projects_for_service(self.id_service)
from modules.DatabaseModule.DBManager import DBManager
user_access = DBManager.userAccess(user)
@@ -154,7 +182,8 @@ def test_get_endpoint_with_token_auth_and_project_user(self):
for user_name in user_names:
user: TeraUser = TeraUser.get_user_by_username(user_name)
self.assertIsNotNone(user)
- projects: List[TeraProject] = TeraProject.query.all()
+ # projects: List[TeraProject] = TeraProject.query.all()
+ projects: List[TeraProject] = TeraServiceProject.get_projects_for_service(self.id_service)
from modules.DatabaseModule.DBManager import DBManager
user_access = DBManager.userAccess(user)
@@ -172,4 +201,4 @@ def test_get_endpoint_with_token_auth_and_project_user(self):
role = user_access.get_user_project_role(user.id_user, project.id_project)
self.assertEqual({'project_role': role['project_role']}, response.json)
else:
- self.assertEqual({'project_role': None}, response.json)
\ No newline at end of file
+ self.assertEqual({'project_role': None}, response.json)
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQuerySites.py b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQuerySites.py
index 364175613..85e6a2260 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQuerySites.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQuerySites.py
@@ -1,6 +1,6 @@
from typing import List
-from BaseServiceAPITest import BaseServiceAPITest
+from tests.modules.FlaskModule.API.service.BaseServiceAPITest import BaseServiceAPITest
from modules.FlaskModule.FlaskModule import flask_app
from opentera.db.models.TeraSite import TeraSite
from opentera.db.models.TeraUser import TeraUser
@@ -29,7 +29,7 @@ def test_get_endpoint_with_token_auth_no_params(self):
service: TeraService = TeraService.get_service_by_uuid(self.service_uuid)
from modules.DatabaseModule.DBManager import DBManager
service_access = DBManager.serviceAccess(service)
- accessible_sites = service_access.get_accessibles_sites_ids()
+ accessible_sites = service_access.get_accessible_sites_ids()
for site_json in response.json:
id_site = site_json['id_site']
if id_site in accessible_sites:
@@ -50,7 +50,7 @@ def test_get_endpoint_with_token_auth_and_id_site(self):
service: TeraService = TeraService.get_service_by_uuid(self.service_uuid)
from modules.DatabaseModule.DBManager import DBManager
service_access = DBManager.serviceAccess(service)
- accessible_sites = service_access.get_accessibles_sites_ids()
+ accessible_sites = service_access.get_accessible_sites_ids()
params = {'id_site': site.id_site}
response = self._get_with_service_token_auth(client=self.test_client, token=self.service_token,
@@ -72,7 +72,7 @@ def test_get_endpoint_with_token_auth_and_id_site(self):
service: TeraService = TeraService.get_service_by_uuid(self.service_uuid)
from modules.DatabaseModule.DBManager import DBManager
service_access = DBManager.serviceAccess(service)
- accessible_sites = service_access.get_accessibles_sites_ids()
+ accessible_sites = service_access.get_accessible_sites_ids()
params = {'id_user': user.id_user}
response = self._get_with_service_token_auth(client=self.test_client, token=self.service_token,
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryTestTypeProjects.py b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryTestTypeProjects.py
index 3e9a20122..694b566c5 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryTestTypeProjects.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryTestTypeProjects.py
@@ -1,4 +1,4 @@
-from BaseServiceAPITest import BaseServiceAPITest
+from tests.modules.FlaskModule.API.service.BaseServiceAPITest import BaseServiceAPITest
from opentera.db.models.TeraTestTypeProject import TeraTestTypeProject
from opentera.db.models.TeraTest import TeraTest
from opentera.db.models.TeraSession import TeraSession
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryTestTypes.py b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryTestTypes.py
index e7d631693..ddefc6f56 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryTestTypes.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryTestTypes.py
@@ -1,5 +1,5 @@
from typing import List
-from BaseServiceAPITest import BaseServiceAPITest
+from tests.modules.FlaskModule.API.service.BaseServiceAPITest import BaseServiceAPITest
from opentera.db.models.TeraTestType import TeraTestType
from opentera.db.models.TeraTestTypeSite import TeraTestTypeSite
from opentera.db.models.TeraTestTypeProject import TeraTestTypeProject
@@ -114,6 +114,36 @@ def test_get_endpoint_with_token_auth_for_id(self):
self.assertTrue(response.json[0]['id_test_type'] in accessible_types)
self.assertEqual(response.json[0]['id_test_type'], 1)
+ def test_get_endpoint_with_token_auth_for_uuid(self):
+ with self._flask_app.app_context():
+
+ all_test_types = TeraTestType.query.all()
+ service: TeraService = TeraService.get_service_by_uuid(self.service_uuid)
+ for test_type in all_test_types:
+ response = self._get_with_service_token_auth(client=self.test_client, token=self.service_token,
+ params={'test_type_uuid': test_type.test_type_uuid},
+ endpoint=self.test_endpoint)
+ self.assertEqual(200, response.status_code)
+
+ # Check if test type is accessible
+ from modules.DatabaseModule.DBManager import DBManager
+ service_access = DBManager.serviceAccess(service)
+ accessible_types = service_access.get_accessible_tests_types_ids()
+ if test_type.id_test_type in accessible_types:
+ self.assertEqual(1, len(response.json))
+ self.assertEqual(response.json[0]['id_test_type'], test_type.id_test_type)
+ self.assertEqual(response.json[0]['test_type_uuid'], test_type.test_type_uuid)
+
+
+ def test_get_endpoint_with_token_auth_for_invalid_uuid(self):
+ with self._flask_app.app_context():
+ response = self._get_with_service_token_auth(client=self.test_client, token=self.service_token,
+ params={'test_type_uuid': '0000000000000000'},
+ endpoint=self.test_endpoint)
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+
+
def test_get_endpoint_with_token_auth_for_key(self):
with self._flask_app.app_context():
response = self._get_with_service_token_auth(client=self.test_client, token=self.service_token,
@@ -201,6 +231,28 @@ def test_post_and_delete(self):
params={'uuid': current_uuid})
self.assertEqual(200, response.status_code, msg="Delete OK")
+ def test_post_with_uuid(self):
+ with self._flask_app.app_context():
+ json_data = {
+ 'test_type': {
+ 'test_type_name': 'Test',
+ 'id_test_type': 0
+ }
+ }
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token, json=json_data)
+ self.assertEqual(200, response.status_code, msg="Created new test")
+
+ test_type_uuid = response.json['test_type_uuid']
+
+ json_data['test_type'].pop('id_test_type')
+ json_data['test_type']['test_type_uuid'] = test_type_uuid
+ json_data['test_type']['test_type_name'] = "Test 2"
+
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token, json=json_data)
+ self.assertEqual(200, response.status_code, msg="Updated test with uuid")
+
+ TeraTestType.delete(response.json['id_test_type'])
+
def _checkJson(self, json_data, minimal=False):
self.assertGreater(len(json_data), 0)
self.assertTrue(json_data.__contains__('id_test_type'))
@@ -214,4 +266,4 @@ def _checkJson(self, json_data, minimal=False):
if not minimal:
self.assertTrue(json_data.__contains__('test_type_description'))
self.assertTrue(json_data.__contains__('test_type_service_key'))
- self.assertTrue(json_data.__contains__('test_type_service_uuid'))
\ No newline at end of file
+ self.assertTrue(json_data.__contains__('test_type_service_uuid'))
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryTests.py b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryTests.py
index fdd4e1412..e23b08c32 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryTests.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryTests.py
@@ -1,4 +1,4 @@
-from BaseServiceAPITest import BaseServiceAPITest
+from tests.modules.FlaskModule.API.service.BaseServiceAPITest import BaseServiceAPITest
from modules.FlaskModule.FlaskModule import flask_app
from datetime import datetime
@@ -189,7 +189,7 @@ def test_post_endpoint_with_update_and_delete(self):
json=json_data, endpoint=self.test_endpoint)
self.assertEqual(400, response.status_code, msg="Missing id_session")
- json_data['test']['id_session'] = 1
+ json_data['test']['id_session'] = 2
# response = self._post_with_service_token_auth(client=self.test_client, token=self.service_token,
# json=json_data, endpoint=self.test_endpoint)
# self.assertEqual(403, response.status_code, msg="Test type not accessible to this service")
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryTestsInvitations.py b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryTestsInvitations.py
new file mode 100644
index 000000000..ce5ef0152
--- /dev/null
+++ b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryTestsInvitations.py
@@ -0,0 +1,785 @@
+from datetime import datetime, timedelta
+import uuid
+
+from modules.DatabaseModule.DBManagerTeraServiceAccess import DBManagerTeraServiceAccess
+from tests.modules.FlaskModule.API.service.BaseServiceAPITest import BaseServiceAPITest
+from opentera.db.models.TeraTestInvitation import TeraTestInvitation
+from opentera.db.models.TeraParticipant import TeraParticipant
+from opentera.db.models.TeraUser import TeraUser
+from opentera.db.models.TeraDevice import TeraDevice
+from opentera.db.models.TeraTestType import TeraTestType
+from opentera.db.models.TeraSession import TeraSession
+from opentera.db.models.TeraService import TeraService
+
+class ServiceQueryTestsInvitationsTest(BaseServiceAPITest):
+ """
+ Test ServiceQueryTestsInvitations API
+ """
+ test_endpoint = '/api/service/tests/invitations'
+
+ def tearDown(self):
+ # Will delete all invitations after each test
+ self._delete_all_invitations()
+ super().tearDown()
+
+ def test_no_auth(self):
+ """
+ Test that a get request with no auth returns 401
+ """
+ with self._flask_app.app_context():
+ response = self.test_client.get(self.test_endpoint)
+ self.assertEqual(401, response.status_code)
+
+ def test_post_no_auth(self):
+ """
+ Test that a post request with no auth returns 401
+ """
+ with self._flask_app.app_context():
+ response = self.test_client.post(self.test_endpoint)
+ self.assertEqual(401, response.status_code)
+
+ def test_delete_no_auth(self):
+ """
+ Test that a delete request with no auth returns 401
+ """
+ with self._flask_app.app_context():
+ response = self.test_client.delete(self.test_endpoint)
+ self.assertEqual(401, response.status_code)
+
+ def test_get_endpoint_invalid_token_auth(self):
+ """
+ Test that a get request with invalid token returns 401
+ """
+ with self._flask_app.app_context():
+ response = self._get_with_service_token_auth(self.test_client, token='invalid')
+ self.assertEqual(401, response.status_code)
+
+ def test_post_endpoint_invalid_token_auth(self):
+ """
+ Test that a post request with invalid token returns 401
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_service_token_auth(self.test_client, token='invalid')
+ self.assertEqual(401, response.status_code)
+
+ def test_delete_endpoint_invalid_token_auth(self):
+ """
+ Test that a delete request with invalid token returns 401
+ """
+ with self._flask_app.app_context():
+ response = self._delete_with_service_token_auth(self.test_client, token='invalid')
+ self.assertEqual(401, response.status_code)
+
+ def test_get_query_bad_params_with_token(self):
+ """
+ Bad params should return 400
+ """
+ with self._flask_app.app_context():
+ params = {'id_invalid': 1}
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token,
+ params=params)
+ self.assertEqual(400, response.status_code)
+
+ def test_get_query_no_params_with_token_returns_all_accessible_invitations(self):
+ """
+ Test that an admin can access all invitations
+ """
+ with self._flask_app.app_context():
+ create_count = 10
+
+ # Create Test type for service
+ test_type_accessible = self._create_test_type_for_service(self.id_service)
+ test_type_not_accessible = self._create_test_type_for_service(1)
+
+ # Create invitations
+ self._create_invitations(create_count, id_test_type=test_type_accessible.id_test_type, id_project=1,
+ id_user=1)
+ self._create_invitations(create_count, id_test_type=test_type_not_accessible.id_test_type, id_project=1,
+ id_user=1)
+
+ # Service should access only invitations of its test type
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token)
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+
+ for json_invitation in response.json:
+ self._validate_json(json_invitation)
+
+ TeraTestType.delete(test_type_accessible.id_test_type)
+ TeraTestType.delete(test_type_not_accessible.id_test_type)
+
+ def test_get_query_no_params_with_token_returns_all_accessible_invitations_with_full(self):
+ """
+ Test that an admin can access all invitations
+ """
+ with self._flask_app.app_context():
+ create_count = 10
+
+ # Create Test type for service
+ test_type_accessible = self._create_test_type_for_service(self.id_service)
+ test_type_not_accessible = self._create_test_type_for_service(1)
+
+ # Create invitations
+ self._create_invitations(create_count, id_test_type=test_type_accessible.id_test_type, id_project=1,
+ id_user=1)
+ self._create_invitations(create_count, id_test_type=test_type_not_accessible.id_test_type, id_project=1,
+ id_user=1)
+
+ # Service should access only invitations of its test type
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token, params={'full': True})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+
+ for json_invitation in response.json:
+ self._validate_json(json_invitation, full=True)
+
+ TeraTestType.delete(test_type_accessible.id_test_type)
+ TeraTestType.delete(test_type_not_accessible.id_test_type)
+
+ def test_get_query_no_params_with_token_returns_all_accessible_invitations_with_urls(self):
+ """
+ Test that an admin can access all invitations
+ """
+ with self._flask_app.app_context():
+ create_count = 10
+
+ # Create Test type for service
+ test_type_accessible = self._create_test_type_for_service(self.id_service)
+ test_type_not_accessible = self._create_test_type_for_service(1)
+
+ # Create invitations
+ self._create_invitations(create_count, id_test_type=test_type_accessible.id_test_type, id_project=1,
+ id_user=1)
+ self._create_invitations(create_count, id_test_type=test_type_not_accessible.id_test_type, id_project=1,
+ id_user=1)
+
+ # Service should access only invitations of its test type
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token, params={'with_urls': True})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+
+ for json_invitation in response.json:
+ self._validate_json(json_invitation, with_urls=True)
+
+ TeraTestType.delete(test_type_accessible.id_test_type)
+ TeraTestType.delete(test_type_not_accessible.id_test_type)
+
+ def test_get_query_with_id_test_invitation_or_key_with_service_token(self):
+ """
+ Test that an service can access an invitation with id_test_invitation or test_invitation_key
+ """
+ with self._flask_app.app_context():
+ create_count = 10
+
+ # Create invitations
+ test_type_accessible = self._create_test_type_for_service(self.id_service)
+ invitations = self._create_invitations(create_count, id_test_type=test_type_accessible.id_test_type,
+ id_project=1, id_user=1)
+
+ # Service should access all invitations
+ for invitation in invitations:
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'id_test_invitation': invitation.id_test_invitation})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(1, len(response.json))
+ for json_invitation in response.json:
+ self._validate_json(json_invitation)
+
+ # Also test with test_invitation_key
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'test_invitation_key': invitation.test_invitation_key})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(1, len(response.json))
+ for json_invitation in response.json:
+ self._validate_json(json_invitation)
+
+ # Also test with invalid id_test_invitation
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'id_test_invitation': 0})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+
+ # Also test with invalid test_invitation_key
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'test_invitation_key': 'invalid'})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+
+ TeraTestType.delete(test_type_accessible.id_test_type)
+
+ def test_get_query_with_id_user_as_admin_returns_only_invitation_for_specific_id_user(self):
+ """
+ Test that an service can access only invitations for a specific user
+ """
+ with self._flask_app.app_context():
+ create_count = 10
+
+ # Create 10 invitations
+ test_type_accessible = self._create_test_type_for_service(self.id_service)
+ self._create_invitations(create_count, id_test_type=test_type_accessible.id_test_type, id_project=1,
+ id_user=1)
+ self._create_invitations(create_count, id_test_type=test_type_accessible.id_test_type, id_project=2,
+ id_user=2)
+
+ # Service should access only invitations of id_user=1
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'id_user': 1})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+ for json_invitation in response.json:
+ self._validate_json(json_invitation)
+
+ # Also test with user_uuid
+ user : TeraUser = TeraUser.get_user_by_id(1)
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'user_uuid': user.user_uuid})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+ for json_invitation in response.json:
+ self._validate_json(json_invitation)
+
+ # Also test invalid id_user
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'id_user': 0})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+
+ TeraTestType.delete(test_type_accessible.id_test_type)
+
+ def test_get_query_with_id_participant_with_service_token_returns_only_invitation_for_specific_id_participant(self):
+ """
+ Test that service can access only invitations for a specific participant
+ """
+ with self._flask_app.app_context():
+ create_count = 10
+ # Create 10 invitations
+ test_type_accessible = self._create_test_type_for_service(self.id_service)
+ self._create_invitations(create_count, id_test_type=test_type_accessible.id_test_type, id_project=1,
+ id_participant=1)
+ self._create_invitations(create_count, id_test_type=test_type_accessible.id_test_type, id_project=1,
+ id_participant=2)
+
+ # Service should access all invitations
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'id_participant': 1})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+ for json_invitation in response.json:
+ self._validate_json(json_invitation)
+
+ # Also test with participant_uuid
+ participant : TeraParticipant = TeraParticipant.get_participant_by_id(1)
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'participant_uuid': participant.participant_uuid})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+
+
+ # Also test invalid id_participant
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'id_participant': 0})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+ TeraTestType.delete(test_type_accessible.id_test_type)
+
+
+ def test_get_query_with_id_device_with_service_token_returns_only_invitation_for_specific_id_device(self):
+ """
+ Test that service can access only invitations for a specific device
+ """
+ with self._flask_app.app_context():
+ create_count = 10
+ # Create 10 invitations
+ test_type_accessible = self._create_test_type_for_service(self.id_service)
+ self._create_invitations(create_count, id_test_type=test_type_accessible.id_test_type, id_project=1,
+ id_device=1)
+ self._create_invitations(create_count, id_test_type=test_type_accessible.id_test_type, id_project=1,
+ id_device=2)
+
+ # Service should access all invitations
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'id_device': 1})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+ for json_invitation in response.json:
+ self._validate_json(json_invitation)
+
+ # Also test with device_uuid
+ device : TeraDevice = TeraDevice.get_device_by_id(1)
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'device_uuid': device.device_uuid})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+ for json_invitation in response.json:
+ self._validate_json(json_invitation)
+
+ # Also test invalid id_device
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'id_device': 0})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+ TeraTestType.delete(test_type_accessible.id_test_type)
+
+ def test_get_query_with_id_test_type_with_service_token_returns_only_invitation_for_specific_id_test_type(self):
+ """
+ Test that a service can access only invitations for a specific test type
+ """
+ with self._flask_app.app_context():
+ create_count = 10
+ # Create 10 invitations
+ test_type_accessible1 = self._create_test_type_for_service(self.id_service)
+ test_type_accessible2 = self._create_test_type_for_service(self.id_service)
+ self._create_invitations(create_count, id_test_type=test_type_accessible1.id_test_type, id_project=1,
+ id_user=1)
+ self._create_invitations(create_count, id_test_type=test_type_accessible2.id_test_type, id_project=1,
+ id_user=1)
+
+ # Service should access all invitations
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'id_test_type': test_type_accessible1.id_test_type})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+ for json_invitation in response.json:
+ self._validate_json(json_invitation)
+
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'id_test_type': test_type_accessible2.id_test_type})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+ for json_invitation in response.json:
+ self._validate_json(json_invitation)
+
+ # Also test with test_type_uuid
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'test_type_uuid': test_type_accessible1.test_type_uuid})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+ for json_invitation in response.json:
+ self._validate_json(json_invitation)
+
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'test_type_uuid': test_type_accessible2.test_type_uuid})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+
+
+ TeraTestType.delete(test_type_accessible1.id_test_type)
+ TeraTestType.delete(test_type_accessible2.id_test_type)
+
+
+
+ def test_get_query_with_id_session_with_service_token_returns_only_invitation_for_specific_id_session(self):
+ """
+ Test that service can access only invitations for a specific session
+ """
+ with self._flask_app.app_context():
+ create_count = 10
+ # Create 10 invitations
+ test_type_accessible = self._create_test_type_for_service(self.id_service)
+ self._create_invitations(create_count, id_test_type=test_type_accessible.id_test_type, id_session=1,
+ id_project=1, id_user=1)
+ self._create_invitations(create_count, id_test_type=test_type_accessible.id_test_type, id_session=2,
+ id_project=1, id_user=1)
+
+ # Service should access all invitations
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'id_session': 1})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+ for json_invitation in response.json:
+ self._validate_json(json_invitation)
+
+ # Test with session_uuid
+ session : TeraSession = TeraSession.get_session_by_id(1)
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'session_uuid': session.session_uuid})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+ for json_invitation in response.json:
+ self._validate_json(json_invitation)
+
+
+ # Service should access all invitations
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'id_session': 2})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+
+ # Test with session_uuid
+ session : TeraSession = TeraSession.get_session_by_id(2)
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'session_uuid': session.session_uuid})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+ for json_invitation in response.json:
+ self._validate_json(json_invitation)
+
+
+ # Also test invalid id_session
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'id_session': 0})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+ TeraTestType.delete(test_type_accessible.id_test_type)
+
+ def test_get_query_with_service_token_with_invalid_id_project(self):
+ """
+ Test that a get request with invalid id_project returns empty list
+ """
+ with self._flask_app.app_context():
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'id_project': 0})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+
+ def test_get_query_with_service_token_with_valid_id_project(self):
+ """
+ Test that a get request with invalid id_project returns empty list
+ """
+ with self._flask_app.app_context():
+ create_count = 10
+ test_type_accessible = self._create_test_type_for_service(self.id_service)
+ self._create_invitations(create_count, id_test_type=test_type_accessible.id_test_type, id_session=1,
+ id_project=1, id_participant=1)
+ self._create_invitations(create_count, id_test_type=test_type_accessible.id_test_type, id_session=2,
+ id_project=1, id_participant=2)
+
+ response = self._get_with_service_token_auth(self.test_client, token=self.service_token,
+ params={'id_project': 1})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(2 * create_count, len(response.json))
+
+ def test_post_query_with_service_token_with_invalid_schema(self):
+ """
+ Test that a post request with invalid schema returns 400
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_service_token_auth(self.test_client,
+ token=self.service_token,
+ json={})
+ self.assertEqual(400, response.status_code)
+
+ def test_post_query_with_service_token_with_valid_schema_but_empty_array(self):
+ """
+ Test that a post request with valid schema but empty array returns 400
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_service_token_auth(self.test_client,
+ token=self.service_token,
+ json=[])
+ self.assertEqual(400, response.status_code)
+
+ def test_post_query_with_service_token_with_invalid_array_item_schema(self):
+ """
+ Test that a post request with invalid array item schema returns 400
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_service_token_auth(self.test_client,
+ token=self.service_token,
+ json=[{'invalid': 'invalid'}])
+ self.assertEqual(400, response.status_code)
+
+ def test_post_query_with_service_token_with_valid_schema_but_empty_array_item(self):
+ """
+ Test that a post request with valid schema but empty array item returns 400
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_service_token_auth(self.test_client,
+ token=self.service_token,
+ json={'tests_invitations': []})
+ self.assertEqual(400, response.status_code)
+
+ def test_post_query_with_service_token_with_valid_schema_but_invalid_array_item(self):
+ """
+ Test that a post request with valid schema but empty array item returns 400
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_service_token_auth(self.test_client,
+ token=self.service_token,
+ json={'tests_invitations': [{'invalid': 'invalid'}]})
+ self.assertEqual(400, response.status_code)
+
+ def test_post_query_as_admin_with_valid_schema_but_invalid_id_project(self):
+ """
+ Test that a post request with valid schema but invalid id_project
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token,
+ json=self._create_tests_invitations_json(id_test_type=2,
+ id_project=0, id_user=1))
+ self.assertEqual(403, response.status_code)
+
+ def test_post_query_as_admin_with_valid_schema_but_forbidden_id_project(self):
+ """
+ Test that a post request with valid schema but invalid id_project
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_service_token_auth(self.test_client, token=self.service_token,
+ json=self._create_tests_invitations_json(id_test_type=2,
+ id_project=2, id_user=1))
+ self.assertEqual(403, response.status_code)
+
+ def test_post_query_with_service_token_with_valid_schema_but_missing_id_user_id_participant_id_device(self):
+ """
+ Test that a post request with valid schema but missing id_user, id_participant, id_device returns 400
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_service_token_auth(self.test_client,
+ token=self.service_token,
+ json=self._create_tests_invitations_json(id_test_type=1,
+ id_project=1))
+ self.assertEqual(400, response.status_code)
+
+ def test_post_query_with_service_token_with_valid_schema_but_invalid_id_user_id_participant_(self):
+ """
+ Test that a post request with valid schema but invalid id_user, id_participant, id_device returns 403
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_service_token_auth(self.test_client,
+ self.service_token,
+ json=self._create_tests_invitations_json(id_test_type=1,
+ id_project=1, id_user=0))
+ self.assertEqual(403, response.status_code)
+
+ response = self._post_with_service_token_auth(self.test_client,
+ self.service_token,
+ json=self._create_tests_invitations_json(id_test_type=1, id_project=1,
+ id_participant=0))
+ self.assertEqual(403, response.status_code)
+
+
+ response = self._post_with_service_token_auth(self.test_client,
+ self.service_token,
+ json=self._create_tests_invitations_json(id_test_type=1, id_project=1,
+ id_device=0))
+ self.assertEqual(403, response.status_code)
+
+ def test_post_query_with_service_token_with_valid_schema_with_multiple_user_participant_device(self):
+ """
+ Test that a post request with valid schema with multiple user, participant, device returns 400
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_service_token_auth(self.test_client,
+ token=self.service_token,
+ json=self._create_tests_invitations_json(id_test_type=1,
+ id_project=1,
+ id_user=1,
+ id_participant=1))
+ self.assertEqual(400, response.status_code)
+
+ response = self._post_with_service_token_auth(self.test_client,
+ token=self.service_token,
+ json=self._create_tests_invitations_json(id_test_type=1,
+ id_project=1,
+ id_user=1,
+ id_device=1))
+ self.assertEqual(400, response.status_code)
+
+ response = self._post_with_service_token_auth(self.test_client,
+ token=self.service_token,
+ json=self._create_tests_invitations_json(id_test_type=1,
+ id_project=1,
+ id_user=1,
+ id_participant=1,
+ id_device=1))
+ self.assertEqual(400, response.status_code)
+
+ def test_post_query_with_service_token_with_valid_schema_with_no_session(self):
+ """
+ Test that a post request with valid schema with no session returns 200
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_service_token_auth(self.test_client,
+ token=self.service_token,
+ json=self._create_tests_invitations_json(id_test_type=2,
+ id_project=1,
+ id_user=1))
+ self.assertEqual(200, response.status_code)
+ for json_invitation in response.json:
+ self._validate_json(json_invitation, full=True, with_urls=True)
+
+
+ def test_post_query_with_service_token_with_valid_schema_with_invalid_session(self):
+ """
+ Test that a post request with valid schema with invalid session returns 403
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_service_token_auth(self.test_client,
+ token=self.service_token,
+ json=self._create_tests_invitations_json(id_test_type=1,
+ id_project=1,
+ id_user=1,
+ id_session=0))
+ self.assertEqual(403, response.status_code)
+
+ def test_post_query_with_service_token_with_valid_schema_with_valid_session(self):
+ """
+ Test that a post request with valid schema with valid session returns 200
+ """
+ with self._flask_app.app_context():
+
+ service_access : DBManagerTeraServiceAccess = DBManagerTeraServiceAccess(TeraService.get_service_by_id(self.id_service))
+
+ for id_session in service_access.get_accessible_sessions_ids():
+ response = self._post_with_service_token_auth(self.test_client,
+ token=self.service_token,
+ json=self._create_tests_invitations_json(id_test_type=2,
+ id_project=1,
+ id_user=1,
+ id_session=id_session))
+ self.assertEqual(200, response.status_code)
+ for json_invitation in response.json:
+ self._validate_json(json_invitation, full=True, with_urls=True)
+
+
+ def test_post_query_with_service_token_with_valid_schema_update_count(self):
+ """
+ Test that a post request with valid schema updates count
+ """
+ with self._flask_app.app_context():
+ # Post a new invitation
+ response = self._post_with_service_token_auth(self.test_client,
+ token=self.service_token,
+ json=self._create_tests_invitations_json(id_test_type=2,
+ id_project=1,
+ id_user=1))
+ self.assertEqual(200, response.status_code)
+ invitation_info = response.json[0]
+ self.assertNotEqual(invitation_info['id_test_invitation'], 0)
+
+ # increment count
+ invitation_info['test_invitation_count'] += 1
+
+ # Post the updated invitation
+ response = self._post_with_service_token_auth(self.test_client,
+ self.service_token,
+ json={'tests_invitations': [{'id_test_invitation': invitation_info['id_test_invitation'],
+ 'test_invitation_count': invitation_info['test_invitation_count']}]})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(1, len(response.json))
+ self.assertEqual(invitation_info['test_invitation_count'], response.json[0]['test_invitation_count'])
+ for json_invitation in response.json:
+ self._validate_json(json_invitation, full=True, with_urls=True)
+
+
+
+ def _create_tests_invitations_json(self, id_test_type: int, id_project: int, id_user: int = None,
+ id_participant: int = None, id_device: int = None, id_session: int = None) -> dict:
+ """
+ Create a json for an invitation
+ """
+ tests_invitations = {'tests_invitations': [
+ {
+ 'id_test_invitation': 0, # New invitation
+ 'id_test_type': id_test_type,
+ 'id_project': id_project,
+ 'test_invitation_max_count': 1,
+ 'test_invitation_count': 0,
+ 'test_invitation_expiration_date': (datetime.now() + timedelta(days=1)).isoformat()
+ } | {k: v for k, v in {'id_user': id_user,
+ 'id_participant': id_participant,
+ 'id_device': id_device,
+ 'id_session': id_session}.items() if v is not None}
+ ]}
+
+ return tests_invitations
+
+ def _create_test_type_for_service(self, id_service: int) -> TeraTestType:
+ """
+ Create a test type for a service
+ """
+ test_type = TeraTestType(id_test_type=1,
+ test_type_name='TestType',
+ test_type_description='TestType Description',
+ test_type_key='TEST' + str(uuid.uuid4()),
+ id_service=id_service)
+ TeraTestType.insert(test_type)
+ return test_type
+
+
+ def _create_invitations(self, count: int,
+ id_test_type: int,
+ id_project: int,
+ id_user: int = None,
+ id_participant: int = None,
+ id_device: int = None,
+ id_session: int = None) -> list[TeraTestInvitation]:
+ """
+ Create a number of invitations.
+ """
+
+ invitations: list[TeraTestInvitation] = []
+
+
+ # Make sure test type has all required fields
+ test_type: TeraTestType = TeraTestType.get_test_type_by_id(id_test_type)
+ TeraTestType.update(test_type.id_test_type, {'test_type_has_json_format': True,
+ 'test_type_has_web_editor': True,
+ 'test_type_has_web_format': True})
+
+ # Make sure we have only one of id_user, id_participant, id_device
+ if sum(x is not None for x in [id_user, id_participant, id_device]) != 1:
+ raise ValueError('Only one of id_user, id_participant, id_device must be set')
+
+ for _ in range(count):
+ invitation = TeraTestInvitation(id_test_type=id_test_type,
+ id_project=id_project,
+ test_invitation_max_count=1,
+ test_invitation_count=0,
+ test_invitation_expiration_date=datetime.now() + timedelta(days=1),
+ id_user=id_user,
+ id_participant=id_participant,
+ id_device=id_device,
+ id_session=id_session)
+ TeraTestInvitation.insert(invitation)
+ invitations.append(invitation)
+
+ return invitations
+
+ def _delete_all_invitations(self):
+ """
+ Delete all invitations
+ """
+ with self._flask_app.app_context():
+ invitations = TeraTestInvitation.query.all()
+ for invitation in invitations:
+ TeraTestInvitation.delete(invitation.id_test_invitation)
+
+ def _validate_json(self, json: dict, full: bool = False, with_urls: bool = False):
+ """
+ Validate a json
+ """
+ self.assertTrue('id_test_invitation' in json)
+ self.assertTrue('id_test_type' in json)
+ self.assertTrue('test_invitation_max_count' in json)
+ self.assertTrue('test_invitation_count' in json)
+ self.assertTrue('test_invitation_expiration_date' in json)
+ self.assertTrue('test_invitation_message' in json)
+ self.assertTrue('test_invitation_creation_date' in json)
+ self.assertTrue('id_user' in json)
+ self.assertTrue('id_participant' in json)
+ self.assertTrue('id_device' in json)
+ self.assertTrue('id_session' in json)
+ self.assertTrue('test_invitation_key' in json)
+
+ if full:
+ self.assertTrue('test_invitation_test_type' in json)
+ if json['id_user']:
+ self.assertTrue('test_invitation_user' in json)
+ if json['id_participant']:
+ self.assertTrue('test_invitation_participant' in json)
+ if json['id_device']:
+ self.assertTrue('test_invitation_device' in json)
+ if json['id_session']:
+ self.assertTrue('test_invitation_session' in json)
+ else:
+ self.assertTrue('test_invitation_test_type' not in json)
+ self.assertTrue('test_invitation_session' not in json)
+ self.assertTrue('test_invitation_user' not in json)
+ self.assertTrue('test_invitation_participant' not in json)
+ self.assertTrue('test_invitation_device' not in json)
+
+ if with_urls:
+ self.assertTrue('test_invitation_url' in json)
+ else:
+ self.assertFalse('test_invitation_url' in json)
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryUserGroups.py b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryUserGroups.py
index 2d81f19c0..72776da5c 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryUserGroups.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryUserGroups.py
@@ -1,4 +1,4 @@
-from BaseServiceAPITest import BaseServiceAPITest
+from tests.modules.FlaskModule.API.service.BaseServiceAPITest import BaseServiceAPITest
from opentera.db.models.TeraUser import TeraUser
from opentera.db.models.TeraUserUserGroup import TeraUserUserGroup
from opentera.db.models.TeraServiceAccess import TeraServiceAccess
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryUsers.py b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryUsers.py
index 3dd307422..e839c0bf1 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryUsers.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceQueryUsers.py
@@ -1,7 +1,9 @@
from typing import List
-from BaseServiceAPITest import BaseServiceAPITest
+from modules.DatabaseModule.DBManagerTeraServiceAccess import DBManagerTeraServiceAccess
+from tests.modules.FlaskModule.API.service.BaseServiceAPITest import BaseServiceAPITest
from opentera.db.models.TeraUser import TeraUser
+from opentera.db.models.TeraService import TeraService
class ServiceQueryUsersTest(BaseServiceAPITest):
@@ -38,10 +40,27 @@ def test_get_endpoint_with_token_auth_with_user_uuid(self):
with self._flask_app.app_context():
# Get all users from DB
users: List[TeraUser] = TeraUser.query.all()
+
+ service: TeraService = TeraService.get_service_by_key('VideoRehabService')
+ service_access = DBManagerTeraServiceAccess(service)
+
for user in users:
params = {'user_uuid': user.user_uuid}
response = self._get_with_service_token_auth(client=self.test_client, token=self.service_token,
- params=params, endpoint=self.test_endpoint)
- self.assertEqual(200, response.status_code)
- user_json = user.to_json()
- self.assertEqual(user_json, response.json)
+ params=params, endpoint=self.test_endpoint)
+ if user.user_uuid in service_access.get_accessible_users_uuids():
+ self.assertEqual(200, response.status_code)
+ user_json = user.to_json()
+ self.assertEqual(user_json, response.json)
+ else:
+ self.assertEqual(403, response.status_code)
+
+ params = {'id_user': user.id_user}
+ response = self._get_with_service_token_auth(client=self.test_client, token=self.service_token,
+ params=params, endpoint=self.test_endpoint)
+ if user.id_user in service_access.get_accessible_users_ids():
+ self.assertEqual(200, response.status_code)
+ user_json = user.to_json()
+ self.assertEqual(user_json, response.json)
+ else:
+ self.assertEqual(403, response.status_code)
diff --git a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceSessionManager.py b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceSessionManager.py
index c94bbd65f..bb86dd0db 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceSessionManager.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/service/test_ServiceSessionManager.py
@@ -1,6 +1,6 @@
from typing import List
-from BaseServiceAPITest import BaseServiceAPITest
+from tests.modules.FlaskModule.API.service.BaseServiceAPITest import BaseServiceAPITest
from modules.FlaskModule.FlaskModule import flask_app
from opentera.db.models.TeraSessionType import TeraSessionType
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/BaseUserAPITest.py b/teraserver/python/tests/modules/FlaskModule/API/user/BaseUserAPITest.py
index 27ea64407..10d0459d4 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/BaseUserAPITest.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/BaseUserAPITest.py
@@ -151,65 +151,90 @@ def setup_redis_keys(self):
TeraServerSettings.get_server_setting_value(
TeraServerSettings.ServerParticipantTokenKey))
- def _get_with_user_token_auth(self, client: FlaskClient, token: str = '', params=None, endpoint=None):
+ def _get_with_user_token_auth(self, client: FlaskClient, token: str = '', params=None, endpoint=None,
+ opt_headers: dict = None):
if params is None:
params = {}
if endpoint is None:
endpoint = self.test_endpoint
headers = {'Authorization': 'OpenTera ' + token}
+ if opt_headers is not None:
+ headers.update(opt_headers)
+
return client.get(endpoint, headers=headers, query_string=params)
def _get_with_user_http_auth(self, client: FlaskClient, username: str = '', password: str = '',
- params=None, endpoint=None):
+ params: dict = None, endpoint: str = None, opt_headers: dict = None):
if params is None:
- params = {}
+ params = {}
if endpoint is None:
endpoint = self.test_endpoint
headers = {'Authorization': _basic_auth_str(username, password)}
+ if opt_headers is not None:
+ headers.update(opt_headers)
+
return client.get(endpoint, headers=headers, query_string=params)
def _post_with_user_token_auth(self, client: FlaskClient, token: str = '', json: dict = {},
- params: dict = None, endpoint: str = None):
+ params: dict = None, endpoint: str = None, opt_headers: dict = None):
if params is None:
params = {}
if endpoint is None:
endpoint = self.test_endpoint
headers = {'Authorization': 'OpenTera ' + token}
+
+ if opt_headers is not None:
+ headers.update(opt_headers)
+
return client.post(endpoint, headers=headers, query_string=params, json=json)
def _post_with_user_http_auth(self, client: FlaskClient, username: str = '', password: str = '',
- json: dict = {}, params: dict = None, endpoint: str = None):
+ json: dict = {}, params: dict = None, endpoint: str = None, opt_headers: dict = None):
if params is None:
params = {}
if endpoint is None:
endpoint = self.test_endpoint
headers = {'Authorization': _basic_auth_str(username, password)}
+ if opt_headers is not None:
+ headers.update(opt_headers)
return client.post(endpoint, headers=headers, query_string=params, json=json)
def _post_file_with_user_http_auth(self, client: FlaskClient, files: dict, username: str = '', password: str = '',
- params: dict = None, endpoint: str = None):
+ params: dict = None, endpoint: str = None, opt_headers: dict = None):
if params is None:
params = {}
if endpoint is None:
endpoint = self.test_endpoint
headers = {'Authorization': _basic_auth_str(username, password)}
+
+ if opt_headers is not None:
+ headers.update(opt_headers)
+
return client.post(endpoint, headers=headers, query_string=params, data=files)
def _delete_with_user_token_auth(self, client: FlaskClient, token: str = '',
- params: dict = None, endpoint: str = None):
+ params: dict = None, endpoint: str = None, opt_headers: dict = None):
if params is None:
params = {}
if endpoint is None:
endpoint = self.test_endpoint
headers = {'Authorization': 'OpenTera ' + token}
+
+ if opt_headers is not None:
+ headers.update(opt_headers)
+
return client.delete(endpoint, headers=headers, query_string=params)
def _delete_with_user_http_auth(self, client: FlaskClient, username: str = '', password: str = '',
- params: dict = None, endpoint: str = None):
+ params: dict = None, endpoint: str = None, opt_headers: dict = None):
if params is None:
params = {}
if endpoint is None:
endpoint = self.test_endpoint
headers = {'Authorization': _basic_auth_str(username, password)}
+
+ if opt_headers is not None:
+ headers.update(opt_headers)
+
return client.delete(endpoint, headers=headers, query_string=params)
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/__init__.py b/teraserver/python/tests/modules/FlaskModule/API/user/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/_test_UserQueryServiceAccessToken.py b/teraserver/python/tests/modules/FlaskModule/API/user/_test_UserQueryServiceAccessToken.py
index fceeb7138..ab97b91bc 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/_test_UserQueryServiceAccessToken.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/_test_UserQueryServiceAccessToken.py
@@ -1,4 +1,4 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
from opentera.db.models.TeraService import TeraService
from opentera.db.models.TeraServiceRole import TeraServiceRole
from opentera.db.models.TeraServiceProject import TeraServiceProject
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLogin.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLogin.py
index 32ed14a2f..76fb490b5 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLogin.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLogin.py
@@ -1,4 +1,4 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
from opentera.db.models.TeraUser import TeraUser
@@ -7,10 +7,39 @@ class UserLoginTest(BaseUserAPITest):
def setUp(self):
super().setUp()
+ # Create users with 2fa enabled
+ with self._flask_app.app_context():
+ self.user1: dict = self._create_2fa_enabled_user('test_user_2fa_1', 'Password12345!', set_secret=True)
+ self.user2: dict = self._create_2fa_enabled_user('test_user_2fa_2', 'Password12345!', set_secret=False)
def tearDown(self):
+ # Delete users with 2fa enabled
+ with self._flask_app.app_context():
+ TeraUser.delete(self.user1['id_user'], hard_delete=True)
+ TeraUser.delete(self.user2['id_user'], hard_delete=True)
super().tearDown()
+ def _create_2fa_enabled_user(self, username, password, set_secret:bool = True):
+ user = TeraUser()
+ user.id_user = 0 # New user
+ user.user_username = username
+ user.user_password = password
+ user.user_firstname = username
+ user.user_lastname = username
+ user.user_email = f"{username}@test.com"
+ user.user_enabled = True
+ user.user_profile = {}
+ if set_secret:
+ user.enable_2fa_otp()
+ else:
+ user.user_2fa_enabled = True
+ user.user_2fa_otp_enabled = False
+ user.user_2fa_otp_secret = None
+
+ TeraUser.insert(user)
+ return user.to_json(minimal=False)
+
+
def test_get_endpoint_no_auth(self):
with self._flask_app.app_context():
response = self.test_client.get(self.test_endpoint)
@@ -21,10 +50,12 @@ def test_get_endpoint_invalid_token_auth(self):
response = self._get_with_user_token_auth(self.test_client, 'invalid')
self.assertEqual(401, response.status_code)
- def test_get_endpoint_login_admin_user_http_auth(self):
+ def test_get_endpoint_login_admin_user_http_auth_with_websocket(self):
with self._flask_app.app_context():
# Using default participant information
- response = self._get_with_user_http_auth(self.test_client, 'admin', 'admin')
+ response = self._get_with_user_http_auth(self.test_client,
+ 'admin', 'admin',
+ {'with_websocket': True})
self.assertEqual(200, response.status_code)
self.assertEqual('application/json', response.headers['Content-Type'])
@@ -35,6 +66,20 @@ def test_get_endpoint_login_admin_user_http_auth(self):
self.assertTrue('user_uuid' in response.json)
self.assertTrue('user_token' in response.json)
+ def test_get_endpoint_login_admin_user_http_auth_no_websocket(self):
+ with self._flask_app.app_context():
+ # Using default participant information
+ response = self._get_with_user_http_auth(self.test_client, 'admin', 'admin')
+
+ self.assertEqual(200, response.status_code)
+ self.assertEqual('application/json', response.headers['Content-Type'])
+ self.assertGreater(len(response.json), 0)
+
+ # Validate fields in json response
+ self.assertTrue('user_uuid' in response.json)
+ self.assertTrue('user_token' in response.json)
+ self.assertFalse('websocket_url' in response.json)
+
def test_get_endpoint_login_admin_user_http_auth_then_token_auth(self):
with self._flask_app.app_context():
# Using default participant information
@@ -52,3 +97,33 @@ def test_get_endpoint_login_admin_user_http_auth_then_token_auth(self):
# Not allowed for this endpoint
response = self._get_with_user_token_auth(self.test_client, token=token)
self.assertEqual(401, response.status_code)
+
+ def test_get_endpoint_login_user1_2fa_already_setup(self):
+ with self._flask_app.app_context():
+
+ # Login should redirect to 2fa verification
+ response = self._get_with_user_http_auth(self.test_client, 'test_user_2fa_1', 'Password12345!')
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('redirect_url' in response.json)
+ self.assertFalse('login_setup_2fa' in response.json['redirect_url'])
+ self.assertTrue('login_validate_2fa' in response.json['redirect_url'])
+
+ # Answer should not provide login information
+ self.assertFalse('websocket_url' in response.json)
+ self.assertFalse('user_uuid' in response.json)
+ self.assertFalse('user_token' in response.json)
+
+ def test_get_endpoint_login_user2_2fa_not_setup(self):
+ with self._flask_app.app_context():
+
+ # Login should redirect to 2fa verification
+ response = self._get_with_user_http_auth(self.test_client, 'test_user_2fa_2', 'Password12345!')
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('redirect_url' in response.json)
+ self.assertTrue('login_setup_2fa' in response.json['redirect_url'])
+ self.assertFalse('login_validate_2fa' in response.json['redirect_url'])
+
+ # Answer should not provide login information
+ self.assertFalse('websocket_url' in response.json)
+ self.assertFalse('user_uuid' in response.json)
+ self.assertFalse('user_token' in response.json)
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLogin2FA.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLogin2FA.py
new file mode 100644
index 000000000..0f9a1a136
--- /dev/null
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLogin2FA.py
@@ -0,0 +1,227 @@
+import pyotp
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
+from opentera.db.models.TeraUser import TeraUser
+
+
+class UserLogin2FATest(BaseUserAPITest):
+ test_endpoint = '/api/user/login/2fa'
+
+ def setUp(self):
+ super().setUp()
+ # Create users with 2fa enabled
+ with self._flask_app.app_context():
+ self.user1: dict = self._create_2fa_enabled_user('test_user_2fa_1', 'Password12345!', set_secret=True)
+ self.user2: dict = self._create_2fa_enabled_user('test_user_2fa_2', 'Password12345!', set_secret=False)
+
+ def tearDown(self):
+ # Delete users with 2fa enabled
+ with self._flask_app.app_context():
+ TeraUser.delete(self.user1['id_user'], hard_delete=True)
+ TeraUser.delete(self.user2['id_user'], hard_delete=True)
+ super().tearDown()
+
+ def _create_2fa_enabled_user(self, username, password, set_secret:bool = True):
+ user = TeraUser()
+ user.id_user = 0 # New user
+ user.user_username = username
+ user.user_password = password
+ user.user_firstname = username
+ user.user_lastname = username
+ user.user_email = f"{username}@test.com"
+ user.user_enabled = True
+ user.user_profile = {}
+ if set_secret:
+ user.enable_2fa_otp()
+ else:
+ user.user_2fa_enabled = True
+ user.user_2fa_otp_enabled = False
+ user.user_2fa_otp_secret = None
+
+ TeraUser.insert(user)
+ return user.to_json(minimal=False)
+
+ def _login_user(self, username, password):
+ response = self._get_with_user_http_auth(self.test_client, username, password, endpoint='/api/user/login')
+ self.assertEqual(200, response.status_code)
+ self.assertEqual('application/json', response.headers['Content-Type'])
+ self.assertGreater(len(response.json), 0)
+ return response
+
+ def test_get_endpoint_no_auth(self):
+ with self._flask_app.app_context():
+ response = self.test_client.get(self.test_endpoint)
+ self.assertEqual(401, response.status_code)
+
+ def test_get_endpoint_invalid_token_auth(self):
+ with self._flask_app.app_context():
+ response = self._get_with_user_token_auth(self.test_client, 'invalid')
+ self.assertEqual(401, response.status_code)
+
+ def test_get_endpoint_with_no_session(self):
+ with self._flask_app.app_context():
+ response = self.test_client.get(self.test_endpoint)
+ self.assertEqual(401, response.status_code)
+
+ def test_get_endpoint_with_admin_without_2fa_enabled(self):
+ with self._flask_app.app_context():
+ user = TeraUser.get_user_by_username('admin')
+ self.assertIsNotNone(user)
+ self.assertFalse(user.user_2fa_enabled)
+ # Fist login
+ response = self._login_user('admin', 'admin')
+ self.assertEqual(200, response.status_code)
+
+ # Now try to login with 2fa
+ response = self._get_with_user_http_auth(self.test_client, 'admin', 'admin',
+ params={'otp_code': '123456'},
+ endpoint=self.test_endpoint)
+ self.assertEqual(403, response.status_code)
+
+ def test_get_endpoint_login_user1_http_auth_no_code(self):
+ with self._flask_app.app_context():
+
+ # Fisrt login to create session
+ response = self._login_user('test_user_2fa_1', 'Password12345!')
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('redirect_url' in response.json)
+ self.assertTrue('login_validate_2fa' in response.json['redirect_url'])
+
+ # Using default admin information, http auth not used
+ response = self._get_with_user_http_auth(self.test_client)
+ self.assertEqual(400, response.status_code)
+
+ def test_get_endpoint_login_user1_http_auth_invalid_code(self):
+ with self._flask_app.app_context():
+
+ # First login to create session
+ response = self._login_user('test_user_2fa_1', 'Password12345!')
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('redirect_url' in response.json)
+ self.assertTrue('login_validate_2fa' in response.json['redirect_url'])
+
+ # Then try to login with invalid code
+ response = self._get_with_user_http_auth(self.test_client,
+ params={'otp_code': '123456'})
+ self.assertEqual(401, response.status_code)
+
+ def test_get_endpoint_login_user1_http_auth_valid_code(self):
+ with self._flask_app.app_context():
+ user = TeraUser.get_user_by_username('test_user_2fa_1')
+ self.assertIsNotNone(user.user_2fa_otp_secret)
+
+ # First login to create session
+ response = self._login_user('test_user_2fa_1', 'Password12345!')
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('redirect_url' in response.json)
+ self.assertTrue('login_validate_2fa' in response.json['redirect_url'])
+ self.assertFalse('login_setup_2fa' in response.json['redirect_url'])
+
+ # Then try to login with valid code
+ totp = pyotp.TOTP(user.user_2fa_otp_secret)
+ response = self._get_with_user_http_auth(self.test_client,
+ params={'otp_code': totp.now()})
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('user_uuid' in response.json)
+ self.assertTrue('user_token' in response.json)
+ self.assertFalse('websocket_url'in response.json)
+
+ def test_get_endpoint_login_user1_http_auth_valid_code_with_websocket(self):
+ with self._flask_app.app_context():
+ user = TeraUser.get_user_by_username('test_user_2fa_1')
+ self.assertIsNotNone(user.user_2fa_otp_secret)
+
+ # First login to create session
+ response = self._login_user('test_user_2fa_1', 'Password12345!')
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('redirect_url' in response.json)
+ self.assertTrue('login_validate_2fa' in response.json['redirect_url'])
+ self.assertFalse('login_setup_2fa' in response.json['redirect_url'])
+
+ # Then try to login with valid code
+ totp = pyotp.TOTP(user.user_2fa_otp_secret)
+ response = self._get_with_user_http_auth(self.test_client,
+ params={'otp_code': totp.now(),
+ 'with_websocket': True})
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('user_uuid' in response.json)
+ self.assertTrue('user_token' in response.json)
+ self.assertTrue('websocket_url'in response.json)
+
+ def test_get_endpoint_login_user2_http_auth_invalid_code(self):
+ with self._flask_app.app_context():
+ user = TeraUser.get_user_by_username('test_user_2fa_2')
+ self.assertIsNone(user.user_2fa_otp_secret)
+
+ # First login to create session
+ response = self._login_user('test_user_2fa_2', 'Password12345!')
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('redirect_url' in response.json)
+ self.assertFalse('login_validate_2fa' in response.json['redirect_url'])
+ self.assertTrue('login_setup_2fa' in response.json['redirect_url'])
+
+ # Then try to login with invalid code
+ response = self._get_with_user_http_auth(self.test_client,
+ params={'otp_code': '123456'})
+ self.assertEqual(403, response.status_code)
+
+ def test_get_endpoint_login_user1_http_auth_valid_code_unknown_app_name(self):
+ with self._flask_app.app_context():
+ user = TeraUser.get_user_by_username('test_user_2fa_1')
+ self.assertIsNotNone(user.user_2fa_otp_secret)
+
+ # First login to create session
+ response = self._login_user('test_user_2fa_1', 'Password12345!')
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('redirect_url' in response.json)
+ self.assertTrue('login_validate_2fa' in response.json['redirect_url'])
+ self.assertFalse('login_setup_2fa' in response.json['redirect_url'])
+
+ # Then try to login with valid code
+ totp = pyotp.TOTP(user.user_2fa_otp_secret)
+ response = self._get_with_user_http_auth(self.test_client,
+ params={'otp_code': totp.now()},
+ opt_headers={'X-Client-Name': 'test', 'X-Client-Version': '0.0.0'})
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('user_uuid' in response.json)
+ self.assertTrue('user_token' in response.json)
+ self.assertFalse('websocket_url'in response.json)
+
+ def test_get_endpoint_login_user1_http_auth_valid_code_outdated_app(self):
+ with self._flask_app.app_context():
+ user = TeraUser.get_user_by_username('test_user_2fa_1')
+ self.assertIsNotNone(user.user_2fa_otp_secret)
+
+ # First login to create session
+ response = self._login_user('test_user_2fa_1', 'Password12345!')
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('redirect_url' in response.json)
+ self.assertTrue('login_validate_2fa' in response.json['redirect_url'])
+ self.assertFalse('login_setup_2fa' in response.json['redirect_url'])
+
+ # Then try to login with valid code
+ totp = pyotp.TOTP(user.user_2fa_otp_secret)
+ response = self._get_with_user_http_auth(self.test_client,
+ params={'otp_code': totp.now()},
+ opt_headers={'X-Client-Name': 'OpenTeraPlus',
+ 'X-Client-Version': '0.0.0'})
+ self.assertEqual(426, response.status_code)
+
+ def test_get_endpoint_login_user1_http_auth_valid_code_valid_app(self):
+ with self._flask_app.app_context():
+ user = TeraUser.get_user_by_username('test_user_2fa_1')
+ self.assertIsNotNone(user.user_2fa_otp_secret)
+
+ # First login to create session
+ response = self._login_user('test_user_2fa_1', 'Password12345!')
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('redirect_url' in response.json)
+ self.assertTrue('login_validate_2fa' in response.json['redirect_url'])
+ self.assertFalse('login_setup_2fa' in response.json['redirect_url'])
+
+ # Then try to login with valid code
+ totp = pyotp.TOTP(user.user_2fa_otp_secret)
+ response = self._get_with_user_http_auth(self.test_client,
+ params={'otp_code': totp.now()},
+ opt_headers={'X-Client-Name': 'OpenTeraPlus',
+ 'X-Client-Version': '1.0.0'})
+ self.assertEqual(200, response.status_code)
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLoginChangePassword.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLoginChangePassword.py
new file mode 100644
index 000000000..6346a86dd
--- /dev/null
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLoginChangePassword.py
@@ -0,0 +1,179 @@
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
+from opentera.db.models.TeraUser import TeraUser
+
+
+class UserLoginChangePassword(BaseUserAPITest):
+ test_endpoint = '/api/user/login/change_password'
+
+ def setUp(self):
+ super().setUp()
+ # Create users with password change needed
+ with self._flask_app.app_context():
+ self.user1: dict = self._create_change_password_user('test_user_1', 'Password12345!')
+
+ def tearDown(self):
+ # Delete users with 2fa enabled
+ with self._flask_app.app_context():
+ TeraUser.delete(self.user1['id_user'], hard_delete=True)
+ super().tearDown()
+
+ @staticmethod
+ def _create_change_password_user(username, password):
+ user = TeraUser()
+ user.id_user = 0 # New user
+ user.user_username = username
+ user.user_password = password
+ user.user_firstname = username
+ user.user_lastname = username
+ user.user_email = f"{username}@test.com"
+ user.user_enabled = True
+ user.user_profile = {}
+ user.user_force_password_change = True
+
+ TeraUser.insert(user)
+ return user.to_json(minimal=False)
+
+ def _login_user(self, username, password):
+ response = self._get_with_user_http_auth(self.test_client, username, password, endpoint='/api/user/login')
+ self.assertEqual(200, response.status_code)
+ self.assertEqual('application/json', response.headers['Content-Type'])
+ self.assertGreater(len(response.json), 0)
+ return response
+
+ def test_post_endpoint_no_auth(self):
+ with self._flask_app.app_context():
+ response = self.test_client.post(self.test_endpoint)
+ self.assertEqual(401, response.status_code)
+
+ def test_post_endpoint_invalid_token_auth(self):
+ with self._flask_app.app_context():
+ response = self._post_with_user_token_auth(self.test_client, 'invalid')
+ self.assertEqual(401, response.status_code)
+
+ def test_post_endpoint_with_no_session(self):
+ with self._flask_app.app_context():
+ response = self.test_client.post(self.test_endpoint)
+ self.assertEqual(401, response.status_code)
+
+ def test_get_endpoint(self):
+ with self._flask_app.app_context():
+ response = self.test_client.get(self.test_endpoint)
+ self.assertEqual(405, response.status_code)
+
+ def test_post_password_without_force_required(self):
+ with self._flask_app.app_context():
+ # First login to create session
+ response = self._login_user('admin', 'admin')
+ self.assertEqual(200, response.status_code)
+ self.assertFalse('redirect_url' in response.json)
+
+ def test_post_password_change_mismatched(self):
+ with self._flask_app.app_context():
+ # First login to create session
+ response = self._login_user('test_user_1', 'Password12345!')
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('redirect_url' in response.json)
+ self.assertTrue('login_change_password' in response.json['redirect_url'])
+
+ params = {'new_password': 'NewPassword12345!',
+ 'confirm_password': 'NotNewPassword12345!'}
+ response = self.test_client.post(self.test_endpoint, json=params)
+ self.assertEqual(400, response.status_code)
+
+ def test_post_password_change_same_as_old(self):
+ with self._flask_app.app_context():
+ # First login to create session
+ response = self._login_user('test_user_1', 'Password12345!')
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('redirect_url' in response.json)
+ self.assertTrue('login_change_password' in response.json['redirect_url'])
+
+ params = {'new_password': 'Password12345!',
+ 'confirm_password': 'Password12345!'}
+ response = self.test_client.post(self.test_endpoint, json=params)
+ self.assertEqual(400, response.status_code)
+
+ def test_post_password_change_insecure(self):
+ with self._flask_app.app_context():
+ # First login to create session
+ response = self._login_user('test_user_1', 'Password12345!')
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('redirect_url' in response.json)
+ self.assertTrue('login_change_password' in response.json['redirect_url'])
+
+ json_data = {'new_password': 'password', 'confirm_password': 'password'}
+ response = self.test_client.post(self.test_endpoint, json=json_data)
+ self.assertEqual(400, response.status_code, msg="Password not long enough")
+
+ json_data['new_password'] = 'password12345!'
+ json_data['confirm_password'] = json_data['new_password']
+ response = self.test_client.post(self.test_endpoint, json=json_data)
+ self.assertEqual(400, response.status_code, msg="Password without capital letters")
+
+ json_data['new_password'] = 'PASSWORD12345!'
+ json_data['confirm_password'] = json_data['new_password']
+ response = self.test_client.post(self.test_endpoint, json=json_data)
+ self.assertEqual(400, response.status_code, msg="Password without lower case letters")
+
+ json_data['new_password'] = 'Password12345'
+ json_data['confirm_password'] = json_data['new_password']
+ response = self.test_client.post(self.test_endpoint, json=json_data)
+ self.assertEqual(400, response.status_code, msg="Password without special characters")
+
+ json_data['new_password'] = 'Password!!!!'
+ json_data['confirm_password'] = json_data['new_password']
+ response = self.test_client.post(self.test_endpoint, json=json_data)
+ self.assertEqual(400, response.status_code, msg="Password without numbers")
+
+ json_data['new_password'] = 'Password12345!!'
+ json_data['confirm_password'] = json_data['new_password']
+ response = self.test_client.post(self.test_endpoint, json=json_data)
+ self.assertEqual(200, response.status_code, msg="Password OK")
+
+ # Reset to original password
+ user = TeraUser.get_user_by_id(self.user1['id_user'])
+ user.user_force_password_change = True
+ user.db().session.commit()
+
+ json_data['new_password'] = 'Password12345!'
+ json_data['confirm_password'] = json_data['new_password']
+ response = self.test_client.post(self.test_endpoint, json=json_data)
+ self.assertEqual(200, response.status_code, msg="Password back to last")
+
+ def test_post_password_change_not_required(self):
+ with self._flask_app.app_context():
+ # First login to create session
+ response = self._login_user('test_user_1', 'Password12345!')
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('redirect_url' in response.json)
+ self.assertTrue('login_change_password' in response.json['redirect_url'])
+
+ user = TeraUser.get_user_by_id(self.user1['id_user'])
+ user.user_force_password_change = False
+ user.db().session.commit()
+
+ json_data = {'new_password': 'Password12345!!', 'confirm_password': 'Password12345!!'}
+ response = self.test_client.post(self.test_endpoint, json=json_data)
+ self.assertEqual(400, response.status_code, msg="Password not required to be changed")
+
+ user = TeraUser.get_user_by_id(self.user1['id_user'])
+ user.user_force_password_change = True
+ user.db().session.commit()
+
+ def test_post_password_change_ok(self):
+ with self._flask_app.app_context():
+ # First login to create session
+ response = self._login_user('test_user_1', 'Password12345!')
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('redirect_url' in response.json)
+ self.assertTrue('login_change_password' in response.json['redirect_url'])
+
+ json_data = {'new_password': 'Password12345!!', 'confirm_password': 'Password12345!!'}
+ response = self.test_client.post(self.test_endpoint, json=json_data)
+ self.assertEqual(200, response.status_code, msg="Password OK")
+
+ # Reset to original password
+ user = TeraUser.get_user_by_id(self.user1['id_user'])
+ user.user_force_password_change = True
+ user.user_password = 'Password12345!'
+ user.db().session.commit()
\ No newline at end of file
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLoginSetup2FA.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLoginSetup2FA.py
new file mode 100644
index 000000000..21fb8873e
--- /dev/null
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLoginSetup2FA.py
@@ -0,0 +1,186 @@
+import pyotp
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
+from opentera.db.models.TeraUser import TeraUser
+
+
+class UserLoginSetup2FATest(BaseUserAPITest):
+ test_endpoint = '/api/user/login/setup_2fa'
+
+ def setUp(self):
+ super().setUp()
+ # Create users with 2fa enabled
+ with self._flask_app.app_context():
+ self.user1: dict = self._create_2fa_enabled_user('test_user_2fa_1', 'Password12345!', set_secret=True)
+ self.user2: dict = self._create_2fa_enabled_user('test_user_2fa_2', 'Password12345!', set_secret=False)
+
+ def tearDown(self):
+ # Delete users with 2fa enabled
+ with self._flask_app.app_context():
+ TeraUser.delete(self.user1['id_user'], hard_delete=True)
+ TeraUser.delete(self.user2['id_user'], hard_delete=True)
+ super().tearDown()
+
+
+ def _create_2fa_enabled_user(self, username, password, set_secret:bool = True):
+ user = TeraUser()
+ user.id_user = 0 # New user
+ user.user_username = username
+ user.user_password = password
+ user.user_firstname = username
+ user.user_lastname = username
+ user.user_email = f"{username}@test.com"
+ user.user_enabled = True
+ user.user_profile = {}
+ if set_secret:
+ user.enable_2fa_otp()
+ else:
+ user.user_2fa_enabled = True
+ user.user_2fa_otp_enabled = False
+ user.user_2fa_otp_secret = None
+
+ TeraUser.insert(user)
+ return user.to_json(minimal=False)
+
+
+ def _login_user(self, username, password):
+ response = self._get_with_user_http_auth(self.test_client, username,
+ password, endpoint='/api/user/login')
+ self.assertEqual(200, response.status_code)
+ self.assertEqual('application/json', response.headers['Content-Type'])
+ self.assertGreater(len(response.json), 0)
+ return response
+
+ def test_get_endpoint_invalid_token_auth(self):
+ with self._flask_app.app_context():
+ response = self._get_with_user_token_auth(self.test_client, 'invalid')
+ self.assertEqual(401, response.status_code)
+
+ def test_get_endpoint_with_no_session(self):
+ with self._flask_app.app_context():
+ response = self.test_client.get(self.test_endpoint)
+ self.assertEqual(401, response.status_code)
+
+ def test_get_endpoint_with_admin_without_2fa_enabled(self):
+ with self._flask_app.app_context():
+ user = TeraUser.get_user_by_username('admin')
+ self.assertIsNotNone(user)
+ self.assertFalse(user.user_2fa_enabled)
+ # Fist login
+ response = self._login_user('admin', 'admin')
+ self.assertEqual(200, response.status_code)
+
+ # Now try to setup 2fa
+ response = self._get_with_user_http_auth(self.test_client, 'admin', 'admin',
+ endpoint=self.test_endpoint)
+
+ self.assertEqual(403, response.status_code)
+
+ def test_get_endpoint_login_user1_2fa_already_setup(self):
+ with self._flask_app.app_context():
+
+ # First login to create session
+ response = self._login_user('test_user_2fa_1', 'Password12345!')
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('redirect_url' in response.json)
+ self.assertFalse('login_setup_2fa' in response.json['redirect_url'])
+
+ # Using default admin information, http auth not used
+ response = self._get_with_user_http_auth(self.test_client)
+ self.assertEqual(403, response.status_code)
+
+ def test_get_endpoint_login_user2_http_auth_should_work_but_and_modify_user_after_post(self):
+ with self._flask_app.app_context():
+
+ # First login to create session
+ response = self._login_user('test_user_2fa_2', 'Password12345!')
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('redirect_url' in response.json)
+ self.assertTrue('login_setup_2fa' in response.json['redirect_url'])
+
+ # Then try to setup 2fa
+ response = self._get_with_user_http_auth(self.test_client)
+ self.assertEqual(200, response.status_code)
+
+ self.assertTrue('qr_code' in response.json)
+ self.assertTrue('otp_secret' in response.json)
+
+ user = TeraUser.get_user_by_username('test_user_2fa_2')
+ self.assertIsNotNone(user)
+ self.assertTrue(user.user_2fa_enabled)
+ self.assertFalse(user.user_2fa_otp_enabled)
+ self.assertIsNone(user.user_2fa_otp_secret)
+
+ # Post will enable 2fa
+ otp_secret = response.json['otp_secret']
+ otp_code = pyotp.TOTP(response.json['otp_secret']).now()
+ params = {'otp_secret': response.json['otp_secret'], 'otp_code': otp_code}
+ response = self.test_client.post(self.test_endpoint, json=params)
+ self.assertEqual(200, response.status_code)
+
+ # Verify response
+ self.assertTrue('redirect_url' in response.json)
+ self.assertTrue('login_validate_2fa' in response.json['redirect_url'])
+
+ # Reload user and verify 2fa is enabled properly
+ user = TeraUser.get_user_by_username('test_user_2fa_2')
+ self.assertIsNotNone(user)
+ self.assertTrue(user.user_2fa_enabled)
+ self.assertTrue(user.user_2fa_otp_enabled)
+ self.assertIsNotNone(user.user_2fa_otp_secret)
+ self.assertEqual(otp_secret, user.user_2fa_otp_secret)
+
+ def test_get_endpoint_login_user2_http_auth_should_fail_after_post_with_wrong_code(self):
+ with self._flask_app.app_context():
+
+ # First login to create session
+ response = self._login_user('test_user_2fa_2', 'Password12345!')
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('redirect_url' in response.json)
+ self.assertTrue('login_setup_2fa' in response.json['redirect_url'])
+
+ # Then try to setup 2fa
+ response = self._get_with_user_http_auth(self.test_client)
+ self.assertEqual(200, response.status_code)
+
+ self.assertTrue('qr_code' in response.json)
+ self.assertTrue('otp_secret' in response.json)
+
+ user = TeraUser.get_user_by_username('test_user_2fa_2')
+ self.assertIsNotNone(user)
+ self.assertTrue(user.user_2fa_enabled)
+ self.assertFalse(user.user_2fa_otp_enabled)
+ self.assertIsNone(user.user_2fa_otp_secret)
+
+ # Post will fail with wrong code
+ params = {'otp_secret': response.json['otp_secret'], 'otp_code': '123456'}
+ response = self.test_client.post(self.test_endpoint, json=params)
+ self.assertEqual(401, response.status_code)
+
+ def test_get_endpoint_login_user2_http_auth_should_fail_after_post_with_wrong_secret(self):
+ with self._flask_app.app_context():
+
+ # First login to create session
+ response = self._login_user('test_user_2fa_2', 'Password12345!')
+ self.assertEqual(200, response.status_code)
+ self.assertTrue('redirect_url' in response.json)
+ self.assertTrue('login_setup_2fa' in response.json['redirect_url'])
+
+ # Then try to setup 2fa
+ response = self._get_with_user_http_auth(self.test_client)
+ self.assertEqual(200, response.status_code)
+
+ self.assertTrue('qr_code' in response.json)
+ self.assertTrue('otp_secret' in response.json)
+
+ user = TeraUser.get_user_by_username('test_user_2fa_2')
+ self.assertIsNotNone(user)
+ self.assertTrue(user.user_2fa_enabled)
+ self.assertFalse(user.user_2fa_otp_enabled)
+ self.assertIsNone(user.user_2fa_otp_secret)
+
+ # Post will fail with wrong secret
+ otp_secret = pyotp.random_base32()
+ otp_code = pyotp.TOTP(otp_secret).now()
+ params = {'otp_secret': response.json['otp_secret'], 'otp_code': otp_code}
+ response = self.test_client.post(self.test_endpoint, json=params)
+ self.assertEqual(401, response.status_code)
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLogout.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLogout.py
index 2d5c37386..a9c3f3aa5 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLogout.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserLogout.py
@@ -1,4 +1,4 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
from opentera.db.models.TeraUser import TeraUser
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryAssets.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryAssets.py
index 8f16ce9b7..dbaf55ff1 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryAssets.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryAssets.py
@@ -1,4 +1,4 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
from opentera.db.models.TeraAsset import TeraAsset
@@ -66,7 +66,7 @@ def test_query_tera_server_assets_no_access(self):
params=params)
self.assertEqual(200, response.status_code)
self.assertEqual(len(response.json), 0)
-
+
def test_query_device_assets_as_admin(self):
with self._flask_app.app_context():
params = {'id_device': 1, 'with_urls': True}
@@ -178,7 +178,7 @@ def test_query_assets_created_by_service_no_access(self):
def test_query_assets_created_by_user_as_admin(self):
with self._flask_app.app_context():
- params = {'id_creator_user': 1, 'with_urls': True}
+ params = {'id_creator_user': 2, 'with_urls': True}
response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
params=params)
self.assertEqual(200, response.status_code)
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryDeviceParticipants.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryDeviceParticipants.py
index cd8d06645..f5f172ae9 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryDeviceParticipants.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryDeviceParticipants.py
@@ -1,6 +1,6 @@
from typing import List
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
from modules.FlaskModule.FlaskModule import flask_app
from opentera.db.models.TeraDevice import TeraDevice
from opentera.db.models.TeraParticipant import TeraParticipant
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryDeviceProjects.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryDeviceProjects.py
index 799fc18aa..65b081151 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryDeviceProjects.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryDeviceProjects.py
@@ -1,4 +1,4 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
from opentera.db.models.TeraDeviceProject import TeraDeviceProject
from opentera.db.models.TeraDeviceSite import TeraDeviceSite
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryDisconnect.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryDisconnect.py
index 4318e492d..daa29886d 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryDisconnect.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryDisconnect.py
@@ -1,4 +1,4 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
from opentera.db.models.TeraDevice import TeraDevice
from opentera.db.models.TeraUser import TeraUser
from opentera.db.models.TeraParticipant import TeraParticipant
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryForms.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryForms.py
index 871afe33c..7e0fea2dd 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryForms.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryForms.py
@@ -1,4 +1,4 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
from opentera.db.models.TeraParticipantGroup import TeraParticipantGroup
from opentera.db.models.TeraParticipant import TeraParticipant
from opentera.db.models.TeraUser import TeraUser
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryParticipantGroup.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryParticipantGroup.py
index 00a4df8c1..a124790e9 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryParticipantGroup.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryParticipantGroup.py
@@ -1,4 +1,4 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
from opentera.db.models.TeraParticipantGroup import TeraParticipantGroup
from opentera.db.models.TeraParticipant import TeraParticipant
from opentera.db.models.TeraSession import TeraSession
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryParticipants.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryParticipants.py
index d26c9e8a4..73ca17a24 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryParticipants.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryParticipants.py
@@ -1,4 +1,4 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
from opentera.db.models.TeraParticipant import TeraParticipant
from opentera.db.models.TeraProject import TeraProject
from opentera.db.models.TeraParticipantGroup import TeraParticipantGroup
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryProjectAccess.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryProjectAccess.py
index aeaf95d88..45fdaee30 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryProjectAccess.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryProjectAccess.py
@@ -1,4 +1,4 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
class UserQueryProjectAccessTest(BaseUserAPITest):
@@ -165,7 +165,7 @@ def test_query_specific_user_group_admins_with_sites(self):
def test_query_specific_user_group_by_users(self):
with self._flask_app.app_context():
- response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
params={'id_user_group': 1, 'by_users': True})
self.assertEqual(200, response.status_code)
self.assertTrue(response.is_json)
@@ -243,7 +243,7 @@ def test_query_specific_user_group_by_users_with_projects_admins(self):
def test_query_specific_user_group_by_users_with_projects_admins_with_sites(self):
with self._flask_app.app_context():
params = {'id_user_group': 4, 'by_users': True, 'with_empty': True, 'admins': True, 'with_sites': True}
- response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
params=params)
self.assertEqual(200, response.status_code)
self.assertTrue(response.is_json)
@@ -291,7 +291,7 @@ def test_query_specific_user_group_with_projects_admins_with_sites(self):
def test_query_specific_project(self):
with self._flask_app.app_context():
- response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
params={'id_project': 1})
self.assertEqual(200, response.status_code)
self.assertTrue(response.is_json)
@@ -471,7 +471,7 @@ def test_query_specific_project_by_users_with_user_groups_admins_with_sites(self
with self._flask_app.app_context():
params = {'id_project': 2, 'by_users': True, 'with_empty': True, 'admins': True, 'with_sites': True}
- response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
params=params)
self.assertEqual(200, response.status_code)
self.assertTrue(response.is_json)
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryProjects.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryProjects.py
index f896dc261..d94beab22 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryProjects.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryProjects.py
@@ -1,4 +1,4 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
from opentera.db.models.TeraProject import TeraProject
from opentera.db.models.TeraServiceProject import TeraServiceProject
from opentera.db.models.TeraUser import TeraUser
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServerSettings.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServerSettings.py
index fb2fcb8b7..37a8ecf7d 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServerSettings.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServerSettings.py
@@ -1,4 +1,4 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
from opentera.db.models.TeraServerSettings import TeraServerSettings
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServiceAccess.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServiceAccess.py
index fac5fa534..75ca96c36 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServiceAccess.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServiceAccess.py
@@ -1,6 +1,7 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
from opentera.db.models.TeraUser import TeraUser
from opentera.db.models.TeraServiceAccess import TeraServiceAccess
+from opentera.db.models.TeraService import TeraService
class UserQueryServiceAccessTest(BaseUserAPITest):
@@ -64,21 +65,22 @@ def test_query_no_params_as_admin(self):
def test_query_for_service(self):
with self._flask_app.app_context():
+ service : TeraService = TeraService.get_service_by_key('BureauActif')
response = self._get_with_user_http_auth(self.test_client, username='user4', password='user4',
- params={'id_service': 4})
+ params={'id_service': service.id_service})
self.assertEqual(200, response.status_code)
self.assertTrue(response.is_json)
self.assertEqual(0, len(response.json))
response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
- params={'id_service': 4})
+ params={'id_service': service.id_service})
self.assertEqual(200, response.status_code)
self.assertTrue(response.is_json)
self.assertEqual(2, len(response.json))
for data_item in response.json:
self._checkJson(json_data=data_item)
- self.assertEqual(data_item['id_service'], 4)
+ self.assertEqual(data_item['id_service'], service.id_service)
def test_query_for_user_group(self):
with self._flask_app.app_context():
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServiceConfigs.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServiceConfigs.py
index 1a64e784e..f169aae5f 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServiceConfigs.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServiceConfigs.py
@@ -1,4 +1,5 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
+from opentera.db.models.TeraService import TeraService
class UserQueryServiceConfigsTest(BaseUserAPITest):
@@ -232,41 +233,43 @@ def test_query_for_participant_as_admin(self):
def test_query_specific_as_admin(self):
with self._flask_app.app_context():
+ service : TeraService = TeraService.get_service_by_key('OpenTeraServer')
response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
- params={'id_service': 1, 'id_user': 1})
+ params={'id_service': service.id_service, 'id_user': 1})
self.assertEqual(200, response.status_code)
self.assertTrue(response.is_json)
self.assertEqual(1, len(response.json))
for data_item in response.json:
self._checkJson(json_data=data_item)
self.assertEqual(data_item['id_user'], 1)
- self.assertEqual(data_item['id_service'], 1)
+ self.assertEqual(data_item['id_service'], service.id_service)
response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
- params={'id_service': 1, 'id_user': 2})
+ params={'id_service': service.id_service, 'id_user': 2})
self.assertEqual(200, response.status_code)
self.assertTrue(response.is_json)
self.assertEqual(0, len(response.json))
+ service: TeraService = TeraService.get_service_by_key('VideoRehabService')
response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
- params={'id_service': 5, 'id_participant': 1})
+ params={'id_service': service.id_service, 'id_participant': 1})
self.assertEqual(200, response.status_code)
self.assertTrue(response.is_json)
self.assertEqual(1, len(response.json))
for data_item in response.json:
self._checkJson(json_data=data_item)
self.assertEqual(data_item['id_participant'], 1)
- self.assertEqual(data_item['id_service'], 5)
+ self.assertEqual(data_item['id_service'], service.id_service)
response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
- params={'id_service': 5, 'id_device': 1})
+ params={'id_service': service.id_service, 'id_device': 1})
self.assertEqual(200, response.status_code)
self.assertTrue(response.is_json)
self.assertEqual(1, len(response.json))
for data_item in response.json:
self._checkJson(json_data=data_item)
self.assertEqual(data_item['id_device'], 1)
- self.assertEqual(data_item['id_service'], 5)
+ self.assertEqual(data_item['id_service'], service.id_service)
def test_post_and_delete(self):
with self._flask_app.app_context():
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServiceProjects.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServiceProjects.py
index 97337d4d2..a9e80f7fc 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServiceProjects.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServiceProjects.py
@@ -1,4 +1,5 @@
-from BaseUserAPITest import BaseUserAPITest
+from opentera.db.models import TeraProject
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
from opentera.db.models.TeraParticipant import TeraParticipant
from opentera.db.models.TeraSite import TeraSite
from opentera.db.models.TeraSessionTypeProject import TeraSessionTypeProject
@@ -118,7 +119,8 @@ def test_query_service_as_admin(self):
self.assertTrue(response.is_json)
self.assertEqual(0, len(response.json))
- params = {'id_service': 5} # Videorehab service
+ service: TeraService = TeraService.get_service_by_key('VideoRehabService')
+ params = {'id_service': service.id_service} # Videorehab service
response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
params=params)
self.assertEqual(200, response.status_code)
@@ -130,7 +132,8 @@ def test_query_service_as_admin(self):
def test_query_service_with_projects_as_admin(self):
with self._flask_app.app_context():
- params = {'id_service': 3, 'with_projects': 1} # File transfer service
+ service: TeraService = TeraService.get_service_by_key('FileTransferService')
+ params = {'id_service': service.id_service, 'with_projects': 1} # File transfer service
response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
params=params)
self.assertEqual(200, response.status_code)
@@ -142,7 +145,8 @@ def test_query_service_with_projects_as_admin(self):
def test_query_service_with_projects_and_with_sites_as_admin(self):
with self._flask_app.app_context():
- params = {'id_service': 3, 'with_projects': 1, 'with_sites': 1} # File transfer service
+ service: TeraService = TeraService.get_service_by_key('FileTransferService')
+ params = {'id_service': service.id_service, 'with_projects': 1, 'with_sites': 1} # File transfer service
response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
params=params)
self.assertEqual(200, response.status_code)
@@ -156,7 +160,8 @@ def test_query_service_with_projects_and_with_sites_as_admin(self):
def test_query_service_with_roles_as_admin(self):
with self._flask_app.app_context():
- params = {'id_service': 5, 'with_roles': 1} # Videorehab service
+ service: TeraService = TeraService.get_service_by_key('VideoRehabService')
+ params = {'id_service': service.id_service, 'with_roles': 1} # Videorehab service
response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
params=params)
self.assertEqual(200, response.status_code)
@@ -226,14 +231,16 @@ def test_query_service_as_user(self):
self.assertTrue(response.is_json)
self.assertEqual(0, len(response.json))
- params = {'id_service': 3} # File transfer service
+ service: TeraService = TeraService.get_service_by_key('FileTransferService')
+ params = {'id_service': service.id_service} # File transfer service
response = self._get_with_user_http_auth(self.test_client, username='user4', password='user4',
params=params)
self.assertEqual(200, response.status_code)
self.assertTrue(response.is_json)
self.assertEqual(0, len(response.json))
- params = {'id_service': 5} # Videorehab service
+ service: TeraService = TeraService.get_service_by_key('VideoRehabService')
+ params = {'id_service': service.id_service} # Videorehab service
response = self._get_with_user_http_auth(self.test_client, username='user', password='user',
params=params)
self.assertEqual(200, response.status_code)
@@ -245,7 +252,8 @@ def test_query_service_as_user(self):
def test_query_service_with_projects_as_user(self):
with self._flask_app.app_context():
- params = {'id_service': 5, 'with_projects': 1} # Videorehab service
+ service: TeraService = TeraService.get_service_by_key('VideoRehabService')
+ params = {'id_service': service.id_service, 'with_projects': 1} # Videorehab service
response = self._get_with_user_http_auth(self.test_client, username='user', password='user',
params=params)
self.assertEqual(200, response.status_code)
@@ -257,7 +265,8 @@ def test_query_service_with_projects_as_user(self):
def test_query_list_as_user(self):
with self._flask_app.app_context():
- params = {'id_service': 5, 'list': 1}
+ service : TeraService = TeraService.get_service_by_key('VideoRehabService')
+ params = {'id_service': service.id_service, 'list': 1}
response = self._get_with_user_http_auth(self.test_client, username='user4', password='user4',
params=params)
@@ -288,30 +297,32 @@ def test_post_service(self):
json=json_data)
self.assertEqual(400, response.status_code, msg="Missing id_service")
- json_data = {'service': {'id_service': 3}}
+ service: TeraService = TeraService.get_service_by_key('FileTransferService')
+ json_data = {'service': {'id_service': service.id_service}}
response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
json=json_data)
self.assertEqual(400, response.status_code, msg="Missing projects")
- json_data = {'service': {'id_service': 3, 'projects': []}}
+ json_data = {'service': {'id_service': service.id_service, 'projects': []}}
response = self._post_with_user_http_auth(self.test_client, username='user', password='user',
json=json_data)
self.assertEqual(403, response.status_code, msg="Only super admins can change things here")
- json_data = {'service': {'id_service': 3, 'projects': []}}
+ json_data = {'service': {'id_service': service.id_service, 'projects': []}}
response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
json=json_data)
self.assertEqual(200, response.status_code, msg="Remove from all projects OK")
- params = {'id_service': 3} # File transfer service
+ params = {'id_service': service.id_service} # File transfer service
response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
params=params)
self.assertEqual(200, response.status_code)
self.assertEqual(0, len(response.json)) # Everything was deleted!
- json_data = {'service': {'id_service': 3, 'projects': [{'id_project': 1},
- {'id_project': 2},
- {'id_project': 3}]}}
+ json_data = {'service': {'id_service': service.id_service,
+ 'projects': [{'id_project': 1},
+ {'id_project': 2},
+ {'id_project': 3}]}}
response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
json=json_data)
self.assertEqual(200, response.status_code, msg="Add all projects OK")
@@ -321,14 +332,18 @@ def test_post_service(self):
self.assertEqual(200, response.status_code)
self.assertEqual(3, len(response.json)) # Everything was added
- json_data = {'service': {'id_service': 3, 'projects': [{'id_project': 1},
- {'id_project': 2}]}}
+ json_data = {'service': {'id_service': service.id_service,
+ 'projects': [{'id_project': 1},
+ {'id_project': 2}]}}
response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
json=json_data)
self.assertEqual(200, response.status_code, msg="Remove one project")
- self.assertIsNotNone(TeraServiceProject.get_service_project_for_service_project(project_id=2, service_id=3))
- self.assertIsNotNone(TeraServiceProject.get_service_project_for_service_project(project_id=1, service_id=3))
- self.assertIsNone(TeraServiceProject.get_service_project_for_service_project(project_id=3, service_id=3))
+ self.assertIsNotNone(TeraServiceProject.get_service_project_for_service_project(project_id=2,
+ service_id=service.id_service))
+ self.assertIsNotNone(TeraServiceProject.get_service_project_for_service_project(project_id=1,
+ service_id=service.id_service))
+ self.assertIsNone(TeraServiceProject.get_service_project_for_service_project(project_id=3,
+ service_id=service.id_service))
response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
params=params)
@@ -354,51 +369,63 @@ def test_post_project(self):
json=json_data)
self.assertEqual(400, response.status_code, msg="Missing id_project")
- json_data = {'project': {'id_project': 2}}
+ project2 : TeraProject = TeraProject.get_project_by_projectname('Default Project #2')
+ json_data = {'project': {'id_project': project2.id_project}}
response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
json=json_data)
self.assertEqual(400, response.status_code, msg="Missing services")
- json_data = {'project': {'id_project': 2, 'services': []}}
+ json_data = {'project': {'id_project': project2.id_project, 'services': []}}
response = self._post_with_user_http_auth(self.test_client, username='user', password='user',
json=json_data)
self.assertEqual(403, response.status_code, msg="Only site admins can change things here")
- json_data = {'project': {'id_project': 2, 'services': []}}
+ json_data = {'project': {'id_project': project2.id_project, 'services': []}}
response = self._post_with_user_http_auth(self.test_client, username='siteadmin', password='siteadmin',
json=json_data)
self.assertEqual(200, response.status_code, msg="Remove all services OK")
- params = {'id_project': 2}
+ params = {'id_project': project2.id_project}
response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin', params=params)
self.assertEqual(200, response.status_code)
self.assertEqual(0, len(response.json)) # Everything was deleted!
- json_data = {'project': {'id_project': 2, 'services': [{'id_service': 2},
- {'id_service': 3},
- {'id_service': 4},
- {'id_service': 5}]}}
+ logging_service: TeraService = TeraService.get_service_by_key('LoggingService')
+ file_service: TeraService = TeraService.get_service_by_key('FileTransferService')
+ bureau_service: TeraService = TeraService.get_service_by_key('BureauActif')
+ video_service: TeraService = TeraService.get_service_by_key('VideoRehabService')
+ json_data = {'project': {'id_project': project2.id_project,
+ 'services': [{'id_service': logging_service.id_service},
+ {'id_service': file_service.id_service},
+ {'id_service': bureau_service.id_service},
+ {'id_service': video_service.id_service}]}}
response = self._post_with_user_http_auth(self.test_client, username='siteadmin', password='siteadmin',
json=json_data)
self.assertEqual(403, response.status_code, msg="One service not allowed - not part of the site project!")
- json_data = {'project': {'id_project': 2, 'services': [{'id_service': 2},
- {'id_service': 3},
- {'id_service': 5}]}}
+ json_data = {'project': {'id_project': project2.id_project,
+ 'services': [{'id_service': logging_service.id_service},
+ {'id_service': file_service.id_service},
+ {'id_service': video_service.id_service}]}}
response = self._post_with_user_http_auth(self.test_client, username='siteadmin', password='siteadmin',
json=json_data)
self.assertEqual(200, response.status_code, msg="New service association OK")
- self.assertIsNone(TeraServiceProject.get_service_project_for_service_project(project_id=2, service_id=4))
- self.assertIsNotNone(TeraServiceProject.get_service_project_for_service_project(project_id=2, service_id=2))
- self.assertIsNotNone(TeraServiceProject.get_service_project_for_service_project(project_id=2, service_id=3))
- self.assertIsNotNone(TeraServiceProject.get_service_project_for_service_project(project_id=2, service_id=5))
+ self.assertIsNone(TeraServiceProject.get_service_project_for_service_project(project_id=project2.id_project,
+ service_id=bureau_service.id_service))
+ self.assertIsNotNone(TeraServiceProject.get_service_project_for_service_project(project_id=project2.id_project,
+ service_id=logging_service.id_service))
+ self.assertIsNotNone(TeraServiceProject.get_service_project_for_service_project(project_id=project2.id_project,
+ service_id=file_service.id_service))
+ self.assertIsNotNone(TeraServiceProject.get_service_project_for_service_project(project_id=project2.id_project,
+ service_id=video_service.id_service))
response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
params=params)
self.assertEqual(200, response.status_code)
self.assertEqual(3, len(response.json)) # Everything was added
- json_data = {'project': {'id_project': 2, 'services': [{'id_service': 3}]}}
+ json_data = {'project': {'id_project': project2.id_project,
+ 'services': [{'id_service': file_service.id_service}]}}
response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
json=json_data)
self.assertEqual(200, response.status_code, msg="Remove 1 service")
@@ -420,17 +447,19 @@ def test_post_service_project_and_delete(self):
json=json_data)
self.assertEqual(400, response.status_code, msg="Badly formatted request")
- json_data = {'service_project': {'id_project': 1, 'id_service': 4}}
+ service: TeraService = TeraService.get_service_by_key('BureauActif')
+ json_data = {'service_project': {'id_project': 1, 'id_service': service.id_service}}
response = self._post_with_user_http_auth(self.test_client, username='user', password='user',
json=json_data)
self.assertEqual(403, response.status_code, msg="Only site admins can change things here")
- json_data = {'service_project': {'id_project': 1, 'id_service': 4}}
+ json_data = {'service_project': {'id_project': 1, 'id_service': service.id_service}}
response = self._post_with_user_http_auth(self.test_client, username='siteadmin', password='siteadmin',
json=json_data)
self.assertEqual(403, response.status_code, msg="Add new association not OK - project not part of the site")
- json_data = {'service_project': {'id_project': 1, 'id_service': 2}}
+ service: TeraService = TeraService.get_service_by_key('LoggingService')
+ json_data = {'service_project': {'id_project': 1, 'id_service': service.id_service}}
response = self._post_with_user_http_auth(self.test_client, username='siteadmin', password='siteadmin',
json=json_data)
self.assertEqual(200, response.status_code, msg="Add new association OK")
@@ -443,7 +472,7 @@ def test_post_service_project_and_delete(self):
current_id = None
for sp in response.json:
- if sp['id_service'] == 2:
+ if sp['id_service'] == service.id_service:
current_id = sp['id_service_project']
break
self.assertFalse(current_id is None)
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServiceRoles.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServiceRoles.py
index 2cec8e421..a2a0faa87 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServiceRoles.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServiceRoles.py
@@ -1,4 +1,4 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
from opentera.db.models.TeraServiceRole import TeraServiceRole
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServiceSites.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServiceSites.py
index 289e5ef84..51eecab2c 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServiceSites.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServiceSites.py
@@ -1,5 +1,6 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
from opentera.db.models.TeraServiceSite import TeraServiceSite
+from opentera.db.models.TeraService import TeraService
class UserQueryServiceSitesTest(BaseUserAPITest):
@@ -92,7 +93,8 @@ def test_query_site_with_services_as_admin(self):
params=params)
self.assertEqual(200, response.status_code)
self.assertTrue(response.is_json)
- self.assertEqual(5, len(response.json))
+ target_count = TeraService.get_count() - 1 # Ignore OpenTeraService
+ self.assertEqual(target_count, len(response.json))
for data_item in response.json:
self._checkJson(json_data=data_item)
@@ -106,7 +108,8 @@ def test_query_service_as_admin(self):
self.assertTrue(response.is_json)
self.assertEqual(0, len(response.json))
- params = {'id_service': 5} # Videorehab service
+ service: TeraService = TeraService.get_service_by_key('VideoRehabService')
+ params = {'id_service': service.id_service} # Videorehab service
response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
params=params)
self.assertEqual(200, response.status_code)
@@ -118,7 +121,8 @@ def test_query_service_as_admin(self):
def test_query_service_with_site_as_admin(self):
with self._flask_app.app_context():
- params = {'id_service': 3, 'with_sites': 1}
+ service: TeraService = TeraService.get_service_by_key('FileTransferService')
+ params = {'id_service': service.id_service, 'with_sites': 1}
response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
params=params)
self.assertEqual(200, response.status_code)
@@ -130,7 +134,8 @@ def test_query_service_with_site_as_admin(self):
def test_query_service_with_roles_as_admin(self):
with self._flask_app.app_context():
- params = {'id_service': 3, 'with_roles': 1}
+ service: TeraService = TeraService.get_service_by_key('FileTransferService')
+ params = {'id_service': service.id_service, 'with_roles': 1}
response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
params=params)
self.assertEqual(200, response.status_code)
@@ -204,14 +209,16 @@ def test_query_service_as_user(self):
self.assertTrue(response.is_json)
self.assertEqual(0, len(response.json))
- params = {'id_service': 3} # File transfer service
+ service: TeraService = TeraService.get_service_by_key('FileTransferService')
+ params = {'id_service': service.id_service} # File transfer service
response = self._get_with_user_http_auth(self.test_client, username='user4', password='user4',
params=params)
self.assertEqual(200, response.status_code)
self.assertTrue(response.is_json)
self.assertEqual(0, len(response.json))
- params = {'id_service': 5} # Videorehab service
+ service: TeraService = TeraService.get_service_by_key('VideoRehabService')
+ params = {'id_service': service.id_service} # Videorehab service
response = self._get_with_user_http_auth(self.test_client, username='user', password='user',
params=params)
self.assertEqual(200, response.status_code)
@@ -223,7 +230,8 @@ def test_query_service_as_user(self):
def test_query_service_with_sites_as_user(self):
with self._flask_app.app_context():
- params = {'id_service': 5, 'with_sites': 1} # Videorehab service
+ service: TeraService = TeraService.get_service_by_key('VideoRehabService')
+ params = {'id_service': service.id_service, 'with_sites': 1} # Videorehab service
response = self._get_with_user_http_auth(self.test_client, username='user', password='user',
params=params)
self.assertEqual(200, response.status_code)
@@ -235,7 +243,8 @@ def test_query_service_with_sites_as_user(self):
def test_query_list_as_user(self):
with self._flask_app.app_context():
- params = {'id_service': 5, 'list': 1}
+ service: TeraService = TeraService.get_service_by_key('VideoRehabService')
+ params = {'id_service': service.id_service, 'list': 1}
response = self._get_with_user_http_auth(self.test_client, username='user4', password='user4',
params=params)
@@ -266,12 +275,15 @@ def test_post_service(self):
json=json_data)
self.assertEqual(400, response.status_code, msg="Missing id_service")
- json_data = {'service': {'id_service': 3}}
+ file_service: TeraService = TeraService.get_service_by_key('FileTransferService')
+ bureau_service: TeraService = TeraService.get_service_by_key('BureauActif')
+ video_service: TeraService = TeraService.get_service_by_key('VideoRehabService')
+ json_data = {'service': {'id_service': file_service.id_service}}
response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
json=json_data)
self.assertEqual(400, response.status_code, msg="Missing sites")
- json_data = {'service': {'id_service': 4, 'sites': []}}
+ json_data = {'service': {'id_service': bureau_service.id_service, 'sites': []}}
response = self._post_with_user_http_auth(self.test_client, username='user', password='user',
json=json_data)
self.assertEqual(403, response.status_code, msg="Only super admins can change things here")
@@ -280,24 +292,25 @@ def test_post_service(self):
json=json_data)
self.assertEqual(403, response.status_code, msg="Nope, not site admin either!")
- json_data = {'service': {'id_service': 5, 'sites': []}}
+ json_data = {'service': {'id_service': video_service.id_service, 'sites': []}}
response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
json=json_data)
self.assertEqual(500, response.status_code, msg="Can't remove - has sessions types with sessions")
- json_data = {'service': {'id_service': 4, 'sites': []}}
+ json_data = {'service': {'id_service': bureau_service.id_service, 'sites': []}}
response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
json=json_data)
self.assertEqual(200, response.status_code, msg="Remove from all projects OK")
- params = {'id_service': 4} # Video rehab
+ params = {'id_service': bureau_service.id_service}
response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
params=params)
self.assertEqual(200, response.status_code)
self.assertEqual(0, len(response.json)) # Everything was deleted!
- json_data = {'service': {'id_service': 4, 'sites': [{'id_site': 1},
- {'id_site': 2}]}}
+ json_data = {'service': {'id_service': bureau_service.id_service,
+ 'sites': [{'id_site': 1},
+ {'id_site': 2}]}}
response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
json=json_data)
self.assertEqual(200, response.status_code, msg="Add all sites OK")
@@ -307,20 +320,23 @@ def test_post_service(self):
self.assertEqual(200, response.status_code)
self.assertEqual(2, len(response.json)) # Everything was added
- json_data = {'service': {'id_service': 4, 'sites': [{'id_site': 1}]}}
+ json_data = {'service': {'id_service': bureau_service.id_service, 'sites': [{'id_site': 1}]}}
response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
json=json_data)
self.assertEqual(200, response.status_code, msg="Remove one site")
- self.assertIsNone(TeraServiceSite.get_service_site_for_service_site(site_id=2, service_id=4))
- self.assertIsNotNone(TeraServiceSite.get_service_site_for_service_site(site_id=1, service_id=4))
+ self.assertIsNone(TeraServiceSite.get_service_site_for_service_site(site_id=2,
+ service_id=bureau_service.id_service))
+ self.assertIsNotNone(TeraServiceSite.get_service_site_for_service_site(site_id=1,
+ service_id=bureau_service.id_service))
response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
params=params)
self.assertEqual(200, response.status_code)
self.assertEqual(1, len(response.json))
- json_data = {'service': {'id_service': 4, 'sites': [{'id_site': 1},
- {'id_site': 2}]}}
+ json_data = {'service': {'id_service': bureau_service.id_service,
+ 'sites': [{'id_site': 1},
+ {'id_site': 2}]}}
response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
json=json_data)
self.assertEqual(200, response.status_code, msg="Add all sites OK")
@@ -330,22 +346,23 @@ def test_post_service(self):
self.assertEqual(200, response.status_code)
self.assertEqual(2, len(response.json)) # Back to initial state
- json_data = {'service': {'id_service': 4, 'sites': [{'id_site': 1}]}}
+ json_data = {'service': {'id_service': bureau_service.id_service, 'sites': [{'id_site': 1}]}}
response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
json=json_data)
self.assertEqual(200, response.status_code, msg="Back to initial sites OK")
# Recreate default associations - projects
- json_data = {'service': {'id_service': 3, 'projects': [{'id_project': 1},
- {'id_project': 2},
- {'id_project': 3}
- ]}}
+ json_data = {'service': {'id_service': file_service.id_service,
+ 'projects': [{'id_project': 1},
+ {'id_project': 2},
+ {'id_project': 3}
+ ]}}
response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
json=json_data,
endpoint='/api/user/services/projects')
self.assertEqual(200, response.status_code)
- json_data = {'service': {'id_service': 5, 'projects': [{'id_project': 1}]}}
+ json_data = {'service': {'id_service': video_service.id_service, 'projects': [{'id_project': 1}]}}
response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
json=json_data,
endpoint='/api/user/services/projects')
@@ -410,10 +427,15 @@ def test_post_site(self):
self.assertEqual(200, response.status_code)
self.assertEqual(0, len(response.json)) # Everything was deleted!
- json_data = {'site': {'id_site': 2, 'services': [{'id_service': 2},
- {'id_service': 3},
- {'id_service': 4},
- {'id_service': 5}]}}
+ logging_service: TeraService = TeraService.get_service_by_key('LoggingService')
+ file_service: TeraService = TeraService.get_service_by_key('FileTransferService')
+ bureau_service: TeraService = TeraService.get_service_by_key('BureauActif')
+ video_service: TeraService = TeraService.get_service_by_key('VideoRehabService')
+ json_data = {'site': {'id_site': 2,
+ 'services': [{'id_service': logging_service.id_service},
+ {'id_service': file_service.id_service},
+ {'id_service': bureau_service.id_service},
+ {'id_service': video_service.id_service}]}}
response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
json=json_data)
self.assertEqual(200, response.status_code, msg="Add all services OK")
@@ -423,14 +445,18 @@ def test_post_site(self):
self.assertEqual(200, response.status_code)
self.assertEqual(4, len(response.json)) # Everything was added
- json_data = {'site': {'id_site': 2, 'services': [{'id_service': 3}]}}
+ json_data = {'site': {'id_site': 2, 'services': [{'id_service': file_service.id_service}]}}
response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
json=json_data)
self.assertEqual(200, response.status_code, msg="Remove 1 service")
- self.assertIsNotNone(TeraServiceSite.get_service_site_for_service_site(site_id=2, service_id=3))
- self.assertIsNone(TeraServiceSite.get_service_site_for_service_site(site_id=2, service_id=4))
- self.assertIsNone(TeraServiceSite.get_service_site_for_service_site(site_id=2, service_id=2))
- self.assertIsNone(TeraServiceSite.get_service_site_for_service_site(site_id=2, service_id=5))
+ self.assertIsNotNone(TeraServiceSite.get_service_site_for_service_site(site_id=2,
+ service_id=file_service.id_service))
+ self.assertIsNone(TeraServiceSite.get_service_site_for_service_site(site_id=2,
+ service_id=bureau_service.id_service))
+ self.assertIsNone(TeraServiceSite.get_service_site_for_service_site(site_id=2,
+ service_id=logging_service.id_service))
+ self.assertIsNone(TeraServiceSite.get_service_site_for_service_site(site_id=2,
+ service_id=video_service.id_service))
response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
params=params)
@@ -438,93 +464,98 @@ def test_post_site(self):
self.assertEqual(1, len(response.json)) # Back to the default state
def test_post_service_site_and_delete(self):
- # Service-Project update
- json_data = {'service_site': {}}
- response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
- json=json_data)
- self.assertEqual(400, response.status_code, msg="Badly formatted request")
-
- json_data = {'service_site': {'id_site': 1}}
- response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
- json=json_data)
- self.assertEqual(400, response.status_code, msg="Badly formatted request")
-
- json_data = {'service_site': {'id_site': 1, 'id_service': 4}}
- response = self._post_with_user_http_auth(self.test_client, username='user', password='user',
- json=json_data)
- self.assertEqual(403, response.status_code, msg="Only super admins can change things here")
-
- response = self._post_with_user_http_auth(self.test_client, username='siteadmin', password='siteadmin',
- json=json_data)
- self.assertEqual(403, response.status_code, msg="Nope, not site admin either!")
-
- response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
- json=json_data)
- self.assertEqual(200, response.status_code, msg="Add new association OK")
-
- params = {'id_site': 1}
- response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin', params=params)
- self.assertEqual(200, response.status_code)
- self.assertEqual(4, len(response.json))
-
- current_id = None
- for sp in response.json:
- if sp['id_service'] == 4:
- current_id = sp['id_service_site']
- break
- self.assertFalse(current_id is None)
-
- params = {'id': current_id}
- response = self._delete_with_user_http_auth(self.test_client, username='user', password='user',
- params=params)
- self.assertEqual(403, response.status_code, msg="Delete denied")
-
- response = self._delete_with_user_http_auth(self.test_client, username='siteadmin', password='siteadmin',
- params=params)
- self.assertEqual(403, response.status_code, msg="Delete still denied")
-
- response = self._delete_with_user_http_auth(self.test_client, username='admin', password='admin',
- params=params)
- self.assertEqual(200, response.status_code, msg="Delete OK")
-
- params = {'id_site': 1}
- response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin', params=params)
- self.assertEqual(200, response.status_code)
- self.assertEqual(3, len(response.json)) # Back to initial state!
-
- # Recreate default associations - projects
- json_data = {'service': {'id_service': 3, 'projects': [{'id_project': 1},
- {'id_project': 2},
- {'id_project': 3}]}}
- response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
- json=json_data,
- endpoint='/api/user/services/projects')
- self.assertEqual(200, response.status_code)
-
- json_data = {'service': {'id_service': 5, 'projects': [{'id_project': 1}]}}
- response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
- json=json_data,
- endpoint='/api/user/services/projects')
- self.assertEqual(200, response.status_code)
-
- # Recreate defaults associations - session types
- json_data = {'site': {'id_site': 1, 'sessiontypes': [{'id_session_type': 1},
- {'id_session_type': 2},
- {'id_session_type': 3},
- {'id_session_type': 4},
- {'id_session_type': 5}]}}
- response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin', json=json_data,
- endpoint='/api/user/sessiontypes/sites')
- self.assertEqual(200, response.status_code)
-
- json_data = {'session_type_project': [{'id_session_type': 1, 'id_project': 1},
- {'id_session_type': 2, 'id_project': 1},
- {'id_session_type': 3, 'id_project': 1},
- {'id_session_type': 4, 'id_project': 1},
- {'id_session_type': 5, 'id_project': 1}]}
- response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin', json=json_data,
- endpoint='/api/user/sessiontypes/projects')
- self.assertEqual(200, response.status_code)
+ with self._flask_app.app_context():
+ # Service-Project update
+ json_data = {'service_site': {}}
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(400, response.status_code, msg="Badly formatted request")
+
+ json_data = {'service_site': {'id_site': 1}}
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(400, response.status_code, msg="Badly formatted request")
+
+ service: TeraService = TeraService.get_service_by_key('BureauActif')
+ json_data = {'service_site': {'id_site': 1, 'id_service': service.id_service}}
+ response = self._post_with_user_http_auth(self.test_client, username='user', password='user',
+ json=json_data)
+ self.assertEqual(403, response.status_code, msg="Only super admins can change things here")
+
+ response = self._post_with_user_http_auth(self.test_client, username='siteadmin', password='siteadmin',
+ json=json_data)
+ self.assertEqual(403, response.status_code, msg="Nope, not site admin either!")
+
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(200, response.status_code, msg="Add new association OK")
+
+ params = {'id_site': 1}
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin', params=params)
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(4, len(response.json))
+
+ current_id = None
+ for sp in response.json:
+ if sp['id_service'] == service.id_service:
+ current_id = sp['id_service_site']
+ break
+ self.assertFalse(current_id is None)
+
+ params = {'id': current_id}
+ response = self._delete_with_user_http_auth(self.test_client, username='user', password='user',
+ params=params)
+ self.assertEqual(403, response.status_code, msg="Delete denied")
+
+ response = self._delete_with_user_http_auth(self.test_client, username='siteadmin', password='siteadmin',
+ params=params)
+ self.assertEqual(403, response.status_code, msg="Delete still denied")
+
+ response = self._delete_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params=params)
+ self.assertEqual(200, response.status_code, msg="Delete OK")
+
+ params = {'id_site': 1}
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin', params=params)
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(3, len(response.json)) # Back to initial state!
+
+ # Recreate default associations - projects
+ service: TeraService = TeraService.get_service_by_key('FileTransferService')
+ json_data = {'service': {'id_service': service.id_service,
+ 'projects': [{'id_project': 1},
+ {'id_project': 2},
+ {'id_project': 3}]}}
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data,
+ endpoint='/api/user/services/projects')
+ self.assertEqual(200, response.status_code)
+
+ service: TeraService = TeraService.get_service_by_key('VideoRehabService')
+ json_data = {'service': {'id_service': service.id_service, 'projects': [{'id_project': 1}]}}
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data,
+ endpoint='/api/user/services/projects')
+ self.assertEqual(200, response.status_code)
+
+ # Recreate defaults associations - session types
+ json_data = {'site': {'id_site': 1, 'sessiontypes': [{'id_session_type': 1},
+ {'id_session_type': 2},
+ {'id_session_type': 3},
+ {'id_session_type': 4},
+ {'id_session_type': 5}]}}
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin', json=json_data,
+ endpoint='/api/user/sessiontypes/sites')
+ self.assertEqual(200, response.status_code)
+
+ json_data = {'session_type_project': [{'id_session_type': 1, 'id_project': 1},
+ {'id_session_type': 2, 'id_project': 1},
+ {'id_session_type': 3, 'id_project': 1},
+ {'id_session_type': 4, 'id_project': 1},
+ {'id_session_type': 5, 'id_project': 1}]}
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin', json=json_data,
+ endpoint='/api/user/sessiontypes/projects')
+ self.assertEqual(200, response.status_code)
def _checkJson(self, json_data, minimal=False):
self.assertGreater(len(json_data), 0)
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServices.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServices.py
index b3e568542..e1cc68965 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServices.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryServices.py
@@ -1,4 +1,4 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
from opentera.db.models.TeraServiceSite import TeraServiceSite
from opentera.db.models.TeraServiceProject import TeraServiceProject
from opentera.db.models.TeraSession import TeraSession
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySessionEvents.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySessionEvents.py
index 0bdbfcaa5..9913ebb2c 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySessionEvents.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySessionEvents.py
@@ -1,4 +1,4 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
class UserQuerySessionEventsTest(BaseUserAPITest):
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySessionTypeProject.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySessionTypeProject.py
index 4e0a9e6ae..7cc8bf291 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySessionTypeProject.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySessionTypeProject.py
@@ -1,4 +1,4 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
from opentera.db.models.TeraSessionType import TeraSessionType
from opentera.db.models.TeraSessionTypeProject import TeraSessionTypeProject
from opentera.db.models.TeraSession import TeraSession
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySessionTypeServices.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySessionTypeServices.py
new file mode 100644
index 000000000..57d005945
--- /dev/null
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySessionTypeServices.py
@@ -0,0 +1,293 @@
+from opentera.db.models import TeraSessionTypeServices
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
+from opentera.db.models.TeraSessionType import TeraSessionType
+from opentera.db.models.TeraService import TeraService
+from opentera.db.models.TeraUser import TeraUser
+from modules.DatabaseModule.DBManagerTeraUserAccess import DBManagerTeraUserAccess
+
+
+class UserQuerySessionTypeServicesTest(BaseUserAPITest):
+ test_endpoint = '/api/user/sessiontypes/services'
+
+ def setUp(self):
+ super().setUp()
+
+ def tearDown(self):
+ super().tearDown()
+
+ def test_no_auth(self):
+ with self._flask_app.app_context():
+ response = self.test_client.get(self.test_endpoint)
+ self.assertEqual(401, response.status_code)
+
+ def test_post_no_auth(self):
+ with self._flask_app.app_context():
+ response = self.test_client.post(self.test_endpoint)
+ self.assertEqual(401, response.status_code)
+
+ def test_delete_no_auth(self):
+ with self._flask_app.app_context():
+ response = self.test_client.delete(self.test_endpoint)
+ self.assertEqual(401, response.status_code)
+
+ def test_get_endpoint_invalid_http_auth(self):
+ with self._flask_app.app_context():
+ response = self._get_with_user_http_auth(self.test_client, username='invalid', password='invalid')
+ self.assertEqual(401, response.status_code)
+
+ def test_get_endpoint_invalid_token_auth(self):
+ with self._flask_app.app_context():
+ response = self._get_with_user_token_auth(self.test_client, token='invalid')
+ self.assertEqual(401, response.status_code)
+
+ def test_post_endpoint_invalid_token_auth(self):
+ with self._flask_app.app_context():
+ response = self._post_with_user_token_auth(self.test_client, token='invalid')
+ self.assertEqual(401, response.status_code)
+
+ def test_post_endpoint_invalid_http_auth(self):
+ with self._flask_app.app_context():
+ response = self._post_with_user_http_auth(self.test_client, username='invalid', password='invalid')
+ self.assertEqual(401, response.status_code)
+
+ def test_delete_endpoint_invalid_http_auth(self):
+ with self._flask_app.app_context():
+ response = self._delete_with_user_http_auth(self.test_client, username='invalid', password='invalid')
+ self.assertEqual(401, response.status_code)
+
+ def test_delete_endpoint_invalid_token_auth(self):
+ with self._flask_app.app_context():
+ response = self._delete_with_user_token_auth(self.test_client, token='invalid')
+ self.assertEqual(401, response.status_code)
+
+ def test_query_no_params_as_admin(self):
+ with self._flask_app.app_context():
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin')
+ self.assertEqual(400, response.status_code)
+
+ def test_query_as_user(self):
+ with self._flask_app.app_context():
+ response = self._get_with_user_http_auth(self.test_client, username='user', password='user')
+ self.assertEqual(400, response.status_code)
+
+ def test_query_forbidden_session_type(self):
+ with self._flask_app.app_context():
+ params = {'id_session_type': 1}
+ response = self._get_with_user_http_auth(self.test_client, params=params,
+ username='user4', password='user4')
+ self.assertEqual(200, response.status_code)
+ self.assertTrue(len(response.json) == 0)
+
+ def test_query_session_type(self):
+ with self._flask_app.app_context():
+ session_types = TeraSessionType.query.all()
+ for session_type in session_types:
+ params = {'id_session_type': session_type.id_session_type}
+ response = self._get_with_user_http_auth(self.test_client, params=params,
+ username='admin', password='admin')
+ self.assertEqual(200, response.status_code)
+ self.assertTrue(len(response.json) == len(session_type.session_type_secondary_services))
+ if len(response.json) > 0:
+ for data in response.json:
+ self._validate_json(data)
+
+ def test_query_session_type_with_services(self):
+ with self._flask_app.app_context():
+ session_types = TeraSessionType.query.all()
+ services = TeraService.query.all()
+ user: TeraUser = TeraUser.get_user_by_username('user')
+ access = DBManagerTeraUserAccess(user)
+ limited_services = access.get_accessible_services()
+ for session_type in session_types:
+ params = {'id_session_type': session_type.id_session_type, 'with_services': 1}
+ response = self._get_with_user_http_auth(self.test_client, params=params,
+ username='admin', password='admin')
+ self.assertEqual(200, response.status_code)
+ self.assertTrue(len(response.json) == len(services))
+ response = self._get_with_user_http_auth(self.test_client, params=params,
+ username='user', password='user')
+ self.assertEqual(200, response.status_code)
+ self.assertTrue(len(response.json) == len(limited_services))
+
+ def test_query_session_type_list(self):
+ with self._flask_app.app_context():
+ session_types = TeraSessionType.query.all()
+ for session_type in session_types:
+ params = {'id_session_type': session_type.id_session_type, 'list': 1}
+ response = self._get_with_user_http_auth(self.test_client, params=params,
+ username='admin', password='admin')
+ self.assertEqual(200, response.status_code)
+ self.assertTrue(len(response.json) == len(session_type.session_type_secondary_services))
+ if len(response.json) > 0:
+ for data in response.json:
+ self._validate_json(data, minimal=True)
+
+ def test_post_session_type(self):
+ with self._flask_app.app_context():
+ # New with minimal infos
+ json_data = {}
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(400, response.status_code, msg="Missing everything") # Missing
+
+ json_data = {'session_type': {}}
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(400, response.status_code, msg="Missing id_session_type")
+
+ json_data = {'session_type': {'id_session_type': 1}}
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(400, response.status_code, msg="Missing services")
+
+ json_data = {'session_type': {'id_session_type': 1, 'services': []}}
+ response = self._post_with_user_http_auth(self.test_client, username='user', password='user',
+ json=json_data)
+ self.assertEqual(403, response.status_code, msg="Only project/site admins can change things here")
+
+ json_data = {'session_type': {'id_session_type': 5, 'services': []}}
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(200, response.status_code, msg="Remove from all services OK")
+
+ params = {'id_session_type': 5}
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params=params)
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json)) # Everything was deleted!
+
+ json_data = {'session_type': {'id_session_type': 5, 'services': [{'id_service': 1},
+ {'id_service': 2},
+ {'id_service': 3}]}}
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(200, response.status_code, msg="Add all services OK")
+
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params=params)
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(3, len(response.json)) # Everything was added
+
+ json_data = {'session_type': {'id_session_type': 5, 'services': [{'id_service': 1}]}}
+ response = self._post_with_user_http_auth(self.test_client, username='siteadmin', password='siteadmin',
+ json=json_data)
+ self.assertEqual(200, response.status_code, msg="Remove one service")
+ self.assertIsNotNone(TeraSessionTypeServices.get_session_type_service_for_session_type_service(
+ session_type_id=5, service_id=1))
+ self.assertIsNone(TeraSessionTypeServices.get_session_type_service_for_session_type_service(
+ session_type_id=5, service_id=2))
+
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params=params)
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(1, len(response.json))
+
+ def test_post_service(self):
+ with self._flask_app.app_context():
+ service = TeraService.get_service_by_key('EmailService')
+ json_data = {}
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(400, response.status_code, msg="Missing everything") # Missing
+
+ json_data = {'service': {}}
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(400, response.status_code, msg="Missing id_service")
+
+ json_data = {'service': {'id_service': service.id_service}}
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(400, response.status_code, msg="Missing sessiontypes")
+
+ json_data = {'service': {'id_service': service.id_service, 'sessiontypes': []}}
+ response = self._post_with_user_http_auth(self.test_client, username='user', password='user',
+ json=json_data)
+ self.assertEqual(403, response.status_code, msg="Only project/site admins can change things here")
+
+ json_data = {'service': {'id_service': service.id_service, 'sessiontypes': []}}
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(200, response.status_code, msg="Remove all sessiontypes OK")
+
+
+ sts = TeraSessionTypeServices.get_sessions_types_for_service(service.id_service)
+ self.assertEqual(0, len(sts)) # Everything was deleted!
+
+ json_data = {'service': {'id_service': service.id_service, 'sessiontypes': [{'id_session_type': 5},
+ {'id_session_type': 4}]}}
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(200, response.status_code, msg="Add all session types OK")
+
+ sts = TeraSessionTypeServices.get_sessions_types_for_service(service.id_service)
+ self.assertEqual(2, len(sts)) # Everything was added
+
+ json_data = {'service': {'id_service': service.id_service, 'sessiontypes': [{'id_session_type': 4}]}}
+ response = self._post_with_user_http_auth(self.test_client, username='siteadmin', password='siteadmin',
+ json=json_data)
+ self.assertEqual(200, response.status_code, msg="Remove one service")
+ self.assertIsNotNone(TeraSessionTypeServices.get_session_type_service_for_session_type_service(
+ session_type_id=4, service_id=service.id_service))
+ self.assertIsNone(TeraSessionTypeServices.get_session_type_service_for_session_type_service(
+ session_type_id=5, service_id=service.id_service))
+
+ sts = TeraSessionTypeServices.get_sessions_types_for_service(service.id_service)
+ self.assertEqual(1, len(sts))
+
+ # Back to normal
+ for st in sts:
+ TeraSessionTypeServices.delete(st.id_session_type_service)
+
+ def test_post_session_type_service_and_delete(self):
+ with self._flask_app.app_context():
+ service = TeraService.get_service_by_key('EmailService')
+ json_data = {'session_type_service': {}}
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(400, response.status_code, msg="Badly formatted request")
+
+ json_data = {'session_type_service': {'id_service': service.id_service}}
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(400, response.status_code, msg="Badly formatted request")
+
+ json_data = {'session_type_service': {'id_service': service.id_service, 'id_session_type': 4}}
+ response = self._post_with_user_http_auth(self.test_client, username='user4', password='user4',
+ json=json_data)
+ self.assertEqual(403, response.status_code, msg="Only site admins can change things here")
+
+ json_data = {'session_type_service': {'id_service': service.id_service, 'id_session_type': 4}}
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(200, response.status_code, msg="Add new association OK")
+
+ sts = TeraSessionTypeServices.get_sessions_types_for_service(service.id_service)
+
+ current_id = None
+ for st in sts:
+ if st.id_session_type == 4:
+ current_id = st.id_session_type_service
+ self.assertFalse(current_id is None)
+
+ response = self._delete_with_user_http_auth(self.test_client, username='user', password='user',
+ params={'id': current_id})
+ self.assertEqual(403, response.status_code, msg="Delete denied")
+
+ response = self._delete_with_user_http_auth(self.test_client, username='siteadmin', password='siteadmin',
+ params={'id': current_id})
+ self.assertEqual(200, response.status_code, msg="Delete OK")
+
+ sts = TeraSessionTypeServices.get_session_type_service_by_id(current_id)
+ self.assertIsNone(sts)
+
+ def _validate_json(self, json, minimal=False):
+ self.assertTrue('id_session_type' in json)
+ self.assertTrue('id_session_type_service' in json)
+ self.assertTrue('id_service' in json)
+ if minimal:
+ self.assertFalse('session_type_name' in json)
+ self.assertFalse('service_name' in json)
+ else:
+ self.assertTrue('session_type_name' in json)
+ self.assertTrue('service_name' in json)
\ No newline at end of file
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySessionTypeSites.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySessionTypeSites.py
index 08342b292..bb1379573 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySessionTypeSites.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySessionTypeSites.py
@@ -1,4 +1,4 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
from opentera.db.models.TeraSessionType import TeraSessionType
from opentera.db.models.TeraSessionTypeSite import TeraSessionTypeSite
from opentera.db.models.TeraSessionTypeProject import TeraSessionTypeProject
@@ -81,14 +81,14 @@ def test_query_site_as_admin(self):
self.assertEqual(200, response.status_code)
self.assertTrue(response.is_json)
self.assertEqual(len(response.json), 0)
-
+
params = {'id_site': 2}
response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
params=params)
self.assertEqual(200, response.status_code)
self.assertTrue(response.is_json)
self.assertEqual(len(response.json), 1)
-
+
for data_item in response.json:
self._checkJson(json_data=data_item)
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySessionTypes.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySessionTypes.py
index f1b7d83d8..4437e4cb1 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySessionTypes.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySessionTypes.py
@@ -3,6 +3,7 @@
from opentera.db.models.TeraSessionTypeProject import TeraSessionTypeProject
from opentera.db.models.TeraSessionTypeSite import TeraSessionTypeSite
from opentera.db.models.TeraServiceProject import TeraServiceProject
+from opentera.db.models.TeraService import TeraService
from opentera.db.models.TeraSession import TeraSession
import datetime
@@ -115,7 +116,7 @@ def test_query_specific_as_user(self):
def test_query_specific_project_as_admin(self):
with self._flask_app.app_context():
response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
- params="id_project=2")
+ params="id_project=3")
self.assertEqual(200, response.status_code)
self.assertTrue(response.is_json)
self.assertEqual(0, len(response.json))
@@ -311,6 +312,56 @@ def test_post_and_delete(self):
sp = TeraServiceProject.get_service_project_for_service_project(project_id=3, service_id=3)
TeraServiceProject.delete(sp.id_service_project)
+ def test_post_with_additional_services(self):
+ with self._flask_app.app_context():
+ email_service = TeraService.get_service_by_key('EmailService')
+ json_data = {
+ 'session_type': {
+ 'id_session_type': 0,
+ 'id_service': 3,
+ 'session_type_category': 1,
+ 'session_type_color': 'red',
+ 'session_type_name': 'Test',
+ 'session_type_online': True,
+ 'session_type_services': [{'id_service': email_service.id_service}],
+ 'session_type_sites': [{'id_site': 1}]
+ }
+ }
+ response = self._post_with_user_http_auth(self.test_client, username='siteadmin', password='siteadmin',
+ json=json_data)
+ self.assertEqual(403, response.status_code, msg="No admin access to service")
+
+ service = TeraService.get_service_by_key('FileTransferService')
+ json_data['session_type']['session_type_services'] = [{'id_service': service.id_service}]
+ response = self._post_with_user_http_auth(self.test_client, username='siteadmin', password='siteadmin',
+ json=json_data)
+ self.assertEqual(200, response.status_code, msg="Post OK")
+ self.assertEqual(1, len(response.json))
+
+ json_data['session_type']['session_type_services'] = [{'id_service': email_service.id_service},
+ {'id_service': service.id_service}]
+ json_data['session_type']['id_session_type'] = response.json[0]['id_session_type']
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(200, response.status_code, msg="Post OK")
+ self.assertEqual(1, len(response.json))
+ session_type = TeraSessionType.get_session_type_by_id(response.json[0]['id_session_type'])
+ self.assertEqual(2, len(session_type.session_type_secondary_services))
+
+ json_data['session_type']['session_type_services'] = []
+ response = self._post_with_user_http_auth(self.test_client, username='siteadmin', password='siteadmin',
+ json=json_data)
+ self.assertEqual(200, response.status_code, msg="Post OK")
+ self.assertEqual(1, len(response.json))
+
+ session_type = TeraSessionType.get_session_type_by_id(response.json[0]['id_session_type'])
+ self.assertEqual(1, len(session_type.session_type_secondary_services))
+ self.assertEqual(email_service.id_service, session_type.session_type_secondary_services[0].id_service)
+
+ id_session_type = response.json[0]['id_session_type']
+ TeraSessionType.delete(id_session_type)
+
+
def _checkJson(self, json_data, minimal=False):
self.assertGreater(len(json_data), 0)
self.assertTrue(json_data.__contains__('id_session_type'))
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySiteAccess.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySiteAccess.py
index 348d4535b..5ca1cd86f 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySiteAccess.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySiteAccess.py
@@ -1,4 +1,4 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
class UserQuerySiteAccessTest(BaseUserAPITest):
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySites.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySites.py
index 05b5fda38..a86cf7f26 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySites.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQuerySites.py
@@ -1,4 +1,4 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
from opentera.db.models.TeraUser import TeraUser
from opentera.db.models.TeraProject import TeraProject
from opentera.db.models.TeraParticipant import TeraParticipant
@@ -216,6 +216,7 @@ def test_post_and_delete(self):
def _checkJson(self, json_data, minimal=False):
self.assertGreater(len(json_data), 0)
- self.assertTrue(json_data.__contains__('id_site'))
- self.assertTrue(json_data.__contains__('site_name'))
- self.assertTrue(json_data.__contains__('site_role'))
+ self.assertTrue('id_site' in json_data)
+ self.assertTrue('site_name' in json_data)
+ self.assertTrue('site_role' in json_data)
+ self.assertTrue('site_2fa_required' in json_data)
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryTestTypeProjects.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryTestTypeProjects.py
index f71888883..8deb7a9be 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryTestTypeProjects.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryTestTypeProjects.py
@@ -1,4 +1,4 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
from opentera.db.models.TeraTestTypeProject import TeraTestTypeProject
from opentera.db.models.TeraTest import TeraTest
from opentera.db.models.TeraSession import TeraSession
@@ -95,7 +95,7 @@ def test_query_project_as_admin(self):
def test_query_project_with_test_type_as_admin(self):
with self._flask_app.app_context():
- params = {'id_project': 2, 'with_test_types': 1}
+ params = {'id_project': 2, 'with_tests_types': 1}
response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
params=params)
self.assertEqual(200, response.status_code)
@@ -165,21 +165,21 @@ def test_query_list_as_admin(self):
def test_query_project_as_user(self):
with self._flask_app.app_context():
params = {'id_project': 10}
- response = self._get_with_user_http_auth(self.test_client, username='user', password='user',
+ response = self._get_with_user_http_auth(self.test_client, username='user', password='user',
params=params)
self.assertEqual(200, response.status_code)
self.assertTrue(response.is_json)
self.assertEqual(0, len(response.json))
params = {'id_project': 1}
- response = self._get_with_user_http_auth(self.test_client, username='user4', password='user4',
+ response = self._get_with_user_http_auth(self.test_client, username='user4', password='user4',
params=params)
self.assertEqual(200, response.status_code)
self.assertTrue(response.is_json)
self.assertEqual(0, len(response.json))
params = {'id_project': 1}
- response = self._get_with_user_http_auth(self.test_client, username='user', password='user',
+ response = self._get_with_user_http_auth(self.test_client, username='user', password='user',
params=params)
self.assertEqual(200, response.status_code)
self.assertTrue(response.is_json)
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryTestTypeSites.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryTestTypeSites.py
index f840c2dfc..bc64684c5 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryTestTypeSites.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryTestTypeSites.py
@@ -1,4 +1,4 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
from opentera.db.models.TeraTestType import TeraTestType
from opentera.db.models.TeraTestTypeSite import TeraTestTypeSite
from opentera.db.models.TeraTestTypeProject import TeraTestTypeProject
@@ -90,9 +90,9 @@ def test_query_site_as_admin(self):
for data_item in json_data:
self._checkJson(json_data=data_item)
- def test_query_site_with_test_types_as_admin(self):
+ def test_query_site_with_tests_types_as_admin(self):
with self._flask_app.app_context():
- params = {'id_site': 1, 'with_test_types': 1}
+ params = {'id_site': 1, 'with_tests_types': 1}
response = self._get_with_user_http_auth(username='admin', password='admin', params=params,
client=self.test_client)
self.assertEqual(response.status_code, 200)
@@ -179,9 +179,9 @@ def test_query_site_as_user(self):
for data_item in json_data:
self._checkJson(json_data=data_item)
- def test_query_site_with_test_types_as_user(self):
+ def test_query_site_with_tests_types_as_user(self):
with self._flask_app.app_context():
- params = {'id_site': 1, 'with_test_types': 1}
+ params = {'id_site': 1, 'with_tests_types': 1}
response = self._get_with_user_http_auth(username='user', password='user', params=params,
client=self.test_client)
self.assertEqual(response.status_code, 200)
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryTestTypes.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryTestTypes.py
index 04b362b5a..1b53041f6 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryTestTypes.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryTestTypes.py
@@ -1,5 +1,6 @@
from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
from opentera.db.models.TeraTest import TeraTest
+from opentera.db.models.TeraTestType import TeraTestType
import datetime
@@ -75,7 +76,7 @@ def test_query_list_as_admin(self):
for data_item in response.json:
self._checkJson(json_data=data_item, minimal=True)
- def test_query_specific_as_admin(self):
+ def test_query_specific_as_admin_with_id(self):
with self._flask_app.app_context():
response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
params={'id_test_type': 1})
@@ -86,9 +87,48 @@ def test_query_specific_as_admin(self):
for data_item in response.json:
self._checkJson(json_data=data_item)
+ def test_query_specific_as_admin_with_uuid(self):
+ with self._flask_app.app_context():
+ test_type = TeraTestType.get_test_type_by_id(1)
+ self.assertIsNotNone(test_type)
+
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'test_type_uuid': test_type.test_type_uuid})
+ self.assertEqual(200, response.status_code)
+ self.assertTrue(response.is_json)
+ self.assertEqual(1, len(response.json))
+
+ for data_item in response.json:
+ self._checkJson(json_data=data_item)
+
+ def test_query_specific_as_admin_with_invalid_id(self):
+ with self._flask_app.app_context():
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'id_test_type': 1000})
+ self.assertEqual(404, response.status_code)
+
+ def test_query_specific_as_admin_with_invalid_uuid(self):
+ with self._flask_app.app_context():
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'test_type_uuid': 'invalid'})
+ self.assertEqual(404, response.status_code)
+
+ def test_query_specific_as_admin_with_invalid_key(self):
+ with self._flask_app.app_context():
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'test_type_key': 'invalid'})
+ self.assertEqual(404, response.status_code)
+
+ def test_query_as_admin_with_invalid_project(self):
+ with self._flask_app.app_context():
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'id_project': 1000})
+ self.assertEqual(403, response.status_code)
+
+
def test_query_specific_project_as_admin(self):
with self._flask_app.app_context():
- response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
params={'id_project': 2})
self.assertEqual(200, response.status_code)
self.assertTrue(response.is_json)
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryTestsInvitations.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryTestsInvitations.py
new file mode 100644
index 000000000..f33c6c982
--- /dev/null
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryTestsInvitations.py
@@ -0,0 +1,779 @@
+from datetime import datetime, timedelta
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
+from opentera.db.models.TeraTestInvitation import TeraTestInvitation
+from opentera.db.models.TeraParticipant import TeraParticipant
+from opentera.db.models.TeraUser import TeraUser
+from opentera.db.models.TeraDevice import TeraDevice
+from opentera.db.models.TeraTestType import TeraTestType
+from opentera.db.models.TeraSession import TeraSession
+
+class UserQueryTestsInvitationsTest(BaseUserAPITest):
+ """
+ Test UserQueryTestsInvitations API
+ """
+ test_endpoint = '/api/user/tests/invitations'
+
+ def tearDown(self):
+ # Will delete all invitations after each test
+ self._delete_all_invitations()
+ super().tearDown()
+
+ def test_no_auth(self):
+ """
+ Test that a get request with no auth returns 401
+ """
+ with self._flask_app.app_context():
+ response = self.test_client.get(self.test_endpoint)
+ self.assertEqual(401, response.status_code)
+
+ def test_post_no_auth(self):
+ """
+ Test that a post request with no auth returns 401
+ """
+ with self._flask_app.app_context():
+ response = self.test_client.post(self.test_endpoint)
+ self.assertEqual(401, response.status_code)
+
+ def test_delete_no_auth(self):
+ """
+ Test that a delete request with no auth returns 401
+ """
+ with self._flask_app.app_context():
+ response = self.test_client.delete(self.test_endpoint)
+ self.assertEqual(401, response.status_code)
+
+ def test_get_endpoint_invalid_http_auth(self):
+ """
+ Test that a get request with invalid http auth returns 401
+ """
+ with self._flask_app.app_context():
+ response = self._get_with_user_http_auth(self.test_client, username='invalid', password='invalid')
+ self.assertEqual(401, response.status_code)
+
+ def test_get_endpoint_invalid_token_auth(self):
+ """
+ Test that a get request with invalid token returns 401
+ """
+ with self._flask_app.app_context():
+ response = self._get_with_user_token_auth(self.test_client, token='invalid')
+ self.assertEqual(401, response.status_code)
+
+ def test_post_endpoint_invalid_token_auth(self):
+ """
+ Test that a post request with invalid token returns 401
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_user_token_auth(self.test_client, token='invalid')
+ self.assertEqual(401, response.status_code)
+
+ def test_post_endpoint_invalid_http_auth(self):
+ """
+ Test that a post request with invalid http auth returns 401
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_user_http_auth(self.test_client, username='invalid', password='invalid')
+ self.assertEqual(401, response.status_code)
+
+ def test_delete_endpoint_invalid_http_auth(self):
+ """
+ Test that a delete request with invalid http auth returns 401
+ """
+ with self._flask_app.app_context():
+ response = self._delete_with_user_http_auth(self.test_client, username='invalid', password='invalid')
+ self.assertEqual(401, response.status_code)
+
+ def test_delete_endpoint_invalid_token_auth(self):
+ """
+ Test that a delete request with invalid token returns 401
+ """
+ with self._flask_app.app_context():
+ response = self._delete_with_user_token_auth(self.test_client, token='invalid')
+ self.assertEqual(401, response.status_code)
+
+ def test_get_query_bad_params_as_admin(self):
+ """
+ Bad params should return 400
+ """
+ with self._flask_app.app_context():
+ params = {'id_invalid': 1}
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params=params)
+ self.assertEqual(400, response.status_code)
+
+ def test_get_query_no_params_as_admin_returns_all_accessible_invitations(self):
+ """
+ Test that an admin can access all invitations
+ """
+ with self._flask_app.app_context():
+ create_count = 10
+ # Create 10 invitations
+ self._create_invitations(create_count, id_test_type=1, id_project=2, id_user=1)
+
+ # Admin should access all invitations
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin')
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+ for invitation in response.json:
+ self._validate_json(invitation)
+
+ # Verify that invitations are not accessible to no access user
+ response = self._get_with_user_http_auth(self.test_client, username='user4', password='user4')
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+
+ def test_get_query_no_params_as_admin_with_full_returns_all_accessible_invitations(self):
+ """
+ Test that an admin can access all invitations
+ """
+ with self._flask_app.app_context():
+ create_count = 10
+ # Create 10 invitations
+ self._create_invitations(create_count, id_test_type=1, id_project=1, id_user=1)
+
+ # Admin should access all invitations
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin', params={'full': True})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+ for invitation in response.json:
+ self._validate_json(invitation, full=True)
+
+ # Verify that invitations are not accessible to no access user
+ response = self._get_with_user_http_auth(self.test_client, username='user4', password='user4')
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+
+ def test_get_query_no_params_as_admin_with_urls_returns_all_accessible_invitations(self):
+ """
+ Test that an admin can access all invitations
+ """
+ with self._flask_app.app_context():
+ create_count = 10
+ # Create 10 invitations
+ self._create_invitations(create_count, id_test_type=2, id_project=1, id_user=1)
+
+ # Admin should access all invitations
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin', params={'with_urls': True})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+ for invitation in response.json:
+ self._validate_json(invitation, with_urls=True)
+
+ # Verify that invitations are not accessible to no access user
+ response = self._get_with_user_http_auth(self.test_client, username='user4', password='user4')
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+
+ def test_get_query_with_id_test_invitation_or_key_as_admin(self):
+ """
+ Test that an admin can access an invitation with id_test_invitation or test_invitation_key
+ """
+ with self._flask_app.app_context():
+ create_count = 10
+ # Create 10 invitations
+ invitations = self._create_invitations(create_count, id_test_type=1, id_project=1, id_user=1)
+
+ # Admin should access all invitations
+ for invitation in invitations:
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'id_test_invitation': invitation.id_test_invitation})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(1, len(response.json))
+ for invitation_response in response.json:
+ self._validate_json(invitation_response)
+
+ # Also test with test_invitation_key
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'test_invitation_key': invitation.test_invitation_key})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(1, len(response.json))
+ for invitation_response in response.json:
+ self._validate_json(invitation_response)
+
+
+ # Verify that invitations are not accessible to no access user
+ response = self._get_with_user_http_auth(self.test_client, username='user4', password='user4',
+ params={'id_test_invitation': invitation.id_test_invitation})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+
+ # Also test with invalid id_test_invitation
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'id_test_invitation': 0})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+
+ # Also test with invalid test_invitation_key
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'test_invitation_key': 'invalid'})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+
+ def test_get_query_with_id_user_as_admin_returns_only_invitation_for_specific_id_user(self):
+ """
+ Test that an admin can access only invitations for a specific user
+ """
+ with self._flask_app.app_context():
+ create_count = 10
+ # Create 10 invitations
+ self._create_invitations(create_count, id_test_type=1, id_project=1, id_user=1)
+ self._create_invitations(create_count, id_test_type=1, id_project=1, id_user=2)
+
+ # Admin should access only invitations of id_user=1
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'id_user': 1})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+ for invitation in response.json:
+ self._validate_json(invitation)
+
+ # Also test with user_uuid
+ user = TeraUser.get_user_by_id(1)
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'user_uuid': user.user_uuid})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+ for invitation in response.json:
+ self._validate_json(invitation)
+
+ # Verify that invitations are not accessible to no access user
+ response = self._get_with_user_http_auth(self.test_client, username='user4', password='user4',
+ params={'id_user': 1})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+
+ # Also test invalid id_user
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'id_user': 0})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+
+ def test_get_query_with_id_participant_as_admin_returns_only_invitation_for_specific_id_participant(self):
+ """
+ Test that an admin can access only invitations for a specific participant
+ """
+ with self._flask_app.app_context():
+ create_count = 10
+ # Create 10 invitations
+ self._create_invitations(create_count, id_test_type=1, id_project=1, id_participant=1)
+ self._create_invitations(create_count, id_test_type=1, id_project=1, id_participant=2)
+
+ # Admin should access all invitations
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'id_participant': 1})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+ for invitation in response.json:
+ self._validate_json(invitation)
+
+ # Also test with participant_uuid
+ participant = TeraParticipant.get_participant_by_id(1)
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'participant_uuid': participant.participant_uuid})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+ for invitation in response.json:
+ self._validate_json(invitation)
+
+ # Verify that invitations are not accessible to no access user
+ response = self._get_with_user_http_auth(self.test_client, username='user4', password='user4',
+ params={'id_participant': 1})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+
+ # Also test invalid id_participant
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'id_participant': 0})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+
+ def test_get_query_with_id_device_as_admin_returns_only_invitation_for_specific_id_device(self):
+ """
+ Test that an admin can access only invitations for a specific device
+ """
+ with self._flask_app.app_context():
+ create_count = 10
+ # Create 10 invitations
+ self._create_invitations(create_count, id_test_type=1, id_project=2, id_device=1)
+ self._create_invitations(create_count, id_test_type=1, id_project=2, id_device=2)
+
+ # Admin should access all invitations
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'id_device': 1})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+ for invitation in response.json:
+ self._validate_json(invitation)
+
+ # Also test with device_uuid
+ device = TeraDevice.get_device_by_id(1)
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'device_uuid': device.device_uuid})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+ for invitation in response.json:
+ self._validate_json(invitation)
+
+ # Verify that invitations are not accessible to no access user
+ response = self._get_with_user_http_auth(self.test_client, username='user4', password='user4',
+ params={'id_device': 1})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+
+ # Also test invalid id_device
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'id_device': 0})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+
+ def test_get_query_with_id_test_type_as_admin_returns_only_invitation_for_specific_id_test_type(self):
+ """
+ Test that an admin can access only invitations for a specific test type
+ """
+ with self._flask_app.app_context():
+ create_count = 10
+ # Create 10 invitations
+ self._create_invitations(create_count, id_test_type=1, id_project=2, id_user=1)
+ self._create_invitations(create_count, id_test_type=2, id_project=2, id_user=1)
+
+ # Admin should access all invitations
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'id_test_type': 1})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+ for invitation in response.json:
+ self._validate_json(invitation)
+
+ # Also test with test_type_uuid
+ test_type = TeraTestType.get_test_type_by_id(1)
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'test_type_uuid': test_type.test_type_uuid})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+ for invitation in response.json:
+ self._validate_json(invitation)
+ # Verify that invitations are not accessible to no access user
+ response = self._get_with_user_http_auth(self.test_client, username='user4', password='user4',
+ params={'id_test_type': 1})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+
+ # Create more invitations with a different user
+ self._create_invitations(create_count, id_test_type=1, id_project=1, id_user=2)
+ self._create_invitations(create_count, id_test_type=2, id_project=1, id_user=2)
+
+ # Admin should access all invitations of test type 1
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'id_test_type': 1})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count * 2, len(response.json))
+
+ def test_get_query_with_id_session_as_admin_returns_only_invitation_for_specific_id_session(self):
+ """
+ Test that an admin can access only invitations for a specific session
+ """
+ with self._flask_app.app_context():
+ create_count = 10
+ # Create 10 invitations
+ self._create_invitations(create_count, id_test_type=1, id_session=1, id_project=1, id_user=1)
+ self._create_invitations(create_count, id_test_type=1, id_session=2, id_project=1, id_user=1)
+
+ # Admin should access all invitations
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'id_session': 1})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+ for invitation in response.json:
+ self._validate_json(invitation)
+
+ # Test with session_uuid
+ session = TeraSession.get_session_by_id(1)
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'session_uuid': session.session_uuid})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(create_count, len(response.json))
+ for invitation in response.json:
+ self._validate_json(invitation)
+
+ # Verify that invitations are not accessible to no access user
+ response = self._get_with_user_http_auth(self.test_client, username='user4', password='user4',
+ params={'id_session': 1})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+
+ # Also test invalid id_session
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'id_session': 0})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+
+
+ def test_get_query_as_admin_with_invalid_id_project_returns_empty_list(self):
+ """
+ Test that a get request with invalid id_project returns 200 with no data
+ """
+ with self._flask_app.app_context():
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'id_project': 0})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+
+ def test_get_query_as_admin_with_valid_id_project_returns_invitations(self):
+ """
+ Test that a get request with valid id_project returns 200 with data
+ """
+ with self._flask_app.app_context():
+ create_count = 10
+ # Create 10 invitations for p1
+ self._create_invitations(create_count, id_test_type=1, id_project=1, id_participant=1)
+ # Create 10 invitation for p2
+ self._create_invitations(create_count, id_test_type=1, id_project=1, id_participant=2)
+
+ # Admin should access all invitations
+ response = self._get_with_user_http_auth(self.test_client, username='admin', password='admin',
+ params={'id_project': 1})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(2 * create_count, len(response.json))
+ for invitation in response.json:
+ self._validate_json(invitation)
+
+
+ # Not access user should not see any invitation
+ response = self._get_with_user_http_auth(self.test_client, username='user4', password='user4',
+ params={'id_project': 1})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(0, len(response.json))
+
+ def test_post_query_as_admin_with_invalid_schema(self):
+ """
+ Test that a post request with invalid schema returns 400
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_user_http_auth(self.test_client,
+ username='admin',
+ password='admin',
+ json={})
+ self.assertEqual(400, response.status_code)
+
+ def test_post_query_as_admin_with_valid_schema_but_empty_array(self):
+ """
+ Test that a post request with valid schema but empty array returns 400
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_user_http_auth(self.test_client,
+ username='admin',
+ password='admin',
+ json=[])
+ self.assertEqual(400, response.status_code)
+
+ def test_post_query_as_admin_with_invalid_array_item_schema(self):
+ """
+ Test that a post request with invalid array item schema returns 400
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_user_http_auth(self.test_client,
+ username='admin',
+ password='admin',
+ json=[{'invalid': 'invalid'}])
+ self.assertEqual(400, response.status_code)
+
+ def test_post_query_as_admin_with_valid_schema_but_empty_array_item(self):
+ """
+ Test that a post request with valid schema but empty array item returns 400
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_user_http_auth(self.test_client,
+ username='admin',
+ password='admin',
+ json={'tests_invitations': []})
+ self.assertEqual(400, response.status_code)
+
+ def test_post_query_as_admin_with_valid_schema_but_invalid_array_item(self):
+ """
+ Test that a post request with valid schema but empty array item returns 400
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_user_http_auth(self.test_client,
+ username='admin',
+ password='admin',
+ json={'tests_invitations': [{'invalid': 'invalid'}]})
+ self.assertEqual(400, response.status_code)
+
+ def test_post_query_as_admin_with_valid_schema_but_missing_id_user_id_participant_id_device(self):
+ """
+ Test that a post request with valid schema but missing id_user, id_participant, id_device returns 400
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_user_http_auth(self.test_client,
+ username='admin',
+ password='admin',
+ json=self._create_tests_invitations_json(id_test_type=2,
+ id_project=1))
+ self.assertEqual(400, response.status_code)
+
+ def test_post_query_as_admin_with_valid_schema_but_invalid_id_user_id_participant(self):
+ """
+ Test that a post request with valid schema but invalid id_user, id_participant, id_device returns 403
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_user_http_auth(self.test_client,
+ username='admin',
+ password='admin',
+ json=self._create_tests_invitations_json(id_test_type=2,
+ id_project=1, id_user=0))
+ self.assertEqual(403, response.status_code)
+
+ response = self._post_with_user_http_auth(self.test_client,
+ username='admin',
+ password='admin',
+ json=self._create_tests_invitations_json(id_test_type=2, id_project=1,
+ id_participant=0))
+ self.assertEqual(403, response.status_code)
+
+
+ response = self._post_with_user_http_auth(self.test_client,
+ username='admin',
+ password='admin',
+ json=self._create_tests_invitations_json(id_test_type=2, id_project=1,
+ id_device=0))
+ self.assertEqual(403, response.status_code)
+
+ def test_post_query_as_admin_with_valid_schema_but_invalid_id_project(self):
+ """
+ Test that a post request with valid schema but invalid id_project
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_user_http_auth(self.test_client,
+ username='admin',
+ password='admin',
+ json=self._create_tests_invitations_json(id_test_type=2,
+ id_project=0, id_user=1))
+ self.assertEqual(403, response.status_code)
+
+ def test_post_query_as_admin_with_valid_schema_but_forbidden_id_project(self):
+ """
+ Test that a post request with valid schema but invalid id_project
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_user_http_auth(self.test_client,
+ username='user4',
+ password='user4',
+ json=self._create_tests_invitations_json(id_test_type=2,
+ id_project=1, id_user=1))
+ self.assertEqual(403, response.status_code)
+
+ def test_post_query_as_admin_with_valid_schema_with_multiple_user_participant_device(self):
+ """
+ Test that a post request with valid schema with multiple user, participant, device returns 400
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_user_http_auth(self.test_client,
+ username='admin',
+ password='admin',
+ json=self._create_tests_invitations_json(id_test_type=2,
+ id_project=1,
+ id_user=1,
+ id_participant=1))
+ self.assertEqual(400, response.status_code)
+
+ response = self._post_with_user_http_auth(self.test_client,
+ username='admin',
+ password='admin',
+ json=self._create_tests_invitations_json(id_test_type=2,
+ id_project=1,
+ id_user=1,
+ id_device=1))
+ self.assertEqual(400, response.status_code)
+
+ response = self._post_with_user_http_auth(self.test_client,
+ username='admin',
+ password='admin',
+ json=self._create_tests_invitations_json(id_test_type=2,
+ id_project=1,
+ id_user=1,
+ id_participant=1,
+ id_device=1))
+ self.assertEqual(400, response.status_code)
+
+ def test_post_query_as_admin_with_valid_schema_with_no_session(self):
+ """
+ Test that a post request with valid schema with no session returns 200
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_user_http_auth(self.test_client,
+ username='admin',
+ password='admin',
+ json=self._create_tests_invitations_json(id_test_type=2,
+ id_project=1,
+ id_user=1))
+ self.assertEqual(200, response.status_code)
+ for invitation in response.json:
+ self._validate_json(invitation, full=True, with_urls=True)
+
+
+ def test_post_query_as_admin_with_valid_schema_with_invalid_session(self):
+ """
+ Test that a post request with valid schema with invalid session returns 403
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_user_http_auth(self.test_client,
+ username='admin',
+ password='admin',
+ json=self._create_tests_invitations_json(id_test_type=2,
+ id_project=1,
+ id_user=1,
+ id_session=0))
+ self.assertEqual(403, response.status_code)
+
+ def test_post_query_as_admin_with_valid_schema_with_valid_session(self):
+ """
+ Test that a post request with valid schema with valid session returns 200
+ """
+ with self._flask_app.app_context():
+ response = self._post_with_user_http_auth(self.test_client,
+ username='admin',
+ password='admin',
+ json=self._create_tests_invitations_json(id_test_type=2,
+ id_project=1,
+ id_user=1,
+ id_session=1))
+ self.assertEqual(200, response.status_code)
+ for invitation in response.json:
+ self._validate_json(invitation, full=True, with_urls=True)
+
+ def test_post_query_as_admin_with_valid_schema_update_count(self):
+ """
+ Test that a post request with valid schema updates count
+ """
+ with self._flask_app.app_context():
+ # Post a new invitation
+ response = self._post_with_user_http_auth(self.test_client,
+ username='admin',
+ password='admin',
+ json=self._create_tests_invitations_json(id_test_type=2,
+ id_project=1,
+ id_user=1))
+ self.assertEqual(200, response.status_code)
+ invitation_info = response.json[0]
+ self.assertNotEqual(invitation_info['id_test_invitation'], 0)
+
+ # increment count
+ invitation_info['test_invitation_count'] += 1
+
+ # Post the updated invitation
+ response = self._post_with_user_http_auth(self.test_client,
+ username='admin',
+ password='admin',
+ json={'tests_invitations': [{'id_test_invitation': invitation_info['id_test_invitation'],
+ 'test_invitation_count': invitation_info['test_invitation_count']}]})
+ self.assertEqual(200, response.status_code)
+ self.assertEqual(1, len(response.json))
+ self.assertEqual(invitation_info['test_invitation_count'], response.json[0]['test_invitation_count'])
+ for invitation in response.json:
+ self._validate_json(invitation, full=True, with_urls=True)
+
+
+ def _create_tests_invitations_json(self, id_test_type: int, id_project: int, id_user: int = None,
+ id_participant: int = None, id_device: int = None, id_session: int = None) -> dict:
+ """
+ Create a json for an invitation
+ """
+ tests_invitations = {'tests_invitations': [
+ {
+ 'id_test_invitation': 0, # New invitation
+ 'id_test_type': id_test_type,
+ 'id_project': id_project,
+ 'test_invitation_max_count': 1,
+ 'test_invitation_count': 0,
+ 'test_invitation_expiration_date': (datetime.now() + timedelta(days=1)).isoformat()
+ } | {k: v for k, v in {'id_user': id_user,
+ 'id_participant': id_participant,
+ 'id_device': id_device,
+ 'id_session': id_session}.items() if v is not None}
+ ]}
+
+ return tests_invitations
+
+ def _create_invitations(self, count: int,
+ id_test_type: int,
+ id_project: int,
+ id_user: int = None,
+ id_participant: int = None,
+ id_device: int = None,
+ id_session: int = None) -> list[TeraTestInvitation]:
+ """
+ Create a number of invitations.
+ """
+ # Make sure test type has all required fields
+ test_type: TeraTestType = TeraTestType.get_test_type_by_id(id_test_type)
+ TeraTestType.update(test_type.id_test_type, {'test_type_has_json_format': True,
+ 'test_type_has_web_editor': True,
+ 'test_type_has_web_format': True})
+
+ invitations: list[TeraTestInvitation] = []
+
+ # Make sure we have only one of id_user, id_participant, id_device
+ if sum(x is not None for x in [id_user, id_participant, id_device]) != 1:
+ raise ValueError('Only one of id_user, id_participant, id_device must be set')
+
+ for _ in range(count):
+ invitation = TeraTestInvitation(id_test_type=id_test_type,
+ id_project=id_project,
+ test_invitation_max_count=1,
+ test_invitation_count=0,
+ test_invitation_expiration_date=datetime.now() + timedelta(days=1),
+ id_user=id_user,
+ id_participant=id_participant,
+ id_device=id_device,
+ id_session=id_session)
+ TeraTestInvitation.insert(invitation)
+ invitations.append(invitation)
+
+ return invitations
+
+ def _delete_all_invitations(self):
+ """
+ Delete all invitations
+ """
+ with self._flask_app.app_context():
+ invitations = TeraTestInvitation.query.all()
+ for invitation in invitations:
+ TeraTestInvitation.delete(invitation.id_test_invitation)
+
+ def _validate_json(self, json: dict, full: bool = False, with_urls: bool = False):
+ """
+ Validate a json
+ """
+ self.assertTrue('id_test_invitation' in json)
+ self.assertTrue('id_test_type' in json)
+ self.assertTrue('test_invitation_max_count' in json)
+ self.assertTrue('test_invitation_count' in json)
+ self.assertTrue('test_invitation_expiration_date' in json)
+ self.assertTrue('test_invitation_message' in json)
+ self.assertTrue('test_invitation_creation_date' in json)
+ self.assertTrue('id_user' in json)
+ self.assertTrue('id_participant' in json)
+ self.assertTrue('id_device' in json)
+ self.assertTrue('id_session' in json)
+ self.assertTrue('test_invitation_key' in json)
+
+ if full:
+ self.assertTrue('test_invitation_test_type' in json)
+ if json['id_user']:
+ self.assertTrue('test_invitation_user' in json)
+ if json['id_participant']:
+ self.assertTrue('test_invitation_participant' in json)
+ if json['id_device']:
+ self.assertTrue('test_invitation_device' in json)
+ if json['id_session']:
+ self.assertTrue('test_invitation_session' in json)
+ else:
+ self.assertTrue('test_invitation_test_type' not in json)
+ self.assertTrue('test_invitation_session' not in json)
+ self.assertTrue('test_invitation_user' not in json)
+ self.assertTrue('test_invitation_participant' not in json)
+ self.assertTrue('test_invitation_device' not in json)
+
+ if with_urls:
+ self.assertTrue('test_invitation_url' in json)
+ else:
+ self.assertFalse('test_invitation_url' in json)
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryUserGroups.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryUserGroups.py
index fe9dc0a72..dcc64eb9a 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryUserGroups.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryUserGroups.py
@@ -1,4 +1,4 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
from opentera.db.models.TeraUserGroup import TeraUserGroup
from opentera.db.models.TeraUser import TeraUser
from opentera.db.models.TeraServiceAccess import TeraServiceAccess
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryUsers.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryUsers.py
index 255be07de..8e029bb99 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryUsers.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryUsers.py
@@ -333,6 +333,96 @@ def test_query_self(self):
self.assertTrue(data_item.__contains__('projects'))
self.assertTrue(data_item.__contains__('sites'))
+ def test_password_strength(self):
+ with self._flask_app.app_context():
+ json_data = {
+ 'user': {
+ 'id_user': 0,
+ 'user_username': 'new_test_user',
+ 'user_enabled': True,
+ 'user_firstname': 'Test',
+ 'user_lastname': 'Test',
+ 'user_profile': '',
+ 'user_user_groups': [{'id_user_group': 3}],
+ 'user_password': 'password'
+ }
+ }
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(400, response.status_code, msg="Password not long enough")
+
+ json_data['user']['user_password'] = 'password12345!'
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(400, response.status_code, msg="Password without capital letters")
+
+ json_data['user']['user_password'] = 'PASSWORD12345!'
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(400, response.status_code, msg="Password without lower case letters")
+
+ json_data['user']['user_password'] = 'Password12345'
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(400, response.status_code, msg="Password without special characters")
+
+ json_data['user']['user_password'] = 'Password!!!!'
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(400, response.status_code, msg="Password without numbers")
+
+ json_data['user']['user_password'] = 'Password12345!'
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(200, response.status_code, msg="Password OK")
+ self.assertGreater(len(response.json), 0)
+ json_data = response.json[0]
+ self._checkJson(json_data)
+ current_id = json_data['id_user']
+
+ # Modify password
+ json_data = {
+ 'user': {
+ 'id_user': current_id,
+ 'user_password': 'password'
+ }
+ }
+
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(400, response.status_code, msg="Password not long enough")
+
+ json_data['user']['user_password'] = 'password12345!'
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(400, response.status_code, msg="Password without capital letters")
+
+ json_data['user']['user_password'] = 'PASSWORD12345!'
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(400, response.status_code, msg="Password without lower case letters")
+
+ json_data['user']['user_password'] = 'Password12345'
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(400, response.status_code, msg="Password without special characters")
+
+ json_data['user']['user_password'] = 'Password!!!!'
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(400, response.status_code, msg="Password without numbers")
+
+ json_data['user']['user_password'] = 'Password12345!!'
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(200, response.status_code, msg="Password OK")
+
+ response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
+ json=json_data)
+ self.assertEqual(400, response.status_code, msg="Password same as old")
+
+ TeraUser.delete(current_id)
+
def test_post_and_delete(self):
with self._flask_app.app_context():
json_data = {
@@ -376,7 +466,7 @@ def test_post_and_delete(self):
json=json_data)
self.assertEqual(400, response.status_code, msg="Invalid password")
- json_data['user']['user_password'] = 'testtest'
+ json_data['user']['user_password'] = 'testTest11*&'
response = self._post_with_user_http_auth(self.test_client, username='admin', password='admin',
json=json_data)
self.assertEqual(response.status_code, 409, msg="Username unavailable")
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryVersions.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryVersions.py
index 0ba73a424..d6602b3cc 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryVersions.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserQueryVersions.py
@@ -1,4 +1,4 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
from opentera.utils.TeraVersions import TeraVersions
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserRefreshToken.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserRefreshToken.py
index a12427e98..a0558609c 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserRefreshToken.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserRefreshToken.py
@@ -1,4 +1,4 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
class UserRefreshTokenTest(BaseUserAPITest):
diff --git a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserSessionManager.py b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserSessionManager.py
index 4d099a737..791e0367b 100644
--- a/teraserver/python/tests/modules/FlaskModule/API/user/test_UserSessionManager.py
+++ b/teraserver/python/tests/modules/FlaskModule/API/user/test_UserSessionManager.py
@@ -1,4 +1,4 @@
-from BaseUserAPITest import BaseUserAPITest
+from tests.modules.FlaskModule.API.user.BaseUserAPITest import BaseUserAPITest
class UserSessionManagerTest(BaseUserAPITest):
@@ -53,4 +53,4 @@ def test_delete_endpoint_invalid_http_auth(self):
def test_delete_endpoint_invalid_token_auth(self):
with self._flask_app.app_context():
response = self._delete_with_user_token_auth(self.test_client, token='invalid')
- self.assertEqual(405, response.status_code)
\ No newline at end of file
+ self.assertEqual(405, response.status_code)
diff --git a/teraserver/python/tests/modules/FlaskModule/__init__.py b/teraserver/python/tests/modules/FlaskModule/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/teraserver/python/tests/modules/__init__.py b/teraserver/python/tests/modules/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/teraserver/python/tests/opentera/__init__.py b/teraserver/python/tests/opentera/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/teraserver/python/tests/opentera/db/__init__.py b/teraserver/python/tests/opentera/db/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/teraserver/python/tests/opentera/db/models/__init__.py b/teraserver/python/tests/opentera/db/models/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/teraserver/python/tests/opentera/db/models/test_DBManagerTeraUserAccess.py b/teraserver/python/tests/opentera/db/models/test_DBManagerTeraUserAccess.py
deleted file mode 100644
index fce65e89d..000000000
--- a/teraserver/python/tests/opentera/db/models/test_DBManagerTeraUserAccess.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from modules.DatabaseModule.DBManager import DBManager
-from opentera.db.models.TeraUser import TeraUser
-from tests.opentera.db.models.BaseModelsTest import BaseModelsTest
-
-
-class DBManagerTeraUserAccessTest(BaseModelsTest):
-
- def setUp(self):
- super().setUp()
-
- def test_admin_get_accessible_users_ids(self):
- with self._flask_app.app_context():
- admin_user = TeraUser.get_user_by_username('admin')
- users = DBManager.userAccess(admin_user).get_accessible_users()
- self.assertEqual(len(users), 6)
-
- def test_admin_accessible_sites(self):
- with self._flask_app.app_context():
- admin_user = TeraUser.get_user_by_username('admin')
- sites = DBManager.userAccess(admin_user).get_accessible_sites()
- self.assertEqual(len(sites), 2)
-
- def test_user_accessible_sites(self):
- with self._flask_app.app_context():
- test_user = TeraUser.get_user_by_username('user')
- sites = DBManager.userAccess(test_user).get_accessible_sites()
- self.assertEqual(len(sites), 1)
diff --git a/teraserver/python/tests/opentera/db/models/test_TeraAsset.py b/teraserver/python/tests/opentera/db/models/test_TeraAsset.py
index 4928760eb..ffac8deb5 100644
--- a/teraserver/python/tests/opentera/db/models/test_TeraAsset.py
+++ b/teraserver/python/tests/opentera/db/models/test_TeraAsset.py
@@ -299,22 +299,22 @@ def test_hard_delete(self):
def test_undelete(self):
with self._flask_app.app_context():
# Create new participant
- from test_TeraParticipant import TeraParticipantTest
+ from tests.opentera.db.models.test_TeraParticipant import TeraParticipantTest
participant = TeraParticipantTest.new_test_participant(id_project=1)
id_participant = participant.id_participant
# Create new device
- from test_TeraDevice import TeraDeviceTest
+ from tests.opentera.db.models.test_TeraDevice import TeraDeviceTest
device = TeraDeviceTest.new_test_device()
id_device = device.id_device
# Create new user
- from test_TeraUser import TeraUserTest
+ from tests.opentera.db.models.test_TeraUser import TeraUserTest
user = TeraUserTest.new_test_user(user_name="asset_user")
id_user = user.id_user
# Create new session
- from test_TeraSession import TeraSessionTest
+ from tests.opentera.db.models.test_TeraSession import TeraSessionTest
ses = TeraSessionTest.new_test_session(participants=[participant], users=[user], devices=[device])
id_session = ses.id_session
@@ -409,4 +409,3 @@ def new_test_asset(id_session: int, service_uuid: str, id_device: int | None = N
asset.asset_type = 'application/test'
TeraAsset.insert(asset)
return asset
-
diff --git a/teraserver/python/tests/opentera/db/models/test_TeraDevice.py b/teraserver/python/tests/opentera/db/models/test_TeraDevice.py
index 901ea1fb6..1957ea482 100644
--- a/teraserver/python/tests/opentera/db/models/test_TeraDevice.py
+++ b/teraserver/python/tests/opentera/db/models/test_TeraDevice.py
@@ -115,23 +115,23 @@ def test_hard_delete(self):
id_device = device.id_device
# Assign device to site
- from test_TeraDeviceSite import TeraDeviceSiteTest
+ from tests.opentera.db.models.test_TeraDeviceSite import TeraDeviceSiteTest
device_site = TeraDeviceSiteTest.new_test_device_site(id_device=id_device, id_site=1)
id_device_site = device_site.id_device_site
# Assign device to project
- from test_TeraDeviceProject import TeraDeviceProjectTest
+ from tests.opentera.db.models.test_TeraDeviceProject import TeraDeviceProjectTest
device_project = TeraDeviceProjectTest.new_test_device_project(id_device=id_device, id_project=1)
id_device_project = device_project.id_device_project
# Assign device to participants
- from test_TeraDeviceParticipant import TeraDeviceParticipantTest
+ from tests.opentera.db.models.test_TeraDeviceParticipant import TeraDeviceParticipantTest
device_participant = TeraDeviceParticipantTest.new_test_device_participant(id_device=id_device,
id_participant=1)
id_device_participant = device_participant.id_device_participant
# Assign device to sessions
- from test_TeraSession import TeraSessionTest
+ from tests.opentera.db.models.test_TeraSession import TeraSessionTest
device_session = TeraSessionTest.new_test_session(id_creator_device=id_device)
id_session = device_session.id_session
@@ -139,19 +139,19 @@ def test_hard_delete(self):
id_session_invitee = device_session.id_session
# Attach asset
- from test_TeraAsset import TeraAssetTest
+ from tests.opentera.db.models.test_TeraAsset import TeraAssetTest
asset = TeraAssetTest.new_test_asset(id_session=id_session,
service_uuid=TeraService.get_openteraserver_service().service_uuid,
id_device=id_device)
id_asset = asset.id_asset
# ... and test
- from test_TeraTest import TeraTestTest
+ from tests.opentera.db.models.test_TeraTest import TeraTestTest
test = TeraTestTest.new_test_test(id_session=id_session, id_device=id_device)
id_test = test.id_test
# Create service config for device
- from test_TeraServiceConfig import TeraServiceConfigTest
+ from tests.opentera.db.models.test_TeraServiceConfig import TeraServiceConfigTest
device_service_config = TeraServiceConfigTest.new_test_service_config(id_device=id_device, id_service=2)
id_service_config = device_service_config.id_service_config
@@ -203,23 +203,23 @@ def test_undelete(self):
id_device = device.id_device
# Assign device to site
- from test_TeraDeviceSite import TeraDeviceSiteTest
+ from tests.opentera.db.models.test_TeraDeviceSite import TeraDeviceSiteTest
device_site = TeraDeviceSiteTest.new_test_device_site(id_device=id_device, id_site=1)
id_device_site = device_site.id_device_site
# Assign device to project
- from test_TeraDeviceProject import TeraDeviceProjectTest
+ from tests.opentera.db.models.test_TeraDeviceProject import TeraDeviceProjectTest
device_project = TeraDeviceProjectTest.new_test_device_project(id_device=id_device, id_project=1)
id_device_project = device_project.id_device_project
# Assign device to participants
- from test_TeraDeviceParticipant import TeraDeviceParticipantTest
+ from tests.opentera.db.models.test_TeraDeviceParticipant import TeraDeviceParticipantTest
device_participant = TeraDeviceParticipantTest.new_test_device_participant(id_device=id_device,
id_participant=1)
id_device_participant = device_participant.id_device_participant
# Assign device to sessions
- from test_TeraSession import TeraSessionTest
+ from tests.opentera.db.models.test_TeraSession import TeraSessionTest
device_session = TeraSessionTest.new_test_session(id_creator_device=id_device)
id_session = device_session.id_session
@@ -227,19 +227,19 @@ def test_undelete(self):
id_session_invitee = device_session.id_session
# Attach asset
- from test_TeraAsset import TeraAssetTest
+ from tests.opentera.db.models.test_TeraAsset import TeraAssetTest
asset = TeraAssetTest.new_test_asset(id_session=id_session,
service_uuid=TeraService.get_openteraserver_service().service_uuid,
id_device=id_device)
id_asset = asset.id_asset
# ... and test
- from test_TeraTest import TeraTestTest
+ from tests.opentera.db.models.test_TeraTest import TeraTestTest
test = TeraTestTest.new_test_test(id_session=id_session, id_device=id_device)
id_test = test.id_test
# Create service config for device
- from test_TeraServiceConfig import TeraServiceConfigTest
+ from tests.opentera.db.models.test_TeraServiceConfig import TeraServiceConfigTest
device_service_config = TeraServiceConfigTest.new_test_service_config(id_device=id_device, id_service=2)
id_service_config = device_service_config.id_service_config
diff --git a/teraserver/python/tests/opentera/db/models/test_TeraParticipant.py b/teraserver/python/tests/opentera/db/models/test_TeraParticipant.py
index 20f03b586..3496cf72b 100644
--- a/teraserver/python/tests/opentera/db/models/test_TeraParticipant.py
+++ b/teraserver/python/tests/opentera/db/models/test_TeraParticipant.py
@@ -68,7 +68,7 @@ def test_hard_delete(self):
id_participant = participant.id_participant
# Assign participant to sessions
- from test_TeraSession import TeraSessionTest
+ from tests.opentera.db.models.test_TeraSession import TeraSessionTest
part_session = TeraSessionTest.new_test_session(id_creator_participant=id_participant)
id_session = part_session.id_session
@@ -76,14 +76,14 @@ def test_hard_delete(self):
id_session_invitee = part_session.id_session
# Attach asset
- from test_TeraAsset import TeraAssetTest
+ from tests.opentera.db.models.test_TeraAsset import TeraAssetTest
asset = TeraAssetTest.new_test_asset(id_session=id_session,
service_uuid=TeraService.get_openteraserver_service().service_uuid,
id_participant=id_participant)
id_asset = asset.id_asset
# ... and test
- from test_TeraTest import TeraTestTest
+ from tests.opentera.db.models.test_TeraTest import TeraTestTest
test = TeraTestTest.new_test_test(id_session=id_session, id_participant=id_participant)
id_test = test.id_test
@@ -117,7 +117,7 @@ def test_hard_delete(self):
def test_undelete(self):
with self._flask_app.app_context():
# Create a new project
- from test_TeraProject import TeraProjectTest
+ from tests.opentera.db.models.test_TeraProject import TeraProjectTest
project = TeraProjectTest.new_test_project()
id_project = project.id_project
@@ -127,7 +127,7 @@ def test_undelete(self):
id_participant = participant.id_participant
# Assign participant to sessions
- from test_TeraSession import TeraSessionTest
+ from tests.opentera.db.models.test_TeraSession import TeraSessionTest
part_session = TeraSessionTest.new_test_session(id_creator_participant=id_participant)
id_session = part_session.id_session
@@ -135,19 +135,19 @@ def test_undelete(self):
id_session_invitee = part_session.id_session
# Attach asset
- from test_TeraAsset import TeraAssetTest
+ from tests.opentera.db.models.test_TeraAsset import TeraAssetTest
asset = TeraAssetTest.new_test_asset(id_session=id_session,
service_uuid=TeraService.get_openteraserver_service().service_uuid,
id_participant=id_participant)
id_asset = asset.id_asset
# ... and test
- from test_TeraTest import TeraTestTest
+ from tests.opentera.db.models.test_TeraTest import TeraTestTest
test = TeraTestTest.new_test_test(id_session=id_session, id_participant=id_participant)
id_test = test.id_test
# ... and service config
- from test_TeraServiceConfig import TeraServiceConfigTest
+ from tests.opentera.db.models.test_TeraServiceConfig import TeraServiceConfigTest
device_service_config = TeraServiceConfigTest.new_test_service_config(id_participant=id_participant,
id_service=2)
id_service_config = device_service_config.id_service_config
@@ -177,7 +177,7 @@ def test_undelete(self):
TeraParticipant.undelete(id_participant)
# Create and associate participant group
- from test_TeraParticipantGroup import TeraParticipantGroupTest
+ from tests.opentera.db.models.test_TeraParticipantGroup import TeraParticipantGroupTest
group = TeraParticipantGroupTest.new_test_group(id_project)
id_group = group.id_participant_group
diff --git a/teraserver/python/tests/opentera/db/models/test_TeraParticipantGroup.py b/teraserver/python/tests/opentera/db/models/test_TeraParticipantGroup.py
index b99384f8c..e9ba557f4 100644
--- a/teraserver/python/tests/opentera/db/models/test_TeraParticipantGroup.py
+++ b/teraserver/python/tests/opentera/db/models/test_TeraParticipantGroup.py
@@ -40,7 +40,7 @@ def test_hard_delete(self):
id_participant_group = group.id_participant_group
# Create a new participant in that group
- from test_TeraParticipant import TeraParticipantTest
+ from tests.opentera.db.models.test_TeraParticipant import TeraParticipantTest
participant = TeraParticipantTest.new_test_participant(id_project=1,
id_participant_group=id_participant_group)
self.assertIsNotNone(participant.id_participant)
@@ -66,7 +66,7 @@ def test_hard_delete(self):
def test_undelete(self):
with self._flask_app.app_context():
# Create a new project
- from test_TeraProject import TeraProjectTest
+ from tests.opentera.db.models.test_TeraProject import TeraProjectTest
project = TeraProjectTest.new_test_project(id_site=1)
id_project = project.id_project
@@ -76,7 +76,7 @@ def test_undelete(self):
id_participant_group = group.id_participant_group
# Create a new participant in that group
- from test_TeraParticipant import TeraParticipantTest
+ from tests.opentera.db.models.test_TeraParticipant import TeraParticipantTest
participant = TeraParticipantTest.new_test_participant(id_project=id_project,
id_participant_group=id_participant_group)
self.assertIsNotNone(participant.id_participant)
diff --git a/teraserver/python/tests/opentera/db/models/test_TeraProject.py b/teraserver/python/tests/opentera/db/models/test_TeraProject.py
index 2ad0e9689..bca7f206a 100644
--- a/teraserver/python/tests/opentera/db/models/test_TeraProject.py
+++ b/teraserver/python/tests/opentera/db/models/test_TeraProject.py
@@ -106,7 +106,7 @@ def test_update_set_inactive(self):
# Create participants
participants = []
- from test_TeraParticipant import TeraParticipantTest
+ from tests.opentera.db.models.test_TeraParticipant import TeraParticipantTest
for i in range(3):
part = TeraParticipantTest.new_test_participant(id_project=new_project.id_project, enabled=True)
participants.append(part)
@@ -116,7 +116,7 @@ def test_update_set_inactive(self):
# Associate devices
devices = []
- from test_TeraDevice import TeraDeviceTest
+ from tests.opentera.db.models.test_TeraDevice import TeraDeviceTest
for i in range(2):
device = TeraDeviceTest.new_test_device()
devices.append(device)
@@ -163,7 +163,7 @@ def test_hard_delete(self):
id_project = project.id_project
# Create a new participant in that project
- from test_TeraParticipant import TeraParticipantTest
+ from tests.opentera.db.models.test_TeraParticipant import TeraParticipantTest
participant = TeraParticipantTest.new_test_participant(id_project=id_project)
self.assertIsNotNone(participant.id_participant)
id_participant = participant.id_participant
@@ -188,7 +188,7 @@ def test_hard_delete(self):
def test_undelete(self):
with self._flask_app.app_context():
# Create site
- from test_TeraSite import TeraSiteTest
+ from tests.opentera.db.models.test_TeraSite import TeraSiteTest
site = TeraSiteTest.new_test_site()
id_site = site.id_site
@@ -198,7 +198,7 @@ def test_undelete(self):
id_project = project.id_project
# Create a new participant in that project
- from test_TeraParticipant import TeraParticipantTest
+ from tests.opentera.db.models.test_TeraParticipant import TeraParticipantTest
participant = TeraParticipantTest.new_test_participant(id_project=id_project)
self.assertIsNotNone(participant.id_participant)
id_participant = participant.id_participant
@@ -243,12 +243,12 @@ def test_project_relationships_deletion_and_access(self):
id_project = project.id_project
# Create participant groups
- from test_TeraParticipantGroup import TeraParticipantGroupTest
+ from tests.opentera.db.models.test_TeraParticipantGroup import TeraParticipantGroupTest
group = TeraParticipantGroupTest.new_test_group(id_project=id_project)
self.assertIsNotNone(group.id_participant_group)
id_participant_group1 = group.id_participant_group
- from test_TeraParticipant import TeraParticipantTest
+ from tests.opentera.db.models.test_TeraParticipant import TeraParticipantTest
participant = TeraParticipantTest.new_test_participant(id_project=1,
id_participant_group=id_participant_group1)
self.assertIsNotNone(participant.id_participant)
diff --git a/teraserver/python/tests/opentera/db/models/test_TeraService.py b/teraserver/python/tests/opentera/db/models/test_TeraService.py
index f47d4e6dd..a518a66e3 100644
--- a/teraserver/python/tests/opentera/db/models/test_TeraService.py
+++ b/teraserver/python/tests/opentera/db/models/test_TeraService.py
@@ -400,7 +400,7 @@ def test_hard_delete(self):
id_service = service.id_service
# Create a new site association for that service
- from test_TeraServiceSite import TeraServiceSiteTest
+ from tests.opentera.db.models.test_TeraServiceSite import TeraServiceSiteTest
site_service = TeraServiceSiteTest.new_test_service_site(id_site=1, id_service=id_service)
self.assertIsNotNone(site_service.id_service_site)
id_site_service = site_service.id_service_site
@@ -430,7 +430,7 @@ def test_undelete(self):
id_service = service.id_service
# Create service roles
- from test_TeraServiceRole import TeraServiceRoleTest
+ from tests.opentera.db.models.test_TeraServiceRole import TeraServiceRoleTest
role = TeraServiceRoleTest.new_test_service_role(id_service=id_service, role_name='admin')
id_role_admin = role.id_service_role
@@ -438,12 +438,12 @@ def test_undelete(self):
id_role_user = role.id_service_role
# Create service sites association
- from test_TeraServiceSite import TeraServiceSiteTest
+ from tests.opentera.db.models.test_TeraServiceSite import TeraServiceSiteTest
service_site = TeraServiceSiteTest.new_test_service_site(id_site=1, id_service=id_service)
id_service_site = service_site.id_service_site
# Create service projects association
- from test_TeraServiceProject import TeraServiceProjectTest
+ from tests.opentera.db.models.test_TeraServiceProject import TeraServiceProjectTest
service_project = TeraServiceProjectTest.new_test_service_project(id_service=id_service, id_project=1)
id_service_project = service_project.id_service_project
diff --git a/teraserver/python/tests/opentera/db/models/test_TeraSession.py b/teraserver/python/tests/opentera/db/models/test_TeraSession.py
index a06789632..eb38d898a 100644
--- a/teraserver/python/tests/opentera/db/models/test_TeraSession.py
+++ b/teraserver/python/tests/opentera/db/models/test_TeraSession.py
@@ -19,8 +19,8 @@ def test_session_defaults(self):
for session in TeraSession.query.all():
my_list = [session.id_creator_device, session.id_creator_participant,
session.id_creator_service, session.id_creator_user]
- # Only one not None
- self.assertEqual(1, len([x for x in my_list if x is not None]))
+ # At least one creator should be set
+ self.assertTrue(any(my_list))
def test_session_new(self):
from datetime import datetime
@@ -67,7 +67,7 @@ def test_soft_delete(self):
id_session = ses.id_session
# Attach asset
- from test_TeraAsset import TeraAssetTest
+ from tests.opentera.db.models.test_TeraAsset import TeraAssetTest
asset = TeraAssetTest.new_test_asset(id_session=id_session,
service_uuid=TeraService.get_openteraserver_service().service_uuid,
id_device=1)
@@ -115,14 +115,14 @@ def test_hard_delete(self):
id_session = ses.id_session
# Attach asset
- from test_TeraAsset import TeraAssetTest
+ from tests.opentera.db.models.test_TeraAsset import TeraAssetTest
asset = TeraAssetTest.new_test_asset(id_session=id_session,
service_uuid=TeraService.get_openteraserver_service().service_uuid,
id_device=1)
id_asset = asset.id_asset
# ... and test
- from test_TeraTest import TeraTestTest
+ from tests.opentera.db.models.test_TeraTest import TeraTestTest
test = TeraTestTest.new_test_test(id_session=id_session, id_participant=1)
id_test = test.id_test
@@ -149,19 +149,19 @@ def test_undelete(self):
id_session = ses.id_session
# Attach asset
- from test_TeraAsset import TeraAssetTest
+ from tests.opentera.db.models.test_TeraAsset import TeraAssetTest
asset = TeraAssetTest.new_test_asset(id_session=id_session,
service_uuid=TeraService.get_openteraserver_service().service_uuid,
id_device=1)
id_asset = asset.id_asset
# ... and test
- from test_TeraTest import TeraTestTest
+ from tests.opentera.db.models.test_TeraTest import TeraTestTest
test = TeraTestTest.new_test_test(id_session=id_session, id_participant=1)
id_test = test.id_test
# ... and event
- from test_TeraSessionEvent import TeraSessionEventTest
+ from tests.opentera.db.models.test_TeraSessionEvent import TeraSessionEventTest
event = TeraSessionEventTest.new_test_session_event(id_session=id_session, id_event_type=1)
id_event = event.id_session_event
diff --git a/teraserver/python/tests/opentera/db/models/test_TeraSessionType.py b/teraserver/python/tests/opentera/db/models/test_TeraSessionType.py
index 0caf78540..1ef3d73c7 100644
--- a/teraserver/python/tests/opentera/db/models/test_TeraSessionType.py
+++ b/teraserver/python/tests/opentera/db/models/test_TeraSessionType.py
@@ -38,7 +38,7 @@ def test_hard_delete(self):
id_session_type = ses_type.id_session_type
# Create a new session of that session type
- from test_TeraSession import TeraSessionTest
+ from tests.opentera.db.models.test_TeraSession import TeraSessionTest
ses = TeraSessionTest.new_test_session(id_session_type=id_session_type, id_creator_service=1)
id_session = ses.id_session
@@ -62,7 +62,7 @@ def test_hard_delete(self):
def test_undelete(self):
with self._flask_app.app_context():
# Create new service
- from test_TeraService import TeraServiceTest
+ from tests.opentera.db.models.test_TeraService import TeraServiceTest
service = TeraServiceTest.new_test_service('SessionTypeService')
id_service = service.id_service
@@ -71,17 +71,17 @@ def test_undelete(self):
id_session_type = ses_type.id_session_type
# Create a new session of that session type
- from test_TeraSession import TeraSessionTest
+ from tests.opentera.db.models.test_TeraSession import TeraSessionTest
ses = TeraSessionTest.new_test_session(id_session_type=id_session_type, id_creator_service=1)
id_session = ses.id_session
# Associate session type to site
- from test_TeraSessionTypeSite import TeraSessionTypeSiteTest
+ from tests.opentera.db.models.test_TeraSessionTypeSite import TeraSessionTypeSiteTest
ses_site = TeraSessionTypeSiteTest.new_test_session_type_site(id_site=1, id_session_type=id_session_type)
id_session_type_site = ses_site.id_session_type_site
# ... and project
- from test_TeraSessionTypeProject import TeraSessionTypeProjectTest
+ from tests.opentera.db.models.test_TeraSessionTypeProject import TeraSessionTypeProjectTest
ses_proj = TeraSessionTypeProjectTest.new_test_session_type_project(id_project=1,
id_session_type=id_session_type)
id_session_type_project = ses_proj.id_session_type_project
diff --git a/teraserver/python/tests/opentera/db/models/test_TeraSessionTypeServices.py b/teraserver/python/tests/opentera/db/models/test_TeraSessionTypeServices.py
new file mode 100644
index 000000000..4c448166a
--- /dev/null
+++ b/teraserver/python/tests/opentera/db/models/test_TeraSessionTypeServices.py
@@ -0,0 +1,233 @@
+from tests.opentera.db.models.BaseModelsTest import BaseModelsTest
+from opentera.db.models.TeraSessionTypeServices import TeraSessionTypeServices
+from opentera.db.models.TeraSessionType import TeraSessionType
+from opentera.db.models.TeraSessionTypeSite import TeraSessionTypeSite
+from opentera.db.models.TeraSessionTypeProject import TeraSessionTypeProject
+from opentera.db.models.TeraService import TeraService
+from opentera.db.models.TeraServiceProject import TeraServiceProject
+from opentera.db.models.TeraServiceSite import TeraServiceSite
+
+
+class TeraSessionTypeServicesTest(BaseModelsTest):
+
+ def test_to_json_full_and_minimal(self):
+ with self._flask_app.app_context():
+ sts_list = TeraSessionTypeServices.query.all()
+ self.assertGreater(len(sts_list), 0)
+ for minimal in [False, True]:
+ for sts in sts_list:
+ self.assertIsNotNone(sts)
+ json = sts.to_json(minimal=minimal)
+ self.assertNotEqual(None, json)
+
+ if not minimal:
+ self.assertTrue('id_session_type_service' in json)
+ self.assertTrue('id_session_type' in json)
+ self.assertTrue('id_service' in json)
+ self.assertFalse('session_type_service_session_type' in json)
+ self.assertFalse('session_type_service_service' in json)
+ else:
+ # Same a minimal for now
+ self.assertTrue('id_session_type_service' in json)
+ self.assertTrue('id_session_type' in json)
+ self.assertTrue('id_service' in json)
+ self.assertFalse('session_type_service_session_type' in json)
+ self.assertFalse('session_type_service_service' in json)
+
+ def test_from_json(self):
+ with self._flask_app.app_context():
+ for sts in TeraSessionTypeServices.query.all():
+ json = sts.to_json()
+ new_sts = TeraSessionTypeServices()
+ new_sts.from_json(json)
+ self.assertEqual(new_sts.id_session_type_service, sts.id_session_type_service)
+ self.assertEqual(new_sts.id_session_type, sts.id_session_type)
+ self.assertEqual(new_sts.id_service, sts.id_service)
+
+ def test_get_with_id(self):
+ with self._flask_app.app_context():
+ sts_ids = [sts.id_session_type_service for sts in TeraSessionTypeServices.query.all()]
+ self.db.session.expire_all() # Clear cache
+ for sts_id in sts_ids:
+ test_sts = TeraSessionTypeServices.get_session_type_service_by_id(sts_id)
+ self.assertEqual(test_sts.id_session_type_service, sts_id)
+
+ def test_get_services_for_session_type(self):
+ with self._flask_app.app_context():
+ ses_types: list[TeraSessionType] = TeraSessionType.query.all()
+ for ses_type in ses_types:
+ secondary_services = ses_type.session_type_secondary_services
+ self.db.session.expire_all() # Clear cache
+ associated_services = TeraSessionTypeServices.get_services_for_session_type(ses_type.id_session_type)
+ self.assertEqual(len(secondary_services), len(associated_services))
+
+ def test_get_session_types_for_service(self):
+ with self._flask_app.app_context():
+ services: list[TeraService] = TeraService.query.all()
+ for service in services:
+ sts_list = TeraSessionTypeServices.get_sessions_types_for_service(service.id_service)
+ if sts_list:
+ for sts in sts_list:
+ st: TeraSessionType = TeraSessionType.get_session_type_by_id(sts.id_session_type)
+ self.assertTrue(sts.id_service in [serv.id_service for serv in st.session_type_secondary_services])
+
+ def test_check_integrity_not_associated_to_site(self):
+ with self._flask_app.app_context():
+ st = TeraSessionType()
+ st.session_type_name = 'Test'
+ st.session_type_online = False
+ st.session_type_color = ''
+ st.session_type_category = 1
+ TeraSessionType.insert(st)
+
+ st_site = TeraSessionTypeSite()
+ st_site.id_site = 1
+ st_site.id_session_type = st.id_session_type
+ TeraSessionTypeSite.insert(st_site)
+
+ self.assertTrue(st.id_session_type in [s_type.id_session_type for s_type in
+ TeraSessionTypeSite.get_sessions_types_for_site(st_site.id_site)])
+
+ # Try to associate EmailService, which is not in the site
+ service: TeraService = TeraService.get_service_by_key('EmailService')
+ self.assertFalse(service.id_service in [s.id_service for s in
+ TeraServiceSite.get_services_for_site(st_site.id_site)])
+
+ sts = TeraSessionTypeServices()
+ sts.id_service = service.id_service
+ sts.id_session_type = st.id_session_type
+ TeraSessionTypeServices.insert(sts)
+
+ self.assertTrue(service.id_service in [s.id_service for s in
+ TeraServiceSite.get_services_for_site(st_site.id_site)])
+
+ tss = TeraServiceSite.get_service_site_for_service_site(st_site.id_site, service.id_service)
+ TeraServiceSite.delete(tss.id_service_site)
+ TeraSessionType.delete(st.id_session_type)
+
+ def test_check_integrity_not_associated_to_project(self):
+ with self._flask_app.app_context():
+ st = TeraSessionType()
+ st.session_type_name = 'Test'
+ st.session_type_online = False
+ st.session_type_color = ''
+ st.session_type_category = 1
+ TeraSessionType.insert(st)
+
+ st_site = TeraSessionTypeSite()
+ st_site.id_site = 1
+ st_site.id_session_type = st.id_session_type
+ TeraSessionTypeSite.insert(st_site)
+
+ self.assertTrue(st.id_session_type in [s_type.id_session_type for s_type in
+ TeraSessionTypeSite.get_sessions_types_for_site(st_site.id_site)])
+
+ st_project = TeraSessionTypeProject()
+ st_project.id_project = 1
+ st_project.id_session_type = st.id_session_type
+ TeraSessionTypeProject.insert(st_project)
+
+ self.assertTrue(st.id_session_type in [s_type.id_session_type for s_type in
+ TeraSessionTypeProject.
+ get_sessions_types_for_project(st_project.id_project)])
+
+ service: TeraService = TeraService.get_service_by_key('EmailService')
+
+ site_service = TeraServiceSite()
+ site_service.id_service = service.id_service
+ site_service.id_site = st_site.id_site
+ TeraServiceSite.insert(site_service)
+
+ self.assertTrue(service.id_service in [serv.id_service for serv in
+ TeraServiceSite.get_services_for_site(site_service.id_site)])
+
+ # Try to associate EmailService, which is not in the project
+ self.assertFalse(service.id_service in [s.id_service for s in
+ TeraServiceProject.get_services_for_project(st_project.id_project)])
+
+ sts = TeraSessionTypeServices()
+ sts.id_service = service.id_service
+ sts.id_session_type = st.id_session_type
+ TeraSessionTypeServices.insert(sts)
+
+ self.assertTrue(service.id_service in [s.id_service for s in
+ TeraServiceProject.get_services_for_project(st_project.id_project)])
+
+ tsp = TeraServiceProject.get_service_project_for_service_project(st_project.id_project, service.id_service)
+ TeraServiceProject.delete(tsp.id_service_project)
+ tss = TeraServiceSite.get_service_site_for_service_site(st_site.id_site, service.id_service)
+ TeraServiceSite.delete(tss.id_service_site)
+ TeraSessionType.delete(st.id_session_type)
+
+
+ def test_check_integrity_not_associated_to_site_and_project(self):
+ with self._flask_app.app_context():
+ st = TeraSessionType()
+ st.session_type_name = 'Test'
+ st.session_type_online = False
+ st.session_type_color = ''
+ st.session_type_category = 1
+ TeraSessionType.insert(st)
+
+ st_site = TeraSessionTypeSite()
+ st_site.id_site = 1
+ st_site.id_session_type = st.id_session_type
+ TeraSessionTypeSite.insert(st_site)
+
+ self.assertTrue(st.id_session_type in [s_type.id_session_type for s_type in
+ TeraSessionTypeSite.get_sessions_types_for_site(st_site.id_site)])
+
+ st_project = TeraSessionTypeProject()
+ st_project.id_project = 1
+ st_project.id_session_type = st.id_session_type
+ TeraSessionTypeProject.insert(st_project)
+
+ self.assertTrue(st.id_session_type in [s_type.id_session_type for s_type in
+ TeraSessionTypeProject.
+ get_sessions_types_for_project(st_project.id_project)])
+
+ service: TeraService = TeraService.get_service_by_key('EmailService')
+
+
+ # Try to associate EmailService, which is not in the project and site
+ self.assertFalse(service.id_service in [serv.id_service for serv in
+ TeraServiceSite.get_services_for_site(st_site.id_site)])
+ self.assertFalse(service.id_service in [s.id_service for s in
+ TeraServiceProject.get_services_for_project(st_project.id_project)])
+
+ sts = TeraSessionTypeServices()
+ sts.id_service = service.id_service
+ sts.id_session_type = st.id_session_type
+ TeraSessionTypeServices.insert(sts)
+
+ self.assertTrue(service.id_service in [serv.id_service for serv in
+ TeraServiceSite.get_services_for_site(st_site.id_site)])
+ self.assertTrue(service.id_service in [s.id_service for s in
+ TeraServiceProject.get_services_for_project(st_project.id_project)])
+
+ tsp = TeraServiceProject.get_service_project_for_service_project(st_project.id_project, service.id_service)
+ TeraServiceProject.delete(tsp.id_service_project)
+ tss = TeraServiceSite.get_service_site_for_service_site(st_site.id_site, service.id_service)
+ TeraServiceSite.delete(tss.id_service_site)
+ TeraSessionType.delete(st.id_session_type)
+
+ def test_delete(self):
+ with self._flask_app.app_context():
+ service: TeraService = TeraService.get_service_by_key('EmailService')
+
+ st = TeraSessionType()
+ st.session_type_name = 'Test'
+ st.session_type_online = False
+ st.session_type_color = ''
+ st.session_type_category = 1
+ TeraSessionType.insert(st)
+
+ sts = TeraSessionTypeServices()
+ sts.id_service = service.id_service
+ sts.id_session_type = st.id_session_type
+ TeraSessionTypeServices.insert(sts)
+
+ sts_id = sts.id_session_type_service
+ TeraSessionTypeServices.delete(sts_id)
+ self.assertIsNone(TeraSessionTypeServices.get_session_type_service_by_id(sts_id))
+ TeraSessionType.delete(st.id_session_type)
\ No newline at end of file
diff --git a/teraserver/python/tests/opentera/db/models/test_TeraSite.py b/teraserver/python/tests/opentera/db/models/test_TeraSite.py
index 91fd2763a..7d80da00e 100644
--- a/teraserver/python/tests/opentera/db/models/test_TeraSite.py
+++ b/teraserver/python/tests/opentera/db/models/test_TeraSite.py
@@ -1,4 +1,3 @@
-from tests.opentera.db.models.BaseModelsTest import BaseModelsTest
from sqlalchemy import exc
from opentera.db.models.TeraSite import TeraSite
from opentera.db.models.TeraProject import TeraProject
@@ -10,7 +9,22 @@
from opentera.db.models.TeraSessionTypeSite import TeraSessionTypeSite
from opentera.db.models.TeraTestTypeSite import TeraTestTypeSite
from opentera.db.models.TeraDevice import TeraDevice
+from opentera.db.models.TeraUser import TeraUser
+from opentera.db.models.TeraUserGroup import TeraUserGroup
+from opentera.db.models.TeraUserUserGroup import TeraUserUserGroup
+from opentera.db.models.TeraServiceAccess import TeraServiceAccess
+from opentera.db.models.TeraService import TeraService
+from tests.opentera.db.models.BaseModelsTest import BaseModelsTest
+from tests.opentera.db.models.test_TeraDevice import TeraDeviceTest
+from tests.opentera.db.models.test_TeraProject import TeraProjectTest
+from tests.opentera.db.models.test_TeraParticipant import TeraParticipantTest
+from tests.opentera.db.models.test_TeraSession import TeraSessionTest
+from tests.opentera.db.models.test_TeraDeviceSite import TeraDeviceSiteTest
+from tests.opentera.db.models.test_TeraServiceSite import TeraServiceSiteTest
+from tests.opentera.db.models.test_TeraServiceRole import TeraServiceRoleTest
+from tests.opentera.db.models.test_TeraSessionTypeSite import TeraSessionTypeSiteTest
+from tests.opentera.db.models.test_TeraTestTypeSite import TeraTestTypeSiteTest
class TeraSiteTest(BaseModelsTest):
@@ -31,11 +45,29 @@ def test_unique_args(self):
self.db.session.add(same_site1)
self.assertRaises(exc.IntegrityError, self.db.session.commit)
+ def test_site_2fa_required_default(self):
+ with self._flask_app.app_context():
+ new_site = TeraSite()
+ self.assertFalse(new_site.site_2fa_required)
+
+ def test_site_2fa_required_update(self):
+ with self._flask_app.app_context():
+ new_site = TeraSiteTest.new_test_site(name='Site With 2FA')
+ new_site.site_2fa_required = True
+ self.assertTrue(new_site.site_2fa_required)
+ self.db.session.add(new_site)
+ self.db.session.commit()
+ id_site = new_site.id_site
+ self.db.session.rollback()
+ same_site = TeraSite.get_site_by_id(id_site)
+ self.assertTrue(same_site.site_2fa_required)
+
def test_to_json(self):
with self._flask_app.app_context():
new_site = TeraSiteTest.new_test_site(name='Site Name')
new_site_json = new_site.to_json()
new_site_json_minimal = new_site.to_json(minimal=True)
+ self.assertEqual(new_site_json['site_2fa_required'], False)
self.assertEqual(new_site_json['site_name'], 'Site Name')
self.assertGreaterEqual(new_site_json['id_site'], 1)
self.assertEqual(new_site_json_minimal['site_name'], 'Site Name')
@@ -118,17 +150,14 @@ def test_hard_delete(self):
site = TeraSiteTest.new_test_site()
id_site = site.id_site
- from test_TeraProject import TeraProjectTest
project = TeraProjectTest.new_test_project(id_site=id_site)
self.assertIsNotNone(project.id_project)
id_project = project.id_project
- from test_TeraParticipant import TeraParticipantTest
participant = TeraParticipantTest.new_test_participant(id_project=id_project)
self.assertIsNotNone(participant.id_participant)
id_participant = participant.id_participant
- from test_TeraSession import TeraSessionTest
ses = TeraSessionTest.new_test_session(id_session_type=1, id_creator_participant=1,
participants=[participant])
id_session = ses.id_session
@@ -165,31 +194,25 @@ def test_undelete(self):
id_site = site.id_site
# Associate device
- from test_TeraDevice import TeraDeviceTest
device = TeraDeviceTest.new_test_device()
id_device = device.id_device
- from test_TeraDeviceSite import TeraDeviceSiteTest
device = TeraDeviceSiteTest.new_test_device_site(id_device=id_device, id_site=id_site)
id_device_site = device.id_device_site
# ... and service
- from test_TeraServiceSite import TeraServiceSiteTest
service_site = TeraServiceSiteTest.new_test_service_site(id_site=id_site, id_service=3)
id_service_site = service_site.id_service_site
# ... and roles
- from test_TeraServiceRole import TeraServiceRoleTest
role = TeraServiceRoleTest.new_test_service_role(id_service=3, id_site=id_site, role_name='Test')
id_role = role.id_service_role
# ... and session type
- from test_TeraSessionTypeSite import TeraSessionTypeSiteTest
ses_type = TeraSessionTypeSiteTest.new_test_session_type_site(id_site=id_site, id_session_type=1)
id_session_type = ses_type.id_session_type_site
# ... and test type
- from test_TeraTestTypeSite import TeraTestTypeSiteTest
test_type = TeraTestTypeSiteTest.new_test_test_type_site(id_site=id_site, id_test_type=1)
id_test_type = test_type.id_test_type_site
@@ -215,11 +238,231 @@ def test_undelete(self):
self.assertIsNotNone(TeraSessionTypeSite.get_session_type_site_by_id(id_session_type))
self.assertIsNotNone(TeraTestTypeSite.get_test_type_site_by_id(id_test_type))
+ def test_2fa_required_site(self):
+ with self._flask_app.app_context():
+ site = TeraSiteTest.new_test_site(name='2FA Site', site_2fa_required=True)
+ self.assertTrue(site.site_2fa_required)
+ self.db.session.add(site)
+ self.db.session.commit()
+ id_site = site.id_site
+ self.db.session.rollback()
+ same_site = TeraSite.get_site_by_id(id_site)
+ self.assertTrue(same_site.site_2fa_required)
+ TeraSiteTest.delete_site(site.id_site)
+
+ def test_enable_2fa_in_site_should_enable_2fa_for_users(self):
+ with self._flask_app.app_context():
+ site = TeraSiteTest.new_test_site(name='2FA Site', site_2fa_required=False)
+ self.assertIsNotNone(site)
+ group = TeraSiteTest.new_test_user_group('Test Group', site.id_site)
+ self.assertIsNotNone(group)
+ user1 = TeraSiteTest.new_test_user('test_user1', 'Password12345!', group.id_user_group)
+ self.assertIsNotNone(user1)
+
+ user2 = TeraSiteTest.new_test_user('test_user2', 'Password12345!', None)
+ self.assertIsNotNone(user2)
+
+ # Enable 2fa in site
+ site.site_2fa_required = True
+ self.db.session.add(site)
+ self.db.session.commit()
+
+ # User should be updated automatically with 2fa if group is associated with site
+ self.assertTrue(user1.user_2fa_enabled)
+ # Else user should not be updated
+ self.assertFalse(user2.user_2fa_enabled)
+ # Delete everything
+ TeraSiteTest.delete_site(site.id_site)
+ TeraSiteTest.delete_user(user1.id_user)
+ TeraSiteTest.delete_user(user2.id_user)
+ TeraSiteTest.delete_user_group(group.id_user_group)
+
+
+ def test_disable_2fa_in_site_should_not_disable_2fa_for_users(self):
+ with self._flask_app.app_context():
+ site = TeraSiteTest.new_test_site(name='2FA Site', site_2fa_required=True)
+ self.assertIsNotNone(site)
+ group = TeraSiteTest.new_test_user_group('Test Group', site.id_site)
+ self.assertIsNotNone(group)
+ user1 = TeraSiteTest.new_test_user('test_user1', 'Password12345!', group.id_user_group)
+ self.assertIsNotNone(user1)
+
+ user2 = TeraSiteTest.new_test_user('test_user2', 'Password12345!', None)
+ self.assertIsNotNone(user2)
+
+ # Site should have 2fa enabled
+ self.db.session.add(site)
+ self.db.session.commit()
+
+ # User should have 2fa enabled if group have access to site
+ self.assertTrue(user1.user_2fa_enabled)
+ # Else user should not be updated
+ self.assertFalse(user2.user_2fa_enabled)
+
+ # Disable 2fa in site
+ site.site_2fa_required = False
+ self.db.session.add(site)
+ self.db.session.commit()
+
+ # User should still have 2fa enabled (not changed)
+ self.assertTrue(user1.user_2fa_enabled)
+ # Else user should not be updated
+ self.assertFalse(user2.user_2fa_enabled)
+
+ # Delete everything
+ TeraSiteTest.delete_site(site.id_site)
+ TeraSiteTest.delete_user(user1.id_user)
+ TeraSiteTest.delete_user(user2.id_user)
+ TeraSiteTest.delete_user_group(group.id_user_group)
+
+ def test_add_group_to_2fa_enabled_site_should_enable_2fa_for_all_users(self):
+ with self._flask_app.app_context():
+ site = TeraSiteTest.new_test_site(name='2FA Site', site_2fa_required=True)
+ self.assertIsNotNone(site)
+
+ # No group associated to the user
+ user1 = TeraSiteTest.new_test_user('test_user1', 'Password12345!', None)
+ self.assertIsNotNone(user1)
+
+ user2 = TeraSiteTest.new_test_user('test_user2', 'Password12345!', None)
+ self.assertIsNotNone(user2)
+
+ # Site should have 2fa enabled
+ self.db.session.add(site)
+ self.db.session.commit()
+
+ # User should have 2fa enabled if group have access to site
+ self.assertFalse(user1.user_2fa_enabled)
+ self.assertFalse(user2.user_2fa_enabled)
+
+ # Add group to site
+ group = TeraSiteTest.new_test_user_group('Test Group', site.id_site)
+ self.assertIsNotNone(group)
+
+ # Add users to group
+ user_user_group = TeraUserUserGroup()
+ user_user_group.id_user = user1.id_user
+ user_user_group.id_user_group = group.id_user_group
+ TeraUserUserGroup.insert(user_user_group)
+
+ user_user_group = TeraUserUserGroup()
+ user_user_group.id_user = user2.id_user
+ user_user_group.id_user_group = group.id_user_group
+ TeraUserUserGroup.insert(user_user_group)
+
+ # User should have 2fa enabled if group have access to site
+ self.assertTrue(user1.user_2fa_enabled)
+ self.assertTrue(user2.user_2fa_enabled)
+
+ # Delete everything
+ TeraSiteTest.delete_site(site.id_site)
+ TeraSiteTest.delete_user(user1.id_user)
+ TeraSiteTest.delete_user(user2.id_user)
+ TeraSiteTest.delete_user_group(group.id_user_group)
+
+ def test_disable_2fa_for_user_in_a_2fa_site_should_not_change_2fa_enabled(self):
+ with self._flask_app.app_context():
+ site = TeraSiteTest.new_test_site(name='2FA Site', site_2fa_required=True)
+ self.assertIsNotNone(site)
+ group = TeraSiteTest.new_test_user_group('Test Group', site.id_site)
+ self.assertIsNotNone(group)
+ user1 = TeraSiteTest.new_test_user('test_user1', 'Password12345!', group.id_user_group)
+ self.assertIsNotNone(user1)
+
+ # Site should have 2fa enabled
+ self.db.session.add(site)
+ self.db.session.commit()
+
+ # User should have 2fa enabled if group have access to site
+ self.assertTrue(user1.user_2fa_enabled)
+
+ # Disable 2fa for user
+ user1.user_2fa_enabled = False
+ self.db.session.add(user1)
+ self.db.session.commit()
+
+ self.assertTrue(user1.user_2fa_enabled)
+
+ # Delete everything
+ TeraSiteTest.delete_site(site.id_site)
+ TeraSiteTest.delete_user(user1.id_user)
+ TeraSiteTest.delete_user_group(group.id_user_group)
+
+ def test_enable_2fa_for_site_needs_to_enable_2fa_for_all_superadmins(self):
+ with self._flask_app.app_context():
+ site = TeraSiteTest.new_test_site(name='2FA Site', site_2fa_required=True)
+ self.assertIsNotNone(site)
+
+ # Query all superadmins
+ superadmins = TeraUser.query.filter(TeraUser.user_superadmin == True).all()
+ for superadmin in superadmins:
+ self.assertTrue(superadmin.user_2fa_enabled)
+
+
@staticmethod
- def new_test_site(name: str = 'Test Site') -> TeraSite:
+ def new_test_site(name: str = 'Test Site', site_2fa_required: bool = False) -> TeraSite:
site = TeraSite()
site.site_name = name
+ site.site_2fa_required = site_2fa_required
TeraSite.insert(site)
return site
+ @staticmethod
+ def new_test_user_group(name: str, id_site: int ) -> TeraUserGroup:
+
+ # Create Service Role first
+ tera_server_service = TeraService.get_openteraserver_service()
+
+ service_role = TeraServiceRole()
+ service_role.service_role_name = 'Test Site Role'
+ service_role.id_service = tera_server_service.id_service
+ service_role.id_site = id_site
+ TeraServiceRole.insert(service_role)
+
+ # Create User Group
+ group: TeraUserGroup = TeraUserGroup()
+ group.user_group_name = name
+ TeraUserGroup.insert(group)
+
+ # Update Service Access
+ service_access = TeraServiceAccess()
+ service_access.id_service_role = service_role.id_service_role
+ service_access.id_user_group = group.id_user_group
+ TeraServiceAccess.insert(service_access)
+
+ return group
+
+ @staticmethod
+ def new_test_user(username: str, password: str, id_user_group: int | None) -> TeraUser:
+ user = TeraUser()
+ user.user_username = username
+ user.user_password = password
+ user.user_firstname = username
+ user.user_lastname = username
+ user.user_email = f"{username}@test.com"
+ user.user_enabled = True
+ user.user_profile = {}
+ TeraUser.insert(user)
+
+ # Update user group if not none
+ if id_user_group is not None:
+ user_user_group = TeraUserUserGroup()
+ user_user_group.id_user = user.id_user
+ user_user_group.id_user_group = id_user_group
+ TeraUserUserGroup.insert(user_user_group)
+
+ return user
+
+
+ @staticmethod
+ def delete_site(id: int):
+ TeraSite.delete(id, hard_delete=True)
+
+ @staticmethod
+ def delete_user_group(id: int):
+ TeraUserGroup.delete(id, hard_delete=True)
+
+ @staticmethod
+ def delete_user(id: int):
+ TeraUser.delete(id, hard_delete=True)
diff --git a/teraserver/python/tests/opentera/db/models/test_TeraTest.py b/teraserver/python/tests/opentera/db/models/test_TeraTest.py
index 97c3ce652..75072ed8e 100644
--- a/teraserver/python/tests/opentera/db/models/test_TeraTest.py
+++ b/teraserver/python/tests/opentera/db/models/test_TeraTest.py
@@ -13,6 +13,13 @@ def test_defaults(self):
with self._flask_app.app_context():
pass
+ def test_db_events_not_none(self):
+ with self._flask_app.app_context():
+ test : TeraTest = TeraTestTest.new_test_test(id_session=1)
+ self.assertIsNotNone(test.to_json_create_event())
+ self.assertIsNotNone(test.to_json_update_event())
+ self.assertIsNotNone(test.to_json_delete_event())
+
def test_soft_delete(self):
with self._flask_app.app_context():
# Create new
@@ -46,22 +53,22 @@ def test_hard_delete(self):
def test_undelete(self):
with self._flask_app.app_context():
# Create new participant
- from test_TeraParticipant import TeraParticipantTest
+ from tests.opentera.db.models.test_TeraParticipant import TeraParticipantTest
participant = TeraParticipantTest.new_test_participant(id_project=1)
id_participant = participant.id_participant
# Create new device
- from test_TeraDevice import TeraDeviceTest
+ from tests.opentera.db.models.test_TeraDevice import TeraDeviceTest
device = TeraDeviceTest.new_test_device()
id_device = device.id_device
# Create new user
- from test_TeraUser import TeraUserTest
+ from tests.opentera.db.models.test_TeraUser import TeraUserTest
user = TeraUserTest.new_test_user(user_name='test_testuser')
id_user = user.id_user
# Create new session
- from test_TeraSession import TeraSessionTest
+ from tests.opentera.db.models.test_TeraSession import TeraSessionTest
ses = TeraSessionTest.new_test_session(participants=[participant], users=[user], devices=[device])
id_session = ses.id_session
diff --git a/teraserver/python/tests/opentera/db/models/test_TeraTestInvitation.py b/teraserver/python/tests/opentera/db/models/test_TeraTestInvitation.py
new file mode 100644
index 000000000..f78b9d377
--- /dev/null
+++ b/teraserver/python/tests/opentera/db/models/test_TeraTestInvitation.py
@@ -0,0 +1,269 @@
+from datetime import datetime, timezone, timedelta
+
+from sqlalchemy.exc import IntegrityError
+
+from tests.opentera.db.models.BaseModelsTest import BaseModelsTest
+from opentera.db.models.TeraTestInvitation import TeraTestInvitation
+
+
+class TeraTestInvitationTest(BaseModelsTest):
+ """
+ Test TeraTestInvitation database model.
+ """
+
+ def test_defaults(self):
+ """
+ Test default values at object creation
+ """
+ with self._flask_app.app_context():
+ pass
+
+ def test_db_events_not_none(self):
+ """
+ Test that db events are not None
+ """
+ with self._flask_app.app_context():
+ invitation : TeraTestInvitation = TeraTestInvitationTest.new_test_invitation(id_test_type=1, id_user=1, id_project=1)
+ self.assertIsNotNone(invitation.to_json_create_event())
+ self.assertIsNotNone(invitation.to_json_update_event())
+ self.assertIsNotNone(invitation.to_json_delete_event())
+
+ def test_create_with_no_user_participant_device(self):
+ """
+ Test creating an invitation with no user, participant or device will raise an IntegrityError.
+ """
+ with self._flask_app.app_context():
+ with self.assertRaises(IntegrityError):
+ TeraTestInvitationTest.new_test_invitation(id_test_type=1)
+
+ def test_create_with_user_and_participant(self):
+ """
+ Test creating an invitation with both user and participant will raise an IntegrityError.
+ """
+ with self._flask_app.app_context():
+ with self.assertRaises(IntegrityError):
+ TeraTestInvitationTest.new_test_invitation(id_test_type=1, id_user=1, id_participant=1)
+
+ def test_create_with_user_and_device(self):
+ """
+ Test creating an invitation with both user and device will raise an IntegrityError.
+ """
+ with self._flask_app.app_context():
+ with self.assertRaises(IntegrityError):
+ TeraTestInvitationTest.new_test_invitation(id_test_type=1, id_user=1, id_device=1)
+
+ def test_create_with_invalid_test_type(self):
+ """
+ Test creating an invitation with an invalid test type will raise an IntegrityError.
+ """
+ with self._flask_app.app_context():
+ with self.assertRaises(IntegrityError):
+ TeraTestInvitationTest.new_test_invitation(id_test_type=9999)
+
+ def test_create_with_invalid_session_type(self):
+ """
+ Test creating an invitation with an invalid session type will raise an IntegrityError.
+ """
+ with self._flask_app.app_context():
+ with self.assertRaises(IntegrityError):
+ TeraTestInvitationTest.new_test_invitation(id_test_type=1, id_session=9999)
+
+ def test_create_with_invalid_user(self):
+ """
+ Test creating an invitation with an invalid user will raise an IntegrityError.
+ """
+ with self._flask_app.app_context():
+ with self.assertRaises(IntegrityError):
+ TeraTestInvitationTest.new_test_invitation(id_test_type=1, id_user=9999)
+
+ def test_create_with_valid_test_type_and_user(self):
+ """
+ Test creating an invitation with a valid test type and user.
+ """
+ with self._flask_app.app_context():
+ invitation = TeraTestInvitationTest.new_test_invitation(id_test_type=1, id_user=1, id_project=1)
+ self.assertIsNotNone(invitation)
+
+ def test_create_with_missing_expiration_date(self):
+ """
+ Test creating an invitation with missing expiration date will raise an IntegrityError.
+ """
+ with self._flask_app.app_context():
+ with self.assertRaises(IntegrityError):
+ invitation = TeraTestInvitation()
+ invitation.id_test_type = 1
+ invitation.id_user = 1
+ TeraTestInvitation.insert(invitation)
+
+ def test_create_with_automatic_key_generation(self):
+ """
+ Test creating an invitation with missing message.
+ """
+ with self._flask_app.app_context():
+ invitation = TeraTestInvitationTest.new_test_invitation(id_test_type=1, id_user=1, id_project=1)
+ self.assertIsNotNone(invitation)
+ self.assertEqual(len(invitation.test_invitation_key), 16)
+
+ def test_create_with_invalid_key(self):
+ """
+ Test creating an invitation with an invalid key will raise an IntegrityError.
+ """
+ with self._flask_app.app_context():
+ with self.assertRaises(IntegrityError):
+ invitation = TeraTestInvitation()
+ invitation.id_test_type = 1
+ invitation.id_user = 1
+ invitation.test_invitation_key = "Invalid key"
+ TeraTestInvitation.insert(invitation)
+
+ def test_create_1000_invitations_should_have_different_keys(self):
+ """
+ Test creating 1000 invitations should have different keys.
+ """
+ with self._flask_app.app_context():
+ keys = set()
+ for _ in range(1000):
+ invitation = TeraTestInvitationTest.new_test_invitation(id_test_type=1, id_user=1, id_project=1)
+ self.assertNotIn(invitation.test_invitation_key, keys)
+ keys.add(invitation.test_invitation_key)
+
+
+ def test_update_test_type_should_raise_integrity_error(self):
+ """
+ Test updating the test type of an invitation will raise an IntegrityError.
+ """
+ with self._flask_app.app_context():
+ invitation = TeraTestInvitationTest.new_test_invitation(id_test_type=1, id_user=1, id_project=1)
+ with self.assertRaises(IntegrityError):
+ invitation.id_test_type = 2
+ TeraTestInvitation.update(invitation.id_test_invitation,
+ TeraTestInvitation.clean_values(invitation.to_json()))
+
+ def test_update_invitation_key_should_raise_integrity_error(self):
+ """
+ Test updating the key of an invitation will raise an IntegrityError.
+ """
+ with self._flask_app.app_context():
+ invitation = TeraTestInvitationTest.new_test_invitation(id_test_type=1, id_user=1, id_project=1)
+ with self.assertRaises(IntegrityError):
+ invitation.test_invitation_key = "New key"
+ TeraTestInvitation.update(invitation.id_test_invitation,
+ TeraTestInvitation.clean_values(invitation.to_json()))
+
+ def test_to_json(self):
+ """
+ Test to_json method
+ """
+ with self._flask_app.app_context():
+ invitation1 : TeraTestInvitation = TeraTestInvitationTest.new_test_invitation(id_test_type=1, id_user=1, id_project=1)
+ invitation2 : TeraTestInvitation = TeraTestInvitationTest.new_test_invitation(id_test_type=1, id_participant=1, id_project=1)
+ invitation3 : TeraTestInvitation = TeraTestInvitationTest.new_test_invitation(id_test_type=1, id_device=1, id_project=1)
+ invitation4 : TeraTestInvitation = TeraTestInvitationTest.new_test_invitation(id_test_type=1, id_session=1, id_user=1, id_project=1)
+
+ for invitation in [invitation1, invitation2, invitation3, invitation4]:
+ self._verify_to_json(invitation, minimal=True)
+ self._verify_to_json(invitation, minimal=False)
+
+ def test_from_json(self):
+ """
+ Test from_json method
+ """
+ with self._flask_app.app_context():
+ invitation1 : TeraTestInvitation = TeraTestInvitationTest.new_test_invitation(id_test_type=1, id_user=1, id_project=1)
+ invitation2 : TeraTestInvitation = TeraTestInvitationTest.new_test_invitation(id_test_type=1, id_participant=1, id_project=1)
+ invitation3 : TeraTestInvitation = TeraTestInvitationTest.new_test_invitation(id_test_type=1, id_device=1, id_project=1)
+ invitation4 : TeraTestInvitation = TeraTestInvitationTest.new_test_invitation(id_test_type=1, id_session=1, id_user=1, id_project=1)
+
+ for invitation in [invitation1, invitation2, invitation3, invitation4]:
+ invitation_json = invitation.to_json(minimal=True)
+ new_invitation = TeraTestInvitation()
+ new_invitation.from_json(invitation_json)
+ self.assertEqual(invitation_json, new_invitation.to_json(minimal=True))
+
+ @staticmethod
+ def new_test_invitation(id_test_type: int,
+ id_session: int = None,
+ id_user: int = None,
+ id_participant: int = None,
+ id_device: int = None,
+ id_project: int = None) -> TeraTestInvitation:
+ """
+ Insert a new test invitation in database.
+ """
+
+ invitation = TeraTestInvitation()
+
+ invitation.id_test_type = id_test_type
+ invitation.id_session = id_session
+ invitation.id_user = id_user
+ invitation.id_participant = id_participant
+ invitation.id_device = id_device
+ invitation.id_project = id_project
+
+ invitation.test_invitation_expiration_date = datetime.now(tz=timezone.utc) + timedelta(days=1)
+ invitation.test_invitation_message = "Test invitation message"
+ TeraTestInvitation.insert(invitation)
+ return invitation
+
+ def _verify_to_json(self, invitation : TeraTestInvitation, minimal: bool = False):
+
+ invitation_json = invitation.to_json(minimal=minimal)
+ self.assertTrue('id_test_invitation' in invitation_json)
+ self.assertEqual(invitation_json['id_test_invitation'], invitation.id_test_invitation)
+ self.assertTrue('id_test_type' in invitation_json)
+ self.assertEqual(invitation_json['id_test_type'], invitation.id_test_type)
+ self.assertTrue('id_user' in invitation_json)
+ self.assertEqual(invitation_json['id_user'], invitation.id_user)
+ self.assertTrue('id_participant' in invitation_json)
+ self.assertEqual(invitation_json['id_participant'], invitation.id_participant)
+ self.assertTrue('id_device' in invitation_json)
+ self.assertEqual(invitation_json['id_device'], invitation.id_device)
+ self.assertTrue('id_project' in invitation_json)
+ self.assertEqual(invitation_json['id_project'], invitation.id_project)
+ self.assertTrue('id_session' in invitation_json)
+ self.assertEqual(invitation_json['id_session'], invitation.id_session)
+ self.assertTrue('test_invitation_key' in invitation_json)
+ self.assertEqual(invitation_json['test_invitation_key'], invitation.test_invitation_key)
+ self.assertTrue('test_invitation_message' in invitation_json)
+ self.assertEqual(invitation_json['test_invitation_message'], invitation.test_invitation_message)
+ self.assertTrue('test_invitation_creation_date' in invitation_json)
+ self.assertEqual(invitation_json['test_invitation_creation_date'], invitation.test_invitation_creation_date.isoformat())
+ self.assertTrue('test_invitation_expiration_date' in invitation_json)
+ self.assertEqual(invitation_json['test_invitation_expiration_date'], invitation.test_invitation_expiration_date.isoformat())
+ self.assertTrue('test_invitation_max_count' in invitation_json)
+ self.assertEqual(invitation_json['test_invitation_max_count'], invitation.test_invitation_max_count)
+ self.assertTrue('test_invitation_count' in invitation_json)
+ self.assertEqual(invitation_json['test_invitation_count'], invitation.test_invitation_count)
+
+ if minimal:
+ self.assertEqual(len(invitation_json), 13)
+ self.assertFalse('test_invitation_test_type' in invitation_json)
+ self.assertFalse('test_invitation_session' in invitation_json)
+ self.assertFalse('test_invitation_user' in invitation_json)
+ self.assertFalse('test_invitation_participant' in invitation_json)
+ self.assertFalse('test_invitation_device' in invitation_json)
+ else:
+ count = 14
+ self.assertTrue('test_invitation_test_type' in invitation_json)
+
+ if invitation.id_session:
+ count += 1
+ self.assertTrue('test_invitation_session' in invitation_json)
+
+ if invitation.id_user:
+ count += 1
+ self.assertTrue('test_invitation_user' in invitation_json)
+
+ if invitation.id_participant:
+ count += 1
+ self.assertTrue('test_invitation_participant' in invitation_json)
+
+ if invitation.id_device:
+ count += 1
+ self.assertTrue('test_invitation_device' in invitation_json)
+
+ if invitation.id_project:
+ count += 1
+ self.assertTrue('test_invitation_project' in invitation_json)
+
+ self.assertEqual(len(invitation_json), count)
diff --git a/teraserver/python/tests/opentera/db/models/test_TeraTestType.py b/teraserver/python/tests/opentera/db/models/test_TeraTestType.py
index 32fedb4ff..b4695b912 100644
--- a/teraserver/python/tests/opentera/db/models/test_TeraTestType.py
+++ b/teraserver/python/tests/opentera/db/models/test_TeraTestType.py
@@ -11,6 +11,13 @@ def test_defaults(self):
with self._flask_app.app_context():
pass
+ def test_db_events_not_none(self):
+ with self._flask_app.app_context():
+ test_type : TeraTestType = TeraTestTypeTest.new_test_test_type()
+ self.assertIsNotNone(test_type.to_json_create_event())
+ self.assertIsNotNone(test_type.to_json_update_event())
+ self.assertIsNotNone(test_type.to_json_delete_event())
+
def test_soft_delete(self):
with self._flask_app.app_context():
# Create new
@@ -35,7 +42,7 @@ def test_hard_delete(self):
test_type = TeraTestTypeTest.new_test_test_type()
id_test_type = test_type.id_test_type
- from test_TeraTest import TeraTestTest
+ from tests.opentera.db.models.test_TeraTest import TeraTestTest
test = TeraTestTest.new_test_test(id_session=1, id_participant=1, id_test_type=id_test_type)
self.assertIsNotNone(test.id_test)
id_test = test.id_test
@@ -63,18 +70,18 @@ def test_undelete(self):
test_type = TeraTestTypeTest.new_test_test_type()
id_test_type = test_type.id_test_type
- from test_TeraTest import TeraTestTest
+ from tests.opentera.db.models.test_TeraTest import TeraTestTest
test = TeraTestTest.new_test_test(id_session=1, id_participant=1, id_test_type=id_test_type)
self.assertIsNotNone(test.id_test)
id_test = test.id_test
# Associate with site
- from test_TeraTestTypeSite import TeraTestTypeSiteTest
+ from tests.opentera.db.models.test_TeraTestTypeSite import TeraTestTypeSiteTest
tt_site = TeraTestTypeSiteTest.new_test_test_type_site(id_site=1, id_test_type=id_test_type)
id_test_type_site = tt_site.id_test_type_site
# ... and project
- from test_TeraTestTypeProject import TeraTestTypeProjectTest
+ from tests.opentera.db.models.test_TeraTestTypeProject import TeraTestTypeProjectTest
tt_project = TeraTestTypeProjectTest.new_test_test_type_project(id_project=1, id_test_type=id_test_type)
id_test_type_project = tt_project.id_test_type_project
diff --git a/teraserver/python/tests/opentera/db/models/test_TeraUser.py b/teraserver/python/tests/opentera/db/models/test_TeraUser.py
index 4e4e134e3..865e10a09 100644
--- a/teraserver/python/tests/opentera/db/models/test_TeraUser.py
+++ b/teraserver/python/tests/opentera/db/models/test_TeraUser.py
@@ -1,4 +1,9 @@
+import uuid
+import jwt
+
+
from modules.DatabaseModule.DBManager import DBManager
+
from opentera.db.models.TeraUser import TeraUser
from opentera.db.models.TeraSite import TeraSite
from opentera.db.models.TeraProject import TeraProject
@@ -8,10 +13,16 @@
from opentera.db.models.TeraService import TeraService
from opentera.db.models.TeraServiceConfig import TeraServiceConfig
from opentera.db.models.TeraUserUserGroup import TeraUserUserGroup
-from tests.opentera.db.models.BaseModelsTest import BaseModelsTest
-import uuid
-import jwt
+from opentera.db.models.TeraServiceRole import TeraServiceRole
+from opentera.db.models.TeraUserGroup import TeraUserGroup
+from opentera.db.models.TeraServiceAccess import TeraServiceAccess
+from tests.opentera.db.models.BaseModelsTest import BaseModelsTest
+from tests.opentera.db.models.test_TeraSession import TeraSessionTest
+from tests.opentera.db.models.test_TeraAsset import TeraAssetTest
+from tests.opentera.db.models.test_TeraTest import TeraTestTest
+from tests.opentera.db.models.test_TeraUserUserGroup import TeraUserUserGroupTest
+from tests.opentera.db.models.test_TeraServiceConfig import TeraServiceConfigTest
class TeraUserTest(BaseModelsTest):
@@ -98,7 +109,6 @@ def test_hard_delete(self):
id_user = user.id_user
# Assign user to sessions
- from test_TeraSession import TeraSessionTest
user_session = TeraSessionTest.new_test_session(id_creator_user=id_user)
id_session = user_session.id_session
@@ -106,14 +116,12 @@ def test_hard_delete(self):
id_session_invitee = user_session.id_session
# Attach asset
- from test_TeraAsset import TeraAssetTest
asset = TeraAssetTest.new_test_asset(id_session=id_session,
service_uuid=TeraService.get_openteraserver_service().service_uuid,
id_user=id_user)
id_asset = asset.id_asset
# ... and test
- from test_TeraTest import TeraTestTest
test = TeraTestTest.new_test_test(id_session=id_session, id_user=id_user)
id_test = test.id_test
@@ -152,12 +160,10 @@ def test_undelete(self):
id_user = user.id_user
# Assign to user group
- from test_TeraUserUserGroup import TeraUserUserGroupTest
uug = TeraUserUserGroupTest.new_test_user_usergroup(id_user=id_user, id_user_group=1)
id_user_user_group = uug.id_user_user_group
# Assign user to sessions
- from test_TeraSession import TeraSessionTest
user_session = TeraSessionTest.new_test_session(id_creator_user=id_user)
id_session = user_session.id_session
@@ -165,19 +171,16 @@ def test_undelete(self):
id_session_invitee = user_session.id_session
# Attach asset
- from test_TeraAsset import TeraAssetTest
asset = TeraAssetTest.new_test_asset(id_session=id_session,
service_uuid=TeraService.get_openteraserver_service().service_uuid,
id_user=id_user)
id_asset = asset.id_asset
# ... and test
- from test_TeraTest import TeraTestTest
test = TeraTestTest.new_test_test(id_session=id_session, id_user=id_user)
id_test = test.id_test
# ... and service config
- from test_TeraServiceConfig import TeraServiceConfigTest
service_conf = TeraServiceConfigTest.new_test_service_config(id_service=1, id_user=id_user)
id_service_conf = service_conf.id_service_config
@@ -227,12 +230,6 @@ def test_token_for_admin_should_have_empty_service_access(self):
self.assertEqual(token_dict['service_access'], {}) # Should be empty
def test_token_for_siteadmin_should_have_valid_service_access(self):
- from opentera.db.models.TeraService import TeraService
- from opentera.db.models.TeraServiceRole import TeraServiceRole
- from opentera.db.models.TeraUserGroup import TeraUserGroup
- from opentera.db.models.TeraServiceAccess import TeraServiceAccess
- from opentera.db.models.TeraUserUserGroup import TeraUserUserGroup
-
with self._flask_app.app_context():
user = TeraUser.get_user_by_username('siteadmin')
self.assertIsNotNone(user)
@@ -282,6 +279,66 @@ def test_token_for_siteadmin_should_have_valid_service_access(self):
# TeraServiceAccess.delete(service_access.id_service_access)
# TeraServiceRole.delete(role.id_service_role)
+ def test_disable_2fa_on_2fa_enabled_user_should_reset_secret_email_and_otp_states(self):
+ with self._flask_app.app_context():
+ user: TeraUser = TeraUserTest.new_test_user(user_name="user_2fa", user_groups=None)
+ self.assertIsNotNone(user)
+ self.assertFalse(user.user_2fa_enabled)
+ # Setup 2FA
+ user.enable_2fa_otp()
+ self.assertTrue(user.user_2fa_enabled)
+ self.assertIsNotNone(user.user_2fa_otp_secret)
+ self.assertTrue(user.user_2fa_otp_enabled)
+ # Commit user
+ self.db.session.add(user)
+ self.db.session.commit()
+ # Disable 2FA
+ user.user_2fa_enabled = False
+ self.db.session.add(user)
+ self.db.session.commit()
+ # Check
+ self.assertFalse(user.user_2fa_enabled)
+ self.assertIsNone(user.user_2fa_otp_secret)
+ self.assertFalse(user.user_2fa_otp_enabled)
+ self.assertFalse(user.user_2fa_email_enabled)
+ # Delete user
+ TeraUser.delete(user.id_user, hard_delete=True)
+
+
+ def test_change_superadmin_2fa_enabled_while_site_has_2fa_required_does_not_change_2fa_enabled(self):
+ with self._flask_app.app_context():
+ sites = TeraSite.query.all()
+ for site in sites:
+ site.site_2fa_required = True
+ self.db.session.add(site)
+ self.db.session.commit()
+
+ superadmins = TeraUser.query.filter_by(user_superadmin=True).all()
+ for superadmin in superadmins:
+ self.assertTrue(superadmin.user_2fa_enabled)
+ superadmin.user_2fa_enabled = False
+ self.db.session.add(superadmin)
+ self.db.session.commit()
+
+ # Reverify flag
+ for superadmin in superadmins:
+ self.assertTrue(superadmin.user_2fa_enabled)
+
+ # Reset site 2fa required
+ for site in sites:
+ site.site_2fa_required = False
+ self.db.session.add(site)
+ self.db.session.commit()
+
+ #Reset superadmin 2fa enabled
+ for superadmin in superadmins:
+ superadmin.user_2fa_enabled = False
+ self.db.session.add(superadmin)
+ self.db.session.commit()
+
+ for superadmin in superadmins:
+ self.assertFalse(superadmin.user_2fa_enabled)
+
@staticmethod
def new_test_user(user_name: str, user_groups: list | None = None) -> TeraUser:
user = TeraUser()
diff --git a/teraserver/python/tests/opentera/db/models/test_TeraUserGroup.py b/teraserver/python/tests/opentera/db/models/test_TeraUserGroup.py
index 761a26918..98decfe92 100644
--- a/teraserver/python/tests/opentera/db/models/test_TeraUserGroup.py
+++ b/teraserver/python/tests/opentera/db/models/test_TeraUserGroup.py
@@ -253,7 +253,7 @@ def test_hard_delete(self):
self.assertIsNotNone(ug.id_user_group)
id_user_group = ug.id_user_group
- from test_TeraUser import TeraUserTest
+ from tests.opentera.db.models.test_TeraUser import TeraUserTest
user = TeraUserTest.new_test_user(user_name="user_ug_harddelete", user_groups=[ug])
self.assertIsNotNone(user.id_user)
id_user = user.id_user
@@ -286,13 +286,13 @@ def test_undelete(self):
self.assertIsNotNone(ug.id_user_group)
id_user_group = ug.id_user_group
- from test_TeraUser import TeraUserTest
+ from tests.opentera.db.models.test_TeraUser import TeraUserTest
user = TeraUserTest.new_test_user(user_name="user_ug_undelete", user_groups=[ug])
self.assertIsNotNone(user.id_user)
id_user = user.id_user
# Add service access
- from test_TeraServiceAccess import TeraServiceAccessTest
+ from tests.opentera.db.models.test_TeraServiceAccess import TeraServiceAccessTest
access = TeraServiceAccessTest.new_test_service_access(id_service_role=1, id_user_group=id_user_group)
id_access = access.id_service_access
diff --git a/teraserver/python/tests/opentera/services/FakeService.py b/teraserver/python/tests/opentera/services/FakeService.py
index ae66102bf..2205cff79 100644
--- a/teraserver/python/tests/opentera/services/FakeService.py
+++ b/teraserver/python/tests/opentera/services/FakeService.py
@@ -1,19 +1,61 @@
-from services.FileTransferService.FlaskModule import CustomAPI, authorizations
+import redis
+import uuid
+from io import BytesIO
from requests import Response
+
+from flask import Flask
+from flask import Response as FlaskResponse
+from flask_babel import Babel
+
+from modules.FlaskModule.FlaskModule import CustomAPI, authorizations
from modules.DatabaseModule.DBManager import DBManager
-from services.FileTransferService.ConfigManager import ConfigManager
+from modules.FlaskModule.FlaskModule import FlaskModule
+from modules.LoginModule.LoginModule import LoginModule
+import modules.Globals as Globals
+
from opentera.modules.BaseModule import BaseModule
from opentera.services.ServiceOpenTera import ServiceOpenTera
from opentera.redis.RedisVars import RedisVars
from opentera.services.ServiceAccessManager import ServiceAccessManager
-from flask import Flask
-from flask import Response as FlaskResponse
-from flask_babel import Babel
-import redis
-import uuid
-from io import BytesIO
+from opentera.db.models.TeraServerSettings import TeraServerSettings
+from opentera.db.models.TeraService import TeraService
+from opentera.db.models.TeraServiceSite import TeraServiceSite
+from opentera.db.models.TeraServiceProject import TeraServiceProject
+from opentera.services.ServiceConfigManager import ServiceConfigManager, DBConfig
+
import opentera.messages.python as messages
+class ConfigManager(ServiceConfigManager, DBConfig):
+ def __init__(self):
+ ServiceConfigManager.__init__(self)
+
+ def create_defaults(self):
+ # Default service config
+ self.service_config['name'] = 'FakeService'
+ self.service_config['hostname'] = '127.0.0.1'
+ self.service_config['port'] = 4041
+ self.service_config['debug_mode'] = True
+ self.service_config['ServiceUUID'] = 'invalid'
+
+ # Default backend configuration
+ self.backend_config['hostname'] = '127.0.0.1'
+ self.backend_config['port'] = 40075
+
+ # Default redis configuration
+ self.redis_config['hostname'] = '127.0.0.1'
+ self.redis_config['port'] = 6379
+ self.redis_config['username'] = ''
+ self.redis_config['password'] = ''
+ self.redis_config['db'] = 0
+
+ # Default database configuration
+ self.db_config['db_type'] = 'ram'
+ self.db_config['name'] = 'fakeservice'
+ self.db_config['url'] = '127.0.0.1'
+ self.db_config['port'] = 5432
+ self.db_config['username'] = ''
+ self.db_config['password'] = ''
+
class FakeFlaskModule(BaseModule):
def __init__(self, config: ConfigManager, flask_app):
@@ -23,8 +65,11 @@ def __init__(self, config: ConfigManager, flask_app):
self.config.server_config = {'hostname': '127.0.0.1', 'port': 40075}
self.flask_app = flask_app
self.api = CustomAPI(self.flask_app, version='1.0.0', title='FakeService API',
- description='FakeService API Documentation', doc='/doc', prefix='/',
+ description='FakeService API Documentation', doc='/doc', prefix='/api',
authorizations=authorizations)
+
+ self.service_api_namespace = self.api.namespace('service', description='Fake TeraServer service API')
+
self.babel = Babel(self.flask_app)
self.flask_app.debug = False
@@ -39,10 +84,15 @@ def __init__(self, config: ConfigManager, flask_app):
self.flask_app.config.update({'SESSION_COOKIE_SECURE': True})
self.flask_app.config.update({'UPLOAD_FOLDER': '.'})
+ Globals.login_module = LoginModule(self.config, self.flask_app)
+
self.setup_api()
def setup_api(self):
- pass
+ with self.flask_app.app_context():
+ additional_args = {'test': True,
+ 'flaskModule': self}
+ FlaskModule.init_service_api(self, self.service_api_namespace, additional_args)
class FakeService(ServiceOpenTera):
@@ -69,28 +119,54 @@ def __init__(self, db=None):
self.setup_service_access_manager()
- # Redis variables & db must be initialized before
- ServiceOpenTera.__init__(self, self.config_man, {'service_key': 'FakeService'})
+ # Create Service in DB
+ db_service = TeraService()
+ db_service.service_name = 'FakeService'
+ db_service.service_key = 'FakeService'
+ db_service.service_hostname = 'localhost'
+ db_service.service_port = 40075
+ db_service.service_enabled = True
+ db_service.service_clientendpoint = '/api/fake'
+ db_service.service_endpoint = '/api/fake'
+ TeraService.insert(db_service)
+
+ # Make sure service is part of one site and project
+ service_site = TeraServiceSite()
+ service_site.id_service = db_service.id_service
+ service_site.id_site = 1
+ TeraServiceSite.insert(service_site)
+
+ service_project = TeraServiceProject()
+ service_project.id_service = db_service.id_service
+ service_project.id_project = 1
+ TeraServiceProject.insert(service_project)
+
# Setup service
- self.config['ServiceUUID'] = str(uuid.uuid4())
- self.config_man.service_config['ServiceUUID'] = self.config['ServiceUUID']
+ self.config_man.service_config['ServiceUUID'] = db_service.service_uuid
+
+ # Redis variables & db must be initialized before
+ ServiceOpenTera.__init__(self, self.config_man, {'service_key': 'FakeService',
+ 'service_uuid': db_service.service_uuid})
+
+ self.config['ServiceUUID'] = db_service.service_uuid
+ self.config['id_service'] = db_service.id_service
# Setup modules
self.flask_module = FakeFlaskModule(self.config_man, self.flask_app)
self.test_client = self.flask_app.test_client()
- with self.flask_app.app_context():
- self.service_token = self.generate_service_token()
- def generate_service_token(self) -> str:
- pass
+ def get_service_uuid(self):
+ return self.config['ServiceUUID']
+
+ def get_service_id(self):
+ return self.config['id_service']
def app_context(self):
return self.flask_app.app_context()
def setup_service_access_manager(self):
- from opentera.db.models.TeraServerSettings import TeraServerSettings
self.redis = redis.Redis(host=self.config_man.redis_config['hostname'],
port=self.config_man.redis_config['port'],
@@ -174,8 +250,4 @@ def asset_event_received(self, event: messages.DatabaseEvent):
if __name__ == '__main__':
service = FakeService()
with service.app_context():
- pass
-
-
-
-
+ pass
diff --git a/teraserver/python/tests/opentera/services/__init__.py b/teraserver/python/tests/opentera/services/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/teraserver/python/tests/opentera/services/test_ServiceAccessManager.py b/teraserver/python/tests/opentera/services/test_ServiceAccessManager.py
index 3afbd8472..67ba5fba0 100644
--- a/teraserver/python/tests/opentera/services/test_ServiceAccessManager.py
+++ b/teraserver/python/tests/opentera/services/test_ServiceAccessManager.py
@@ -1,8 +1,14 @@
import unittest
+import uuid
+from datetime import datetime, timedelta
+from flask_restx import Resource, inputs
+
+from opentera.services.ServiceAccessManager import ServiceAccessManager, current_test_invitation
+from opentera.db.models.TeraUser import TeraUser
+from opentera.db.models.TeraTestType import TeraTestType
+from opentera.db.models.TeraTestInvitation import TeraTestInvitation
from tests.opentera.services.FakeService import FakeService, FakeFlaskModule
-from opentera.services.ServiceAccessManager import ServiceAccessManager
import tests.opentera.services.utils as utils
-from flask_restx import Resource, inputs
class TestQueryWithServiceRoles(Resource):
@@ -43,6 +49,21 @@ def get(self):
return 'OK', 200
+class TestQueryWithTestInvitationKey(Resource):
+ def __init__(self, _api, *args, **kwargs):
+ Resource.__init__(self, _api, *args, **kwargs)
+ self.module = kwargs.get('flaskModule', None)
+ self.test = kwargs.get('test', False)
+
+ @ServiceAccessManager.service_test_invitation_required(invitation_key_param_name="test_invitation_key")
+ def get(self):
+
+ if current_test_invitation == None:
+ return 'No invitation', 400
+
+ return 'OK', 200
+
+
class ServiceAccessManagerTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
@@ -60,6 +81,8 @@ def setUpClass(cls):
resource_class_kwargs=kwargs)
cls.__service.flask_module.api.add_resource(TestQueryWithServiceRolesAll, '/test_all',
resource_class_kwargs=kwargs)
+ cls.__service.flask_module.api.add_resource(TestQueryWithTestInvitationKey, '/test_invitations',
+ resource_class_kwargs=kwargs)
@classmethod
def tearDownClass(cls):
@@ -74,26 +97,26 @@ def tearDown(self):
def test_endpoint_no_token(self):
with self.__service.app_context():
# No token
- response = self.__service.test_client.get('/test')
+ response = self.__service.test_client.get('/api/test')
self.assertEqual(403, response.status_code)
def test_endpoint_no_token_any(self):
with self.__service.app_context():
# No token
- response = self.__service.test_client.get('/test_any')
+ response = self.__service.test_client.get('/api/test_any')
self.assertEqual(403, response.status_code)
def test_endpoint_no_token_all(self):
with self.__service.app_context():
# No token
- response = self.__service.test_client.get('/test_all')
+ response = self.__service.test_client.get('/api/test_all')
self.assertEqual(403, response.status_code)
def test_endpoint_user_token_with_superadmin(self):
with self.__service.app_context():
# User token with no role
user_token = utils._generate_fake_user_token(roles=[], superadmin=True)
- response = self.__service.test_client.get('/test',
+ response = self.__service.test_client.get('/api/test',
headers={'Authorization': 'OpenTera ' + user_token})
self.assertEqual(200, response.status_code)
@@ -101,7 +124,7 @@ def test_endpoint_user_token_with_superadmin_any(self):
with self.__service.app_context():
# User token with no role
user_token = utils._generate_fake_user_token(roles=[], superadmin=True)
- response = self.__service.test_client.get('/test_any',
+ response = self.__service.test_client.get('/api/test_any',
headers={'Authorization': 'OpenTera ' + user_token})
self.assertEqual(200, response.status_code)
@@ -109,7 +132,7 @@ def test_endpoint_user_token_with_superadmin_all(self):
with self.__service.app_context():
# User token with no role
user_token = utils._generate_fake_user_token(roles=[], superadmin=True)
- response = self.__service.test_client.get('/test_all',
+ response = self.__service.test_client.get('/api/test_all',
headers={'Authorization': 'OpenTera ' + user_token})
self.assertEqual(200, response.status_code)
@@ -117,7 +140,7 @@ def test_endpoint_user_token_with_no_role(self):
with self.__service.app_context():
# User token with no role
user_token = utils._generate_fake_user_token(roles=[])
- response = self.__service.test_client.get('/test',
+ response = self.__service.test_client.get('/api/test',
headers={'Authorization': 'OpenTera ' + user_token})
self.assertEqual(403, response.status_code)
@@ -125,7 +148,7 @@ def test_endpoint_user_token_with_no_role_any(self):
with self.__service.app_context():
# User token with no role
user_token = utils._generate_fake_user_token(roles=[])
- response = self.__service.test_client.get('/test_any',
+ response = self.__service.test_client.get('/api/test_any',
headers={'Authorization': 'OpenTera ' + user_token})
self.assertEqual(403, response.status_code)
@@ -133,7 +156,7 @@ def test_endpoint_user_token_with_no_role_all(self):
with self.__service.app_context():
# User token with no role
user_token = utils._generate_fake_user_token(roles=[])
- response = self.__service.test_client.get('/test_all',
+ response = self.__service.test_client.get('/api/test_all',
headers={'Authorization': 'OpenTera ' + user_token})
self.assertEqual(403, response.status_code)
@@ -141,7 +164,7 @@ def test_endpoint_user_token_with_required_role(self):
with self.__service.app_context():
# User token with no role
user_token = utils._generate_fake_user_token(roles=['test-role'])
- response = self.__service.test_client.get('/test',
+ response = self.__service.test_client.get('/api/test',
headers={'Authorization': 'OpenTera ' + user_token})
self.assertEqual(200, response.status_code)
@@ -149,17 +172,17 @@ def test_endpoint_user_token_with_required_role_any(self):
with self.__service.app_context():
# User token with no role
user_token = utils._generate_fake_user_token(roles=['test-role1'])
- response = self.__service.test_client.get('/test_any',
+ response = self.__service.test_client.get('/api/test_any',
headers={'Authorization': 'OpenTera ' + user_token})
self.assertEqual(200, response.status_code)
user_token = utils._generate_fake_user_token(roles=['test-role2'])
- response = self.__service.test_client.get('/test_any',
+ response = self.__service.test_client.get('/api/test_any',
headers={'Authorization': 'OpenTera ' + user_token})
self.assertEqual(200, response.status_code)
user_token = utils._generate_fake_user_token(roles=['test-role1', 'test-role2'])
- response = self.__service.test_client.get('/test_any',
+ response = self.__service.test_client.get('/api/test_any',
headers={'Authorization': 'OpenTera ' + user_token})
self.assertEqual(200, response.status_code)
@@ -167,17 +190,17 @@ def test_endpoint_user_token_with_required_role_all(self):
with self.__service.app_context():
# User token with no role
user_token = utils._generate_fake_user_token(roles=['test-role1'])
- response = self.__service.test_client.get('/test_all',
+ response = self.__service.test_client.get('/api/test_all',
headers={'Authorization': 'OpenTera ' + user_token})
self.assertEqual(403, response.status_code)
user_token = utils._generate_fake_user_token(roles=['test-role2'])
- response = self.__service.test_client.get('/test_all',
+ response = self.__service.test_client.get('/api/test_all',
headers={'Authorization': 'OpenTera ' + user_token})
self.assertEqual(403, response.status_code)
user_token = utils._generate_fake_user_token(roles=['test-role1', 'test-role2'])
- response = self.__service.test_client.get('/test_all',
+ response = self.__service.test_client.get('/api/test_all',
headers={'Authorization': 'OpenTera ' + user_token})
self.assertEqual(200, response.status_code)
@@ -185,7 +208,7 @@ def test_endpoint_participant_token_should_fail(self):
with self.__service.app_context():
# User token with no role
participant_token = utils._generate_fake_dynamic_participant_token()
- response = self.__service.test_client.get('/test',
+ response = self.__service.test_client.get('/api/test',
headers={'Authorization': 'OpenTera ' + participant_token})
self.assertEqual(403, response.status_code)
@@ -193,7 +216,7 @@ def test_endpoint_participant_token_should_fail_any(self):
with self.__service.app_context():
# User token with no role
participant_token = utils._generate_fake_dynamic_participant_token()
- response = self.__service.test_client.get('/test_any',
+ response = self.__service.test_client.get('/api/test_any',
headers={'Authorization': 'OpenTera ' + participant_token})
self.assertEqual(403, response.status_code)
@@ -201,6 +224,123 @@ def test_endpoint_participant_token_should_fail_all(self):
with self.__service.app_context():
# User token with no role
participant_token = utils._generate_fake_dynamic_participant_token()
- response = self.__service.test_client.get('/test_any',
+ response = self.__service.test_client.get('/api/test_any',
headers={'Authorization': 'OpenTera ' + participant_token})
self.assertEqual(403, response.status_code)
+
+ def test_endpoint_invitation_key_with_no_key(self):
+ with self.__service.app_context():
+ # No key
+ response = self.__service.test_client.get('/api/test_invitations')
+ self.assertEqual(400, response.status_code)
+
+ def test_endpoint_invitation_key_with_key_for_user_participant_device(self):
+ with self.__service.app_context():
+ # Create test type for this service
+ test_type : TeraTestType = TeraTestType()
+ test_type.test_type_name = 'TestType'
+ test_type.test_type_description = 'TestType description'
+ test_type.test_type_key = str(uuid.uuid4())
+ test_type.id_service = self.__service.get_service_id()
+ TeraTestType.insert(test_type)
+
+ # Create test invitation with user
+ user_invitation : TeraTestInvitation = TeraTestInvitation()
+ user_invitation.id_test_type = test_type.id_test_type
+ user_invitation.test_invitation_key = str(uuid.uuid4())
+ user_invitation.id_user = 1 # admin
+ user_invitation.id_project = 1
+ user_invitation.test_invitation_expiration_date = datetime.now() + timedelta(days=1)
+ TeraTestInvitation.insert(user_invitation)
+
+ # Create test invitation with participant
+ participant_invitation : TeraTestInvitation = TeraTestInvitation()
+ participant_invitation.id_test_type = test_type.id_test_type
+ participant_invitation.test_invitation_key = str(uuid.uuid4())
+ participant_invitation.id_participant = 1
+ participant_invitation.id_project = 1
+ participant_invitation.test_invitation_expiration_date = datetime.now() + timedelta(days=1)
+ TeraTestInvitation.insert(participant_invitation)
+
+ # Create test invitation with device
+ device_invitation : TeraTestInvitation = TeraTestInvitation()
+ device_invitation.id_test_type = test_type.id_test_type
+ device_invitation.test_invitation_key = str(uuid.uuid4())
+ device_invitation.id_device = 1
+ device_invitation.id_project = 1
+ device_invitation.test_invitation_expiration_date = datetime.now() + timedelta(days=1)
+ TeraTestInvitation.insert(device_invitation)
+
+ # Create test invitation with maximum count reached
+ max_invitation : TeraTestInvitation = TeraTestInvitation()
+ max_invitation.id_test_type = test_type.id_test_type
+ max_invitation.test_invitation_key = str(uuid.uuid4())
+ max_invitation.id_device = 1
+ max_invitation.id_project = 1
+ max_invitation.test_invitation_expiration_date = datetime.now() + timedelta(days=1)
+ max_invitation.test_invitation_max_count = 1
+ TeraTestInvitation.insert(max_invitation)
+ # Update (count originally set to 0)
+ TeraTestInvitation.update(max_invitation.id_test_invitation, {'test_invitation_count': 1})
+
+ # Create test invitation with expired date
+ expired_invitation : TeraTestInvitation = TeraTestInvitation()
+ expired_invitation.id_test_type = test_type.id_test_type
+ expired_invitation.test_invitation_key = str(uuid.uuid4())
+ expired_invitation.id_device = 1
+ expired_invitation.id_project = 1
+ expired_invitation.test_invitation_expiration_date = datetime.now() - timedelta(days=1)
+ expired_invitation.test_invitation_max_count = 1
+ TeraTestInvitation.insert(expired_invitation)
+
+
+ # Call API with newly created key
+ response = self.__service.test_client.get('/api/test_invitations',
+ query_string={'test_invitation_key': user_invitation.test_invitation_key})
+ self.assertEqual(200, response.status_code)
+
+ response = self.__service.test_client.get('/api/test_invitations',
+ query_string={'test_invitation_key': participant_invitation.test_invitation_key})
+ self.assertEqual(200, response.status_code)
+
+ response = self.__service.test_client.get('/api/test_invitations',
+ query_string={'test_invitation_key': device_invitation.test_invitation_key})
+ self.assertEqual(200, response.status_code)
+
+ response = self.__service.test_client.get('/api/test_invitations',
+ query_string={'test_invitation_key': max_invitation.test_invitation_key})
+ self.assertEqual(403, response.status_code)
+
+ response = self.__service.test_client.get('/api/test_invitations',
+ query_string={'test_invitation_key': expired_invitation.test_invitation_key})
+ self.assertEqual(403, response.status_code)
+
+ def test_endpoint_invitation_key_with_key_for_user_participant_device_with_max_count_zero(self):
+ with self.__service.app_context():
+ # Create test type for this service
+ test_type : TeraTestType = TeraTestType()
+ test_type.test_type_name = 'TestType'
+ test_type.test_type_description = 'TestType description'
+ test_type.test_type_key = str(uuid.uuid4())
+ test_type.id_service = self.__service.get_service_id()
+ TeraTestType.insert(test_type)
+
+ # Create test invitation with user
+ user_invitation : TeraTestInvitation = TeraTestInvitation()
+ user_invitation.id_test_type = test_type.id_test_type
+ user_invitation.test_invitation_key = str(uuid.uuid4())
+ user_invitation.id_user = 1 # admin
+ user_invitation.id_project = 1
+ user_invitation.test_invitation_expiration_date = datetime.now() + timedelta(days=1)
+ user_invitation.test_invitation_max_count = 0
+ TeraTestInvitation.insert(user_invitation)
+
+
+ # Update (count originally set to 0)
+ TeraTestInvitation.update(user_invitation.id_test_invitation, {'test_invitation_count': 1000})
+
+
+ # Call API with newly created key
+ response = self.__service.test_client.get('/api/test_invitations',
+ query_string={'test_invitation_key': user_invitation.test_invitation_key})
+ self.assertEqual(200, response.status_code)
diff --git a/teraserver/python/tests/opentera/services/utils.py b/teraserver/python/tests/opentera/services/utils.py
index 626a46c16..5e3b8b39b 100644
--- a/teraserver/python/tests/opentera/services/utils.py
+++ b/teraserver/python/tests/opentera/services/utils.py
@@ -1,23 +1,8 @@
-import unittest
-from flask_restx import Resource, inputs
import time
from opentera.services.ServiceAccessManager import ServiceAccessManager
-import opentera.redis.RedisVars as RedisVars
import jwt
import uuid
-
-def infinite_jti_sequence():
- num = 0
- while True:
- yield num
- num += 1
-
-
-# Initialize generator, call next(user_jti_generator) to get next sequence number
-user_jti_generator = infinite_jti_sequence()
-participant_jti_generator = infinite_jti_sequence()
-
@staticmethod
def _generate_fake_user_token(name='FakeUser', user_uuid=str(uuid.uuid4()), roles=[],
superadmin=False, expiration=3600):
@@ -29,7 +14,7 @@ def _generate_fake_user_token(name='FakeUser', user_uuid=str(uuid.uuid4()), role
'iat': int(now),
'exp': int(now) + expiration,
'iss': 'TeraServer',
- 'jti': next(user_jti_generator),
+ 'jti': str(uuid.uuid4()),
'user_uuid': user_uuid,
'id_user': 1,
'user_fullname': name,
@@ -46,7 +31,7 @@ def _generate_fake_static_participant_token(participant_uuid=str(uuid.uuid4())):
token_key = ServiceAccessManager.api_participant_static_token_key
payload = {
'iss': 'TeraServer',
- 'jti': next(participant_jti_generator),
+ 'jti': str(uuid.uuid4()),
'participant_uuid': participant_uuid,
'id_participant': 1
}
@@ -63,7 +48,7 @@ def _generate_fake_dynamic_participant_token(name='FakeParticipant', participant
'iat': int(now),
'exp': int(now) + expiration,
'iss': 'TeraServer',
- 'jti': next(participant_jti_generator),
+ 'jti': str(uuid.uuid4()),
'participant_uuid': participant_uuid,
'id_participant': 2,
'user_fullname': name
diff --git a/teraserver/python/tests/services/EmailService/BaseEmailServiceAPITest.py b/teraserver/python/tests/services/EmailService/BaseEmailServiceAPITest.py
new file mode 100644
index 000000000..c6f5dee51
--- /dev/null
+++ b/teraserver/python/tests/services/EmailService/BaseEmailServiceAPITest.py
@@ -0,0 +1,200 @@
+import unittest
+from services.EmailService.libemailservice.db.DBManager import DBManager
+from services.EmailService.ConfigManager import ConfigManager
+from opentera.config.ConfigManager import ConfigManager as ConfigManagerServer
+import services.EmailService.Globals as Globals
+from services.EmailService.FlaskModule import flask_app
+from flask.testing import FlaskClient
+import uuid
+import random
+from string import digits, ascii_lowercase, ascii_uppercase
+from tests.services.EmailService.FakeEmailService import FakeEmailService
+from opentera.services.ServiceAccessManager import ServiceAccessManager
+from opentera.db.models.TeraService import TeraService
+from opentera.db.models.TeraUser import TeraUser
+from modules.LoginModule.LoginModule import LoginModule
+from requests.auth import _basic_auth_str
+
+
+def infinite_jti_sequence():
+ num = 0
+ while True:
+ yield num
+ num += 1
+
+
+# Initialize generator, call next(user_jti_generator) to get next sequence number
+user_jti_generator = infinite_jti_sequence()
+
+
+class BaseEmailServiceAPITest(unittest.TestCase):
+ _config = None
+ _db_man = None
+ test_endpoint = ''
+ user_token_key = ''.join(random.choice(digits + ascii_lowercase + ascii_uppercase) for _ in range(36))
+ participant_token_key = ''.join(random.choice(digits + ascii_lowercase + ascii_uppercase) for _ in range(36))
+ service_token_key = ''.join(random.choice(digits + ascii_lowercase + ascii_uppercase) for _ in range(36))
+ device_token_key = ''.join(random.choice(digits + ascii_lowercase + ascii_uppercase) for _ in range(36))
+ user_login_endpoint = '/api/user/login'
+ user_logout_endpoint = '/api/user/logout'
+
+ @classmethod
+ def setUpClass(cls):
+ cls._config = BaseEmailServiceAPITest.getConfig()
+ # Instance of Fake service API will create a new flask_app
+ cls._service = FakeEmailService()
+
+ # API Need those variables to be set
+ Globals.service = cls._service
+ flask_app.config.update({'UPLOAD_FOLDER': '.'})
+
+ # Initialize DBManager with custom flask app
+ cls._db_man: DBManager = DBManager(app=cls._service.flask_app, test=True)
+ # Cheating using same db as FakeService
+ cls._db_man.db = cls._service.db_manager.db
+
+ # This is needed for Logins and tokens
+ config_server = ConfigManagerServer()
+ config_server.create_defaults()
+ cls._login_module = LoginModule(config_server, cls._service.flask_app)
+
+ with cls._service.flask_app.app_context():
+ # Creating default users / tests. Time-consuming, only once per test file.
+ cls._db_man.create_defaults(cls._config, test=True)
+
+ def app_context(self):
+ self.assertIsNotNone(self._service)
+ self.assertIsNotNone(self._service.flask_app)
+ return self._service.flask_app.app_context()
+
+ @classmethod
+ def tearDownClass(cls):
+ with cls._service.flask_app.app_context():
+ cls._db_man.db.session.remove()
+
+ @classmethod
+ def getConfig(cls) -> ConfigManager:
+ config = ConfigManager()
+ config.create_defaults()
+ return config
+
+ def setUp(self):
+ self.assertIsNotNone(self._service)
+ self.assertIsNotNone(self._service.flask_app)
+ self.test_client = self._service.flask_app.test_client()
+ self.assertIsNotNone(self.test_client)
+ with self.app_context():
+ service = TeraService.get_service_by_key('EmailService')
+ Globals.service.service_info = service.to_json(minimal=False)
+
+ user: TeraUser = TeraUser.get_user_by_username('admin')
+ self.assertIsNotNone(user)
+ self.assertIsNotNone(service)
+ self.user_admin_token = user.get_token(ServiceAccessManager.api_user_token_key)
+ self.assertIsNotNone(self.user_admin_token)
+ self.assertGreater(len(self.user_admin_token), 0)
+
+ user: TeraUser = TeraUser.get_user_by_username('user3')
+ self.assertIsNotNone(user)
+ self.user_user3_token = user.get_token(ServiceAccessManager.api_user_token_key)
+ self.assertIsNotNone(self.user_user3_token)
+ self.assertGreater(len(self.user_user3_token), 0)
+
+ user: TeraUser = TeraUser.get_user_by_username('user4')
+ self.assertIsNotNone(user)
+ self.user_user4_token = user.get_token(ServiceAccessManager.api_user_token_key)
+ self.assertIsNotNone(self.user_user4_token)
+ self.assertGreater(len(self.user_user4_token), 0)
+
+ user: TeraUser = TeraUser.get_user_by_username('siteadmin')
+ self.assertIsNotNone(user)
+ self.user_siteadmin_token = user.get_token(ServiceAccessManager.api_user_token_key)
+ self.assertIsNotNone(self.user_siteadmin_token)
+ self.assertGreater(len(self.user_siteadmin_token), 0)
+
+ def tearDown(self):
+ with self.app_context():
+ # Make sure pending queries are rollbacked.
+ self._db_man.db.session.rollback()
+
+ @staticmethod
+ def _generate_fake_user_token(name='FakeUser', user_uuid=str(uuid.uuid4()), superadmin=False, expiration=3600):
+ import time
+ import jwt
+
+ # Creating token with user info
+ now = time.time()
+ token_key = ServiceAccessManager.api_user_token_key
+
+ payload = {
+ 'iat': int(now),
+ 'exp': int(now) + expiration,
+ 'iss': 'TeraServer',
+ 'jti': next(user_jti_generator),
+ 'user_uuid': user_uuid,
+ 'id_user': 1,
+ 'user_fullname': name,
+ 'user_superadmin': superadmin
+ }
+
+ return jwt.encode(payload, token_key, algorithm='HS256')
+
+ def _get_with_token_auth(self, client: FlaskClient, token=None, params=None, endpoint=None):
+ if params is None:
+ params = {}
+ if endpoint is None:
+ endpoint = self.test_endpoint
+ if token is not None:
+ headers = {'Authorization': 'OpenTera ' + token}
+ else:
+ headers = {}
+
+ return client.get(endpoint, headers=headers, query_string=params)
+
+ def _get_with_user_token_auth(self, client: FlaskClient, token: str = '', params=None, endpoint=None):
+ if params is None:
+ params = {}
+ if endpoint is None:
+ endpoint = self.test_endpoint
+ headers = {'Authorization': 'OpenTera ' + token}
+ return client.get(endpoint, headers=headers, query_string=params)
+
+ def _get_with_user_http_auth(self, client: FlaskClient, username: str = '', password: str = '',
+ params=None, endpoint=None):
+ if params is None:
+ params = {}
+ if endpoint is None:
+ endpoint = self.test_endpoint
+
+ headers = {'Authorization': _basic_auth_str(username, password)}
+ return client.get(endpoint, headers=headers, query_string=params)
+
+ def _post_with_token_auth(self, client: FlaskClient, token: str = '', json: dict = None,
+ params: dict = None, endpoint: str = None):
+ if params is None:
+ params = {}
+ if endpoint is None:
+ endpoint = self.test_endpoint
+ headers = {'Authorization': 'OpenTera ' + token}
+ return client.post(endpoint, headers=headers, query_string=params, json=json,
+ content_type='application/json')
+
+ def _post_file_with_token_auth(self, client: FlaskClient, token: str = '', files: dict = None,
+ params: dict = None, endpoint: str = None):
+ if params is None:
+ params = {}
+ if endpoint is None:
+ endpoint = self.test_endpoint
+ headers = {'Authorization': 'OpenTera ' + token}
+
+ return client.post(endpoint, headers=headers, query_string=params, data=files,
+ content_type='multipart/form-data')
+
+ def _delete_with_token_auth(self, client: FlaskClient, token: str = '',
+ params: dict = None, endpoint: str = None):
+ if params is None:
+ params = {}
+ if endpoint is None:
+ endpoint = self.test_endpoint
+ headers = {'Authorization': 'OpenTera ' + token}
+ return client.delete(endpoint, headers=headers, query_string=params)
diff --git a/teraserver/python/tests/services/EmailService/FakeEmailService.py b/teraserver/python/tests/services/EmailService/FakeEmailService.py
new file mode 100644
index 000000000..775bbee2f
--- /dev/null
+++ b/teraserver/python/tests/services/EmailService/FakeEmailService.py
@@ -0,0 +1,269 @@
+from modules.DatabaseModule.DBManager import DBManager
+from modules.LoginModule.LoginModule import LoginModule
+from opentera.modules.BaseModule import BaseModule
+from opentera.services.ServiceOpenTera import ServiceOpenTera
+from opentera.redis.RedisVars import RedisVars
+from opentera.services.ServiceAccessManager import ServiceAccessManager
+from services.EmailService.ConfigManager import ConfigManager
+from services.EmailService.FlaskModule import CustomAPI, authorizations
+
+from flask import Flask
+from flask import Response as FlaskResponse
+from flask_babel import Babel
+
+# Flask mail
+from flask_mail import Mail
+
+from requests import Response
+
+import redis
+import uuid
+from io import BytesIO
+import opentera.messages.python as messages
+
+
+class FakeFlaskModule(BaseModule):
+ def __init__(self, config: ConfigManager, flask_app):
+ BaseModule.__init__(self, 'FakeFlaskModule', config)
+
+ # Will allow for user api to work
+ self.config.server_config = {'hostname': '127.0.0.1', 'port': 40075}
+
+ self.flask_app = flask_app
+ self.api = CustomAPI(self.flask_app, version='1.0.0', title='EmailService API',
+ description='FakeEmailService API Documentation', doc='/doc', prefix='/api',
+ authorizations=authorizations)
+
+ self.babel = Babel(self.flask_app)
+
+ self.flask_app.debug = False
+ self.flask_app.testing = True
+ self.flask_app.secret_key = str(uuid.uuid4()) # Normally service UUID
+ self.flask_app.config.update({'SESSION_TYPE': 'redis'})
+ redis_url = redis.from_url('redis://%(username)s:%(password)s@%(hostname)s:%(port)s/%(db)s'
+ % self.config.redis_config)
+
+ self.flask_app.config.update({'SESSION_REDIS': redis_url})
+ self.flask_app.config.update({'BABEL_DEFAULT_LOCALE': 'fr'})
+ self.flask_app.config.update({'SESSION_COOKIE_SECURE': True})
+ self.flask_app.config.update({'UPLOAD_FOLDER': '.'})
+
+ # Init mailer
+ self.flask_app.config.update({"MAIL_SERVER": '127.0.0.1'})
+ self.flask_app.config.update({"MAIL_PORT": 25})
+ self.flask_app.config.update({"MAIL_USE_TLS": False})
+ self.flask_app.config.update({"MAIL_USE_SSL": False})
+ self.flask_app.config.update({"MAIL_USERNAME": ''})
+ self.flask_app.config.update({"MAIL_PASSWORD": ''})
+ self.flask_app.config.update({"MAIL_DEFAULT_SENDER": 'test@opentera.org'})
+ self.flask_app.config.update({"MAIL_MAX_EMAILS": 25})
+
+ self.mail_man = Mail(flask_app)
+ self.mail_man.init_app(self.flask_app)
+
+ self.email_api_namespace = self.api.namespace('', description='EmailService API')
+ self.service_api_namespace = self.api.namespace('service', description='Fake EmailService service API')
+ self.user_api_namespace = self.api.namespace('user', description='Fake EmailService user API')
+ self.participant_api_namespace = self.api.namespace('participant',
+ description='Fake EmailService participant API')
+ self.device_api_namespace = self.api.namespace('device', description='Fake EmailService device API')
+ self.setup_fake_email_api(flask_app)
+ self.setup_fake_service_api(flask_app)
+ self.setup_fake_user_api(flask_app)
+ self.setup_fake_participant_api(flask_app)
+ self.setup_fake_device_api(flask_app)
+
+ def setup_fake_email_api(self, flask_app):
+ from services.EmailService.FlaskModule import FlaskModule
+ with flask_app.app_context():
+ # Setup Fake Service API
+ kwargs = {'flaskModule': self,
+ 'test': True}
+ FlaskModule.init_api(self, self.email_api_namespace, kwargs)
+
+ def setup_fake_service_api(self, flask_app):
+ from modules.FlaskModule.FlaskModule import FlaskModule
+ with flask_app.app_context():
+ # Setup Fake API
+ kwargs = {'flaskModule': self,
+ 'test': True}
+
+ # The trick is to initialize main server api to the newly created namespace
+ FlaskModule.init_service_api(self, self.service_api_namespace, kwargs)
+
+ def setup_fake_user_api(self, flask_app):
+ from modules.FlaskModule.FlaskModule import FlaskModule
+ with flask_app.app_context():
+ # Setup Fake API
+ kwargs = {'flaskModule': self,
+ 'test': True}
+
+ # The trick is to initialize main server api to the newly created namespace
+ FlaskModule.init_user_api(self, self.user_api_namespace, kwargs)
+
+ def setup_fake_participant_api(self, flask_app):
+ from modules.FlaskModule.FlaskModule import FlaskModule
+ with flask_app.app_context():
+ # Setup Fake API
+ kwargs = {'flaskModule': self,
+ 'test': True}
+
+ # The trick is to initialize main server api to the newly created namespace
+ FlaskModule.init_participant_api(self, self.participant_api_namespace, kwargs)
+
+ def setup_fake_device_api(self, flask_app):
+ from modules.FlaskModule.FlaskModule import FlaskModule
+ with flask_app.app_context():
+ # Setup Fake API
+ kwargs = {'flaskModule': self,
+ 'test': True}
+
+ # The trick is to initialize main server api to the newly created namespace
+ FlaskModule.init_device_api(self, self.device_api_namespace, kwargs)
+
+
+class FakeEmailService(ServiceOpenTera):
+ """
+ The only thing we want here is a way to simulate communication with the base server.
+ We will simulate the service API with the database.
+ """
+ service_token = str()
+
+ def __init__(self, db=None):
+ self.flask_app = Flask('FakeEmailService')
+ self.config_man = ConfigManager()
+ self.config_man.create_defaults()
+ self.db_manager = DBManager(self.config_man, self.flask_app)
+ # Cheating on db (reusing already opened from test)
+ if db is not None:
+ self.db_manager.db = db
+ else:
+ self.db_manager.open_local({}, echo=False, ram=True)
+
+ with self.flask_app.app_context():
+ # Update redis vars and basic token
+ self.db_manager.create_defaults(self.config_man, test=True)
+
+ self.setup_service_access_manager()
+
+ # Redis variables & db must be initialized before
+ ServiceOpenTera.__init__(self, self.config_man, {'service_key': 'EmailService'})
+
+ self.login_module = LoginModule(self.config_man)
+
+ from opentera.db.models.TeraService import TeraService
+ email_service: TeraService = TeraService.get_service_by_key('EmailService')
+
+ # Use EmailService UUID
+ if email_service:
+ self.config['ServiceUUID'] = email_service.service_uuid
+ self.config_man.service_config['ServiceUUID'] = email_service.service_uuid
+
+ # Setup modules
+ self.flask_module = FakeFlaskModule(self.config_man, self.flask_app)
+ self.test_client = self.flask_app.test_client()
+
+ with self.flask_app.app_context():
+ self.service_token = self.generate_service_token()
+
+ def generate_service_token(self) -> str:
+ with self.flask_app.app_context():
+ # Use redis key to generate token
+ # Fake service uuid
+ from opentera.db.models.TeraService import TeraService
+ email_service: TeraService = TeraService.get_service_by_key('EmailService')
+ if email_service:
+ return email_service.get_token(ServiceAccessManager.api_service_token_key)
+ else:
+ return ''
+
+ def setup_service_access_manager(self):
+ from opentera.db.models.TeraServerSettings import TeraServerSettings
+
+ self.redis = redis.Redis(host=self.config_man.redis_config['hostname'],
+ port=self.config_man.redis_config['port'],
+ db=self.config_man.redis_config['db'],
+ username=self.config_man.redis_config['username'],
+ password=self.config_man.redis_config['password'],
+ client_name=self.__class__.__name__)
+
+ # Initialize service from redis, posing as FileTransferService
+ # User token key (dynamic)
+ ServiceAccessManager.api_user_token_key = 'test_api_user_token_key'
+ self.redis.set(RedisVars.RedisVar_UserTokenAPIKey,
+ ServiceAccessManager.api_user_token_key)
+
+ # Participant token key from DB (static)
+ ServiceAccessManager.api_participant_static_token_key = TeraServerSettings.get_server_setting_value(
+ TeraServerSettings.ServerParticipantTokenKey)
+ self.redis.set(RedisVars.RedisVar_ParticipantStaticTokenAPIKey,
+ ServiceAccessManager.api_participant_static_token_key)
+
+ # Participant token key (dynamic)
+ ServiceAccessManager.api_participant_token_key = 'test_api_participant_token_key'
+ self.redis.set(RedisVars.RedisVar_ParticipantTokenAPIKey,
+ ServiceAccessManager.api_participant_token_key)
+
+ # Device Token Key from DB (static)
+ ServiceAccessManager.api_device_static_token_key = TeraServerSettings.get_server_setting_value(
+ TeraServerSettings.ServerDeviceTokenKey)
+ self.redis.set(RedisVars.RedisVar_DeviceStaticTokenAPIKey, ServiceAccessManager.api_device_static_token_key)
+
+ # Device Token Key (dynamic = static)
+ ServiceAccessManager.api_device_token_key = TeraServerSettings.get_server_setting_value(
+ TeraServerSettings.ServerDeviceTokenKey)
+ self.redis.set(RedisVars.RedisVar_DeviceTokenAPIKey, ServiceAccessManager.api_device_token_key)
+
+ # Service Token Key (dynamic)
+ ServiceAccessManager.api_service_token_key = 'test_api_service_token_key'
+ self.redis.set(RedisVars.RedisVar_ServiceTokenAPIKey, ServiceAccessManager.api_service_token_key)
+ ServiceAccessManager.config_man = self.config_man
+
+ def get_users_uuids(self):
+ with self.flask_app.app_context():
+ from opentera.db.models.TeraUser import TeraUser
+ return [user.user_uuid for user in TeraUser.query.all() if user.user_enabled]
+
+ def get_enabled_users(self):
+ with self.flask_app.app_context():
+ from opentera.db.models.TeraUser import TeraUser
+ return [user for user in TeraUser.query.all() if user.user_enabled]
+
+ @staticmethod
+ def convert_to_standard_request_response(flask_response: FlaskResponse):
+ result = Response()
+ result.status_code = flask_response.status_code
+ result.headers = flask_response.headers
+ result.encoding = flask_response.content_encoding
+ result.raw = BytesIO(flask_response.data)
+ return result
+
+ def post_to_opentera(self, api_url: str, json_data: dict, token=None) -> Response:
+ with self.flask_app.app_context():
+ # Synchronous call to OpenTera fake backend
+ if not token:
+ token = self.service_token
+ request_headers = {'Authorization': 'OpenTera ' + token}
+ answer = self.test_client.post(api_url, headers=request_headers, json=json_data)
+ return FakeEmailService.convert_to_standard_request_response(answer)
+
+ def get_from_opentera(self, api_url: str, params: dict, token=None) -> Response:
+ with self.flask_app.app_context():
+ # Synchronous call to OpenTera fake backend
+ if not token:
+ token = self.service_token
+ request_headers = {'Authorization': 'OpenTera ' + token}
+ answer = self.test_client.get(api_url, headers=request_headers, query_string=params)
+ return FakeEmailService.convert_to_standard_request_response(answer)
+
+ def delete_from_opentera(self, api_url: str, params: dict, token=None) -> Response:
+ with self.flask_app.app_context():
+ # Synchronous call to OpenTera fake backend
+ if not token:
+ token = self.service_token
+ request_headers = {'Authorization': 'OpenTera ' + token}
+ answer = self.test_client.delete(api_url, headers=request_headers, query_string=params)
+ return FakeEmailService.convert_to_standard_request_response(answer)
+
+ def asset_event_received(self, event: messages.DatabaseEvent):
+ pass
diff --git a/teraserver/python/tests/services/EmailService/__init__.py b/teraserver/python/tests/services/EmailService/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/teraserver/python/tests/services/EmailService/models/test_EmailTemplate.py b/teraserver/python/tests/services/EmailService/models/test_EmailTemplate.py
new file mode 100644
index 000000000..d21708dd4
--- /dev/null
+++ b/teraserver/python/tests/services/EmailService/models/test_EmailTemplate.py
@@ -0,0 +1,69 @@
+from services.EmailService.ConfigManager import ConfigManager
+from services.EmailService.libemailservice.db.DBManager import DBManager
+import unittest
+from flask import Flask
+
+from services.EmailService.libemailservice.db.models import EmailTemplate
+
+
+class EmailServiceEmailTemplateTest(unittest.TestCase):
+
+ def setUp(self):
+ self.flask_app = Flask('EmailServiceEmailTemplateTest')
+ with self.flask_app.app_context():
+ self.config = ConfigManager()
+ self.config.create_defaults()
+ self.dbman = DBManager(app=self.flask_app, test=True)
+ self.dbman.open_local({}, False, True)
+ self.dbman.create_defaults(self.config, test=True)
+
+ def tearDown(self):
+ with self.flask_app.app_context():
+ self.dbman.db.session.rollback()
+
+ def test_get_template_by_id(self):
+ with self.flask_app.app_context():
+ template = EmailTemplate.get_template_by_id(0)
+ self.assertIsNone(template)
+
+ template = EmailTemplate.get_template_by_id(1)
+ self.assertIsNotNone(template)
+
+ def test_get_project_template_by_key(self):
+ with self.flask_app.app_context():
+ template = EmailTemplate.get_template_by_key('PROJECT_EMAIL')
+ self.assertIsNotNone(template)
+ self.assertIsNotNone(template.id_project)
+
+ def test_get_global_template_by_key(self):
+ with self.flask_app.app_context():
+ template = EmailTemplate.get_template_by_key('GENERAL_TEST_EMAIL')
+ self.assertIsNotNone(template)
+
+ def test_get_site_template_by_key(self):
+ with self.flask_app.app_context():
+ template = EmailTemplate.get_template_by_key('SITE_EMAIL')
+ self.assertIsNotNone(template)
+ self.assertIsNotNone(template.id_site)
+
+ def test_get_template_by_key_wrong_key(self):
+ with self.flask_app.app_context():
+ template = EmailTemplate.get_template_by_key('WRONG_KEY')
+ self.assertIsNone(template)
+
+ def test_get_overriden_template_by_key(self):
+ with self.flask_app.app_context():
+ template = EmailTemplate.get_template_by_key('GENERAL_TEST_EMAIL', project_id=1)
+ self.assertIsNotNone(template)
+ self.assertTrue('PROJECT' in template.email_template)
+ self.assertTrue('GLOBAL' in template.email_template)
+ self.assertEqual(template.id_project, 1)
+
+ def test_get_not_overriden_template_by_key(self):
+ with self.flask_app.app_context():
+ template = EmailTemplate.get_template_by_key('GENERAL_TEST_EMAIL', project_id=2)
+ self.assertIsNotNone(template)
+ self.assertFalse('PROJECT' in template.email_template)
+ self.assertTrue('GLOBAL' in template.email_template)
+ self.assertIsNone(template.id_project)
+ self.assertIsNone(template.id_site)
diff --git a/teraserver/python/tests/services/EmailService/test_QueryEmailTemplate.py b/teraserver/python/tests/services/EmailService/test_QueryEmailTemplate.py
new file mode 100644
index 000000000..90838f51d
--- /dev/null
+++ b/teraserver/python/tests/services/EmailService/test_QueryEmailTemplate.py
@@ -0,0 +1,464 @@
+from services.EmailService.libemailservice.db.models import EmailTemplate
+from tests.services.EmailService.BaseEmailServiceAPITest import BaseEmailServiceAPITest
+from opentera.db.models.TeraParticipant import TeraParticipant
+from opentera.db.models.TeraDevice import TeraDevice
+from opentera.db.models.TeraService import TeraService
+from opentera.services.ServiceAccessManager import ServiceAccessManager
+
+
+class EmailEmailTemplateTest(BaseEmailServiceAPITest):
+ test_endpoint = '/api/templates'
+
+ def setUp(self):
+ super().setUp()
+
+ def tearDown(self):
+ super().tearDown()
+
+ def test_get_endpoint_with_invalid_token(self):
+ with self.app_context():
+ response = self._get_with_token_auth(self.test_client, token="invalid")
+ self.assertEqual(response.status_code, 403)
+
+ def test_get_endpoint_with_static_participant_token(self):
+ with self.app_context():
+ for participant in TeraParticipant.query.all():
+ self.assertIsNotNone(participant)
+ if participant.participant_enabled and participant.participant_token:
+ self.assertIsNotNone(participant.participant_token)
+ self.assertGreater(len(participant.participant_token), 0)
+ response = self._get_with_token_auth(self.test_client, token=participant.participant_token)
+ self.assertEqual(response.status_code, 403)
+
+ def test_get_endpoint_with_static_device_token(self):
+ with self.app_context():
+ for device in TeraDevice.query.all():
+ self.assertIsNotNone(device)
+ if device.device_enabled:
+ device_token = device.device_token
+ self.assertIsNotNone(device_token)
+ self.assertGreater(len(device_token), 0)
+ response = self._get_with_token_auth(self.test_client, token=device_token)
+ self.assertEqual(response.status_code, 401)
+
+ def test_get_endpoint_with_service_token_no_params(self):
+ with self.app_context():
+ service: TeraService = TeraService.get_service_by_key('EmailService')
+ self.assertIsNotNone(service)
+ service_token = service.get_token(ServiceAccessManager.api_service_token_key)
+ self.assertGreater(len(service_token), 0)
+ response = self._get_with_token_auth(self.test_client, token=service_token)
+ self.assertEqual(response.status_code, 403)
+
+ def test_get_endpoint_without_params(self):
+ with self.app_context():
+ params = {}
+ response = self._get_with_token_auth(self.test_client, token=self.user_admin_token, params=params)
+ self.assertEqual(response.status_code, 400)
+
+ def test_get_endpoint_invalid_template_id(self):
+ with self.app_context():
+ params = {'id_template': 100}
+ response = self._get_with_token_auth(self.test_client, token=self.user_admin_token, params=params)
+ self.assertEqual(response.status_code, 403)
+
+ def test_get_endpoint_forbidden_template_id(self):
+ with self.app_context():
+ template = EmailTemplate.get_template_by_key('SITE_EMAIL')
+ self.assertIsNotNone(template)
+ params = {'id_template': template.id_email_template}
+ response = self._get_with_token_auth(self.test_client, token=self.user_user4_token, params=params)
+ self.assertEqual(response.status_code, 403)
+
+ template = EmailTemplate.get_template_by_key('PROJECT_EMAIL')
+ self.assertIsNotNone(template)
+ params = {'id_template': template.id_email_template}
+ response = self._get_with_token_auth(self.test_client, token=self.user_user4_token, params=params)
+ self.assertEqual(response.status_code, 403)
+
+ def test_get_endpoint_global_template_id(self):
+ with self.app_context():
+ template = EmailTemplate.get_template_by_key('GENERAL_TEST_EMAIL')
+ self.assertIsNotNone(template)
+ params = {'id_template': template.id_email_template}
+ response = self._get_with_token_auth(self.test_client, token=self.user_user4_token, params=params)
+ self.assertEqual(response.status_code, 200)
+ self._checkJson(response.json[0])
+
+ def test_get_endpoint_valid_template_id(self):
+ with self.app_context():
+ template = EmailTemplate.get_template_by_key('SITE_EMAIL')
+ self.assertIsNotNone(template)
+ params = {'id_template': template.id_email_template}
+ response = self._get_with_token_auth(self.test_client, token=self.user_admin_token, params=params)
+ self.assertEqual(response.status_code, 200)
+ self._checkJson(response.json[0])
+
+ template = EmailTemplate.get_template_by_key('PROJECT_EMAIL')
+ self.assertIsNotNone(template)
+ params = {'id_template': template.id_email_template}
+ response = self._get_with_token_auth(self.test_client, token=self.user_user3_token, params=params)
+ self.assertEqual(response.status_code, 200)
+ self._checkJson(response.json[0])
+
+ def test_get_endpoint_invalid_template_key(self):
+ with self.app_context():
+ params = {'key': 'INVALID'}
+ response = self._get_with_token_auth(self.test_client, token=self.user_admin_token, params=params)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(len(response.json), 0)
+
+ def test_get_endpoint_forbidden_template_key(self):
+ with self.app_context():
+ params = {'key': 'SITE_EMAIL'}
+ response = self._get_with_token_auth(self.test_client, token=self.user_user4_token, params=params)
+ self.assertEqual(response.status_code, 403)
+
+ params = {'key': 'PROJECT_EMAIL'}
+ response = self._get_with_token_auth(self.test_client, token=self.user_user4_token, params=params)
+ self.assertEqual(response.status_code, 403)
+
+ def test_get_endpoint_global_template_key(self):
+ with self.app_context():
+ params = {'key': 'GENERAL_TEST_EMAIL'}
+ response = self._get_with_token_auth(self.test_client, token=self.user_user4_token, params=params)
+ self.assertEqual(response.status_code, 200)
+ self._checkJson(response.json[0])
+
+ def test_get_endpoint_valid_template_key(self):
+ with self.app_context():
+ params = {'key': 'PROJECT_EMAIL'}
+ response = self._get_with_token_auth(self.test_client, token=self.user_user3_token, params=params)
+ self.assertEqual(response.status_code, 200)
+ self._checkJson(response.json[0])
+
+ params = {'key': 'SITE_EMAIL'}
+ response = self._get_with_token_auth(self.test_client, token=self.user_admin_token, params=params)
+ self.assertEqual(response.status_code, 200)
+ self._checkJson(response.json[0])
+
+ def test_get_endpoint_invalid_site(self):
+ with self.app_context():
+ params = {'id_site': 100}
+ response = self._get_with_token_auth(self.test_client, token=self.user_admin_token, params=params)
+ self.assertEqual(response.status_code, 403)
+
+ def test_get_endpoint_forbidden_site(self):
+ with self.app_context():
+ params = {'id_site': 2}
+ response = self._get_with_token_auth(self.test_client, token=self.user_user3_token, params=params)
+ self.assertEqual(response.status_code, 403)
+
+ def test_get_endpoint_valid_site(self):
+ with self.app_context():
+ params = {'id_site': 2}
+ response = self._get_with_token_auth(self.test_client, token=self.user_admin_token, params=params)
+ self.assertEqual(response.status_code, 200)
+ templates = EmailTemplate.get_templates_for_site(2)
+ self.assertEqual(len(response.json), len(templates))
+ for template in response.json:
+ self._checkJson(template)
+
+ def test_get_endpoint_invalid_project(self):
+ with self.app_context():
+ params = {'id_project': 100}
+ response = self._get_with_token_auth(self.test_client, token=self.user_admin_token, params=params)
+ self.assertEqual(response.status_code, 403)
+
+ def test_get_endpoint_forbidden_project(self):
+ with self.app_context():
+ params = {'id_project': 3}
+ response = self._get_with_token_auth(self.test_client, token=self.user_user3_token, params=params)
+ self.assertEqual(response.status_code, 403)
+
+ def test_get_endpoint_valid_project(self):
+ with self.app_context():
+ params = {'id_project': 1}
+ response = self._get_with_token_auth(self.test_client, token=self.user_user3_token, params=params)
+ self.assertEqual(response.status_code, 200)
+ templates = EmailTemplate.get_templates_for_project(1)
+ self.assertEqual(len(response.json), len(templates))
+ for template in response.json:
+ self._checkJson(template)
+
+ def test_get_endpoint_key_and_project_no_match(self):
+ with self.app_context():
+ params = {'id_project': 1, 'key': 'INVALID KEY'}
+ response = self._get_with_token_auth(self.test_client, token=self.user_user3_token, params=params)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(len(response.json), 0)
+
+ def test_get_endpoint_key_and_project_match(self):
+ with self.app_context():
+ params = {'id_project': 1, 'key': 'PROJECT_EMAIL'}
+ response = self._get_with_token_auth(self.test_client, token=self.user_user3_token, params=params)
+ self.assertEqual(response.status_code, 200)
+ templates = EmailTemplate.get_template_by_key('PROJECT_EMAIL', project_id=1)
+ self.assertEqual(len(response.json), 1)
+ self.assertEqual(response.json[0]["id_email_template"], templates.id_email_template)
+ self._checkJson(response.json[0])
+
+ def test_get_endpoint_key_and_site_no_match(self):
+ with self.app_context():
+ params = {'id_site': 1, 'key': 'SITE_EMAIL'}
+ response = self._get_with_token_auth(self.test_client, token=self.user_admin_token, params=params)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(len(response.json), 0)
+
+ def test_get_endpoint_key_and_site_match(self):
+ with self.app_context():
+ params = {'id_site': 2, 'key': 'SITE_EMAIL'}
+ response = self._get_with_token_auth(self.test_client, token=self.user_admin_token, params=params)
+ self.assertEqual(response.status_code, 200)
+ templates = EmailTemplate.get_template_by_key('SITE_EMAIL', site_id=2)
+ self.assertEqual(len(response.json), 1)
+ self.assertEqual(response.json[0]["id_email_template"], templates.id_email_template)
+ self._checkJson(response.json[0])
+
+ def test_post_endpoint_with_invalid_token(self):
+ with self.app_context():
+ response = self._post_with_token_auth(self.test_client, token="invalid")
+ self.assertEqual(response.status_code, 403)
+
+ def test_post_endpoint_with_static_participant_token(self):
+ with self.app_context():
+ for participant in TeraParticipant.query.all():
+ self.assertIsNotNone(participant)
+ if participant.participant_enabled and participant.participant_token:
+ self.assertIsNotNone(participant.participant_token)
+ self.assertGreater(len(participant.participant_token), 0)
+ response = self._post_with_token_auth(self.test_client, token=participant.participant_token)
+ self.assertEqual(response.status_code, 403)
+
+ def test_post_endpoint_with_static_device_token(self):
+ with self.app_context():
+ for device in TeraDevice.query.all():
+ self.assertIsNotNone(device)
+ if device.device_enabled:
+ device_token = device.device_token
+ self.assertIsNotNone(device_token)
+ self.assertGreater(len(device_token), 0)
+ response = self._post_with_token_auth(self.test_client, token=device_token)
+ self.assertEqual(response.status_code, 401)
+
+ def test_post_endpoint_with_service_token_no_params(self):
+ with self.app_context():
+ service: TeraService = TeraService.get_service_by_key('EmailService')
+ self.assertIsNotNone(service)
+ service_token = service.get_token(ServiceAccessManager.api_service_token_key)
+ self.assertGreater(len(service_token), 0)
+ response = self._post_with_token_auth(self.test_client, token=service_token)
+ self.assertEqual(response.status_code, 403)
+
+ def test_post_endpoint_without_params(self):
+ with self.app_context():
+ json_data = {}
+ response = self._post_with_token_auth(self.test_client, token=self.user_admin_token, json=json_data)
+ self.assertEqual(response.status_code, 400)
+
+ def test_post_endpoint_with_missing_id(self):
+ with self.app_context():
+ json_data = {'email_template': {}}
+ response = self._post_with_token_auth(self.test_client, token=self.user_admin_token, json=json_data)
+ self.assertEqual(response.status_code, 400)
+
+ def test_post_endpoint_with_site_and_project(self):
+ with self.app_context():
+ json_data = {'email_template': {'id_email_template': 0, 'id_site': 1, 'id_project': 1}}
+ response = self._post_with_token_auth(self.test_client, token=self.user_admin_token, json=json_data)
+ self.assertEqual(response.status_code, 400)
+
+ def test_post_endpoint_with_forbidden_site(self):
+ with self.app_context():
+ json_data = {'email_template': {'id_email_template': 0, 'id_site': 2}}
+ response = self._post_with_token_auth(self.test_client, token=self.user_user4_token, json=json_data)
+ self.assertEqual(response.status_code, 403)
+
+ def test_post_endpoint_with_forbidden_project(self):
+ with self.app_context():
+ json_data = {'email_template': {'id_email_template': 0, 'id_project': 2}}
+ response = self._post_with_token_auth(self.test_client, token=self.user_user4_token, json=json_data)
+ self.assertEqual(response.status_code, 403)
+
+ def test_post_endpoint_with_forbidden_global(self):
+ with self.app_context():
+ json_data = {'email_template': {'id_email_template': 0}}
+ response = self._post_with_token_auth(self.test_client, token=self.user_user3_token, json=json_data)
+ self.assertEqual(response.status_code, 403)
+
+ def test_post_endpoint_update_bad_id(self):
+ with self.app_context():
+ json_data = {'email_template': {'id_email_template': 999}}
+ response = self._post_with_token_auth(self.test_client, token=self.user_user3_token, json=json_data)
+ self.assertEqual(response.status_code, 403)
+
+ def test_post_endpoint_update_forbidden_site(self):
+ with self.app_context():
+ template: EmailTemplate = EmailTemplate.get_template_by_key('SITE_EMAIL')
+ json_data = {'email_template': {'id_email_template': template.id_email_template, 'id_site': 1}}
+ response = self._post_with_token_auth(self.test_client, token=self.user_siteadmin_token, json=json_data)
+ self.assertEqual(response.status_code, 403)
+
+ def test_post_endpoint_update_forbidden_global(self):
+ with self.app_context():
+ template: EmailTemplate = EmailTemplate.get_template_by_key('GENERAL_TEST_EMAIL')
+ json_data = {'email_template': {'id_email_template': template.id_email_template, 'id_site': 1}}
+ response = self._post_with_token_auth(self.test_client, token=self.user_siteadmin_token, json=json_data)
+ self.assertEqual(response.status_code, 403)
+
+ def test_post_endpoint_update_with_unknown_fields(self):
+ with self.app_context():
+ template: EmailTemplate = EmailTemplate.get_template_by_key('PROJECT_EMAIL')
+ json_data = {'email_template': {'id_email_template': template.id_email_template, 'bad_field': 1}}
+ response = self._post_with_token_auth(self.test_client, token=self.user_siteadmin_token, json=json_data)
+ self.assertEqual(response.status_code, 400)
+
+ def test_post_endpoint_update_success(self):
+ with self.app_context():
+ template: EmailTemplate = EmailTemplate.get_template_by_key('PROJECT_EMAIL')
+ text = 'This is a NEW PROJECT test email, using $variable .'
+ original_text = template.email_template
+ json_data = {'email_template': {'id_email_template': template.id_email_template, 'email_template': text}}
+ response = self._post_with_token_auth(self.test_client, token=self.user_siteadmin_token, json=json_data)
+ self.assertEqual(response.status_code, 200)
+ template: EmailTemplate = EmailTemplate.get_template_by_key('PROJECT_EMAIL')
+ self.assertEqual(template.email_template, text)
+ # Back to original text
+ json_data = {'email_template': {'id_email_template': template.id_email_template,
+ 'email_template': original_text}}
+ response = self._post_with_token_auth(self.test_client, token=self.user_siteadmin_token, json=json_data)
+ self.assertEqual(response.status_code, 200)
+
+ def test_post_endpoint_new_forbidden_site(self):
+ with self.app_context():
+ json_data = {'email_template': {'id_email_template': 0, 'id_site': 2}}
+ response = self._post_with_token_auth(self.test_client, token=self.user_user3_token, json=json_data)
+ self.assertEqual(response.status_code, 403)
+
+ def test_post_endpoint_new_forbidden_project(self):
+ with self.app_context():
+ json_data = {'email_template': {'id_email_template': 0, 'id_project': 1}}
+ response = self._post_with_token_auth(self.test_client, token=self.user_user4_token, json=json_data)
+ self.assertEqual(response.status_code, 403)
+
+ def test_post_endpoint_new_forbidden_global(self):
+ with self.app_context():
+ json_data = {'email_template': {'id_email_template': 0}}
+ response = self._post_with_token_auth(self.test_client, token=self.user_siteadmin_token, json=json_data)
+ self.assertEqual(response.status_code, 403)
+
+ def test_post_endpoint_new_with_missing_fields(self):
+ with self.app_context():
+ json_data = {'email_template': {'id_email_template': 0, 'id_site': 1}}
+ response = self._post_with_token_auth(self.test_client, token=self.user_siteadmin_token, json=json_data)
+ self.assertEqual(response.status_code, 400)
+ self.assertTrue('Missing' in response.json)
+
+ def test_post_endpoint_new_success(self):
+ with self.app_context():
+ text = 'This is a NEW PROJECT test email, using $variable .'
+ key = 'TEST_EMAIL'
+ json_data = {'email_template': {'id_email_template': 0, 'id_site': 1, 'email_template': text,
+ 'email_template_key': key}}
+ response = self._post_with_token_auth(self.test_client, token=self.user_siteadmin_token, json=json_data)
+ self.assertEqual(response.status_code, 200)
+ self.assertTrue('id_email_template' in response.json)
+ template: EmailTemplate = EmailTemplate.get_template_by_key('TEST_EMAIL')
+ self.assertIsNotNone(template)
+ self.assertEqual(template.email_template, text)
+ # Delete
+ EmailTemplate.delete(response.json['id_email_template'])
+ template: EmailTemplate = EmailTemplate.get_template_by_id(response.json['id_email_template'])
+ self.assertIsNone(template)
+
+ def test_delete_endpoint_with_invalid_token(self):
+ with self.app_context():
+ response = self._delete_with_token_auth(self.test_client, token="invalid")
+ self.assertEqual(response.status_code, 403)
+
+ def test_delete_endpoint_with_static_participant_token(self):
+ with self.app_context():
+ for participant in TeraParticipant.query.all():
+ self.assertIsNotNone(participant)
+ if participant.participant_enabled and participant.participant_token:
+ self.assertIsNotNone(participant.participant_token)
+ self.assertGreater(len(participant.participant_token), 0)
+ response = self._delete_with_token_auth(self.test_client, token=participant.participant_token)
+ self.assertEqual(response.status_code, 403)
+
+ def test_delete_endpoint_with_static_device_token(self):
+ with self.app_context():
+ for device in TeraDevice.query.all():
+ self.assertIsNotNone(device)
+ if device.device_enabled:
+ device_token = device.device_token
+ self.assertIsNotNone(device_token)
+ self.assertGreater(len(device_token), 0)
+ response = self._delete_with_token_auth(self.test_client, token=device_token)
+ self.assertEqual(response.status_code, 401)
+
+ def test_delete_endpoint_with_service_token_no_params(self):
+ with self.app_context():
+ service: TeraService = TeraService.get_service_by_key('EmailService')
+ self.assertIsNotNone(service)
+ service_token = service.get_token(ServiceAccessManager.api_service_token_key)
+ self.assertGreater(len(service_token), 0)
+ response = self._delete_with_token_auth(self.test_client, token=service_token)
+ self.assertEqual(response.status_code, 403)
+
+ def test_delete_endpoint_without_params(self):
+ with self.app_context():
+ params = {}
+ response = self._delete_with_token_auth(self.test_client, token=self.user_admin_token, params=params)
+ self.assertEqual(response.status_code, 400)
+
+ def test_delete_endpoint_invalid_id(self):
+ with self.app_context():
+ params = {'id': 99}
+ response = self._delete_with_token_auth(self.test_client, token=self.user_admin_token, params=params)
+ self.assertEqual(response.status_code, 403)
+
+ def test_delete_endpoint_forbidden_site(self):
+ with self.app_context():
+ template: EmailTemplate = EmailTemplate.get_template_by_key('SITE_EMAIL')
+ params = {'id': template.id_email_template}
+ response = self._delete_with_token_auth(self.test_client, token=self.user_user3_token, params=params)
+ self.assertEqual(response.status_code, 403)
+
+ def test_delete_endpoint_forbidden_project(self):
+ with self.app_context():
+ template: EmailTemplate = EmailTemplate.get_template_by_key('PROJECT_EMAIL')
+ params = {'id': template.id_email_template}
+ response = self._delete_with_token_auth(self.test_client, token=self.user_user4_token, params=params)
+ self.assertEqual(response.status_code, 403)
+
+ def test_delete_endpoint_forbidden_global(self):
+ with self.app_context():
+ template: EmailTemplate = EmailTemplate.get_template_by_key('GENERAL_TEST_EMAIL')
+ params = {'id': template.id_email_template}
+ response = self._delete_with_token_auth(self.test_client, token=self.user_siteadmin_token, params=params)
+ self.assertEqual(response.status_code, 403)
+
+ def test_delete_endpoint_success(self):
+ with self.app_context():
+ template = EmailTemplate()
+ template.email_template_key = 'TEST_EMAIL'
+ template.email_template = 'This is a TEST email.'
+ template.id_site = 1
+ EmailTemplate.db().session.add(template)
+ EmailTemplate.db().session.commit()
+
+ params = {'id': template.id_email_template}
+ response = self._delete_with_token_auth(self.test_client, token=self.user_siteadmin_token, params=params)
+ self.assertEqual(response.status_code, 200)
+ template = EmailTemplate.get_template_by_id(template.id_email_template)
+ self.assertIsNone(template)
+
+ def _checkJson(self, json_data):
+ self.assertGreater(len(json_data), 0)
+ self.assertTrue(json_data.__contains__('id_email_template'))
+ self.assertTrue(json_data.__contains__('id_site'))
+ self.assertTrue(json_data.__contains__('id_project'))
+ self.assertTrue(json_data.__contains__('email_template_key'))
+ self.assertTrue(json_data.__contains__('email_template'))
+ self.assertTrue(json_data.__contains__('email_template_language'))
diff --git a/teraserver/python/tests/services/EmailService/test_QuerySendEmail.py b/teraserver/python/tests/services/EmailService/test_QuerySendEmail.py
new file mode 100644
index 000000000..c7d7aa6e3
--- /dev/null
+++ b/teraserver/python/tests/services/EmailService/test_QuerySendEmail.py
@@ -0,0 +1,182 @@
+from services.EmailService.libemailservice.db.models import EmailTemplate
+from tests.services.EmailService.BaseEmailServiceAPITest import BaseEmailServiceAPITest
+from opentera.db.models.TeraParticipant import TeraParticipant
+from opentera.db.models.TeraDevice import TeraDevice
+from opentera.db.models.TeraService import TeraService
+from opentera.db.models.TeraUser import TeraUser
+from opentera.services.ServiceAccessManager import ServiceAccessManager
+import services.EmailService.Globals as Globals
+
+
+class EmailSendEmailTest(BaseEmailServiceAPITest):
+ test_endpoint = '/api/send'
+
+ def setUp(self):
+ super().setUp()
+
+ def tearDown(self):
+ super().tearDown()
+
+ def test_post_endpoint_with_invalid_token(self):
+ with self.app_context():
+ response = self._post_with_token_auth(self.test_client, token="invalid")
+ self.assertEqual(response.status_code, 403)
+
+ def test_post_endpoint_with_static_participant_token(self):
+ with self.app_context():
+ for participant in TeraParticipant.query.all():
+ self.assertIsNotNone(participant)
+ if participant.participant_enabled and participant.participant_token:
+ self.assertIsNotNone(participant.participant_token)
+ self.assertGreater(len(participant.participant_token), 0)
+ response = self._post_with_token_auth(self.test_client, token=participant.participant_token)
+ self.assertEqual(response.status_code, 403)
+
+ def test_post_endpoint_with_static_device_token(self):
+ with self.app_context():
+ for device in TeraDevice.query.all():
+ self.assertIsNotNone(device)
+ if device.device_enabled:
+ device_token = device.device_token
+ self.assertIsNotNone(device_token)
+ self.assertGreater(len(device_token), 0)
+ response = self._post_with_token_auth(self.test_client, token=device_token)
+ self.assertEqual(response.status_code, 401)
+
+ def test_get_endpoint_with_service_token_no_params(self):
+ with self.app_context():
+ service: TeraService = TeraService.get_service_by_key('EmailService')
+ self.assertIsNotNone(service)
+ service_token = service.get_token(ServiceAccessManager.api_service_token_key)
+ self.assertGreater(len(service_token), 0)
+ response = self._post_with_token_auth(self.test_client, token=service_token)
+ self.assertEqual(response.status_code, 403)
+
+ def test_missing_uuids(self):
+ with self.app_context():
+ json_data = {}
+ response = self._post_with_token_auth(self.test_client, token=self.user_admin_token, json=json_data)
+ self.assertEqual(response.status_code, 400)
+
+ def test_forbidden_user_uuids(self):
+ with self.app_context():
+ admin_uuid = TeraUser.get_user_by_username('admin').user_uuid
+ self.assertIsNotNone(admin_uuid)
+ json_data = {'user_uuid': admin_uuid}
+ response = self._post_with_token_auth(self.test_client, token=self.user_user3_token, json=json_data)
+ self.assertEqual(response.status_code, 403)
+
+ json_data = {'user_uuid': [admin_uuid, '11111111']}
+ response = self._post_with_token_auth(self.test_client, token=self.user_admin_token, json=json_data)
+ self.assertEqual(response.status_code, 403)
+
+ def test_forbidden_participant_uuids(self):
+ with self.app_context():
+ part_uuid = TeraParticipant.get_participant_by_username('participant1').participant_uuid
+ self.assertIsNotNone(part_uuid)
+ json_data = {'participant_uuid': part_uuid}
+ response = self._post_with_token_auth(self.test_client, token=self.user_user4_token, json=json_data)
+ self.assertEqual(response.status_code, 403)
+
+ json_data = {'participant_uuid': [part_uuid, '11111111']}
+ response = self._post_with_token_auth(self.test_client, token=self.user_admin_token, json=json_data)
+ self.assertEqual(response.status_code, 403)
+
+ def test_template_and_body_content(self):
+ with self.app_context():
+ json_data = {'id_template': 1, 'body': "This is a test email"}
+ response = self._post_with_token_auth(self.test_client, token=self.user_admin_token, json=json_data)
+ self.assertEqual(response.status_code, 400)
+
+ def test_missing_body_and_template(self):
+ with self.app_context():
+ part_uuid = TeraParticipant.get_participant_by_username('participant1').participant_uuid
+ self.assertIsNotNone(part_uuid)
+ json_data = {'participant_uuid': part_uuid}
+ response = self._post_with_token_auth(self.test_client, token=self.user_admin_token, json=json_data)
+ self.assertEqual(response.status_code, 400)
+
+ def test_sender_no_email(self):
+ with self.app_context():
+ part_uuid = TeraParticipant.get_participant_by_name('Participant #2').participant_uuid
+ self.assertIsNotNone(part_uuid)
+ json_data = {'participant_uuid': part_uuid, 'body': 'This is a test email'}
+ response = self._post_with_token_auth(self.test_client, token=self.user_user3_token, json=json_data)
+ self.assertEqual(response.status_code, 400)
+
+ def test_invalid_template(self):
+ with self.app_context():
+ part_uuid = TeraParticipant.get_participant_by_username('participant1').participant_uuid
+ self.assertIsNotNone(part_uuid)
+ json_data = {'participant_uuid': part_uuid, 'id_template': 0}
+ response = self._post_with_token_auth(self.test_client, token=self.user_admin_token, json=json_data)
+ self.assertEqual(response.status_code, 403)
+
+ def test_no_access_to_template_site(self):
+ with self.app_context():
+ user_uuid = TeraUser.get_user_by_username('user4').user_uuid
+ self.assertIsNotNone(user_uuid)
+ template = EmailTemplate.get_template_by_key('SITE_EMAIL')
+ self.assertIsNotNone(template)
+ json_data = {'user_uuid': user_uuid, 'id_template': template.id_email_template}
+ response = self._post_with_token_auth(self.test_client, token=self.user_user4_token, json=json_data)
+ self.assertEqual(response.status_code, 403)
+
+ def test_no_access_to_template_project(self):
+ with self.app_context():
+ user_uuid = TeraUser.get_user_by_username('user4').user_uuid
+ self.assertIsNotNone(user_uuid)
+ template = EmailTemplate.get_template_by_key('PROJECT_EMAIL')
+ self.assertIsNotNone(template)
+ json_data = {'user_uuid': user_uuid, 'id_template': template.id_email_template}
+ response = self._post_with_token_auth(self.test_client, token=self.user_user4_token, json=json_data)
+ self.assertEqual(response.status_code, 403)
+
+ def test_no_access_to_global_template(self):
+ with self.app_context():
+ part_uuid = TeraParticipant.get_participant_by_username('participant1').participant_uuid
+ self.assertIsNotNone(part_uuid)
+ template = EmailTemplate.get_template_by_key('GENERAL_TEST_EMAIL')
+ self.assertIsNotNone(template)
+ json_data = {'participant_uuid': part_uuid, 'id_template': template.id_email_template}
+ response = self._post_with_token_auth(self.test_client, token=self.user_user3_token, json=json_data)
+ self.assertEqual(response.status_code, 403)
+
+ def test_send_success_with_template(self):
+ with self.app_context():
+ part_uuid = TeraParticipant.get_participant_by_username('participant1').participant_uuid
+ self.assertIsNotNone(part_uuid)
+ template = EmailTemplate.get_template_by_key('GENERAL_TEST_EMAIL')
+ self.assertIsNotNone(template)
+ json_data = {'participant_uuid': part_uuid, 'id_template': template.id_email_template,
+ 'body_variables': {'variable': 'V1Test'}}
+ with Globals.service.flask_module.mail_man.record_messages() as outbox:
+ response = self._post_with_token_auth(self.test_client, token=self.user_admin_token, json=json_data)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(len(outbox), 1)
+ self.assertTrue('GLOBAL' in outbox[0].html)
+ self.assertTrue('V1Test' in outbox[0].html)
+
+ def test_send_success(self):
+ with self.app_context():
+ part_uuid = TeraParticipant.get_participant_by_username('participant1').participant_uuid
+ self.assertIsNotNone(part_uuid)
+ json_data = {'participant_uuid': part_uuid, 'body': 'This is a test email', 'subject': 'Test Email'}
+ with Globals.service.flask_module.mail_man.record_messages() as outbox:
+ response = self._post_with_token_auth(self.test_client, token=self.user_admin_token, json=json_data)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(len(outbox), 1)
+ self.assertEqual(outbox[0].subject, "Test Email")
+
+
+ def test_send_with_variables(self):
+ with self.app_context():
+ part_uuid = TeraParticipant.get_participant_by_username('participant1').participant_uuid
+ self.assertIsNotNone(part_uuid)
+ json_data = {'participant_uuid': part_uuid, 'body': 'Variable #1 = $variable1, Variable #2 = $variable2',
+ 'subject': 'Test Email', 'body_variables': {'variable1': 'Unit', 'variable2': 'Test'}}
+ with Globals.service.flask_module.mail_man.record_messages() as outbox:
+ response = self._post_with_token_auth(self.test_client, token=self.user_admin_token, json=json_data)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(len(outbox), 1)
+ self.assertEqual(outbox[0].html, "Variable #1 = Unit, Variable #2 = Test")
diff --git a/teraserver/python/tests/services/FileTransferService/BaseFileTransferServiceAPITest.py b/teraserver/python/tests/services/FileTransferService/BaseFileTransferServiceAPITest.py
index 0eca382fd..b43854c4f 100644
--- a/teraserver/python/tests/services/FileTransferService/BaseFileTransferServiceAPITest.py
+++ b/teraserver/python/tests/services/FileTransferService/BaseFileTransferServiceAPITest.py
@@ -8,7 +8,7 @@
import uuid
import random
from string import digits, ascii_lowercase, ascii_uppercase
-from FakeFileTransferService import FakeFileTransferService
+from tests.services.FileTransferService.FakeFileTransferService import FakeFileTransferService
from opentera.services.ServiceAccessManager import ServiceAccessManager
from opentera.db.models.TeraService import TeraService
from modules.LoginModule.LoginModule import LoginModule
diff --git a/teraserver/python/tests/services/FileTransferService/__init__.py b/teraserver/python/tests/services/FileTransferService/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/teraserver/python/tests/services/FileTransferService/test_QueryAssetFileAndInfos.py b/teraserver/python/tests/services/FileTransferService/test_QueryAssetFileAndInfos.py
index 2b03a9eb5..09578997e 100644
--- a/teraserver/python/tests/services/FileTransferService/test_QueryAssetFileAndInfos.py
+++ b/teraserver/python/tests/services/FileTransferService/test_QueryAssetFileAndInfos.py
@@ -7,6 +7,8 @@
from opentera.db.models.TeraParticipant import TeraParticipant
from opentera.db.models.TeraDevice import TeraDevice
from opentera.db.models.TeraService import TeraService
+from opentera.db.models.TeraSession import TeraSession
+from opentera.db.models.TeraAsset import TeraAsset
from opentera.services.ServiceAccessManager import ServiceAccessManager
from services.FileTransferService.libfiletransferservice.db.models.AssetFileData import AssetFileData
@@ -24,7 +26,7 @@ def setUp(self):
self.participant_static_token = TeraParticipant.get_participant_by_id(1).participant_token
self.participant_dynamic_token = TeraParticipant.get_participant_by_id(1).dynamic_token(
ServiceAccessManager.api_participant_token_key)
- self.service_token = TeraService.get_service_by_key('VideoRehabService').get_token(
+ self.service_token = TeraService.get_service_by_key('FileTransferService').get_token(
ServiceAccessManager.api_service_token_key)
# Create test file to stream
@@ -140,7 +142,10 @@ def test_full_as_user(self):
endpoint=self.asset_endpoint)
self.assertEqual(403, response.status_code, 'Forbidden access to session')
- file_asset['id_session'] = 1 # OK, OK... I'll do something right for once!
+ service: TeraService = TeraService.get_service_by_key('FileTransferService')
+ service_ses = TeraSession.query.filter(TeraSession.id_creator_service == service.id_service).first()
+ file_asset['id_session'] = service_ses.id_session
+
with open('testfile', 'rb') as f:
files = {'file': (f, 'testfile'),
'file_asset': json.dumps(file_asset)}
@@ -581,7 +586,10 @@ def test_full_as_service(self):
self.assertEqual(403, response.status_code, 'Forbidden access to session')
- file_asset['id_session'] = 27
+ service: TeraService = TeraService.get_service_by_key('FileTransferService')
+ service_ses = TeraSession.query.filter(TeraSession.id_creator_service == service.id_service).first()
+ file_asset['id_session'] = service_ses.id_session
+
with open('testfile', 'rb') as f:
files = {'file': (f, 'testfile'),
'file_asset': json.dumps(file_asset)}
@@ -731,7 +739,11 @@ def test_multiple_queries_as_post(self):
for i in range(3):
f = io.BytesIO(os.urandom(1024 * 1))
- file_asset = {'id_session': 5,
+ service: TeraService = TeraService.get_service_by_key('FileTransferService')
+ service_ses = TeraSession.query.filter(TeraSession.id_creator_service == service.id_service).first()
+ id_session = service_ses.id_session
+
+ file_asset = {'id_session': id_session,
'asset_name': 'Test Asset #' + str(i),
'asset_type': 'application/octet-stream'
}
@@ -745,11 +757,14 @@ def test_multiple_queries_as_post(self):
asset_uuids.append(response.json['asset_uuid'])
# Get access token
- params = {'id_session': 5, 'with_urls': True}
+ params = {'id_session': id_session, 'with_urls': True}
response = self._get_with_token_auth(self.test_client, token=self.user_token, params=params,
endpoint='/api/user/assets')
self.assertEqual(200, response.status_code)
- self.assertEqual(len(response.json), 3)
+
+ target_count = len(TeraAsset.get_assets_for_session(id_session))
+
+ self.assertEqual(len(response.json), target_count)
assets = []
for asset in response.json:
assets.append({'asset_uuid': asset['asset_uuid'], 'access_token': asset['access_token']})
@@ -763,10 +778,15 @@ def test_multiple_queries_as_post(self):
json_data = {'assets': assets}
response = self._post_with_token_auth(self.test_client, token=self.user_token, json=json_data)
self.assertEqual(200, response.status_code, 'Asset query is fine')
- self.assertEqual(len(response.json), 3)
+ service_assets = (AssetFileData.query.filter(AssetFileData.asset_uuid.in_(
+ [asset['asset_uuid'] for asset in assets])).all())
+ service_assets_uuid = [asset.asset_uuid for asset in service_assets]
+ self.assertEqual(len(response.json), len(service_assets))
# Delete created assets
for asset in assets:
+ if asset['asset_uuid'] not in service_assets_uuid:
+ continue
params = {'uuid': asset['asset_uuid'], 'access_token': asset['access_token']}
response = self._delete_with_token_auth(self.test_client, token=self.user_token, params=params,
endpoint=self.asset_endpoint)
diff --git a/teraserver/python/tests/services/LoggingService/BaseLoggingServiceAPITest.py b/teraserver/python/tests/services/LoggingService/BaseLoggingServiceAPITest.py
index 151e548f0..3cb6a5d1e 100644
--- a/teraserver/python/tests/services/LoggingService/BaseLoggingServiceAPITest.py
+++ b/teraserver/python/tests/services/LoggingService/BaseLoggingServiceAPITest.py
@@ -6,7 +6,7 @@
import uuid
import random
from string import digits, ascii_lowercase, ascii_uppercase
-from FakeLoggingService import FakeLoggingService
+from tests.services.LoggingService.FakeLoggingService import FakeLoggingService
from opentera.services.ServiceAccessManager import ServiceAccessManager
from requests.auth import _basic_auth_str
diff --git a/teraserver/python/tests/services/LoggingService/__init__.py b/teraserver/python/tests/services/LoggingService/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/teraserver/python/tests/services/LoggingService/test_QueryLogEntries.py b/teraserver/python/tests/services/LoggingService/test_QueryLogEntries.py
index 246965a0a..be4eca81c 100644
--- a/teraserver/python/tests/services/LoggingService/test_QueryLogEntries.py
+++ b/teraserver/python/tests/services/LoggingService/test_QueryLogEntries.py
@@ -1,4 +1,4 @@
-from BaseLoggingServiceAPITest import BaseLoggingServiceAPITest
+from tests.services.LoggingService.BaseLoggingServiceAPITest import BaseLoggingServiceAPITest
from services.LoggingService.libloggingservice.db.models.LogEntry import LogEntry
from datetime import datetime, timedelta
@@ -28,7 +28,7 @@ def test_get_endpoint_with_valid_token_and_admin(self):
token = self._generate_fake_user_token(name='FakeUser', superadmin=True, expiration=3600)
all_entries = []
- for i in range(50):
+ for _ in range(50):
current_time = datetime.now()
entry = self._create_log_entry(current_time, 1, 'test', 'test_message')
self.assertIsNotNone(entry)
@@ -52,14 +52,14 @@ def test_get_endpoint_with_valid_token_and_admin_with_start_end_dates(self):
current_time = datetime.now() + timedelta(hours=1)
tomorrow = current_time + timedelta(days=1)
- for i in range(50):
+ for _ in range(50):
entry = self._create_log_entry(current_time, 1, 'test', 'test_message')
self.assertIsNotNone(entry)
LogEntry.insert(entry)
self.assertIsNotNone(entry.id_log_entry)
all_entries.append(entry)
- for i in range(50):
+ for _ in range(50):
entry = self._create_log_entry(tomorrow, 1, 'test', 'test_message')
self.assertIsNotNone(entry)
LogEntry.insert(entry)
@@ -91,7 +91,7 @@ def test_get_endpoint_with_valid_token_and_admin_with_offset(self):
all_entries = []
current_time = datetime.now()
- for i in range(50):
+ for _ in range(50):
entry = self._create_log_entry(current_time, 1, 'test', 'test_message')
self.assertIsNotNone(entry)
LogEntry.insert(entry)
diff --git a/teraserver/python/tests/services/LoggingService/test_QueryLoginEntries.py b/teraserver/python/tests/services/LoggingService/test_QueryLoginEntries.py
index 4cb7bcf5c..1e2e2ef61 100644
--- a/teraserver/python/tests/services/LoggingService/test_QueryLoginEntries.py
+++ b/teraserver/python/tests/services/LoggingService/test_QueryLoginEntries.py
@@ -1,4 +1,4 @@
-from BaseLoggingServiceAPITest import BaseLoggingServiceAPITest
+from tests.services.LoggingService.BaseLoggingServiceAPITest import BaseLoggingServiceAPITest
from services.LoggingService.libloggingservice.db.models.LoginEntry import LoginEntry
from opentera.services.ServiceAccessManager import ServiceAccessManager
from opentera.services.TeraUserClient import TeraUserClient
diff --git a/teraserver/python/tests/services/__init__.py b/teraserver/python/tests/services/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/teraserver/python/translations/en/LC_MESSAGES/messages.po b/teraserver/python/translations/en/LC_MESSAGES/messages.po
index 44f081496..14f2940e1 100644
--- a/teraserver/python/translations/en/LC_MESSAGES/messages.po
+++ b/teraserver/python/translations/en/LC_MESSAGES/messages.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2024-03-04 11:09-0500\n"
+"POT-Creation-Date: 2025-01-08 11:51-0500\n"
"PO-Revision-Date: 2021-01-25 13:01-0500\n"
"Last-Translator: \n"
"Language: en\n"
@@ -16,1088 +16,1367 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Generated-By: Babel 2.14.0\n"
+"Generated-By: Babel 2.16.0\n"
-#: modules/FlaskModule/API/device/DeviceLogin.py:82
+#: modules/FlaskModule/API/user/UserQueryUsers.py:53
+#: modules/FlaskModule/FlaskUtils.py:13
+msgid "Password missing special character"
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserQueryUsers.py:55
+#: modules/FlaskModule/FlaskUtils.py:15
+msgid "Password missing numeric character"
+msgstr ""
+
+#: modules/FlaskModule/FlaskUtils.py:17
+msgid "Password not long enough (10 characters)"
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserQueryUsers.py:59
+#: modules/FlaskModule/FlaskUtils.py:19
+msgid "Password missing lower case letter"
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserQueryUsers.py:61
+#: modules/FlaskModule/FlaskUtils.py:21
+msgid "Password missing upper case letter"
+msgstr ""
+
+#: modules/FlaskModule/API/device/DeviceLogin.py:90
msgid "Unable to get online devices."
msgstr ""
-#: modules/FlaskModule/API/device/DeviceLogin.py:98
+#: modules/FlaskModule/API/device/DeviceLogin.py:106
msgid "Device already logged in."
msgstr ""
-#: modules/FlaskModule/API/device/DeviceLogout.py:29
+#: modules/FlaskModule/API/device/DeviceLogout.py:32
msgid "Device logged out."
msgstr ""
-#: modules/FlaskModule/API/device/DeviceLogout.py:31
+#: modules/FlaskModule/API/device/DeviceLogout.py:34
msgid "Device not logged in"
msgstr ""
-#: modules/FlaskModule/API/device/DeviceQueryDevices.py:69
+#: modules/FlaskModule/API/device/DeviceQueryAssets.py:42
+#: modules/FlaskModule/API/participant/ParticipantQueryAssets.py:48
+msgid "No access to session"
+msgstr ""
+
+#: modules/FlaskModule/API/device/DeviceQueryDevices.py:75
msgid "Missing device schema"
msgstr ""
-#: modules/FlaskModule/API/device/DeviceQueryDevices.py:78
+#: modules/FlaskModule/API/device/DeviceQueryDevices.py:84
msgid "Missing config"
msgstr ""
-#: modules/FlaskModule/API/device/DeviceQueryDevices.py:82
-#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:49
-#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:55
-#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:62
-#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:68
-#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:74
-#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:80
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:52
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:66
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:121
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:144
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:179
-#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:72
-#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:79
-#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:89
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:52
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:57
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:99
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:106
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:158
-#: modules/FlaskModule/API/service/ServiceQueryRoles.py:112
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:100
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:104
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:108
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:116
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:194
-#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:86
+#: modules/FlaskModule/API/device/DeviceQueryDevices.py:88
+#: modules/FlaskModule/API/participant/ParticipantQueryAssets.py:44
+#: modules/FlaskModule/API/service/ServiceQueryDevices.py:61
+#: modules/FlaskModule/API/service/ServiceQueryDevices.py:130
+#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:51
+#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:57
+#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:64
+#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:70
+#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:76
+#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:82
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:55
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:69
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:127
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:150
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:188
+#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:76
+#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:83
+#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:91
+#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:102
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:55
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:60
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:105
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:112
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:167
+#: modules/FlaskModule/API/service/ServiceQueryRoles.py:121
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:106
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:110
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:114
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:122
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:203
#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:91
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:66
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:72
+#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:96
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:133
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:171
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:205
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:271
#: modules/FlaskModule/API/service/ServiceQuerySessions.py:76
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:84
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:90
-#: modules/FlaskModule/API/service/ServiceQuerySites.py:36
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:121
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:159
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:193
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:256
-#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:96
-#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:101
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:50
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:54
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:106
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:207
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:301
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:122
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:257
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:112
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:165
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:105
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:191
-#: modules/FlaskModule/API/user/UserQueryDevices.py:293
-#: modules/FlaskModule/API/user/UserQueryDevices.py:297
-#: modules/FlaskModule/API/user/UserQueryDevices.py:417
-#: modules/FlaskModule/API/user/UserQueryDisconnect.py:52
-#: modules/FlaskModule/API/user/UserQueryDisconnect.py:60
-#: modules/FlaskModule/API/user/UserQueryDisconnect.py:66
-#: modules/FlaskModule/API/user/UserQueryDisconnect.py:72
-#: modules/FlaskModule/API/user/UserQueryDisconnect.py:78
-#: modules/FlaskModule/API/user/UserQueryDisconnect.py:84
-#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:107
-#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:159
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:363
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:366
-#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:204
-#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:274
-#: modules/FlaskModule/API/user/UserQueryProjects.py:151
-#: modules/FlaskModule/API/user/UserQueryProjects.py:156
-#: modules/FlaskModule/API/user/UserQueryProjects.py:264
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:117
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:122
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:82
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:86
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:94
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:100
+#: modules/FlaskModule/API/service/ServiceQuerySites.py:39
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:130
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:168
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:202
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:268
+#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:107
+#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:117
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:261
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:263
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:265
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:267
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:269
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:271
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:286
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:353
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:52
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:56
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:111
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:215
+#: modules/FlaskModule/API/user/UserQueryAssetsArchive.py:116
+#: modules/FlaskModule/API/user/UserQueryAssetsArchive.py:125
+#: modules/FlaskModule/API/user/UserQueryAssetsArchive.py:136
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:306
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:125
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:262
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:115
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:170
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:107
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:195
+#: modules/FlaskModule/API/user/UserQueryDevices.py:296
+#: modules/FlaskModule/API/user/UserQueryDevices.py:300
+#: modules/FlaskModule/API/user/UserQueryDevices.py:422
+#: modules/FlaskModule/API/user/UserQueryDisconnect.py:53
+#: modules/FlaskModule/API/user/UserQueryDisconnect.py:61
+#: modules/FlaskModule/API/user/UserQueryDisconnect.py:67
+#: modules/FlaskModule/API/user/UserQueryDisconnect.py:73
+#: modules/FlaskModule/API/user/UserQueryDisconnect.py:79
+#: modules/FlaskModule/API/user/UserQueryDisconnect.py:85
+#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:110
+#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:164
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:368
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:371
+#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:207
+#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:279
+#: modules/FlaskModule/API/user/UserQueryProjects.py:155
+#: modules/FlaskModule/API/user/UserQueryProjects.py:160
+#: modules/FlaskModule/API/user/UserQueryProjects.py:270
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:121
#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:126
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:134
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:213
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:217
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:221
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:225
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:151
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:155
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:159
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:165
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:240
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:130
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:138
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:219
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:223
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:227
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:231
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:154
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:158
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:162
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:168
#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:245
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:249
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:253
-#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:79
-#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:136
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:129
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:259
-#: modules/FlaskModule/API/user/UserQueryServices.py:121
-#: modules/FlaskModule/API/user/UserQueryServices.py:134
-#: modules/FlaskModule/API/user/UserQueryServices.py:231
-#: modules/FlaskModule/API/user/UserQueryServices.py:239
-#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:91
-#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:144
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:204
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:203
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:267
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:55
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:107
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:138
-#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:187
-#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:275
-#: modules/FlaskModule/API/user/UserQuerySites.py:124
-#: modules/FlaskModule/API/user/UserQuerySites.py:127
-#: modules/FlaskModule/API/user/UserQuerySites.py:176
-#: modules/FlaskModule/API/user/UserQueryStats.py:52
-#: modules/FlaskModule/API/user/UserQueryStats.py:57
-#: modules/FlaskModule/API/user/UserQueryStats.py:62
-#: modules/FlaskModule/API/user/UserQueryStats.py:68
-#: modules/FlaskModule/API/user/UserQueryStats.py:73
-#: modules/FlaskModule/API/user/UserQueryStats.py:81
-#: modules/FlaskModule/API/user/UserQueryStats.py:86
-#: modules/FlaskModule/API/user/UserQueryStats.py:91
-#: modules/FlaskModule/API/user/UserQueryTestType.py:62
-#: modules/FlaskModule/API/user/UserQueryTestType.py:169
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:205
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:193
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:256
-#: modules/FlaskModule/API/user/UserQueryTests.py:140
-#: modules/FlaskModule/API/user/UserQueryUserGroups.py:147
-#: modules/FlaskModule/API/user/UserQueryUserPreferences.py:47
-#: modules/FlaskModule/API/user/UserQueryUserPreferences.py:96
-#: modules/FlaskModule/API/user/UserQueryUsers.py:214
-#: modules/FlaskModule/API/user/UserQueryUsers.py:219
-#: modules/FlaskModule/API/user/UserQueryUsers.py:348
-#: opentera/services/ServiceAccessManager.py:116
-#: opentera/services/ServiceAccessManager.py:166
-#: opentera/services/ServiceAccessManager.py:195
-#: opentera/services/ServiceAccessManager.py:214
-#: opentera/services/ServiceAccessManager.py:242
-#: opentera/services/ServiceAccessManager.py:297
-#: opentera/services/ServiceAccessManager.py:407
-#: opentera/services/ServiceAccessManager.py:413
-#: opentera/services/ServiceAccessManager.py:424
-#: opentera/services/ServiceAccessManager.py:440
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:250
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:254
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:258
+#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:83
+#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:142
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:133
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:265
+#: modules/FlaskModule/API/user/UserQueryServices.py:124
+#: modules/FlaskModule/API/user/UserQueryServices.py:137
+#: modules/FlaskModule/API/user/UserQueryServices.py:236
+#: modules/FlaskModule/API/user/UserQueryServices.py:244
+#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:94
+#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:149
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:208
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:189
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:207
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:273
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:59
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:113
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:144
+#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:190
+#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:280
+#: modules/FlaskModule/API/user/UserQuerySites.py:128
+#: modules/FlaskModule/API/user/UserQuerySites.py:132
+#: modules/FlaskModule/API/user/UserQuerySites.py:183
+#: modules/FlaskModule/API/user/UserQueryStats.py:54
+#: modules/FlaskModule/API/user/UserQueryStats.py:59
+#: modules/FlaskModule/API/user/UserQueryStats.py:64
+#: modules/FlaskModule/API/user/UserQueryStats.py:70
+#: modules/FlaskModule/API/user/UserQueryStats.py:75
+#: modules/FlaskModule/API/user/UserQueryStats.py:83
+#: modules/FlaskModule/API/user/UserQueryStats.py:88
+#: modules/FlaskModule/API/user/UserQueryStats.py:93
+#: modules/FlaskModule/API/user/UserQueryTestType.py:72
+#: modules/FlaskModule/API/user/UserQueryTestType.py:77
+#: modules/FlaskModule/API/user/UserQueryTestType.py:186
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:209
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:197
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:262
+#: modules/FlaskModule/API/user/UserQueryTests.py:147
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:264
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:266
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:268
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:270
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:272
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:274
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:289
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:354
+#: modules/FlaskModule/API/user/UserQueryUserGroups.py:150
+#: modules/FlaskModule/API/user/UserQueryUserPreferences.py:48
+#: modules/FlaskModule/API/user/UserQueryUserPreferences.py:99
+#: modules/FlaskModule/API/user/UserQueryUsers.py:229
+#: modules/FlaskModule/API/user/UserQueryUsers.py:234
+#: modules/FlaskModule/API/user/UserQueryUsers.py:356
+#: opentera/services/ServiceAccessManager.py:118
+#: opentera/services/ServiceAccessManager.py:168
+#: opentera/services/ServiceAccessManager.py:197
+#: opentera/services/ServiceAccessManager.py:216
+#: opentera/services/ServiceAccessManager.py:244
+#: opentera/services/ServiceAccessManager.py:299
+#: opentera/services/ServiceAccessManager.py:429
+#: opentera/services/ServiceAccessManager.py:435
#: opentera/services/ServiceAccessManager.py:446
-#: opentera/services/ServiceAccessManager.py:457
+#: opentera/services/ServiceAccessManager.py:462
+#: opentera/services/ServiceAccessManager.py:468
+#: opentera/services/ServiceAccessManager.py:479
+#: opentera/services/ServiceAccessManager.py:502
+#: opentera/services/ServiceAccessManager.py:518
+#: opentera/services/ServiceAccessManager.py:527
+#: opentera/services/ServiceAccessManager.py:533
+#: opentera/services/ServiceAccessManager.py:541
+#: opentera/services/ServiceAccessManager.py:562
+#: opentera/services/ServiceAccessManager.py:579
+#: opentera/services/ServiceAccessManager.py:593
msgid "Forbidden"
msgstr ""
-#: modules/FlaskModule/API/device/DeviceQueryDevices.py:93
-#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:74
-#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:89
-#: modules/FlaskModule/API/device/DeviceQuerySessions.py:157
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:233
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:247
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:284
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:136
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:153
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:201
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:116
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:138
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:176
-#: modules/FlaskModule/API/service/ServiceQueryRoles.py:73
-#: modules/FlaskModule/API/service/ServiceQueryRoles.py:94
-#: modules/FlaskModule/API/service/ServiceQueryRoles.py:123
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:138
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:150
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:172
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:205
-#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:104
-#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:119
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:185
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:218
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:233
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:274
-#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:111
-#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:132
-#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:166
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:231
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:242
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:277
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:151
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:168
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:225
-#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:180
-#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:221
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:276
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:331
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:232
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:268
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:130
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:145
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:187
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:121
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:136
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:189
-#: modules/FlaskModule/API/user/UserQueryDevices.py:328
-#: modules/FlaskModule/API/user/UserQueryDevices.py:343
-#: modules/FlaskModule/API/user/UserQueryDevices.py:460
-#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:120
-#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:135
-#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:178
-#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:244
-#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:285
-#: modules/FlaskModule/API/user/UserQueryProjects.py:196
-#: modules/FlaskModule/API/user/UserQueryProjects.py:211
-#: modules/FlaskModule/API/user/UserQueryProjects.py:282
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:156
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:168
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:190
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:236
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:208
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:264
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:287
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:322
-#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:98
-#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:119
-#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:147
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:244
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:286
-#: modules/FlaskModule/API/user/UserQueryServices.py:162
-#: modules/FlaskModule/API/user/UserQueryServices.py:182
-#: modules/FlaskModule/API/user/UserQueryServices.py:259
-#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:104
-#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:119
-#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:155
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:251
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:291
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:244
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:286
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:197
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:212
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:342
-#: modules/FlaskModule/API/user/UserQuerySessions.py:179
-#: modules/FlaskModule/API/user/UserQuerySessions.py:194
-#: modules/FlaskModule/API/user/UserQuerySessions.py:272
-#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:246
-#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:286
-#: modules/FlaskModule/API/user/UserQuerySites.py:140
-#: modules/FlaskModule/API/user/UserQuerySites.py:155
-#: modules/FlaskModule/API/user/UserQuerySites.py:194
-#: modules/FlaskModule/API/user/UserQueryTestType.py:222
-#: modules/FlaskModule/API/user/UserQueryTestType.py:237
-#: modules/FlaskModule/API/user/UserQueryTestType.py:345
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:252
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:293
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:233
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:274
-#: modules/FlaskModule/API/user/UserQueryTests.py:151
-#: modules/FlaskModule/API/user/UserQueryUserGroups.py:190
-#: modules/FlaskModule/API/user/UserQueryUserGroups.py:205
-#: modules/FlaskModule/API/user/UserQueryUserGroups.py:233
-#: modules/FlaskModule/API/user/UserQueryUserGroups.py:262
-#: modules/FlaskModule/API/user/UserQueryUserGroups.py:310
-#: modules/FlaskModule/API/user/UserQueryUserPreferences.py:111
-#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:138
-#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:174
-#: modules/FlaskModule/API/user/UserQueryUsers.py:240
-#: modules/FlaskModule/API/user/UserQueryUsers.py:272
-#: modules/FlaskModule/API/user/UserQueryUsers.py:385
+#: modules/FlaskModule/API/device/DeviceQueryDevices.py:99
+#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:79
+#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:94
+#: modules/FlaskModule/API/device/DeviceQuerySessions.py:162
+#: modules/FlaskModule/API/participant/ParticipantQuerySessions.py:188
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:239
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:253
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:293
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:142
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:159
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:210
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:122
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:144
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:185
+#: modules/FlaskModule/API/service/ServiceQueryRoles.py:79
+#: modules/FlaskModule/API/service/ServiceQueryRoles.py:100
+#: modules/FlaskModule/API/service/ServiceQueryRoles.py:132
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:144
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:156
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:178
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:214
+#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:109
+#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:124
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:245
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:288
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypes.py:229
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:198
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:231
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:242
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:286
+#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:127
+#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:148
+#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:185
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:237
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:248
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:286
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:363
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:156
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:173
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:233
+#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:183
+#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:226
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:279
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:336
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:235
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:273
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:133
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:148
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:192
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:123
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:138
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:193
+#: modules/FlaskModule/API/user/UserQueryDevices.py:331
+#: modules/FlaskModule/API/user/UserQueryDevices.py:346
+#: modules/FlaskModule/API/user/UserQueryDevices.py:465
+#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:123
+#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:138
+#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:183
+#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:247
+#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:290
+#: modules/FlaskModule/API/user/UserQueryProjects.py:200
+#: modules/FlaskModule/API/user/UserQueryProjects.py:215
+#: modules/FlaskModule/API/user/UserQueryProjects.py:288
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:160
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:172
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:194
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:242
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:211
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:269
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:291
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:328
+#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:102
+#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:123
+#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:153
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:248
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:292
+#: modules/FlaskModule/API/user/UserQueryServices.py:165
+#: modules/FlaskModule/API/user/UserQueryServices.py:185
+#: modules/FlaskModule/API/user/UserQueryServices.py:264
+#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:107
+#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:122
+#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:160
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:255
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:297
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:217
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:262
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:248
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:292
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:220
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:235
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:385
+#: modules/FlaskModule/API/user/UserQuerySessions.py:183
+#: modules/FlaskModule/API/user/UserQuerySessions.py:198
+#: modules/FlaskModule/API/user/UserQuerySessions.py:278
+#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:249
+#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:291
+#: modules/FlaskModule/API/user/UserQuerySites.py:145
+#: modules/FlaskModule/API/user/UserQuerySites.py:160
+#: modules/FlaskModule/API/user/UserQuerySites.py:201
+#: modules/FlaskModule/API/user/UserQueryTestType.py:239
+#: modules/FlaskModule/API/user/UserQueryTestType.py:254
+#: modules/FlaskModule/API/user/UserQueryTestType.py:364
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:256
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:299
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:237
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:280
+#: modules/FlaskModule/API/user/UserQueryTests.py:158
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:364
+#: modules/FlaskModule/API/user/UserQueryUserGroups.py:193
+#: modules/FlaskModule/API/user/UserQueryUserGroups.py:208
+#: modules/FlaskModule/API/user/UserQueryUserGroups.py:236
+#: modules/FlaskModule/API/user/UserQueryUserGroups.py:265
+#: modules/FlaskModule/API/user/UserQueryUserGroups.py:315
+#: modules/FlaskModule/API/user/UserQueryUserPreferences.py:114
+#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:142
+#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:180
+#: modules/FlaskModule/API/user/UserQueryUsers.py:255
+#: modules/FlaskModule/API/user/UserQueryUsers.py:293
+#: modules/FlaskModule/API/user/UserQueryUsers.py:393
msgid "Database error"
msgstr ""
#: modules/FlaskModule/API/device/DeviceQueryParticipants.py:41
-#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:82
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:72
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:67
-#: modules/FlaskModule/API/service/ServiceQueryServices.py:76
-#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:62
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:122
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:109
-#: modules/FlaskModule/API/user/UserQueryDisconnect.py:86
-#: modules/FlaskModule/API/user/UserQueryProjects.py:116
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:84
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:123
-#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:67
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:112
-#: modules/FlaskModule/API/user/UserQueryServices.py:104
-#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:62
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:101
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:79
-#: modules/FlaskModule/API/user/UserQuerySessions.py:103
-#: modules/FlaskModule/API/user/UserQuerySites.py:97
-#: modules/FlaskModule/API/user/UserQueryTestType.py:112
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:99
-#: modules/FlaskModule/API/user/UserQueryUserPreferences.py:67
+#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:84
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:75
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:70
+#: modules/FlaskModule/API/service/ServiceQueryServices.py:79
+#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:64
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:132
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:110
+#: modules/FlaskModule/API/user/UserQueryDisconnect.py:87
+#: modules/FlaskModule/API/user/UserQueryProjects.py:118
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:86
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:125
+#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:69
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:114
+#: modules/FlaskModule/API/user/UserQueryServices.py:105
+#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:63
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:103
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:83
+#: modules/FlaskModule/API/user/UserQuerySessions.py:105
+#: modules/FlaskModule/API/user/UserQuerySites.py:99
+#: modules/FlaskModule/API/user/UserQueryTestType.py:127
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:101
+#: modules/FlaskModule/API/user/UserQueryUserPreferences.py:68
msgid "Invalid request"
msgstr ""
-#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:31
-#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:97
-#: modules/FlaskModule/API/device/DeviceQuerySessions.py:72
-#: modules/FlaskModule/API/device/DeviceQuerySessions.py:193
+#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:33
+#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:105
+#: modules/FlaskModule/API/device/DeviceQuerySessions.py:74
+#: modules/FlaskModule/API/device/DeviceQuerySessions.py:201
msgid "Forbidden for security reasons"
msgstr ""
-#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:47
-#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:53
-#: modules/FlaskModule/API/device/DeviceQueryStatus.py:48
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:270
-#: modules/FlaskModule/API/service/ServiceQueryDevices.py:73
-#: modules/FlaskModule/API/service/ServiceQueryDevices.py:87
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:76
-#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:104
-#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:119
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:49
-#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:46
-#: modules/FlaskModule/API/service/ServiceQuerySiteProjectAccessRoles.py:43
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:55
-#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:152
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:263
-#: modules/FlaskModule/API/service/ServiceQueryUsers.py:38
-#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:70
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:65
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:61
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:157
-#: modules/FlaskModule/API/user/UserQueryForms.py:85
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:76
-#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:81
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:53
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:63
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:60
-#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:46
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:62
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:59
-#: modules/FlaskModule/API/user/UserQuerySessions.py:59
-#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:80
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:62
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:60
+#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:52
+#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:58
+#: modules/FlaskModule/API/device/DeviceQueryStatus.py:50
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:279
+#: modules/FlaskModule/API/service/ServiceQueryDevices.py:83
+#: modules/FlaskModule/API/service/ServiceQueryDevices.py:100
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:79
+#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:118
+#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:136
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:52
+#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:48
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:64
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypes.py:216
+#: modules/FlaskModule/API/service/ServiceQuerySiteProjectAccessRoles.py:46
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:61
+#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:171
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:272
+#: modules/FlaskModule/API/service/ServiceQueryUsers.py:55
+#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:71
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:66
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:62
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:161
+#: modules/FlaskModule/API/user/UserQueryForms.py:98
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:78
+#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:82
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:55
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:65
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:62
+#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:47
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:64
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:58
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:61
+#: modules/FlaskModule/API/user/UserQuerySessions.py:61
+#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:81
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:64
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:62
msgid "Missing arguments"
msgstr ""
-#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:61
-#: modules/FlaskModule/API/device/DeviceQuerySessions.py:137
-#: modules/LoginModule/LoginModule.py:584
-#: modules/LoginModule/LoginModule.py:684
-#: modules/LoginModule/LoginModule.py:750
-#: modules/LoginModule/LoginModule.py:777
-#: modules/LoginModule/LoginModule.py:796
+#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:66
+#: modules/FlaskModule/API/device/DeviceQuerySessions.py:142
+#: modules/FlaskModule/API/participant/ParticipantQuerySessions.py:169
+#: modules/LoginModule/LoginModule.py:598
+#: modules/LoginModule/LoginModule.py:698
+#: modules/LoginModule/LoginModule.py:764
+#: modules/LoginModule/LoginModule.py:791
+#: modules/LoginModule/LoginModule.py:810
+#: modules/LoginModule/LoginModule.py:829
+#: modules/LoginModule/LoginModule.py:831
msgid "Unauthorized"
msgstr ""
-#: modules/FlaskModule/API/device/DeviceQuerySessions.py:87
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:136
-#: modules/FlaskModule/API/user/UserQuerySessions.py:120
+#: modules/FlaskModule/API/device/DeviceQuerySessions.py:92
+#: modules/FlaskModule/API/participant/ParticipantQuerySessions.py:120
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:149
+#: modules/FlaskModule/API/user/UserQuerySessions.py:124
msgid "Missing session"
msgstr ""
-#: modules/FlaskModule/API/device/DeviceQuerySessions.py:95
+#: modules/FlaskModule/API/device/DeviceQuerySessions.py:100
+#: modules/FlaskModule/API/participant/ParticipantQuerySessions.py:128
msgid "Missing id_session value"
msgstr ""
-#: modules/FlaskModule/API/device/DeviceQuerySessions.py:99
+#: modules/FlaskModule/API/device/DeviceQuerySessions.py:104
+#: modules/FlaskModule/API/participant/ParticipantQuerySessions.py:132
msgid "Missing id_session_type value"
msgstr ""
-#: modules/FlaskModule/API/device/DeviceQuerySessions.py:104
+#: modules/FlaskModule/API/device/DeviceQuerySessions.py:109
+#: modules/FlaskModule/API/participant/ParticipantQuerySessions.py:137
msgid "Missing session participants and/or users and/or devices"
msgstr ""
-#: modules/FlaskModule/API/device/DeviceQuerySessions.py:114
+#: modules/FlaskModule/API/device/DeviceQuerySessions.py:119
+#: modules/FlaskModule/API/participant/ParticipantQuerySessions.py:146
msgid "No access to session type"
msgstr ""
-#: modules/FlaskModule/API/device/DeviceQuerySessions.py:119
+#: modules/FlaskModule/API/device/DeviceQuerySessions.py:124
+#: modules/FlaskModule/API/participant/ParticipantQuerySessions.py:151
msgid "Missing argument 'session name'"
msgstr ""
-#: modules/FlaskModule/API/device/DeviceQuerySessions.py:121
+#: modules/FlaskModule/API/device/DeviceQuerySessions.py:126
+#: modules/FlaskModule/API/participant/ParticipantQuerySessions.py:153
msgid "Missing argument 'session_start_datetime'"
msgstr ""
-#: modules/FlaskModule/API/device/DeviceQuerySessions.py:170
-#: modules/FlaskModule/API/service/ServiceQueryAccess.py:130
+#: modules/FlaskModule/API/device/DeviceQuerySessions.py:175
+#: modules/FlaskModule/API/participant/ParticipantQuerySessions.py:201
+#: modules/FlaskModule/API/service/ServiceQueryAccess.py:132
msgid "Invalid participant uuid"
msgstr ""
-#: modules/FlaskModule/API/device/DeviceQueryStatus.py:67
+#: modules/FlaskModule/API/device/DeviceQueryStatus.py:69
msgid "Status update forbidden on offline device."
msgstr ""
-#: modules/FlaskModule/API/device/DeviceRegister.py:73
-#: modules/FlaskModule/API/device/DeviceRegister.py:99
+#: modules/FlaskModule/API/device/DeviceRegister.py:76
+#: modules/FlaskModule/API/device/DeviceRegister.py:105
msgid "Invalid registration key"
msgstr ""
-#: modules/FlaskModule/API/device/DeviceRegister.py:103
+#: modules/FlaskModule/API/device/DeviceRegister.py:109
msgid "Invalid content type"
msgstr ""
-#: modules/FlaskModule/API/device/DeviceRegister.py:137
+#: modules/FlaskModule/API/device/DeviceRegister.py:143
msgid "Invalid CSR signature"
msgstr ""
-#: modules/FlaskModule/API/participant/ParticipantLogin.py:92
+#: modules/FlaskModule/API/participant/ParticipantLogin.py:96
msgid "Participant already logged in."
msgstr ""
-#: modules/FlaskModule/API/participant/ParticipantLogin.py:126
+#: modules/FlaskModule/API/participant/ParticipantLogin.py:138
msgid "Missing current_participant"
msgstr ""
-#: modules/FlaskModule/API/participant/ParticipantLogout.py:41
+#: modules/FlaskModule/API/participant/ParticipantLogout.py:44
msgid "Participant logged out."
msgstr ""
-#: modules/FlaskModule/API/participant/ParticipantLogout.py:43
+#: modules/FlaskModule/API/participant/ParticipantLogout.py:46
msgid "Participant not logged in"
msgstr ""
-#: modules/FlaskModule/API/participant/ParticipantQueryParticipants.py:55
-#: modules/FlaskModule/API/participant/ParticipantQuerySessions.py:73
+#: modules/FlaskModule/API/participant/ParticipantQueryParticipants.py:58
msgid "Not implemented"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryAccess.py:57
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:61
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:57
-#: modules/FlaskModule/API/user/UserQueryAssets.py:60
-#: modules/FlaskModule/API/user/UserQueryTests.py:54
+#: modules/FlaskModule/API/service/ServiceQueryAccess.py:59
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:64
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:60
+#: modules/FlaskModule/API/user/UserQueryAssets.py:61
+#: modules/FlaskModule/API/user/UserQueryTests.py:56
msgid "No arguments specified"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryAccess.py:67
+#: modules/FlaskModule/API/service/ServiceQueryAccess.py:69
msgid "Missing at least one from argument for uuids"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryAccess.py:77
+#: modules/FlaskModule/API/service/ServiceQueryAccess.py:79
msgid "Invalid user uuid"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryAccess.py:126
+#: modules/FlaskModule/API/service/ServiceQueryAccess.py:128
msgid "Participant cannot be admin"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryAccess.py:163
+#: modules/FlaskModule/API/service/ServiceQueryAccess.py:165
msgid "Device cannot be admin"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryAccess.py:167
+#: modules/FlaskModule/API/service/ServiceQueryAccess.py:169
msgid "Invalid device uuid"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:64
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:90
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:60
-#: modules/FlaskModule/API/user/UserQueryAssets.py:63
-#: modules/FlaskModule/API/user/UserQueryAssets.py:91
-#: modules/FlaskModule/API/user/UserQueryTests.py:57
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:67
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:93
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:63
+#: modules/FlaskModule/API/user/UserQueryAssets.py:64
+#: modules/FlaskModule/API/user/UserQueryAssets.py:92
+#: modules/FlaskModule/API/user/UserQueryTests.py:59
msgid "Device access denied"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:68
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:64
-#: modules/FlaskModule/API/user/UserQueryAssets.py:67
-#: modules/FlaskModule/API/user/UserQueryTests.py:61
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:71
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:67
+#: modules/FlaskModule/API/user/UserQueryAssets.py:68
+#: modules/FlaskModule/API/user/UserQueryTests.py:63
msgid "Session access denied"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:72
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:86
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:68
-#: modules/FlaskModule/API/user/UserQueryAssets.py:71
-#: modules/FlaskModule/API/user/UserQueryAssets.py:87
-#: modules/FlaskModule/API/user/UserQueryTests.py:65
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:75
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:89
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:71
+#: modules/FlaskModule/API/user/UserQueryAssets.py:72
+#: modules/FlaskModule/API/user/UserQueryAssets.py:88
+#: modules/FlaskModule/API/user/UserQueryTests.py:67
msgid "Participant access denied"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:76
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:82
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:72
-#: modules/FlaskModule/API/user/UserQueryAssets.py:75
-#: modules/FlaskModule/API/user/UserQueryAssets.py:83
-#: modules/FlaskModule/API/user/UserQueryTests.py:69
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:79
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:85
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:75
+#: modules/FlaskModule/API/user/UserQueryAssets.py:76
+#: modules/FlaskModule/API/user/UserQueryAssets.py:84
+#: modules/FlaskModule/API/user/UserQueryTests.py:71
msgid "User access denied"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:97
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:80
-#: modules/FlaskModule/API/user/UserQueryAssets.py:100
-#: modules/FlaskModule/API/user/UserQueryTests.py:76
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:100
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:83
+#: modules/FlaskModule/API/user/UserQueryAssets.py:101
+#: modules/FlaskModule/API/user/UserQueryTests.py:78
msgid "Missing argument"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:163
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:169
msgid "Missing asset field"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:169
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:175
msgid "Missing id_asset field"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:173
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:141
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:179
#: modules/FlaskModule/API/service/ServiceQueryTests.py:147
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:153
msgid "Unknown session"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:176
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:182
msgid "Invalid asset type"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:179
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:185
msgid "Invalid asset name"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:189
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:195
msgid "Service can't create assets for that session"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:196
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:196
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:202
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:202
msgid "Invalid participant"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:204
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:204
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:210
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:210
msgid "Invalid user"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:212
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:212
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:218
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:218
msgid "Invalid device"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:273
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:282
msgid "Service can't delete assets for that session"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryDevices.py:93
+#: modules/FlaskModule/API/service/ServiceQueryDevices.py:106
msgid "Unknown device type"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryDevices.py:97
+#: modules/FlaskModule/API/service/ServiceQueryDevices.py:110
msgid "Unknown device subtype"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:84
-#: modules/FlaskModule/API/user/UserQueryDisconnect.py:88
+#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:86
+#: modules/FlaskModule/API/user/UserQueryDisconnect.py:89
msgid "Success"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:57
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:191
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:252
-#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:203
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:296
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:209
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:63
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:308
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:266
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:270
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:271
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:60
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:200
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:267
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:264
+#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:208
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:301
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:215
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:64
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:314
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:272
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:276
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:238
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:277
msgid "Not found"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:95
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:101
msgid "Missing participant_group"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:102
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:108
msgid "Missing id_participant_group"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:107
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:93
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:180
-#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:198
-#: modules/FlaskModule/API/user/UserQueryProjects.py:138
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:179
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:113
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:99
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:275
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:183
+#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:201
+#: modules/FlaskModule/API/user/UserQueryProjects.py:142
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:183
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:278
msgid "Missing id_project"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:114
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:120
msgid "Missing group name"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:184
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:193
msgid "The id_participant_group given was not found"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:190
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:199
msgid "Deletion impossible: Participant group still has participant(s)"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:125
+#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:142
msgid "Unknown project"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:128
+#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:145
msgid "Invalid participant name"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:131
+#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:148
msgid "Invalid participant email"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:155
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:312
+#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:172
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:315
msgid "Can't insert participant: participant's project is disabled or invalid."
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:165
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:285
+#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:182
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:288
msgid "Can't update participant: participant's project is disabled."
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:48
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:51
msgid "Missing parameter"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:87
-#: modules/FlaskModule/API/user/UserQueryProjects.py:132
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:93
+#: modules/FlaskModule/API/user/UserQueryProjects.py:136
msgid "Missing project"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:96
-#: modules/FlaskModule/API/user/UserQueryProjects.py:140
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:102
+#: modules/FlaskModule/API/user/UserQueryProjects.py:144
msgid "Missing id_site arguments"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:169
-#: modules/FlaskModule/API/user/UserQueryProjects.py:275
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:178
+#: modules/FlaskModule/API/user/UserQueryProjects.py:281
msgid ""
"Can't delete project: please delete all participants with sessions before"
" deleting."
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryRoles.py:52
+#: modules/FlaskModule/API/service/ServiceQueryRoles.py:58
msgid "Missing service_role field"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryRoles.py:58
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:154
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:172
-#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:85
+#: modules/FlaskModule/API/service/ServiceQueryRoles.py:64
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:160
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:176
+#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:89
msgid "Missing id_service_role"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryRoles.py:81
-#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:119
-#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:106
+#: modules/FlaskModule/API/service/ServiceQueryRoles.py:87
+#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:135
+#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:110
msgid "Missing fields"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:87
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:104
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:93
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:108
msgid "Missing service_access"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:91
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:108
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:97
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:112
msgid "Missing id_service_access"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:95
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:112
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:101
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:116
msgid "Can't combine id_user_group, id_participant_group and id_device in request"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:114
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:132
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:120
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:136
msgid "Bad id_service_role"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:158
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:176
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:192
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:164
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:180
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:195
msgid "Missing at least one id field"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryServices.py:38
+#: modules/FlaskModule/API/service/ServiceQueryServices.py:41
msgid "Missing service key, id or uuid"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:76
-#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:77
+#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:81
+#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:80
msgid "Missing session_event field"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:82
-#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:83
+#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:87
+#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:86
msgid "Missing id_session or id_session_event fields"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:95
-msgid "Missing arguments: at least one id is required"
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:127
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:202
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:133
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:113
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:125
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:107
+msgid "Missing id_session_type"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:142
-#: modules/FlaskModule/API/user/UserQuerySessions.py:126
-msgid "Missing id_session"
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:129
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:126
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:146
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:148
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:135
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:133
+msgid "Missing projects"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:157
-msgid "Service doesn't have access to at least one participant of that session."
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:141
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:138
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:145
+msgid "Access denied to at least one project"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:164
-msgid "Service doesn't have access to at least one user of that session."
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:157
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:188
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:281
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:289
+msgid ""
+"Can't delete session type from project: please delete all sessions of "
+"that type in the project before deleting."
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:171
-msgid "Service doesn't have access to at least one device of that session."
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:164
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:161
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:165
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:168
+msgid "Missing project ID"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:189
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:129
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:121
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:101
-msgid "Missing id_session_type"
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:166
+msgid "Missing sessions_types"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:115
-#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:93
-#: modules/FlaskModule/API/user/UserQueryTestType.py:134
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:127
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:119
-msgid "Missing id_test_type"
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:197
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:194
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:200
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:181
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:201
+msgid "Unknown format"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:117
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:143
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:144
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:131
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:129
-msgid "Missing projects"
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:202
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:199
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:198
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:211
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:205
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:186
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:204
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:206
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:194
+msgid "Badly formatted request"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:129
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:141
-msgid "Access denied to at least one project"
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypes.py:128
+msgid "Service doesn't have access to all listed sites"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:145
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:176
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:266
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:157
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:188
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:285
-msgid ""
-"Can't delete test type from project: please delete all tests of that type"
-" in the project before deleting."
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypes.py:134
+msgid "Service doesn't have access to all listed projects"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:152
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:161
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:164
-msgid "Missing project ID"
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypes.py:140
+msgid "Service doesn't have access to all listed participants"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:154
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:166
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:152
-msgid "Missing test types"
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypes.py:154
+msgid "Service doesn't have access to this session type"
+msgstr ""
+
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypes.py:158
+msgid "Session type not found"
+msgstr ""
+
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypes.py:218
+msgid "Invalid arguments"
msgstr ""
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypes.py:220
+msgid "Invalid schema"
+msgstr ""
+
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypes.py:223
+msgid "Unsupported Media Type"
+msgstr ""
+
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypes.py:225
+msgid "Bad Request"
+msgstr ""
+
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypes.py:227
+msgid "Integrity error"
+msgstr ""
+
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:105
+msgid "Missing arguments: at least one id is required"
+msgstr ""
+
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:155
+#: modules/FlaskModule/API/user/UserQuerySessions.py:130
+msgid "Missing id_session"
+msgstr ""
+
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:170
+msgid "Service doesn't have access to at least one participant of that session."
+msgstr ""
+
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:177
+msgid "Service doesn't have access to at least one user of that session."
+msgstr ""
+
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:184
+msgid "Service doesn't have access to at least one device of that session."
+msgstr ""
+
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:124
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:273
+#: modules/FlaskModule/API/user/UserQueryTestType.py:151
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:131
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:123
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:276
+msgid "Missing id_test_type"
+msgstr ""
+
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:154
#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:185
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:196
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:197
-msgid "Unknown format"
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:278
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:161
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:192
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:291
+msgid ""
+"Can't delete test type from project: please delete all tests of that type"
+" in the project before deleting."
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:190
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:195
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:207
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:201
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:200
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:202
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:190
-msgid "Badly formatted request"
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:163
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:170
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:156
+msgid "Missing test types"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:87
-#: modules/FlaskModule/API/user/UserQueryTestType.py:128
+#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:98
+#: modules/FlaskModule/API/user/UserQueryTestType.py:145
msgid "Missing test_type"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:155
+#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:104
+msgid "Missing id_test_type or test_type_uuid"
+msgstr ""
+
+#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:174
msgid "Test type not related to this service. Can't delete."
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:131
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:137
msgid "Missing test field"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:137
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:143
msgid "Missing id_test field"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:154
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:160
msgid "Missing id_test_type field"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:159
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:165
msgid "Invalid test type"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:189
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:195
msgid "Service can't create tests for that session"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:266
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:275
msgid "Service can't delete tests for that session"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:81
-#: modules/FlaskModule/API/user/UserQueryUserGroups.py:132
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:277
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:280
+msgid "Missing test_invitation_expiration_date"
+msgstr ""
+
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:279
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:282
+msgid "Do not set test_invitation_key"
+msgstr ""
+
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:283
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:286
+msgid "You must specify one of id_user, id_participant or id_device"
+msgstr ""
+
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:292
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:295
+msgid "You can only update test_invitation_count"
+msgstr ""
+
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:320
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:322
+msgid "Required parameter is missing"
+msgstr ""
+
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:329
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:331
+msgid "Invalid JSON structure"
+msgstr ""
+
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:331
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:333
+msgid "Invalid JSON schema"
+msgstr ""
+
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:86
+#: modules/FlaskModule/API/user/UserQueryUserGroups.py:135
msgid "Missing user_group"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:88
-#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:196
-#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:179
-#: modules/FlaskModule/API/user/UserQueryUserGroups.py:139
-#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:102
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:93
+#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:199
+#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:182
+#: modules/FlaskModule/API/user/UserQueryUserGroups.py:142
+#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:106
msgid "Missing id_user_group"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:100
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:105
msgid "Missing service role name or id_service_role"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:113
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:118
msgid "Can't set access to service other than self"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:119
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:124
msgid "No access for at a least one project in the list"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:125
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:130
msgid "No access for at a least one site in the list"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:137
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:142
msgid "Bad role name for service"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:155
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:160
msgid "A new usergroup must have at least one service access"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:217
-#: modules/FlaskModule/API/user/UserQueryUserGroups.py:302
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:225
+#: modules/FlaskModule/API/user/UserQueryUserGroups.py:307
msgid ""
"Can't delete user group: please delete all users part of that user group "
"before deleting."
msgstr ""
-#: modules/FlaskModule/API/service/ServiceSessionManager.py:116
-#: modules/FlaskModule/API/user/UserSessionManager.py:107
+#: modules/FlaskModule/API/service/ServiceQueryUsers.py:42
+#: modules/FlaskModule/API/service/ServiceQueryUsers.py:50
+msgid "Service doesn't have permission to access the requested data"
+msgstr ""
+
+#: modules/FlaskModule/API/service/ServiceSessionManager.py:118
+#: modules/FlaskModule/API/user/UserSessionManager.py:108
msgid "Missing action"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceSessionManager.py:131
-#: modules/FlaskModule/API/user/UserSessionManager.py:121
-#: modules/FlaskModule/API/user/UserSessionManager.py:134
+#: modules/FlaskModule/API/service/ServiceSessionManager.py:133
+#: modules/FlaskModule/API/user/UserSessionManager.py:122
+#: modules/FlaskModule/API/user/UserSessionManager.py:135
msgid "Invalid session"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceSessionManager.py:139
+#: modules/FlaskModule/API/service/ServiceSessionManager.py:141
msgid "Service doesn't have access to that session"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceSessionManager.py:160
-#: modules/FlaskModule/API/user/UserSessionManager.py:155
+#: modules/FlaskModule/API/service/ServiceSessionManager.py:162
+#: modules/FlaskModule/API/user/UserSessionManager.py:156
msgid "Missing required id_session_type for new sessions"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceSessionManager.py:166
+#: modules/FlaskModule/API/service/ServiceSessionManager.py:168
msgid "Invalid session type"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceSessionManager.py:201
-#: modules/FlaskModule/API/user/UserSessionManager.py:192
+#: modules/FlaskModule/API/service/ServiceSessionManager.py:203
+#: modules/FlaskModule/API/user/UserSessionManager.py:193
msgid "Service not found"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceSessionManager.py:209
-#: modules/FlaskModule/API/user/UserSessionManager.py:197
+#: modules/FlaskModule/API/service/ServiceSessionManager.py:211
+#: modules/FlaskModule/API/user/UserSessionManager.py:198
msgid "Not implemented yet"
msgstr ""
-#: modules/FlaskModule/API/service/ServiceSessionManager.py:224
-#: modules/FlaskModule/API/user/UserSessionManager.py:213
+#: modules/FlaskModule/API/service/ServiceSessionManager.py:226
+#: modules/FlaskModule/API/user/UserSessionManager.py:214
msgid "No answer from service."
msgstr ""
-#: modules/FlaskModule/API/user/UserLogin.py:82
+#: modules/FlaskModule/API/user/UserLogin.py:41
+msgid "Password change required for this user."
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserLogin.py:52
+msgid "2FA required for this user."
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserLogin.py:56
+msgid "2FA enabled but OTP not set for this user.Please setup 2FA."
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserLogin.py:79
+#: modules/FlaskModule/API/user/UserLogin2FA.py:101
+#: modules/FlaskModule/API/user/UserLoginBase.py:154
+#: modules/FlaskModule/API/user/UserLoginSetup2FA.py:85
+#: modules/FlaskModule/API/user/UserLoginSetup2FA.py:155
+msgid "Client major version too old, not accepting login"
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserLogin2FA.py:49
+#: modules/FlaskModule/API/user/UserLoginSetup2FA.py:49
+#: modules/FlaskModule/API/user/UserLoginSetup2FA.py:119
+msgid "User does not have 2FA enabled"
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserLogin2FA.py:54
+msgid "User does not have 2FA OTP enabled or secret set"
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserLogin2FA.py:74
+#: modules/FlaskModule/API/user/UserLoginSetup2FA.py:133
+msgid "Invalid OTP code"
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserLoginBase.py:90
+msgid "User already logged in :"
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserLoginBase.py:92
msgid "User already logged in."
msgstr ""
-#: modules/FlaskModule/API/user/UserLogin.py:116
-#: modules/FlaskModule/API/user/UserLogin.py:134
+#: modules/FlaskModule/API/user/UserLoginBase.py:101
+msgid "Too many 2FA attempts. Please wait and try again."
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserLoginBase.py:134
+msgid "Client major version mismatch"
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserLoginBase.py:151
msgid "Client version mismatch"
msgstr ""
-#: modules/FlaskModule/API/user/UserLogin.py:136
-msgid "Client major version too old, not accepting login"
+#: modules/FlaskModule/API/user/UserLoginBase.py:171
+msgid "Unknown client name :"
msgstr ""
-#: modules/FlaskModule/API/user/UserLogin.py:143
-msgid "Invalid client version handler"
+#: modules/FlaskModule/API/user/UserLoginChangePassword.py:34
+#: modules/FlaskModule/Views/LoginChangePasswordView.py:48
+msgid "New password and confirm password do not match"
msgstr ""
-#: modules/FlaskModule/API/user/UserLogout.py:34
-msgid "User logged out."
+#: modules/FlaskModule/API/user/UserLoginChangePassword.py:37
+msgid "User not required to change password"
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserLoginChangePassword.py:47
+#: modules/FlaskModule/API/user/UserQueryUsers.py:260
+msgid "New password same as old password"
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserLoginSetup2FA.py:53
+#: modules/FlaskModule/API/user/UserLoginSetup2FA.py:123
+msgid "User already has 2FA OTP secret set"
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserLoginSetup2FA.py:146
+msgid "2FA enabled for this user."
msgstr ""
#: modules/FlaskModule/API/user/UserLogout.py:36
+msgid "User logged out."
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserLogout.py:38
msgid "User not logged in"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryAssets.py:79
+#: modules/FlaskModule/API/user/UserQueryAssets.py:80
msgid "Service access denied"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryAssets.py:174
+#: modules/FlaskModule/API/user/UserQueryAssets.py:177
msgid ""
"Asset information update and creation must be done directly into a "
"service (such as Filetransfer service)"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryAssets.py:182
+#: modules/FlaskModule/API/user/UserQueryAssets.py:187
msgid ""
"Asset information deletion must be done directly into a service (such as "
"Filetransfer service)"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:128
-#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:207
-msgid "User is not admin of the participant's project"
+#: modules/FlaskModule/API/user/UserQueryAssetsArchive.py:88
+msgid "Only one of the ID parameter is supported at once"
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserQueryAssetsArchive.py:151
+msgid "Missing required parameter"
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserQueryAssetsArchive.py:188
+msgid "Unable to create archive information from FileTransferService"
msgstr ""
#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:131
-#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:210
+#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:212
+msgid "User is not admin of the participant's project"
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:134
+#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:215
msgid "Access denied to device"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:149
+#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:152
msgid "Device not assigned to project or participant"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:141
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:127
-#: modules/FlaskModule/API/user/UserQueryDevices.py:259
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:144
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:130
+#: modules/FlaskModule/API/user/UserQueryDevices.py:262
msgid "Missing id_device"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:157
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:165
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:188
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:232
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:188
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:243
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:135
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:168
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:273
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:127
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:165
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:133
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:171
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:275
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:125
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:157
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:160
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:168
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:191
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:235
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:192
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:247
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:139
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:172
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:279
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:119
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:153
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:241
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:244
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:131
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:169
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:137
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:175
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:281
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:129
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:161
msgid "Access denied"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:172
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:208
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:321
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:175
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:211
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:326
msgid ""
"Can't delete device from project. Please remove all participants "
"associated with the device or all sessions in the project referring to "
"the device before deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:182
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:157
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:185
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:160
msgid "Missing devices"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:238
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:241
msgid ""
"At least one device is not part of the allowed device for that project "
"site"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:314
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:319
msgid ""
"Can't delete device from project: please remove all participants with "
"device before deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:317
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:322
msgid ""
"Can't delete device from project: please remove all sessions in this "
"project referring to that device before deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:129
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:136
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:123
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:121
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:132
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:140
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:127
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:125
msgid "Missing sites"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:148
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:175
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:151
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:178
msgid ""
"Can't delete device from site. Please remove all participants associated "
"with the device or all sessions in the site referring to the device "
"before deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:155
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:164
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:158
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:150
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:158
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:168
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:162
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:154
msgid "Missing site ID"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:252
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:263
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:252
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:257
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:269
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:258
msgid "Bad parameter"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:50
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:51
msgid "Too Many IDs"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:58
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:59
msgid "No access to device subtype"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:64
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:65
msgid "No access to device type"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:102
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:105
msgid "Missing device_subtype"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:109
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:112
msgid "Missing id_device_subtype"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:121
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:124
msgid "Invalid device subtype"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:169
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:174
msgid "Device subtype not found"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:179
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:184
msgid ""
"Can't delete device subtype: please delete all devices of that subtype "
"before deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:189
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:194
msgid "Device subtype successfully deleted"
msgstr ""
@@ -1107,678 +1386,731 @@ msgid "Unexisting ID/Forbidden access"
msgstr ""
#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:79
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:400
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:182
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:405
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:185
msgid "Database Error"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:95
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:97
msgid "Missing device type"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:102
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:104
msgid "Missing id_device_type"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:113
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:115
msgid "Invalid device type"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:160
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:164
msgid "Tried to delete with 2 parameters"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:168
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:171
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:172
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:175
msgid "Device type not found"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:181
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:185
msgid ""
"Can't delete device type: please delete all associated devices before "
"deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:193
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:197
msgid "Device type successfully deleted"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDevices.py:130
+#: modules/FlaskModule/API/user/UserQueryDevices.py:131
msgid "Too many IDs"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDevices.py:253
+#: modules/FlaskModule/API/user/UserQueryDevices.py:256
msgid "Missing device"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDevices.py:275
-#: modules/FlaskModule/API/user/UserQueryUserGroups.py:177
+#: modules/FlaskModule/API/user/UserQueryDevices.py:278
+#: modules/FlaskModule/API/user/UserQueryUserGroups.py:180
msgid "No site admin access for at a least one project in the list"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDevices.py:287
-#: modules/FlaskModule/API/user/UserQueryUserGroups.py:158
+#: modules/FlaskModule/API/user/UserQueryDevices.py:290
+#: modules/FlaskModule/API/user/UserQueryUserGroups.py:161
msgid "No site admin access for at a least one site in the list"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDevices.py:412
-#: modules/FlaskModule/API/user/UserQueryUsers.py:343
+#: modules/FlaskModule/API/user/UserQueryDevices.py:417
+#: modules/FlaskModule/API/user/UserQueryUsers.py:351
msgid "Invalid id"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDevices.py:436
+#: modules/FlaskModule/API/user/UserQueryDevices.py:441
msgid ""
"Can't delete device: please delete all participants association before "
"deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDevices.py:439
+#: modules/FlaskModule/API/user/UserQueryDevices.py:444
msgid ""
"Can't delete device: please remove all sessions referring to that device "
"before deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDevices.py:442
+#: modules/FlaskModule/API/user/UserQueryDevices.py:447
msgid ""
"Can't delete device: please remove all sessions created by that device "
"before deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDevices.py:445
+#: modules/FlaskModule/API/user/UserQueryDevices.py:450
msgid ""
"Can't delete device: please delete all assets created by that device "
"before deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDevices.py:448
+#: modules/FlaskModule/API/user/UserQueryDevices.py:453
msgid ""
"Can't delete device: please delete all tests created by that device "
"before deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDevices.py:451
+#: modules/FlaskModule/API/user/UserQueryDevices.py:456
msgid ""
"Can't delete device: please remove all related sessions, assets and tests"
" before deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDevices.py:469
+#: modules/FlaskModule/API/user/UserQueryDevices.py:474
msgid "Device successfully deleted"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryDisconnect.py:49
-#: modules/FlaskModule/API/user/UserQueryDisconnect.py:57
+#: modules/FlaskModule/API/user/UserQueryDisconnect.py:50
+#: modules/FlaskModule/API/user/UserQueryDisconnect.py:58
msgid "Use Logout instead to disconnect current user"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryForms.py:82
+#: modules/FlaskModule/API/user/UserQueryForms.py:95
msgid "Missing type"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryForms.py:137
+#: modules/FlaskModule/API/user/UserQueryForms.py:150
msgid "Missing session type id"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryForms.py:148
+#: modules/FlaskModule/API/user/UserQueryForms.py:161
msgid "No reply from service while querying session type config"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryForms.py:182
+#: modules/FlaskModule/API/user/UserQueryForms.py:195
msgid "Invalid service specified"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryForms.py:195
+#: modules/FlaskModule/API/user/UserQueryForms.py:208
msgid "Unknown form type: "
msgstr ""
#: modules/FlaskModule/API/user/UserQueryOnlineDevices.py:60
-#: modules/FlaskModule/API/user/UserQueryOnlineParticipants.py:69
-#: modules/FlaskModule/API/user/UserQueryOnlineUsers.py:57
+#: modules/FlaskModule/API/user/UserQueryOnlineParticipants.py:70
+#: modules/FlaskModule/API/user/UserQueryOnlineUsers.py:59
msgid "Internal server error when making RPC call."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:93
+#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:96
msgid "Missing group"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:102
+#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:105
msgid "Missing id_participant_group or id_project"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:170
+#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:175
msgid ""
"Can't delete participant group: please delete all sessions from all "
"participants before deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:220
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:223
msgid "Missing participant"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:226
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:229
msgid "Missing id_participant"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:230
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:233
msgid "Missing id_project or id_participant_group"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:245
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:248
msgid "No admin access to project"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:253
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:256
msgid "No admin access to group"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:262
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:265
msgid "Participant group not found."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:266
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:269
msgid "Mismatch between id_project and group's project"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:383
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:388
msgid "Can't delete participant: please remove all related sessions beforehand."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:385
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:390
msgid ""
"Can't delete participant: please remove all sessions created by this "
"participant beforehand."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:388
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:393
msgid "Can't delete participant: please remove all related assets beforehand."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:390
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:395
msgid "Can't delete participant: please remove all related tests beforehand."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:392
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:397
msgid ""
"Can't delete participant: please remove all related sessions, assets and "
"tests before deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:200
-#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:183
+#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:203
+#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:186
msgid "Missing role name or id"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:231
+#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:234
msgid "Invalid role name or id for that project"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:270
+#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:275
msgid "No project access to delete."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryProjects.py:169
+#: modules/FlaskModule/API/user/UserQueryProjects.py:173
msgid "No access to a session type for at least one of it"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryProjects.py:181
+#: modules/FlaskModule/API/user/UserQueryProjects.py:185
msgid "At least one session type is not associated to the project site"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryServiceAccessToken.py:35
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:189
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:142
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:134
-#: modules/FlaskModule/API/user/UserQueryServices.py:128
+#: modules/FlaskModule/API/user/UserQueryServiceAccessToken.py:37
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:192
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:146
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:138
+#: modules/FlaskModule/API/user/UserQueryServices.py:131
msgid "Missing id_service"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryServiceAccessToken.py:38
+#: modules/FlaskModule/API/user/UserQueryServiceAccessToken.py:40
msgid "No access to specified service"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:72
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:146
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:73
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:149
msgid "Can't combine id_user, id_participant and id_device in request"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:121
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:124
msgid "Missing service_config"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:175
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:198
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:178
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:201
msgid "Invalid config format provided"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:170
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:211
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:339
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:174
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:215
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:345
msgid ""
"Can't delete service-project: please remove all related sessions, assets "
"and tests before deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:181
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:166
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:185
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:170
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:115
msgid "Missing services"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:249
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:253
msgid ""
"At least one service is not part of the allowed service for that project "
"site"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:311
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:317
msgid "Operation not completed"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:332
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:338
msgid ""
"Can't delete service-project: please remove all sessions involving a "
"session type using this project beforehand."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:335
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:341
msgid "Can't delete service-project: please remove all related assets beforehand."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:337
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:343
msgid "Can't delete service-project: please remove all related tests beforehand."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:155
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:185
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:278
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:159
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:189
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:284
msgid ""
"Can't delete service from site: please delete all sessions, assets and "
"tests related to that service beforehand."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryServices.py:150
+#: modules/FlaskModule/API/user/UserQueryServices.py:153
msgid "OpenTera service can't be updated using this API"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryServices.py:164
-#: modules/FlaskModule/API/user/UserQueryServices.py:184
+#: modules/FlaskModule/API/user/UserQueryServices.py:167
+#: modules/FlaskModule/API/user/UserQueryServices.py:187
msgid "Invalid config json schema"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryServices.py:168
+#: modules/FlaskModule/API/user/UserQueryServices.py:171
msgid "Missing service_key"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryServices.py:236
+#: modules/FlaskModule/API/user/UserQueryServices.py:241
msgid "Invalid service"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryServices.py:251
+#: modules/FlaskModule/API/user/UserQueryServices.py:256
msgid ""
"Can't delete service: please delete all sessions, assets and tests "
"related to that service beforehand."
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:154
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:186
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:158
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:190
msgid ""
"Can't delete session type from project: please delete all sessions using "
"that type in that project before deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:163
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:160
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:167
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:148
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:164
msgid "Missing session types"
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:212
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:216
msgid "At least one session type is not associated to the site of its project"
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:283
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:138
msgid ""
-"Can't delete session type from project: please delete all sessions of "
-"that type in the project before deleting."
+"Can't remove associated service from session type: please delete all "
+"sessions using that type before deleting."
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:146
+msgid "Missing service ID"
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:171
+msgid ""
+"Can't remove session type association from service: please delete all "
+"sessions using that type before deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:150
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:185
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:278
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:254
+msgid ""
+"Can't remove associated service from session type: please delete all "
+"sessions of that type before deleting."
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:154
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:189
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:284
msgid ""
"Can't delete session type from site: please delete all sessions of that "
"type in the site before deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:95
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:101
msgid "Missing session_type"
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:111
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:117
msgid "Missing site(s) to associate that session type to"
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:136
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:142
msgid "Missing id_service for session type of type service"
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:151
-#: modules/FlaskModule/API/user/UserQueryTestType.py:183
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:157
+#: modules/FlaskModule/API/user/UserQueryTestType.py:200
msgid "No site admin access for at least one site in the list"
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:161
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:167
msgid "At least one site isn't associated with the service of that session type"
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:178
-#: modules/FlaskModule/API/user/UserQueryTestType.py:206
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:184
+#: modules/FlaskModule/API/user/UserQueryTestType.py:223
msgid "No project admin access for at a least one project in the list"
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:264
-#: modules/FlaskModule/API/user/UserQueryTestType.py:286
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:201
+msgid "No service admin access for at a least one service in the list"
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:287
+#: modules/FlaskModule/API/user/UserQueryTestType.py:303
msgid "Session type not associated to project site"
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:292
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:332
msgid "Session type has a a service not associated to its site"
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:316
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:359
msgid "Cannot delete because you are not admin in all projects."
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:321
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:364
msgid "Unable to delete - not admin in at least one project"
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:334
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:377
msgid ""
"Can't delete session type: please delete all sessions with that type "
"before deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySessions.py:135
+#: modules/FlaskModule/API/user/UserQuerySessions.py:139
msgid "Missing session participants and users"
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySessions.py:141
+#: modules/FlaskModule/API/user/UserQuerySessions.py:145
msgid "No access to session."
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySessions.py:156
-#: modules/FlaskModule/API/user/UserQuerySessions.py:247
+#: modules/FlaskModule/API/user/UserQuerySessions.py:160
+#: modules/FlaskModule/API/user/UserQuerySessions.py:253
msgid "User doesn't have access to at least one participant of that session."
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySessions.py:161
-#: modules/FlaskModule/API/user/UserQuerySessions.py:252
+#: modules/FlaskModule/API/user/UserQuerySessions.py:165
+#: modules/FlaskModule/API/user/UserQuerySessions.py:258
msgid "User doesn't have access to at least one user of that session."
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySessions.py:166
-#: modules/FlaskModule/API/user/UserQuerySessions.py:257
+#: modules/FlaskModule/API/user/UserQuerySessions.py:170
+#: modules/FlaskModule/API/user/UserQuerySessions.py:263
msgid "User doesn't have access to at least one device of that session."
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySessions.py:261
+#: modules/FlaskModule/API/user/UserQuerySessions.py:267
msgid "Session is in progress: can't delete that session."
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:181
+#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:184
msgid "Missing id_site"
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:230
+#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:233
msgid "Invalid role name or id for that site"
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:271
+#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:276
msgid "No site access to delete"
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySites.py:112
+#: modules/FlaskModule/API/user/UserQuerySites.py:116
msgid "Missing site"
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySites.py:119
+#: modules/FlaskModule/API/user/UserQuerySites.py:123
msgid "Missing id_site field"
msgstr ""
-#: modules/FlaskModule/API/user/UserQuerySites.py:187
+#: modules/FlaskModule/API/user/UserQuerySites.py:194
msgid ""
"Can't delete site: please delete all participants with sessions before "
"deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryStats.py:94
+#: modules/FlaskModule/API/user/UserQueryStats.py:96
msgid "Missing id argument"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryTestType.py:151
+#: modules/FlaskModule/API/user/UserQueryTestType.py:58
+#: modules/FlaskModule/API/user/UserQueryTestType.py:63
+#: modules/FlaskModule/API/user/UserQueryTestType.py:68
+msgid "Test type not found"
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserQueryTestType.py:168
msgid "Missing project(s) to associate that test type to"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryTestType.py:192
+#: modules/FlaskModule/API/user/UserQueryTestType.py:209
msgid "At least one site isn't associated with the service of that test type"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryTestType.py:306
+#: modules/FlaskModule/API/user/UserQueryTestType.py:323
msgid "Test type has a a service not associated to its site"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryTestType.py:328
+#: modules/FlaskModule/API/user/UserQueryTestType.py:347
msgid "Unable to delete - not admin in the related test type service"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryTestType.py:338
+#: modules/FlaskModule/API/user/UserQueryTestType.py:357
msgid ""
"Can't delete test type: please delete all tests of that type before "
"deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:213
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:217
msgid "At least one test type is not associated to the site of its project"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:143
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:176
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:266
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:147
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:180
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:272
msgid ""
"Can't delete test type from site: please delete all tests of that type in"
" the site before deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryTests.py:123
+#: modules/FlaskModule/API/user/UserQueryTests.py:127
msgid ""
"Test information update and creation must be done directly into a service"
" (such as Test service)"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUndelete.py:30
+#: modules/FlaskModule/API/user/UserQueryUndelete.py:32
msgid "No access to this API"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUndelete.py:46
+#: modules/FlaskModule/API/user/UserQueryUndelete.py:48
msgid "Item to undelete not found"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUndelete.py:49
+#: modules/FlaskModule/API/user/UserQueryUndelete.py:51
msgid "Item can't be undeleted"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUndelete.py:52
+#: modules/FlaskModule/API/user/UserQueryUndelete.py:54
msgid "Item isn't deleted"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUserGroups.py:142
+#: modules/FlaskModule/API/user/UserQueryUserGroups.py:145
msgid "Missing user group name"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUserPreferences.py:89
+#: modules/FlaskModule/API/user/UserQueryUserPreferences.py:92
msgid "Missing app tag"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:57
+#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:59
msgid "At least one id must be specified"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:89
+#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:93
msgid "Missing user user group"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:100
-#: modules/FlaskModule/API/user/UserQueryUsers.py:183
+#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:104
+#: modules/FlaskModule/API/user/UserQueryUsers.py:198
msgid "Missing id_user"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:106
+#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:110
msgid "No access to specified user"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:108
+#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:112
msgid "No access to specified user group"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:113
+#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:117
msgid "Super admins can't be associated to an user group"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:158
+#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:164
msgid "Can't delete specified relationship"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:161
+#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:167
msgid "No access to relationship's user"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:163
+#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:169
msgid "No access to relationship's user group"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUsers.py:176
+#: modules/FlaskModule/API/user/UserQueryUsers.py:57
+msgid "Password not long enough"
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserQueryUsers.py:191
msgid "Missing user"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUsers.py:198
+#: modules/FlaskModule/API/user/UserQueryUsers.py:213
msgid "No access for at a least one user group in the list"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUsers.py:229
+#: modules/FlaskModule/API/user/UserQueryUsers.py:244
msgid "Username can't be modified"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUsers.py:250
+#: modules/FlaskModule/API/user/UserQueryUsers.py:257
+#: modules/FlaskModule/API/user/UserQueryUsers.py:295
+msgid "Password not strong enough"
+msgstr ""
+
+#: modules/FlaskModule/API/user/UserQueryUsers.py:271
msgid "Missing required fields: "
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUsers.py:253
+#: modules/FlaskModule/API/user/UserQueryUsers.py:274
msgid "Invalid password"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUsers.py:257
+#: modules/FlaskModule/API/user/UserQueryUsers.py:278
msgid "Username unavailable."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUsers.py:336
+#: modules/FlaskModule/API/user/UserQueryUsers.py:344
msgid "Sorry, you can't delete yourself!"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUsers.py:366
+#: modules/FlaskModule/API/user/UserQueryUsers.py:374
msgid ""
"Can't delete user: please remove all sessions that this user is part of "
"before deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUsers.py:369
+#: modules/FlaskModule/API/user/UserQueryUsers.py:377
msgid ""
"Can't delete user: please remove all sessions created by this user before"
" deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUsers.py:372
+#: modules/FlaskModule/API/user/UserQueryUsers.py:380
msgid ""
"Can't delete user: please remove all tests created by this user before "
"deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUsers.py:375
+#: modules/FlaskModule/API/user/UserQueryUsers.py:383
msgid ""
"Can't delete user: please remove all assets created by this user before "
"deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryUsers.py:378
+#: modules/FlaskModule/API/user/UserQueryUsers.py:386
msgid ""
"Can't delete user: please delete all assets created by this user before "
"deleting."
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryVersions.py:40
+#: modules/FlaskModule/API/user/UserQueryVersions.py:42
msgid "No version information found"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryVersions.py:71
+#: modules/FlaskModule/API/user/UserQueryVersions.py:75
msgid "Wrong ClientVersions"
msgstr ""
-#: modules/FlaskModule/API/user/UserQueryVersions.py:77
+#: modules/FlaskModule/API/user/UserQueryVersions.py:81
msgid "Not authorized"
msgstr ""
-#: modules/FlaskModule/API/user/UserSessionManager.py:129
+#: modules/FlaskModule/API/user/UserSessionManager.py:130
msgid "User doesn't have access to that session"
msgstr ""
-#: modules/FlaskModule/API/user/UserSessionManager.py:160
+#: modules/FlaskModule/API/user/UserSessionManager.py:161
msgid "User doesn't have access to that service."
msgstr ""
-#: modules/FlaskModule/API/user/UserSessionManager.py:165
+#: modules/FlaskModule/API/user/UserSessionManager.py:166
msgid "Missing parameters"
msgstr ""
-#: modules/FlaskModule/API/user/UserSessionManager.py:168
+#: modules/FlaskModule/API/user/UserSessionManager.py:169
msgid "Missing reply code in parameters"
msgstr ""
-#: modules/FlaskModule/API/user/UserSessionManager.py:181
-#: modules/FlaskModule/API/user/UserSessionManager.py:184
+#: modules/FlaskModule/API/user/UserSessionManager.py:182
+#: modules/FlaskModule/API/user/UserSessionManager.py:185
msgid "Invalid reply code"
msgstr ""
-#: modules/LoginModule/LoginModule.py:619
-#: modules/LoginModule/LoginModule.py:652
+#: modules/FlaskModule/Views/LoginChangePasswordView.py:38
+msgid "Missing information"
+msgstr ""
+
+#: modules/FlaskModule/Views/LoginChangePasswordView.py:53
+msgid "New password must be different from current"
+msgstr ""
+
+#: modules/LoginModule/LoginModule.py:219
+msgid "Unauthorized - User must login first to change password"
+msgstr ""
+
+#: modules/LoginModule/LoginModule.py:222
+msgid "Unauthorized - 2FA is enabled, must login first and use token"
+msgstr ""
+
+#: modules/LoginModule/LoginModule.py:633
+#: modules/LoginModule/LoginModule.py:666
msgid "Disabled device"
msgstr ""
-#: modules/LoginModule/LoginModule.py:629
+#: modules/LoginModule/LoginModule.py:643
msgid "Invalid token"
msgstr ""
-#: modules/LoginModule/LoginModule.py:729
+#: modules/LoginModule/LoginModule.py:743
msgid "Invalid Token"
msgstr ""
-#: opentera/db/models/TeraSessionType.py:151
+#: opentera/db/models/TeraSessionType.py:152
#: opentera/forms/TeraSessionForm.py:105
msgid "Unknown"
msgstr ""
-#: opentera/db/models/TeraSessionType.py:153
+#: opentera/db/models/TeraSessionType.py:154
#: opentera/forms/TeraSessionTypeForm.py:35
#: opentera/forms/TeraTestTypeForm.py:24
msgid "Service"
msgstr ""
-#: opentera/db/models/TeraSessionType.py:155
+#: opentera/db/models/TeraSessionType.py:156
msgid "File Transfer"
msgstr ""
-#: opentera/db/models/TeraSessionType.py:157
+#: opentera/db/models/TeraSessionType.py:158
msgid "Data Collect"
msgstr ""
-#: opentera/db/models/TeraSessionType.py:159
+#: opentera/db/models/TeraSessionType.py:160
msgid "Protocol"
msgstr ""
@@ -1826,7 +2158,7 @@ msgid "Device Onlineable?"
msgstr ""
#: opentera/forms/TeraDeviceForm.py:44 opentera/forms/TeraParticipantForm.py:50
-#: opentera/forms/TeraUserForm.py:30
+#: opentera/forms/TeraUserForm.py:48
msgid "Last Connection"
msgstr ""
@@ -1838,8 +2170,8 @@ msgstr ""
#: opentera/forms/TeraProjectForm.py:18
#: opentera/forms/TeraServiceConfigForm.py:18
#: opentera/forms/TeraServiceForm.py:18 opentera/forms/TeraSessionForm.py:120
-#: opentera/forms/TeraSessionTypeForm.py:25 opentera/forms/TeraSiteForm.py:12
-#: opentera/forms/TeraUserForm.py:13 opentera/forms/TeraUserGroupForm.py:18
+#: opentera/forms/TeraSessionTypeForm.py:25 opentera/forms/TeraSiteForm.py:13
+#: opentera/forms/TeraUserForm.py:14 opentera/forms/TeraUserGroupForm.py:18
#: opentera/forms/TeraVersionsForm.py:18
msgid "Information"
msgstr ""
@@ -1857,7 +2189,7 @@ msgid "Device Configuration"
msgstr ""
#: opentera/forms/TeraDeviceForm.py:55 opentera/forms/TeraDeviceForm.py:57
-#: opentera/forms/TeraUserForm.py:28
+#: opentera/forms/TeraUserForm.py:50
msgid "Notes"
msgstr ""
@@ -1963,7 +2295,7 @@ msgstr ""
msgid "Role"
msgstr ""
-#: opentera/forms/TeraProjectForm.py:28 opentera/forms/TeraSiteForm.py:17
+#: opentera/forms/TeraProjectForm.py:28 opentera/forms/TeraSiteForm.py:18
msgid "Site Name"
msgstr ""
@@ -1981,7 +2313,7 @@ msgstr ""
msgid "Service Config ID"
msgstr ""
-#: opentera/forms/TeraServiceConfigForm.py:27 opentera/forms/TeraUserForm.py:17
+#: opentera/forms/TeraServiceConfigForm.py:27 opentera/forms/TeraUserForm.py:18
msgid "User ID"
msgstr ""
@@ -2001,43 +2333,49 @@ msgstr ""
msgid "Mirrored image"
msgstr ""
-#: opentera/forms/TeraServiceConfigForm.py:54
+#: opentera/forms/TeraServiceConfigForm.py:53
+msgid "Blurred background"
+msgstr ""
+
+#: opentera/forms/TeraServiceConfigForm.py:55
msgid "URL"
msgstr ""
-#: opentera/forms/TeraServiceConfigForm.py:58
+#: opentera/forms/TeraServiceConfigForm.py:59
msgid "Pan-Tilt-Zoom Camera"
msgstr ""
-#: opentera/forms/TeraServiceConfigForm.py:59
+#: opentera/forms/TeraServiceConfigForm.py:60
msgid "Control Type"
msgstr ""
-#: opentera/forms/TeraServiceConfigForm.py:60
+#: opentera/forms/TeraServiceConfigForm.py:61
msgid "Vivotek"
msgstr ""
-#: opentera/forms/TeraServiceConfigForm.py:65
+#: opentera/forms/TeraServiceConfigForm.py:66
msgid "Network Address"
msgstr ""
-#: opentera/forms/TeraServiceConfigForm.py:68
+#: opentera/forms/TeraServiceConfigForm.py:69
msgid "Port"
msgstr ""
-#: opentera/forms/TeraServiceConfigForm.py:71 opentera/forms/TeraUserForm.py:20
+#: opentera/forms/TeraServiceConfigForm.py:72 opentera/forms/TeraUserForm.py:21
+#: templates/login.html:125
msgid "Username"
msgstr ""
-#: opentera/forms/TeraServiceConfigForm.py:74 opentera/forms/TeraUserForm.py:26
+#: opentera/forms/TeraServiceConfigForm.py:75 opentera/forms/TeraUserForm.py:46
+#: templates/login.html:131
msgid "Password"
msgstr ""
-#: opentera/forms/TeraServiceConfigForm.py:78
+#: opentera/forms/TeraServiceConfigForm.py:79
msgid "Microphone"
msgstr ""
-#: opentera/forms/TeraServiceConfigForm.py:79
+#: opentera/forms/TeraServiceConfigForm.py:80
msgid "Secondary Camera"
msgstr ""
@@ -2090,10 +2428,18 @@ msgid "Service Enabled"
msgstr ""
#: opentera/forms/TeraServiceForm.py:43
-msgid "Service has editable config"
+msgid "Service Contains Assets"
+msgstr ""
+
+#: opentera/forms/TeraServiceForm.py:44
+msgid "Service Contains Tests"
msgstr ""
#: opentera/forms/TeraServiceForm.py:45
+msgid "Service has editable config"
+msgstr ""
+
+#: opentera/forms/TeraServiceForm.py:47
msgid "Service Default Configuration"
msgstr ""
@@ -2201,11 +2547,15 @@ msgstr ""
msgid "Session Type Configuration"
msgstr ""
-#: opentera/forms/TeraSiteForm.py:16
+#: opentera/forms/TeraSiteForm.py:17
msgid "Site ID"
msgstr ""
-#: opentera/forms/TeraSiteForm.py:18
+#: opentera/forms/TeraSiteForm.py:20
+msgid "Users Require 2FA"
+msgstr ""
+
+#: opentera/forms/TeraSiteForm.py:22
msgid "Site Role"
msgstr ""
@@ -2237,35 +2587,47 @@ msgstr ""
msgid "Expose Web editor"
msgstr ""
-#: opentera/forms/TeraUserForm.py:18
+#: opentera/forms/TeraUserForm.py:19
msgid "User UUID"
msgstr ""
-#: opentera/forms/TeraUserForm.py:19
+#: opentera/forms/TeraUserForm.py:20
msgid "User Full Name"
msgstr ""
-#: opentera/forms/TeraUserForm.py:21
+#: opentera/forms/TeraUserForm.py:22
msgid "User Enabled"
msgstr ""
-#: opentera/forms/TeraUserForm.py:22
+#: opentera/forms/TeraUserForm.py:26
+msgid "Force password change"
+msgstr ""
+
+#: opentera/forms/TeraUserForm.py:28
+msgid "2FA Enabled"
+msgstr ""
+
+#: opentera/forms/TeraUserForm.py:31
+msgid "2FA OTP Enabled"
+msgstr ""
+
+#: opentera/forms/TeraUserForm.py:42
msgid "First Name"
msgstr ""
-#: opentera/forms/TeraUserForm.py:23
+#: opentera/forms/TeraUserForm.py:43
msgid "Last Name"
msgstr ""
-#: opentera/forms/TeraUserForm.py:24
+#: opentera/forms/TeraUserForm.py:44
msgid "Email"
msgstr ""
-#: opentera/forms/TeraUserForm.py:27
+#: opentera/forms/TeraUserForm.py:47
msgid "User Is Super Administrator"
msgstr ""
-#: opentera/forms/TeraUserForm.py:29
+#: opentera/forms/TeraUserForm.py:51
msgid "Profile"
msgstr ""
@@ -2294,7 +2656,6 @@ msgid "OpenTeraServer patch version number"
msgstr ""
#: opentera/forms/TeraVersionsForm.py:43 templates/about.html:26
-#: templates/disabled_doc.html:26
msgid "Versions"
msgstr ""
@@ -2330,79 +2691,85 @@ msgstr ""
msgid "Join me!"
msgstr ""
-#: opentera/services/BaseWebRTCService.py:383
+#: opentera/services/BaseWebRTCService.py:382
msgid "Cannot create session event"
msgstr ""
-#: opentera/services/BaseWebRTCService.py:423
+#: opentera/services/BaseWebRTCService.py:422
msgid "Cannot create process"
msgstr ""
-#: opentera/services/BaseWebRTCService.py:437
+#: opentera/services/BaseWebRTCService.py:436
msgid "Cannot create session"
msgstr ""
-#: opentera/services/BaseWebRTCService.py:460
+#: opentera/services/BaseWebRTCService.py:459
msgid "Cannot create STOP session event"
msgstr ""
-#: opentera/services/BaseWebRTCService.py:517
+#: opentera/services/BaseWebRTCService.py:516
msgid "Error stopping session - check server logs"
msgstr ""
-#: opentera/services/BaseWebRTCService.py:519
+#: opentera/services/BaseWebRTCService.py:518
msgid "No matching session to stop"
msgstr ""
-#: opentera/services/BaseWebRTCService.py:543
-#: opentera/services/BaseWebRTCService.py:620
-#: opentera/services/BaseWebRTCService.py:707
+#: opentera/services/BaseWebRTCService.py:542
+#: opentera/services/BaseWebRTCService.py:619
+#: opentera/services/BaseWebRTCService.py:712
msgid "User"
msgstr ""
-#: opentera/services/BaseWebRTCService.py:545
+#: opentera/services/BaseWebRTCService.py:544
msgid "Error creating user invited session event"
msgstr ""
-#: opentera/services/BaseWebRTCService.py:557
-#: opentera/services/BaseWebRTCService.py:635
-#: opentera/services/BaseWebRTCService.py:722
+#: opentera/services/BaseWebRTCService.py:556
+#: opentera/services/BaseWebRTCService.py:634
+#: opentera/services/BaseWebRTCService.py:733
msgid "Participant"
msgstr ""
-#: opentera/services/BaseWebRTCService.py:559
+#: opentera/services/BaseWebRTCService.py:558
msgid "Error creating participant invited session event"
msgstr ""
-#: opentera/services/BaseWebRTCService.py:572
-#: opentera/services/BaseWebRTCService.py:651
-#: opentera/services/BaseWebRTCService.py:737
+#: opentera/services/BaseWebRTCService.py:571
+#: opentera/services/BaseWebRTCService.py:650
+#: opentera/services/BaseWebRTCService.py:755
msgid "Device"
msgstr ""
-#: opentera/services/BaseWebRTCService.py:574
+#: opentera/services/BaseWebRTCService.py:573
msgid "Error creating device invited session event"
msgstr ""
-#: opentera/services/BaseWebRTCService.py:590
+#: opentera/services/BaseWebRTCService.py:589
msgid "Error updating session"
msgstr ""
-#: opentera/services/BaseWebRTCService.py:622
+#: opentera/services/BaseWebRTCService.py:621
msgid "Error creating user left session event"
msgstr ""
-#: opentera/services/BaseWebRTCService.py:637
+#: opentera/services/BaseWebRTCService.py:636
msgid "Error creating participant left session event"
msgstr ""
-#: opentera/services/BaseWebRTCService.py:653
+#: opentera/services/BaseWebRTCService.py:652
msgid "Error creating device left session event"
msgstr ""
-#: opentera/services/BaseWebRTCService.py:710
+#: opentera/services/BaseWebRTCService.py:704
#: opentera/services/BaseWebRTCService.py:725
-#: opentera/services/BaseWebRTCService.py:740
+#: opentera/services/BaseWebRTCService.py:746
+msgid "Creator refused to join session - session ended"
+msgstr ""
+
+#: opentera/services/BaseWebRTCService.py:715
+#: opentera/services/BaseWebRTCService.py:736
+#: opentera/services/BaseWebRTCService.py:758
msgid "Cannot create refused session event"
msgstr ""
@@ -2414,22 +2781,119 @@ msgstr ""
msgid "Latest version: "
msgstr ""
-#: templates/about.html:63 templates/disabled_doc.html:35
+#: templates/about.html:64
msgid "License"
msgstr ""
-#: templates/about.html:77 templates/disabled_doc.html:53
+#: templates/about.html:78
msgid "Authors"
msgstr ""
-#: templates/about.html:87 templates/disabled_doc.html:63
+#: templates/about.html:88
msgid "Contributors"
msgstr ""
-#: templates/disabled_doc.html:48
+#: templates/disabled_doc.html:27
msgid "Documentation is disabled!"
msgstr ""
+#: templates/login.html:5
+msgid "OpenTera Login Page"
+msgstr ""
+
+#: templates/login.html:89
+msgid "Invalid username or password"
+msgstr ""
+
+#: templates/login.html:139
+msgid "Login"
+msgstr ""
+
+#: templates/login_change_password.html:5
+msgid "OpenTera - Change Password"
+msgstr ""
+
+#: templates/login_change_password.html:96
+msgid "Password successfully changed!"
+msgstr ""
+
+#: templates/login_change_password.html:97
+msgid "Redirecting to login screen..."
+msgstr ""
+
+#: templates/login_change_password.html:104
+msgid "Password change required"
+msgstr ""
+
+#: templates/login_change_password.html:110
+msgid "New Password"
+msgstr ""
+
+#: templates/login_change_password.html:115
+msgid "Confirm Password"
+msgstr ""
+
+#: templates/login_change_password.html:122
+msgid "Change Password"
+msgstr ""
+
+#: templates/login_setup_2fa.html:107
+msgid "You need to setup multi-factor authentication before continuing."
+msgstr ""
+
+#: templates/login_setup_2fa.html:121
+msgid "OTP configuration"
+msgstr ""
+
+#: templates/login_setup_2fa.html:127
+msgid "Scan the QR code with your authenticator app"
+msgstr ""
+
+#: templates/login_setup_2fa.html:128
+msgid "Enter the generated code:"
+msgstr ""
+
+#: templates/login_setup_2fa.html:134 templates/login_setup_2fa.html:151
+#: templates/login_validate_2fa.html:162
+msgid "Validate"
+msgstr ""
+
+#: templates/login_setup_2fa.html:144
+msgid "Use email for authentication"
+msgstr ""
+
+#: templates/login_setup_2fa.html:149
+msgid "No email address configured. Please contact your administrator."
+msgstr ""
+
+#: templates/login_validate_2fa.html:32
+msgid "Code validation timeout. Redirecting to login."
+msgstr ""
+
+#: templates/login_validate_2fa.html:117
+msgid "Too many attempts - returning to login"
+msgstr ""
+
+#: templates/login_validate_2fa.html:120
+msgid "Invalid code"
+msgstr ""
+
+#: templates/login_validate_2fa.html:120
+msgid "attempts left"
+msgstr ""
+
+#: templates/login_validate_2fa.html:144
+msgid "Multi Factor Authentication"
+msgstr ""
+
+#: templates/login_validate_2fa.html:151
+msgid "Authentication code"
+msgstr ""
+
+#: templates/login_validate_2fa.html:171
+msgid "Successfully authenticated"
+msgstr ""
+
#~ msgid "Can't delete participant: please delete all sessions before deleting."
#~ msgstr ""
@@ -2457,3 +2921,15 @@ msgstr ""
#~ msgid "Invalid client name :"
#~ msgstr ""
+#~ msgid "Invalid client version handler"
+#~ msgstr ""
+
+#~ msgid "2FA enabled but OTP not set for this user. Please setup 2FA."
+#~ msgstr ""
+
+#~ msgid "Invalid old password"
+#~ msgstr ""
+
+#~ msgid "2FA Email Enabled"
+#~ msgstr ""
+
diff --git a/teraserver/python/translations/fr/LC_MESSAGES/messages.po b/teraserver/python/translations/fr/LC_MESSAGES/messages.po
index 4f0795223..00b5c3bab 100644
--- a/teraserver/python/translations/fr/LC_MESSAGES/messages.po
+++ b/teraserver/python/translations/fr/LC_MESSAGES/messages.po
@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2024-03-04 11:09-0500\n"
-"PO-Revision-Date: 2024-03-04 11:13-0500\n"
+"POT-Creation-Date: 2025-01-08 11:51-0500\n"
+"PO-Revision-Date: 2025-01-08 11:57-0500\n"
"Last-Translator: \n"
"Language-Team: fr
\n"
"Language: fr\n"
@@ -16,650 +16,740 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-"Generated-By: Babel 2.14.0\n"
-"X-Generator: Poedit 3.4.2\n"
-
-#: modules/FlaskModule/API/device/DeviceLogin.py:82
+"Generated-By: Babel 2.16.0\n"
+"X-Generator: Poedit 3.5\n"
+
+#: modules/FlaskModule/API/user/UserQueryUsers.py:53
+#: modules/FlaskModule/FlaskUtils.py:13
+msgid "Password missing special character"
+msgstr "Le mot de passe requiert un caractère spécial"
+
+#: modules/FlaskModule/API/user/UserQueryUsers.py:55
+#: modules/FlaskModule/FlaskUtils.py:15
+msgid "Password missing numeric character"
+msgstr "Le mot de passe requiert au moins un chiffre"
+
+#: modules/FlaskModule/FlaskUtils.py:17
+msgid "Password not long enough (10 characters)"
+msgstr "Le mot de passe n'est pas assez long (10 caractères min.)"
+
+#: modules/FlaskModule/API/user/UserQueryUsers.py:59
+#: modules/FlaskModule/FlaskUtils.py:19
+msgid "Password missing lower case letter"
+msgstr "Le mot de passe requiert au moins une minuscule"
+
+#: modules/FlaskModule/API/user/UserQueryUsers.py:61
+#: modules/FlaskModule/FlaskUtils.py:21
+msgid "Password missing upper case letter"
+msgstr "Le mot de passe requiert au moins une majuscule"
+
+#: modules/FlaskModule/API/device/DeviceLogin.py:90
msgid "Unable to get online devices."
msgstr "Impossible d'obtenir les appareils connectés."
-#: modules/FlaskModule/API/device/DeviceLogin.py:98
+#: modules/FlaskModule/API/device/DeviceLogin.py:106
msgid "Device already logged in."
msgstr "L'appareil est déjà connecté."
-#: modules/FlaskModule/API/device/DeviceLogout.py:29
+#: modules/FlaskModule/API/device/DeviceLogout.py:32
msgid "Device logged out."
msgstr "Appareil déconnecté."
-#: modules/FlaskModule/API/device/DeviceLogout.py:31
+#: modules/FlaskModule/API/device/DeviceLogout.py:34
msgid "Device not logged in"
msgstr "L'appareil n'est pas connecté"
-#: modules/FlaskModule/API/device/DeviceQueryDevices.py:69
+#: modules/FlaskModule/API/device/DeviceQueryAssets.py:42
+#: modules/FlaskModule/API/participant/ParticipantQueryAssets.py:48
+msgid "No access to session"
+msgstr "Aucun accès à la séance"
+
+#: modules/FlaskModule/API/device/DeviceQueryDevices.py:75
msgid "Missing device schema"
msgstr "Schéma de l'appareil manquant"
-#: modules/FlaskModule/API/device/DeviceQueryDevices.py:78
+#: modules/FlaskModule/API/device/DeviceQueryDevices.py:84
msgid "Missing config"
msgstr "Configuration manquante"
-#: modules/FlaskModule/API/device/DeviceQueryDevices.py:82
-#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:49
-#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:55
-#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:62
-#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:68
-#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:74
-#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:80
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:52
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:66
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:121
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:144
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:179
-#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:72
-#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:79
-#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:89
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:52
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:57
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:99
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:106
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:158
-#: modules/FlaskModule/API/service/ServiceQueryRoles.py:112
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:100
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:104
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:108
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:116
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:194
-#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:86
+#: modules/FlaskModule/API/device/DeviceQueryDevices.py:88
+#: modules/FlaskModule/API/participant/ParticipantQueryAssets.py:44
+#: modules/FlaskModule/API/service/ServiceQueryDevices.py:61
+#: modules/FlaskModule/API/service/ServiceQueryDevices.py:130
+#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:51
+#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:57
+#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:64
+#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:70
+#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:76
+#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:82
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:55
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:69
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:127
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:150
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:188
+#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:76
+#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:83
+#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:91
+#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:102
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:55
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:60
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:105
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:112
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:167
+#: modules/FlaskModule/API/service/ServiceQueryRoles.py:121
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:106
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:110
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:114
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:122
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:203
#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:91
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:66
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:72
+#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:96
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:133
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:171
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:205
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:271
#: modules/FlaskModule/API/service/ServiceQuerySessions.py:76
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:84
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:90
-#: modules/FlaskModule/API/service/ServiceQuerySites.py:36
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:121
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:159
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:193
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:256
-#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:96
-#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:101
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:50
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:54
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:106
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:207
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:301
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:122
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:257
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:112
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:165
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:105
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:191
-#: modules/FlaskModule/API/user/UserQueryDevices.py:293
-#: modules/FlaskModule/API/user/UserQueryDevices.py:297
-#: modules/FlaskModule/API/user/UserQueryDevices.py:417
-#: modules/FlaskModule/API/user/UserQueryDisconnect.py:52
-#: modules/FlaskModule/API/user/UserQueryDisconnect.py:60
-#: modules/FlaskModule/API/user/UserQueryDisconnect.py:66
-#: modules/FlaskModule/API/user/UserQueryDisconnect.py:72
-#: modules/FlaskModule/API/user/UserQueryDisconnect.py:78
-#: modules/FlaskModule/API/user/UserQueryDisconnect.py:84
-#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:107
-#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:159
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:363
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:366
-#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:204
-#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:274
-#: modules/FlaskModule/API/user/UserQueryProjects.py:151
-#: modules/FlaskModule/API/user/UserQueryProjects.py:156
-#: modules/FlaskModule/API/user/UserQueryProjects.py:264
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:117
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:122
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:82
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:86
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:94
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:100
+#: modules/FlaskModule/API/service/ServiceQuerySites.py:39
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:130
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:168
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:202
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:268
+#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:107
+#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:117
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:261
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:263
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:265
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:267
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:269
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:271
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:286
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:353
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:52
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:56
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:111
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:215
+#: modules/FlaskModule/API/user/UserQueryAssetsArchive.py:116
+#: modules/FlaskModule/API/user/UserQueryAssetsArchive.py:125
+#: modules/FlaskModule/API/user/UserQueryAssetsArchive.py:136
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:306
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:125
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:262
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:115
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:170
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:107
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:195
+#: modules/FlaskModule/API/user/UserQueryDevices.py:296
+#: modules/FlaskModule/API/user/UserQueryDevices.py:300
+#: modules/FlaskModule/API/user/UserQueryDevices.py:422
+#: modules/FlaskModule/API/user/UserQueryDisconnect.py:53
+#: modules/FlaskModule/API/user/UserQueryDisconnect.py:61
+#: modules/FlaskModule/API/user/UserQueryDisconnect.py:67
+#: modules/FlaskModule/API/user/UserQueryDisconnect.py:73
+#: modules/FlaskModule/API/user/UserQueryDisconnect.py:79
+#: modules/FlaskModule/API/user/UserQueryDisconnect.py:85
+#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:110
+#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:164
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:368
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:371
+#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:207
+#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:279
+#: modules/FlaskModule/API/user/UserQueryProjects.py:155
+#: modules/FlaskModule/API/user/UserQueryProjects.py:160
+#: modules/FlaskModule/API/user/UserQueryProjects.py:270
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:121
#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:126
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:134
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:213
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:217
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:221
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:225
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:151
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:155
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:159
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:165
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:240
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:130
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:138
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:219
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:223
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:227
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:231
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:154
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:158
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:162
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:168
#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:245
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:249
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:253
-#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:79
-#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:136
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:129
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:259
-#: modules/FlaskModule/API/user/UserQueryServices.py:121
-#: modules/FlaskModule/API/user/UserQueryServices.py:134
-#: modules/FlaskModule/API/user/UserQueryServices.py:231
-#: modules/FlaskModule/API/user/UserQueryServices.py:239
-#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:91
-#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:144
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:204
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:203
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:267
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:55
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:107
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:138
-#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:187
-#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:275
-#: modules/FlaskModule/API/user/UserQuerySites.py:124
-#: modules/FlaskModule/API/user/UserQuerySites.py:127
-#: modules/FlaskModule/API/user/UserQuerySites.py:176
-#: modules/FlaskModule/API/user/UserQueryStats.py:52
-#: modules/FlaskModule/API/user/UserQueryStats.py:57
-#: modules/FlaskModule/API/user/UserQueryStats.py:62
-#: modules/FlaskModule/API/user/UserQueryStats.py:68
-#: modules/FlaskModule/API/user/UserQueryStats.py:73
-#: modules/FlaskModule/API/user/UserQueryStats.py:81
-#: modules/FlaskModule/API/user/UserQueryStats.py:86
-#: modules/FlaskModule/API/user/UserQueryStats.py:91
-#: modules/FlaskModule/API/user/UserQueryTestType.py:62
-#: modules/FlaskModule/API/user/UserQueryTestType.py:169
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:205
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:193
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:256
-#: modules/FlaskModule/API/user/UserQueryTests.py:140
-#: modules/FlaskModule/API/user/UserQueryUserGroups.py:147
-#: modules/FlaskModule/API/user/UserQueryUserPreferences.py:47
-#: modules/FlaskModule/API/user/UserQueryUserPreferences.py:96
-#: modules/FlaskModule/API/user/UserQueryUsers.py:214
-#: modules/FlaskModule/API/user/UserQueryUsers.py:219
-#: modules/FlaskModule/API/user/UserQueryUsers.py:348
-#: opentera/services/ServiceAccessManager.py:116
-#: opentera/services/ServiceAccessManager.py:166
-#: opentera/services/ServiceAccessManager.py:195
-#: opentera/services/ServiceAccessManager.py:214
-#: opentera/services/ServiceAccessManager.py:242
-#: opentera/services/ServiceAccessManager.py:297
-#: opentera/services/ServiceAccessManager.py:407
-#: opentera/services/ServiceAccessManager.py:413
-#: opentera/services/ServiceAccessManager.py:424
-#: opentera/services/ServiceAccessManager.py:440
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:250
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:254
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:258
+#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:83
+#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:142
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:133
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:265
+#: modules/FlaskModule/API/user/UserQueryServices.py:124
+#: modules/FlaskModule/API/user/UserQueryServices.py:137
+#: modules/FlaskModule/API/user/UserQueryServices.py:236
+#: modules/FlaskModule/API/user/UserQueryServices.py:244
+#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:94
+#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:149
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:208
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:189
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:207
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:273
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:59
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:113
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:144
+#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:190
+#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:280
+#: modules/FlaskModule/API/user/UserQuerySites.py:128
+#: modules/FlaskModule/API/user/UserQuerySites.py:132
+#: modules/FlaskModule/API/user/UserQuerySites.py:183
+#: modules/FlaskModule/API/user/UserQueryStats.py:54
+#: modules/FlaskModule/API/user/UserQueryStats.py:59
+#: modules/FlaskModule/API/user/UserQueryStats.py:64
+#: modules/FlaskModule/API/user/UserQueryStats.py:70
+#: modules/FlaskModule/API/user/UserQueryStats.py:75
+#: modules/FlaskModule/API/user/UserQueryStats.py:83
+#: modules/FlaskModule/API/user/UserQueryStats.py:88
+#: modules/FlaskModule/API/user/UserQueryStats.py:93
+#: modules/FlaskModule/API/user/UserQueryTestType.py:72
+#: modules/FlaskModule/API/user/UserQueryTestType.py:77
+#: modules/FlaskModule/API/user/UserQueryTestType.py:186
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:209
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:197
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:262
+#: modules/FlaskModule/API/user/UserQueryTests.py:147
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:264
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:266
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:268
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:270
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:272
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:274
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:289
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:354
+#: modules/FlaskModule/API/user/UserQueryUserGroups.py:150
+#: modules/FlaskModule/API/user/UserQueryUserPreferences.py:48
+#: modules/FlaskModule/API/user/UserQueryUserPreferences.py:99
+#: modules/FlaskModule/API/user/UserQueryUsers.py:229
+#: modules/FlaskModule/API/user/UserQueryUsers.py:234
+#: modules/FlaskModule/API/user/UserQueryUsers.py:356
+#: opentera/services/ServiceAccessManager.py:118
+#: opentera/services/ServiceAccessManager.py:168
+#: opentera/services/ServiceAccessManager.py:197
+#: opentera/services/ServiceAccessManager.py:216
+#: opentera/services/ServiceAccessManager.py:244
+#: opentera/services/ServiceAccessManager.py:299
+#: opentera/services/ServiceAccessManager.py:429
+#: opentera/services/ServiceAccessManager.py:435
#: opentera/services/ServiceAccessManager.py:446
-#: opentera/services/ServiceAccessManager.py:457
+#: opentera/services/ServiceAccessManager.py:462
+#: opentera/services/ServiceAccessManager.py:468
+#: opentera/services/ServiceAccessManager.py:479
+#: opentera/services/ServiceAccessManager.py:502
+#: opentera/services/ServiceAccessManager.py:518
+#: opentera/services/ServiceAccessManager.py:527
+#: opentera/services/ServiceAccessManager.py:533
+#: opentera/services/ServiceAccessManager.py:541
+#: opentera/services/ServiceAccessManager.py:562
+#: opentera/services/ServiceAccessManager.py:579
+#: opentera/services/ServiceAccessManager.py:593
msgid "Forbidden"
msgstr "Accès refusé"
-#: modules/FlaskModule/API/device/DeviceQueryDevices.py:93
-#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:74
-#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:89
-#: modules/FlaskModule/API/device/DeviceQuerySessions.py:157
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:233
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:247
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:284
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:136
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:153
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:201
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:116
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:138
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:176
-#: modules/FlaskModule/API/service/ServiceQueryRoles.py:73
-#: modules/FlaskModule/API/service/ServiceQueryRoles.py:94
-#: modules/FlaskModule/API/service/ServiceQueryRoles.py:123
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:138
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:150
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:172
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:205
-#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:104
-#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:119
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:185
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:218
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:233
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:274
-#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:111
-#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:132
-#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:166
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:231
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:242
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:277
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:151
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:168
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:225
-#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:180
-#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:221
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:276
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:331
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:232
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:268
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:130
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:145
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:187
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:121
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:136
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:189
-#: modules/FlaskModule/API/user/UserQueryDevices.py:328
-#: modules/FlaskModule/API/user/UserQueryDevices.py:343
-#: modules/FlaskModule/API/user/UserQueryDevices.py:460
-#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:120
-#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:135
-#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:178
-#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:244
-#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:285
-#: modules/FlaskModule/API/user/UserQueryProjects.py:196
-#: modules/FlaskModule/API/user/UserQueryProjects.py:211
-#: modules/FlaskModule/API/user/UserQueryProjects.py:282
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:156
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:168
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:190
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:236
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:208
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:264
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:287
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:322
-#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:98
-#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:119
-#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:147
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:244
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:286
-#: modules/FlaskModule/API/user/UserQueryServices.py:162
-#: modules/FlaskModule/API/user/UserQueryServices.py:182
-#: modules/FlaskModule/API/user/UserQueryServices.py:259
-#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:104
-#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:119
-#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:155
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:251
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:291
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:244
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:286
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:197
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:212
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:342
-#: modules/FlaskModule/API/user/UserQuerySessions.py:179
-#: modules/FlaskModule/API/user/UserQuerySessions.py:194
-#: modules/FlaskModule/API/user/UserQuerySessions.py:272
-#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:246
-#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:286
-#: modules/FlaskModule/API/user/UserQuerySites.py:140
-#: modules/FlaskModule/API/user/UserQuerySites.py:155
-#: modules/FlaskModule/API/user/UserQuerySites.py:194
-#: modules/FlaskModule/API/user/UserQueryTestType.py:222
-#: modules/FlaskModule/API/user/UserQueryTestType.py:237
-#: modules/FlaskModule/API/user/UserQueryTestType.py:345
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:252
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:293
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:233
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:274
-#: modules/FlaskModule/API/user/UserQueryTests.py:151
-#: modules/FlaskModule/API/user/UserQueryUserGroups.py:190
-#: modules/FlaskModule/API/user/UserQueryUserGroups.py:205
-#: modules/FlaskModule/API/user/UserQueryUserGroups.py:233
-#: modules/FlaskModule/API/user/UserQueryUserGroups.py:262
-#: modules/FlaskModule/API/user/UserQueryUserGroups.py:310
-#: modules/FlaskModule/API/user/UserQueryUserPreferences.py:111
-#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:138
-#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:174
-#: modules/FlaskModule/API/user/UserQueryUsers.py:240
-#: modules/FlaskModule/API/user/UserQueryUsers.py:272
-#: modules/FlaskModule/API/user/UserQueryUsers.py:385
+#: modules/FlaskModule/API/device/DeviceQueryDevices.py:99
+#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:79
+#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:94
+#: modules/FlaskModule/API/device/DeviceQuerySessions.py:162
+#: modules/FlaskModule/API/participant/ParticipantQuerySessions.py:188
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:239
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:253
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:293
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:142
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:159
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:210
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:122
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:144
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:185
+#: modules/FlaskModule/API/service/ServiceQueryRoles.py:79
+#: modules/FlaskModule/API/service/ServiceQueryRoles.py:100
+#: modules/FlaskModule/API/service/ServiceQueryRoles.py:132
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:144
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:156
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:178
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:214
+#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:109
+#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:124
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:245
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:288
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypes.py:229
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:198
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:231
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:242
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:286
+#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:127
+#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:148
+#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:185
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:237
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:248
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:286
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:363
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:156
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:173
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:233
+#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:183
+#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:226
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:279
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:336
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:235
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:273
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:133
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:148
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:192
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:123
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:138
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:193
+#: modules/FlaskModule/API/user/UserQueryDevices.py:331
+#: modules/FlaskModule/API/user/UserQueryDevices.py:346
+#: modules/FlaskModule/API/user/UserQueryDevices.py:465
+#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:123
+#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:138
+#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:183
+#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:247
+#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:290
+#: modules/FlaskModule/API/user/UserQueryProjects.py:200
+#: modules/FlaskModule/API/user/UserQueryProjects.py:215
+#: modules/FlaskModule/API/user/UserQueryProjects.py:288
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:160
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:172
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:194
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:242
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:211
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:269
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:291
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:328
+#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:102
+#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:123
+#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:153
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:248
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:292
+#: modules/FlaskModule/API/user/UserQueryServices.py:165
+#: modules/FlaskModule/API/user/UserQueryServices.py:185
+#: modules/FlaskModule/API/user/UserQueryServices.py:264
+#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:107
+#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:122
+#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:160
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:255
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:297
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:217
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:262
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:248
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:292
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:220
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:235
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:385
+#: modules/FlaskModule/API/user/UserQuerySessions.py:183
+#: modules/FlaskModule/API/user/UserQuerySessions.py:198
+#: modules/FlaskModule/API/user/UserQuerySessions.py:278
+#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:249
+#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:291
+#: modules/FlaskModule/API/user/UserQuerySites.py:145
+#: modules/FlaskModule/API/user/UserQuerySites.py:160
+#: modules/FlaskModule/API/user/UserQuerySites.py:201
+#: modules/FlaskModule/API/user/UserQueryTestType.py:239
+#: modules/FlaskModule/API/user/UserQueryTestType.py:254
+#: modules/FlaskModule/API/user/UserQueryTestType.py:364
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:256
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:299
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:237
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:280
+#: modules/FlaskModule/API/user/UserQueryTests.py:158
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:364
+#: modules/FlaskModule/API/user/UserQueryUserGroups.py:193
+#: modules/FlaskModule/API/user/UserQueryUserGroups.py:208
+#: modules/FlaskModule/API/user/UserQueryUserGroups.py:236
+#: modules/FlaskModule/API/user/UserQueryUserGroups.py:265
+#: modules/FlaskModule/API/user/UserQueryUserGroups.py:315
+#: modules/FlaskModule/API/user/UserQueryUserPreferences.py:114
+#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:142
+#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:180
+#: modules/FlaskModule/API/user/UserQueryUsers.py:255
+#: modules/FlaskModule/API/user/UserQueryUsers.py:293
+#: modules/FlaskModule/API/user/UserQueryUsers.py:393
msgid "Database error"
msgstr "Erreur de base de données"
#: modules/FlaskModule/API/device/DeviceQueryParticipants.py:41
-#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:82
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:72
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:67
-#: modules/FlaskModule/API/service/ServiceQueryServices.py:76
-#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:62
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:122
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:109
-#: modules/FlaskModule/API/user/UserQueryDisconnect.py:86
-#: modules/FlaskModule/API/user/UserQueryProjects.py:116
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:84
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:123
-#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:67
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:112
-#: modules/FlaskModule/API/user/UserQueryServices.py:104
-#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:62
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:101
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:79
-#: modules/FlaskModule/API/user/UserQuerySessions.py:103
-#: modules/FlaskModule/API/user/UserQuerySites.py:97
-#: modules/FlaskModule/API/user/UserQueryTestType.py:112
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:99
-#: modules/FlaskModule/API/user/UserQueryUserPreferences.py:67
+#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:84
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:75
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:70
+#: modules/FlaskModule/API/service/ServiceQueryServices.py:79
+#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:64
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:132
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:110
+#: modules/FlaskModule/API/user/UserQueryDisconnect.py:87
+#: modules/FlaskModule/API/user/UserQueryProjects.py:118
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:86
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:125
+#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:69
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:114
+#: modules/FlaskModule/API/user/UserQueryServices.py:105
+#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:63
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:103
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:83
+#: modules/FlaskModule/API/user/UserQuerySessions.py:105
+#: modules/FlaskModule/API/user/UserQuerySites.py:99
+#: modules/FlaskModule/API/user/UserQueryTestType.py:127
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:101
+#: modules/FlaskModule/API/user/UserQueryUserPreferences.py:68
msgid "Invalid request"
msgstr "Requête invalide"
-#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:31
-#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:97
-#: modules/FlaskModule/API/device/DeviceQuerySessions.py:72
-#: modules/FlaskModule/API/device/DeviceQuerySessions.py:193
+#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:33
+#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:105
+#: modules/FlaskModule/API/device/DeviceQuerySessions.py:74
+#: modules/FlaskModule/API/device/DeviceQuerySessions.py:201
msgid "Forbidden for security reasons"
msgstr "Accès interdit pour raison de sécurité"
-#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:47
-#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:53
-#: modules/FlaskModule/API/device/DeviceQueryStatus.py:48
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:270
-#: modules/FlaskModule/API/service/ServiceQueryDevices.py:73
-#: modules/FlaskModule/API/service/ServiceQueryDevices.py:87
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:76
-#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:104
-#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:119
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:49
-#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:46
-#: modules/FlaskModule/API/service/ServiceQuerySiteProjectAccessRoles.py:43
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:55
-#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:152
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:263
-#: modules/FlaskModule/API/service/ServiceQueryUsers.py:38
-#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:70
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:65
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:61
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:157
-#: modules/FlaskModule/API/user/UserQueryForms.py:85
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:76
-#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:81
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:53
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:63
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:60
-#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:46
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:62
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:59
-#: modules/FlaskModule/API/user/UserQuerySessions.py:59
-#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:80
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:62
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:60
+#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:52
+#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:58
+#: modules/FlaskModule/API/device/DeviceQueryStatus.py:50
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:279
+#: modules/FlaskModule/API/service/ServiceQueryDevices.py:83
+#: modules/FlaskModule/API/service/ServiceQueryDevices.py:100
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:79
+#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:118
+#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:136
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:52
+#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:48
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:64
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypes.py:216
+#: modules/FlaskModule/API/service/ServiceQuerySiteProjectAccessRoles.py:46
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:61
+#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:171
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:272
+#: modules/FlaskModule/API/service/ServiceQueryUsers.py:55
+#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:71
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:66
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:62
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:161
+#: modules/FlaskModule/API/user/UserQueryForms.py:98
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:78
+#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:82
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:55
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:65
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:62
+#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:47
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:64
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:58
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:61
+#: modules/FlaskModule/API/user/UserQuerySessions.py:61
+#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:81
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:64
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:62
msgid "Missing arguments"
msgstr "Arguments manquants"
-#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:61
-#: modules/FlaskModule/API/device/DeviceQuerySessions.py:137
-#: modules/LoginModule/LoginModule.py:584 modules/LoginModule/LoginModule.py:684
-#: modules/LoginModule/LoginModule.py:750 modules/LoginModule/LoginModule.py:777
-#: modules/LoginModule/LoginModule.py:796
+#: modules/FlaskModule/API/device/DeviceQuerySessionEvents.py:66
+#: modules/FlaskModule/API/device/DeviceQuerySessions.py:142
+#: modules/FlaskModule/API/participant/ParticipantQuerySessions.py:169
+#: modules/LoginModule/LoginModule.py:598 modules/LoginModule/LoginModule.py:698
+#: modules/LoginModule/LoginModule.py:764 modules/LoginModule/LoginModule.py:791
+#: modules/LoginModule/LoginModule.py:810 modules/LoginModule/LoginModule.py:829
+#: modules/LoginModule/LoginModule.py:831
msgid "Unauthorized"
msgstr "Non autorisé"
-#: modules/FlaskModule/API/device/DeviceQuerySessions.py:87
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:136
-#: modules/FlaskModule/API/user/UserQuerySessions.py:120
+#: modules/FlaskModule/API/device/DeviceQuerySessions.py:92
+#: modules/FlaskModule/API/participant/ParticipantQuerySessions.py:120
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:149
+#: modules/FlaskModule/API/user/UserQuerySessions.py:124
msgid "Missing session"
msgstr "Champ session manquant"
-#: modules/FlaskModule/API/device/DeviceQuerySessions.py:95
+#: modules/FlaskModule/API/device/DeviceQuerySessions.py:100
+#: modules/FlaskModule/API/participant/ParticipantQuerySessions.py:128
msgid "Missing id_session value"
msgstr "Champ id_session manquant"
-#: modules/FlaskModule/API/device/DeviceQuerySessions.py:99
+#: modules/FlaskModule/API/device/DeviceQuerySessions.py:104
+#: modules/FlaskModule/API/participant/ParticipantQuerySessions.py:132
msgid "Missing id_session_type value"
msgstr "Champ id_session_type manquant"
-#: modules/FlaskModule/API/device/DeviceQuerySessions.py:104
+#: modules/FlaskModule/API/device/DeviceQuerySessions.py:109
+#: modules/FlaskModule/API/participant/ParticipantQuerySessions.py:137
msgid "Missing session participants and/or users and/or devices"
msgstr "Utilisateurs et/ou participants et/ou appareils manquants pour la séance"
-#: modules/FlaskModule/API/device/DeviceQuerySessions.py:114
+#: modules/FlaskModule/API/device/DeviceQuerySessions.py:119
+#: modules/FlaskModule/API/participant/ParticipantQuerySessions.py:146
msgid "No access to session type"
msgstr "Aucun accès au type de séance"
-#: modules/FlaskModule/API/device/DeviceQuerySessions.py:119
+#: modules/FlaskModule/API/device/DeviceQuerySessions.py:124
+#: modules/FlaskModule/API/participant/ParticipantQuerySessions.py:151
msgid "Missing argument 'session name'"
msgstr "Paramètre 'session name' manquant"
-#: modules/FlaskModule/API/device/DeviceQuerySessions.py:121
+#: modules/FlaskModule/API/device/DeviceQuerySessions.py:126
+#: modules/FlaskModule/API/participant/ParticipantQuerySessions.py:153
msgid "Missing argument 'session_start_datetime'"
msgstr "Paramètre 'session_start_datetime' manquant"
-#: modules/FlaskModule/API/device/DeviceQuerySessions.py:170
-#: modules/FlaskModule/API/service/ServiceQueryAccess.py:130
+#: modules/FlaskModule/API/device/DeviceQuerySessions.py:175
+#: modules/FlaskModule/API/participant/ParticipantQuerySessions.py:201
+#: modules/FlaskModule/API/service/ServiceQueryAccess.py:132
msgid "Invalid participant uuid"
msgstr "UUID participant invalide"
-#: modules/FlaskModule/API/device/DeviceQueryStatus.py:67
+#: modules/FlaskModule/API/device/DeviceQueryStatus.py:69
msgid "Status update forbidden on offline device."
msgstr "Mise à jour de l'état interdite sur un appareil hors-ligne."
-#: modules/FlaskModule/API/device/DeviceRegister.py:73
-#: modules/FlaskModule/API/device/DeviceRegister.py:99
+#: modules/FlaskModule/API/device/DeviceRegister.py:76
+#: modules/FlaskModule/API/device/DeviceRegister.py:105
msgid "Invalid registration key"
msgstr "Clé d'enregistrement invalide"
-#: modules/FlaskModule/API/device/DeviceRegister.py:103
+#: modules/FlaskModule/API/device/DeviceRegister.py:109
msgid "Invalid content type"
msgstr "Content-Type invalide"
-#: modules/FlaskModule/API/device/DeviceRegister.py:137
+#: modules/FlaskModule/API/device/DeviceRegister.py:143
msgid "Invalid CSR signature"
msgstr "La signature du certificat CSR est invalides"
-#: modules/FlaskModule/API/participant/ParticipantLogin.py:92
+#: modules/FlaskModule/API/participant/ParticipantLogin.py:96
msgid "Participant already logged in."
msgstr "Le participant est déjà connecté."
-#: modules/FlaskModule/API/participant/ParticipantLogin.py:126
+#: modules/FlaskModule/API/participant/ParticipantLogin.py:138
msgid "Missing current_participant"
msgstr "Champ current_participant manquant"
-#: modules/FlaskModule/API/participant/ParticipantLogout.py:41
+#: modules/FlaskModule/API/participant/ParticipantLogout.py:44
msgid "Participant logged out."
msgstr "Participant hors ligne."
-#: modules/FlaskModule/API/participant/ParticipantLogout.py:43
+#: modules/FlaskModule/API/participant/ParticipantLogout.py:46
msgid "Participant not logged in"
msgstr "Le participant n'est pas connecté"
-#: modules/FlaskModule/API/participant/ParticipantQueryParticipants.py:55
-#: modules/FlaskModule/API/participant/ParticipantQuerySessions.py:73
+#: modules/FlaskModule/API/participant/ParticipantQueryParticipants.py:58
msgid "Not implemented"
msgstr "Non implémenté"
-#: modules/FlaskModule/API/service/ServiceQueryAccess.py:57
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:61
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:57
-#: modules/FlaskModule/API/user/UserQueryAssets.py:60
-#: modules/FlaskModule/API/user/UserQueryTests.py:54
+#: modules/FlaskModule/API/service/ServiceQueryAccess.py:59
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:64
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:60
+#: modules/FlaskModule/API/user/UserQueryAssets.py:61
+#: modules/FlaskModule/API/user/UserQueryTests.py:56
msgid "No arguments specified"
msgstr "Aucun argument spécifié"
-#: modules/FlaskModule/API/service/ServiceQueryAccess.py:67
+#: modules/FlaskModule/API/service/ServiceQueryAccess.py:69
msgid "Missing at least one from argument for uuids"
msgstr "Au moins un champ \"UUID\" manquant"
-#: modules/FlaskModule/API/service/ServiceQueryAccess.py:77
+#: modules/FlaskModule/API/service/ServiceQueryAccess.py:79
msgid "Invalid user uuid"
msgstr "UUID d'utilisateur invalide"
-#: modules/FlaskModule/API/service/ServiceQueryAccess.py:126
+#: modules/FlaskModule/API/service/ServiceQueryAccess.py:128
msgid "Participant cannot be admin"
msgstr "Un participant ne peut être administrateur"
-#: modules/FlaskModule/API/service/ServiceQueryAccess.py:163
+#: modules/FlaskModule/API/service/ServiceQueryAccess.py:165
msgid "Device cannot be admin"
msgstr "Un appareil ne peut être administrateur"
-#: modules/FlaskModule/API/service/ServiceQueryAccess.py:167
+#: modules/FlaskModule/API/service/ServiceQueryAccess.py:169
msgid "Invalid device uuid"
msgstr "UUID de l'appareil invalide"
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:64
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:90
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:60
-#: modules/FlaskModule/API/user/UserQueryAssets.py:63
-#: modules/FlaskModule/API/user/UserQueryAssets.py:91
-#: modules/FlaskModule/API/user/UserQueryTests.py:57
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:67
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:93
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:63
+#: modules/FlaskModule/API/user/UserQueryAssets.py:64
+#: modules/FlaskModule/API/user/UserQueryAssets.py:92
+#: modules/FlaskModule/API/user/UserQueryTests.py:59
msgid "Device access denied"
msgstr "Accès à l'appareil interdit"
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:68
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:64
-#: modules/FlaskModule/API/user/UserQueryAssets.py:67
-#: modules/FlaskModule/API/user/UserQueryTests.py:61
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:71
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:67
+#: modules/FlaskModule/API/user/UserQueryAssets.py:68
+#: modules/FlaskModule/API/user/UserQueryTests.py:63
msgid "Session access denied"
msgstr "Accès à la session refusé"
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:72
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:86
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:68
-#: modules/FlaskModule/API/user/UserQueryAssets.py:71
-#: modules/FlaskModule/API/user/UserQueryAssets.py:87
-#: modules/FlaskModule/API/user/UserQueryTests.py:65
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:75
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:89
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:71
+#: modules/FlaskModule/API/user/UserQueryAssets.py:72
+#: modules/FlaskModule/API/user/UserQueryAssets.py:88
+#: modules/FlaskModule/API/user/UserQueryTests.py:67
msgid "Participant access denied"
msgstr "Accès au participant refusé"
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:76
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:82
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:72
-#: modules/FlaskModule/API/user/UserQueryAssets.py:75
-#: modules/FlaskModule/API/user/UserQueryAssets.py:83
-#: modules/FlaskModule/API/user/UserQueryTests.py:69
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:79
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:85
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:75
+#: modules/FlaskModule/API/user/UserQueryAssets.py:76
+#: modules/FlaskModule/API/user/UserQueryAssets.py:84
+#: modules/FlaskModule/API/user/UserQueryTests.py:71
msgid "User access denied"
msgstr "Accès à l'utilisateur refusé"
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:97
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:80
-#: modules/FlaskModule/API/user/UserQueryAssets.py:100
-#: modules/FlaskModule/API/user/UserQueryTests.py:76
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:100
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:83
+#: modules/FlaskModule/API/user/UserQueryAssets.py:101
+#: modules/FlaskModule/API/user/UserQueryTests.py:78
msgid "Missing argument"
msgstr "Paramètre manquant"
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:163
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:169
msgid "Missing asset field"
msgstr "Champ asset manquant"
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:169
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:175
msgid "Missing id_asset field"
msgstr "Champ id_asset manquant"
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:173
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:141
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:179
#: modules/FlaskModule/API/service/ServiceQueryTests.py:147
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:153
msgid "Unknown session"
msgstr "Séance inconnue"
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:176
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:182
msgid "Invalid asset type"
msgstr "Mauvais type de données (asset)"
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:179
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:185
msgid "Invalid asset name"
msgstr "Nom de donnée (asset) invalide"
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:189
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:195
msgid "Service can't create assets for that session"
msgstr "Le service ne peut pas créer de données pour cette séance"
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:196
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:196
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:202
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:202
msgid "Invalid participant"
msgstr "Nom de participant incorrect"
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:204
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:204
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:210
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:210
msgid "Invalid user"
msgstr "Utilisateur invalide"
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:212
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:212
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:218
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:218
msgid "Invalid device"
msgstr "Appareil invalide"
-#: modules/FlaskModule/API/service/ServiceQueryAssets.py:273
+#: modules/FlaskModule/API/service/ServiceQueryAssets.py:282
msgid "Service can't delete assets for that session"
msgstr "Le service ne peut pas effacer les données de cette séance"
-#: modules/FlaskModule/API/service/ServiceQueryDevices.py:93
+#: modules/FlaskModule/API/service/ServiceQueryDevices.py:106
msgid "Unknown device type"
msgstr "Type d’appareil inconnu"
-#: modules/FlaskModule/API/service/ServiceQueryDevices.py:97
+#: modules/FlaskModule/API/service/ServiceQueryDevices.py:110
msgid "Unknown device subtype"
msgstr "Sous-type d'appareil inconnu"
-#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:84
-#: modules/FlaskModule/API/user/UserQueryDisconnect.py:88
+#: modules/FlaskModule/API/service/ServiceQueryDisconnect.py:86
+#: modules/FlaskModule/API/user/UserQueryDisconnect.py:89
msgid "Success"
msgstr "Succès"
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:57
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:191
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:252
-#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:203
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:296
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:209
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:63
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:308
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:266
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:270
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:271
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:60
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:200
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:267
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:264
+#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:208
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:301
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:215
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:64
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:314
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:272
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:276
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:238
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:277
msgid "Not found"
msgstr "Non trouvé"
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:95
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:101
msgid "Missing participant_group"
msgstr "Groupe participant manquant"
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:102
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:108
msgid "Missing id_participant_group"
msgstr "ID groupe participant manquant"
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:107
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:93
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:180
-#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:198
-#: modules/FlaskModule/API/user/UserQueryProjects.py:138
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:179
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:113
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:99
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:275
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:183
+#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:201
+#: modules/FlaskModule/API/user/UserQueryProjects.py:142
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:183
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:278
msgid "Missing id_project"
msgstr "Champ manquant : id_project"
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:114
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:120
msgid "Missing group name"
msgstr "Nom du groupe manquant"
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:184
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:193
msgid "The id_participant_group given was not found"
msgstr "L'ID du groupe participant n'a pu être trouvé"
-#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:190
+#: modules/FlaskModule/API/service/ServiceQueryParticipantGroups.py:199
msgid "Deletion impossible: Participant group still has participant(s)"
msgstr "Suppression impossible: le groupe participant a encore des participants"
-#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:125
+#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:142
msgid "Unknown project"
msgstr "Projet inconnu"
-#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:128
+#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:145
msgid "Invalid participant name"
msgstr "Nom de participant incorrect"
-#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:131
+#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:148
msgid "Invalid participant email"
msgstr "Courriel de participant incorrect"
-#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:155
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:312
+#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:172
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:315
msgid "Can't insert participant: participant's project is disabled or invalid."
msgstr ""
"Impossible d'ajouter le participant: le projet du participant est désactivé ou "
"invalide."
-#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:165
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:285
+#: modules/FlaskModule/API/service/ServiceQueryParticipants.py:182
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:288
msgid "Can't update participant: participant's project is disabled."
msgstr ""
"Impossible de mettre à jour le participant: le projet du participant est "
"désactivé."
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:48
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:51
msgid "Missing parameter"
msgstr "Paramètre manquant"
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:87
-#: modules/FlaskModule/API/user/UserQueryProjects.py:132
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:93
+#: modules/FlaskModule/API/user/UserQueryProjects.py:136
msgid "Missing project"
msgstr "Projet manquant"
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:96
-#: modules/FlaskModule/API/user/UserQueryProjects.py:140
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:102
+#: modules/FlaskModule/API/user/UserQueryProjects.py:144
msgid "Missing id_site arguments"
msgstr "Champ id_site manquant"
-#: modules/FlaskModule/API/service/ServiceQueryProjects.py:169
-#: modules/FlaskModule/API/user/UserQueryProjects.py:275
+#: modules/FlaskModule/API/service/ServiceQueryProjects.py:178
+#: modules/FlaskModule/API/user/UserQueryProjects.py:281
msgid ""
"Can't delete project: please delete all participants with sessions before "
"deleting."
@@ -667,119 +757,206 @@ msgstr ""
"Impossible de supprimer le projet: veuillez supprimer tous les participants "
"ayant des séances au préalable."
-#: modules/FlaskModule/API/service/ServiceQueryRoles.py:52
+#: modules/FlaskModule/API/service/ServiceQueryRoles.py:58
msgid "Missing service_role field"
msgstr "Champ iservice_role manquant"
-#: modules/FlaskModule/API/service/ServiceQueryRoles.py:58
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:154
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:172
-#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:85
+#: modules/FlaskModule/API/service/ServiceQueryRoles.py:64
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:160
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:176
+#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:89
msgid "Missing id_service_role"
msgstr "Champ id_service_role manquant"
-#: modules/FlaskModule/API/service/ServiceQueryRoles.py:81
-#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:119
-#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:106
+#: modules/FlaskModule/API/service/ServiceQueryRoles.py:87
+#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:135
+#: modules/FlaskModule/API/user/UserQueryServiceRoles.py:110
msgid "Missing fields"
msgstr "Champs manquants"
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:87
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:104
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:93
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:108
msgid "Missing service_access"
msgstr "Accès Service manquant (service_access)"
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:91
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:108
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:97
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:112
msgid "Missing id_service_access"
msgstr "Champ manquant: id_service_access"
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:95
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:112
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:101
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:116
msgid "Can't combine id_user_group, id_participant_group and id_device in request"
msgstr ""
"Ne peut pas combiner id_user_group, id_participant et id_device dans la requête"
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:114
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:132
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:120
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:136
msgid "Bad id_service_role"
msgstr "Mauvais id_service_role"
-#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:158
-#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:176
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:192
+#: modules/FlaskModule/API/service/ServiceQueryServiceAccess.py:164
+#: modules/FlaskModule/API/user/UserQueryServiceAccess.py:180
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:195
msgid "Missing at least one id field"
msgstr "Au moins un champ id manquant"
-#: modules/FlaskModule/API/service/ServiceQueryServices.py:38
+#: modules/FlaskModule/API/service/ServiceQueryServices.py:41
msgid "Missing service key, id or uuid"
msgstr "Clé, ID ou UUID de service manquant"
-#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:76
-#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:77
+#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:81
+#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:80
msgid "Missing session_event field"
msgstr "Champ session_event manquant"
-#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:82
-#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:83
+#: modules/FlaskModule/API/service/ServiceQuerySessionEvents.py:87
+#: modules/FlaskModule/API/user/UserQuerySessionEvents.py:86
msgid "Missing id_session or id_session_event fields"
msgstr "Champs manquants: id_session ou id_session_event"
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:95
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:127
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:202
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:133
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:113
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:125
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:107
+msgid "Missing id_session_type"
+msgstr "Champ id_session_type manquant"
+
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:129
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:126
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:146
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:148
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:135
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:133
+msgid "Missing projects"
+msgstr "Projets manquants"
+
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:141
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:138
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:145
+msgid "Access denied to at least one project"
+msgstr "Accès refusé pour au moins un projet"
+
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:157
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:188
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:281
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:289
+msgid ""
+"Can't delete session type from project: please delete all sessions of that type "
+"in the project before deleting."
+msgstr ""
+"Impossible de retirer le type de séance du projet: veuillez supprimer toutes les "
+"séances de ce type dans ce projet avant de supprimer."
+
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:164
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:161
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:165
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:168
+msgid "Missing project ID"
+msgstr "ID de projet manquant"
+
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:166
+msgid "Missing sessions_types"
+msgstr "Types de séances manquantes"
+
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:197
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:194
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:200
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:181
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:201
+msgid "Unknown format"
+msgstr "Format inconnu"
+
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypeProjects.py:202
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:199
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:198
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:211
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:205
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:186
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:204
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:206
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:194
+msgid "Badly formatted request"
+msgstr "Requête mal formée"
+
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypes.py:128
+msgid "Service doesn't have access to all listed sites"
+msgstr "Le service n'a pas accès aux sites spécifiés"
+
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypes.py:134
+msgid "Service doesn't have access to all listed projects"
+msgstr "Le service n'a pas accès aux projets spécifiés"
+
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypes.py:140
+msgid "Service doesn't have access to all listed participants"
+msgstr "Le service n'a pas accès à au moins un participant de la séance"
+
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypes.py:154
+msgid "Service doesn't have access to this session type"
+msgstr "Le service n'a pas accès à ce type de séance"
+
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypes.py:158
+msgid "Session type not found"
+msgstr "Type de séance non-trouvé"
+
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypes.py:218
+msgid "Invalid arguments"
+msgstr "Arguments invalides"
+
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypes.py:220
+msgid "Invalid schema"
+msgstr "Schéma invalide"
+
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypes.py:223
+msgid "Unsupported Media Type"
+msgstr "Type de média non-supporté"
+
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypes.py:225
+msgid "Bad Request"
+msgstr "Requête erronée"
+
+#: modules/FlaskModule/API/service/ServiceQuerySessionTypes.py:227
+msgid "Integrity error"
+msgstr "Erreur d'intégrité"
+
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:105
msgid "Missing arguments: at least one id is required"
msgstr "Au moins un champ id manquant"
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:142
-#: modules/FlaskModule/API/user/UserQuerySessions.py:126
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:155
+#: modules/FlaskModule/API/user/UserQuerySessions.py:130
msgid "Missing id_session"
msgstr "Champ id_session manquant"
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:157
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:170
msgid "Service doesn't have access to at least one participant of that session."
msgstr "Le service n'a pas accès à au moins un participant de la séance."
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:164
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:177
msgid "Service doesn't have access to at least one user of that session."
msgstr "Le service n'a pas accès à au moins un utilisateur de la séance."
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:171
+#: modules/FlaskModule/API/service/ServiceQuerySessions.py:184
msgid "Service doesn't have access to at least one device of that session."
msgstr "Le service n'a pas accès à au moins un appareil de la séance."
-#: modules/FlaskModule/API/service/ServiceQuerySessions.py:189
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:129
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:121
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:101
-msgid "Missing id_session_type"
-msgstr "Champ id_session_type manquant"
-
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:115
-#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:93
-#: modules/FlaskModule/API/user/UserQueryTestType.py:134
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:127
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:119
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:124
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:273
+#: modules/FlaskModule/API/user/UserQueryTestType.py:151
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:131
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:123
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:276
msgid "Missing id_test_type"
msgstr "Champ id_test_type manquant"
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:117
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:143
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:144
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:131
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:129
-msgid "Missing projects"
-msgstr "Projets manquants"
-
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:129
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:141
-msgid "Access denied to at least one project"
-msgstr "Accès refusé pour au moins un projet"
-
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:145
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:176
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:266
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:157
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:188
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:285
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:154
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:185
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:278
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:161
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:192
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:291
msgid ""
"Can't delete test type from project: please delete all tests of that type in the "
"project before deleting."
@@ -787,106 +964,123 @@ msgstr ""
"Impossible de supprimer le type de test: veuillez supprimer tous les tests de ce "
"type au préalable."
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:152
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:161
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:164
-msgid "Missing project ID"
-msgstr "ID de projet manquant"
-
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:154
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:166
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:152
+#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:163
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:170
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:156
msgid "Missing test types"
msgstr "Types de test manquants"
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:185
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:196
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:197
-msgid "Unknown format"
-msgstr "Format inconnu"
-
-#: modules/FlaskModule/API/service/ServiceQueryTestTypeProjects.py:190
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:195
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:207
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:201
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:200
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:202
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:190
-msgid "Badly formatted request"
-msgstr "Requête mal formée"
-
-#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:87
-#: modules/FlaskModule/API/user/UserQueryTestType.py:128
+#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:98
+#: modules/FlaskModule/API/user/UserQueryTestType.py:145
msgid "Missing test_type"
msgstr "Champ test_type manquant"
-#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:155
+#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:104
+msgid "Missing id_test_type or test_type_uuid"
+msgstr "Champ id_test_type ou test_type_uuid manquant"
+
+#: modules/FlaskModule/API/service/ServiceQueryTestTypes.py:174
msgid "Test type not related to this service. Can't delete."
msgstr "Le type de test n’est pas associé au service. Impossible de supprimer."
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:131
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:137
msgid "Missing test field"
msgstr "Champ \"test\" manquant"
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:137
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:143
msgid "Missing id_test field"
msgstr "Champ id_test manquant"
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:154
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:160
msgid "Missing id_test_type field"
msgstr "Champ id_test_type manquant"
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:159
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:165
msgid "Invalid test type"
msgstr "Type de test invalide"
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:189
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:195
msgid "Service can't create tests for that session"
msgstr "Le service ne peut pas créer de tests pour cette séance"
-#: modules/FlaskModule/API/service/ServiceQueryTests.py:266
+#: modules/FlaskModule/API/service/ServiceQueryTests.py:275
msgid "Service can't delete tests for that session"
msgstr "Le service ne peut pas effacer les tests de cette séance"
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:81
-#: modules/FlaskModule/API/user/UserQueryUserGroups.py:132
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:277
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:280
+msgid "Missing test_invitation_expiration_date"
+msgstr "Date d'expiration de l'invitation manquante"
+
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:279
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:282
+msgid "Do not set test_invitation_key"
+msgstr "Ne spécifiez pas la clé de l'invitation"
+
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:283
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:286
+msgid "You must specify one of id_user, id_participant or id_device"
+msgstr "Ne peut pas combiner id_user, id_participant et id_device dans la requête"
+
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:292
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:295
+msgid "You can only update test_invitation_count"
+msgstr "Seulement test_invitation_count peut être mis à jour"
+
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:320
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:322
+msgid "Required parameter is missing"
+msgstr "Paramètre requis manquant"
+
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:329
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:331
+msgid "Invalid JSON structure"
+msgstr "Structure JSON invalide"
+
+#: modules/FlaskModule/API/service/ServiceQueryTestsInvitations.py:331
+#: modules/FlaskModule/API/user/UserQueryTestsInvitations.py:333
+msgid "Invalid JSON schema"
+msgstr "Schéma JSON invalide"
+
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:86
+#: modules/FlaskModule/API/user/UserQueryUserGroups.py:135
msgid "Missing user_group"
msgstr "Champ user_group manquant"
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:88
-#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:196
-#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:179
-#: modules/FlaskModule/API/user/UserQueryUserGroups.py:139
-#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:102
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:93
+#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:199
+#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:182
+#: modules/FlaskModule/API/user/UserQueryUserGroups.py:142
+#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:106
msgid "Missing id_user_group"
msgstr "Champ id_user_group manquant"
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:100
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:105
msgid "Missing service role name or id_service_role"
msgstr "Nom du rôle ou id_service_role manquant"
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:113
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:118
msgid "Can't set access to service other than self"
msgstr "Impossible d'ajuster les accès à un service autre que soi-même"
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:119
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:124
msgid "No access for at a least one project in the list"
msgstr "Aucun accès pour au moins un projet dans la liste"
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:125
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:130
msgid "No access for at a least one site in the list"
msgstr "Aucun accès pour au moins un site dans la liste"
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:137
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:142
msgid "Bad role name for service"
msgstr "Nom de rôle invalide pour le service"
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:155
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:160
msgid "A new usergroup must have at least one service access"
msgstr "Un nouveau groupe utilisateur doit avoir au moins un accès à un service"
-#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:217
-#: modules/FlaskModule/API/user/UserQueryUserGroups.py:302
+#: modules/FlaskModule/API/service/ServiceQueryUserGroups.py:225
+#: modules/FlaskModule/API/user/UserQueryUserGroups.py:307
msgid ""
"Can't delete user group: please delete all users part of that user group before "
"deleting."
@@ -894,75 +1088,149 @@ msgstr ""
"Impossible de supprimer le groupe d'utilisateurs: veuillez retirer tous les "
"utilisateurs de ce groupe au préalable."
-#: modules/FlaskModule/API/service/ServiceSessionManager.py:116
-#: modules/FlaskModule/API/user/UserSessionManager.py:107
+#: modules/FlaskModule/API/service/ServiceQueryUsers.py:42
+#: modules/FlaskModule/API/service/ServiceQueryUsers.py:50
+msgid "Service doesn't have permission to access the requested data"
+msgstr "Le service n'a pas accès aux données demandées"
+
+#: modules/FlaskModule/API/service/ServiceSessionManager.py:118
+#: modules/FlaskModule/API/user/UserSessionManager.py:108
msgid "Missing action"
msgstr "Action manquante"
-#: modules/FlaskModule/API/service/ServiceSessionManager.py:131
-#: modules/FlaskModule/API/user/UserSessionManager.py:121
-#: modules/FlaskModule/API/user/UserSessionManager.py:134
+#: modules/FlaskModule/API/service/ServiceSessionManager.py:133
+#: modules/FlaskModule/API/user/UserSessionManager.py:122
+#: modules/FlaskModule/API/user/UserSessionManager.py:135
msgid "Invalid session"
msgstr "Séance invalide"
-#: modules/FlaskModule/API/service/ServiceSessionManager.py:139
+#: modules/FlaskModule/API/service/ServiceSessionManager.py:141
msgid "Service doesn't have access to that session"
msgstr "Le service n'a pas accès à cette séance"
-#: modules/FlaskModule/API/service/ServiceSessionManager.py:160
-#: modules/FlaskModule/API/user/UserSessionManager.py:155
+#: modules/FlaskModule/API/service/ServiceSessionManager.py:162
+#: modules/FlaskModule/API/user/UserSessionManager.py:156
msgid "Missing required id_session_type for new sessions"
msgstr "Champ id_session_type manquant pour les nouvelles séances"
-#: modules/FlaskModule/API/service/ServiceSessionManager.py:166
+#: modules/FlaskModule/API/service/ServiceSessionManager.py:168
msgid "Invalid session type"
msgstr "Type de séance invalide"
-#: modules/FlaskModule/API/service/ServiceSessionManager.py:201
-#: modules/FlaskModule/API/user/UserSessionManager.py:192
+#: modules/FlaskModule/API/service/ServiceSessionManager.py:203
+#: modules/FlaskModule/API/user/UserSessionManager.py:193
msgid "Service not found"
msgstr "Service non trouvé"
-#: modules/FlaskModule/API/service/ServiceSessionManager.py:209
-#: modules/FlaskModule/API/user/UserSessionManager.py:197
+#: modules/FlaskModule/API/service/ServiceSessionManager.py:211
+#: modules/FlaskModule/API/user/UserSessionManager.py:198
msgid "Not implemented yet"
msgstr "Non encore implémenté"
-#: modules/FlaskModule/API/service/ServiceSessionManager.py:224
-#: modules/FlaskModule/API/user/UserSessionManager.py:213
+#: modules/FlaskModule/API/service/ServiceSessionManager.py:226
+#: modules/FlaskModule/API/user/UserSessionManager.py:214
msgid "No answer from service."
msgstr "Aucune réponse du service."
-#: modules/FlaskModule/API/user/UserLogin.py:82
+#: modules/FlaskModule/API/user/UserLogin.py:41
+msgid "Password change required for this user."
+msgstr "Changement de mot de passe requis pour cet utilisateur."
+
+#: modules/FlaskModule/API/user/UserLogin.py:52
+msgid "2FA required for this user."
+msgstr "2FA requise pour cet utilisateur."
+
+#: modules/FlaskModule/API/user/UserLogin.py:56
+msgid "2FA enabled but OTP not set for this user.Please setup 2FA."
+msgstr ""
+"Authentification multi-facteurs requise, mais non configurée pour cet "
+"utilisateur. Veuillez configurer celle-ci."
+
+#: modules/FlaskModule/API/user/UserLogin.py:79
+#: modules/FlaskModule/API/user/UserLogin2FA.py:101
+#: modules/FlaskModule/API/user/UserLoginBase.py:154
+#: modules/FlaskModule/API/user/UserLoginSetup2FA.py:85
+#: modules/FlaskModule/API/user/UserLoginSetup2FA.py:155
+msgid "Client major version too old, not accepting login"
+msgstr "La version du client est trop vieille (major), accès refusé"
+
+#: modules/FlaskModule/API/user/UserLogin2FA.py:49
+#: modules/FlaskModule/API/user/UserLoginSetup2FA.py:49
+#: modules/FlaskModule/API/user/UserLoginSetup2FA.py:119
+msgid "User does not have 2FA enabled"
+msgstr "L'utilisation n'a pas la double authentification d'activée"
+
+#: modules/FlaskModule/API/user/UserLogin2FA.py:54
+msgid "User does not have 2FA OTP enabled or secret set"
+msgstr ""
+"L'utilisateur n'a pas la double authentification par OTP d'activée ou de "
+"configurée"
+
+#: modules/FlaskModule/API/user/UserLogin2FA.py:74
+#: modules/FlaskModule/API/user/UserLoginSetup2FA.py:133
+msgid "Invalid OTP code"
+msgstr "Code OTP invalide"
+
+#: modules/FlaskModule/API/user/UserLoginBase.py:90
+msgid "User already logged in :"
+msgstr "L'utilisateur est déjà connecté :"
+
+#: modules/FlaskModule/API/user/UserLoginBase.py:92
msgid "User already logged in."
msgstr "L'utilisateur est déjà connecté."
-#: modules/FlaskModule/API/user/UserLogin.py:116
-#: modules/FlaskModule/API/user/UserLogin.py:134
+#: modules/FlaskModule/API/user/UserLoginBase.py:101
+msgid "Too many 2FA attempts. Please wait and try again."
+msgstr "Trop d'essais d'authentification. Veuillez attendre et essayer à nouveau."
+
+#: modules/FlaskModule/API/user/UserLoginBase.py:134
+msgid "Client major version mismatch"
+msgstr "La version majeure du client ne correspond pas"
+
+#: modules/FlaskModule/API/user/UserLoginBase.py:151
msgid "Client version mismatch"
msgstr "La version du client ne correspond pas"
-#: modules/FlaskModule/API/user/UserLogin.py:136
-msgid "Client major version too old, not accepting login"
-msgstr "La version du client est trop vieille (major), accès refusé"
+#: modules/FlaskModule/API/user/UserLoginBase.py:171
+msgid "Unknown client name :"
+msgstr "Nom du client inconnu :"
+
+#: modules/FlaskModule/API/user/UserLoginChangePassword.py:34
+#: modules/FlaskModule/Views/LoginChangePasswordView.py:48
+msgid "New password and confirm password do not match"
+msgstr "Le nouveau et l'ancien mot de passe ne correspondent pas"
+
+#: modules/FlaskModule/API/user/UserLoginChangePassword.py:37
+msgid "User not required to change password"
+msgstr "L'utilisateur n'a pas à changer son mot de passe"
+
+#: modules/FlaskModule/API/user/UserLoginChangePassword.py:47
+#: modules/FlaskModule/API/user/UserQueryUsers.py:260
+msgid "New password same as old password"
+msgstr "Le nouveau mot de passe est identique à l'ancien"
-#: modules/FlaskModule/API/user/UserLogin.py:143
-msgid "Invalid client version handler"
-msgstr "Mauvaise version du client"
+#: modules/FlaskModule/API/user/UserLoginSetup2FA.py:53
+#: modules/FlaskModule/API/user/UserLoginSetup2FA.py:123
+msgid "User already has 2FA OTP secret set"
+msgstr "L'utilisateur a déjà configuré la double authentification par OTP"
-#: modules/FlaskModule/API/user/UserLogout.py:34
+#: modules/FlaskModule/API/user/UserLoginSetup2FA.py:146
+msgid "2FA enabled for this user."
+msgstr "Double authentification activée pour cet utilisateur."
+
+#: modules/FlaskModule/API/user/UserLogout.py:36
msgid "User logged out."
msgstr "Utilisateur déconnecté."
-#: modules/FlaskModule/API/user/UserLogout.py:36
+#: modules/FlaskModule/API/user/UserLogout.py:38
msgid "User not logged in"
msgstr "L'utilisateur n'est pas connecté"
-#: modules/FlaskModule/API/user/UserQueryAssets.py:79
+#: modules/FlaskModule/API/user/UserQueryAssets.py:80
msgid "Service access denied"
msgstr "Accès au service refusé"
-#: modules/FlaskModule/API/user/UserQueryAssets.py:174
+#: modules/FlaskModule/API/user/UserQueryAssets.py:177
msgid ""
"Asset information update and creation must be done directly into a service (such "
"as Filetransfer service)"
@@ -971,7 +1239,7 @@ msgstr ""
"directement dans un service (comme le service de transfert de fichiers - "
"FileTransfer)"
-#: modules/FlaskModule/API/user/UserQueryAssets.py:182
+#: modules/FlaskModule/API/user/UserQueryAssets.py:187
msgid ""
"Asset information deletion must be done directly into a service (such as "
"Filetransfer service)"
@@ -979,48 +1247,66 @@ msgstr ""
"La suppression d'information sur les ressources doivent être fait directement "
"dans un service (comme le service de transfert de fichiers - FileTransfer)"
-#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:128
-#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:207
+#: modules/FlaskModule/API/user/UserQueryAssetsArchive.py:88
+msgid "Only one of the ID parameter is supported at once"
+msgstr "Un seul des paramètres d'ID est supporté à la fois"
+
+#: modules/FlaskModule/API/user/UserQueryAssetsArchive.py:151
+msgid "Missing required parameter"
+msgstr "Paramètre requis manquant"
+
+#: modules/FlaskModule/API/user/UserQueryAssetsArchive.py:188
+msgid "Unable to create archive information from FileTransferService"
+msgstr ""
+"Impossible de créer les informations de l'archive dans le service de transfert "
+"de fichiers"
+
+#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:131
+#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:212
msgid "User is not admin of the participant's project"
msgstr "L’utilisateur n’est pas administrateur du projet du participant"
-#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:131
-#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:210
+#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:134
+#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:215
msgid "Access denied to device"
msgstr "Aucun accès à l’appareil"
-#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:149
+#: modules/FlaskModule/API/user/UserQueryDeviceParticipants.py:152
msgid "Device not assigned to project or participant"
msgstr "Appareil non assigné à un projet ou un participant"
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:141
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:127
-#: modules/FlaskModule/API/user/UserQueryDevices.py:259
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:144
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:130
+#: modules/FlaskModule/API/user/UserQueryDevices.py:262
msgid "Missing id_device"
msgstr "Champ manquant : id_device"
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:157
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:165
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:188
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:232
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:188
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:243
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:135
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:168
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:273
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:127
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:165
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:133
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:171
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:275
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:125
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:157
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:160
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:168
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:191
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:235
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:192
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:247
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:139
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:172
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:279
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:119
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:153
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:241
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:244
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:131
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:169
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:137
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:175
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:281
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:129
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:161
msgid "Access denied"
msgstr "Accès refusé"
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:172
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:208
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:321
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:175
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:211
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:326
msgid ""
"Can't delete device from project. Please remove all participants associated with "
"the device or all sessions in the project referring to the device before "
@@ -1030,16 +1316,16 @@ msgstr ""
"participants associés à cet appareil et/ou toutes les séances de ce projet "
"impliquant cet appareil."
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:182
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:157
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:185
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:160
msgid "Missing devices"
msgstr "Appareils manquants"
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:238
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:241
msgid "At least one device is not part of the allowed device for that project site"
msgstr "Au moins un appareil n'est pas admissible pour ce projet pour ce site"
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:314
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:319
msgid ""
"Can't delete device from project: please remove all participants with device "
"before deleting."
@@ -1047,7 +1333,7 @@ msgstr ""
"Impossible de retirer l'appareil du projet: veuillez désassocier tous les "
"participants liés à cet appareil dans ce projet au préalable."
-#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:317
+#: modules/FlaskModule/API/user/UserQueryDeviceProjects.py:322
msgid ""
"Can't delete device from project: please remove all sessions in this project "
"referring to that device before deleting."
@@ -1055,15 +1341,15 @@ msgstr ""
"Impossible de retirer l'appareil du projet: veuillez retirer toutes les séances "
"impliquant cet appareil dans ce projet au préalable."
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:129
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:136
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:123
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:121
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:132
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:140
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:127
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:125
msgid "Missing sites"
msgstr "Sites manquants"
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:148
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:175
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:151
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:178
msgid ""
"Can't delete device from site. Please remove all participants associated with "
"the device or all sessions in the site referring to the device before deleting."
@@ -1072,48 +1358,48 @@ msgstr ""
"associés à cet appareil dans ce site et/ou toutes les séances de ce site "
"impliquant cet appareil."
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:155
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:164
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:158
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:150
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:158
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:168
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:162
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:154
msgid "Missing site ID"
msgstr "Champ id_site manquant"
-#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:252
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:263
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:252
+#: modules/FlaskModule/API/user/UserQueryDeviceSites.py:257
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:269
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:258
msgid "Bad parameter"
msgstr "Mauvais Paramètre(s)"
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:50
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:51
msgid "Too Many IDs"
msgstr "Trop d'IDs"
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:58
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:59
msgid "No access to device subtype"
msgstr "Aucun accès au sous-type d'appareil"
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:64
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:65
msgid "No access to device type"
msgstr "Aucun accès au type d'appareil"
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:102
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:105
msgid "Missing device_subtype"
msgstr "Champ device_subtype manquant"
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:109
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:112
msgid "Missing id_device_subtype"
msgstr "Champ id_device_subtype manquant"
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:121
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:124
msgid "Invalid device subtype"
msgstr "Sous-type d’appareil invalide"
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:169
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:174
msgid "Device subtype not found"
msgstr "Sous-type d'appareil non trouvé"
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:179
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:184
msgid ""
"Can't delete device subtype: please delete all devices of that subtype before "
"deleting."
@@ -1121,7 +1407,7 @@ msgstr ""
"Impossible de supprimer le sous-type d'appareil: veuillez supprimer ou modifier "
"tous les appareils utilisant ce sous-type au préalable."
-#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:189
+#: modules/FlaskModule/API/user/UserQueryDeviceSubTypes.py:194
msgid "Device subtype successfully deleted"
msgstr "Sous-type d'appareil supprimé avec succès"
@@ -1131,74 +1417,74 @@ msgid "Unexisting ID/Forbidden access"
msgstr "ID inexistant/Accès interdit"
#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:79
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:400
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:182
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:405
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:185
msgid "Database Error"
msgstr "Erreur de bases de données"
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:95
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:97
msgid "Missing device type"
msgstr "Champ device_type manquant"
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:102
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:104
msgid "Missing id_device_type"
msgstr "Champ id_device_type manquant"
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:113
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:115
msgid "Invalid device type"
msgstr "Type d’appareil invalide"
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:160
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:164
msgid "Tried to delete with 2 parameters"
msgstr "Tentative de suppression avec 2 paramètres conflictuels"
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:168
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:171
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:172
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:175
msgid "Device type not found"
msgstr "Type d'appareil non trouvé"
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:181
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:185
msgid ""
"Can't delete device type: please delete all associated devices before deleting."
msgstr ""
"Impossible de supprimer le type d'appareil: veuillez supprimer tous les "
"appareils associés au préalable."
-#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:193
+#: modules/FlaskModule/API/user/UserQueryDeviceTypes.py:197
msgid "Device type successfully deleted"
msgstr "Type d'appareil supprimé avec succès"
-#: modules/FlaskModule/API/user/UserQueryDevices.py:130
+#: modules/FlaskModule/API/user/UserQueryDevices.py:131
msgid "Too many IDs"
msgstr "Trop de paramètres ID"
-#: modules/FlaskModule/API/user/UserQueryDevices.py:253
+#: modules/FlaskModule/API/user/UserQueryDevices.py:256
msgid "Missing device"
msgstr "Appareil manquant"
-#: modules/FlaskModule/API/user/UserQueryDevices.py:275
-#: modules/FlaskModule/API/user/UserQueryUserGroups.py:177
+#: modules/FlaskModule/API/user/UserQueryDevices.py:278
+#: modules/FlaskModule/API/user/UserQueryUserGroups.py:180
msgid "No site admin access for at a least one project in the list"
msgstr "Aucun accès administrateur pour au moins un projet dans la liste"
-#: modules/FlaskModule/API/user/UserQueryDevices.py:287
-#: modules/FlaskModule/API/user/UserQueryUserGroups.py:158
+#: modules/FlaskModule/API/user/UserQueryDevices.py:290
+#: modules/FlaskModule/API/user/UserQueryUserGroups.py:161
msgid "No site admin access for at a least one site in the list"
msgstr "Aucun administrateur de site pour au moins un site dans la liste"
-#: modules/FlaskModule/API/user/UserQueryDevices.py:412
-#: modules/FlaskModule/API/user/UserQueryUsers.py:343
+#: modules/FlaskModule/API/user/UserQueryDevices.py:417
+#: modules/FlaskModule/API/user/UserQueryUsers.py:351
msgid "Invalid id"
msgstr "ID invalide"
-#: modules/FlaskModule/API/user/UserQueryDevices.py:436
+#: modules/FlaskModule/API/user/UserQueryDevices.py:441
msgid ""
"Can't delete device: please delete all participants association before deleting."
msgstr ""
"Impossible de supprimer l'appareil: veuillez supprimer ou retirer tous les "
"participants associés au préalable."
-#: modules/FlaskModule/API/user/UserQueryDevices.py:439
+#: modules/FlaskModule/API/user/UserQueryDevices.py:444
msgid ""
"Can't delete device: please remove all sessions referring to that device before "
"deleting."
@@ -1206,7 +1492,7 @@ msgstr ""
"Impossible de supprimer l'appareil: veuillez retirer toutes les séances "
"impliquant cet appareil."
-#: modules/FlaskModule/API/user/UserQueryDevices.py:442
+#: modules/FlaskModule/API/user/UserQueryDevices.py:447
msgid ""
"Can't delete device: please remove all sessions created by that device before "
"deleting."
@@ -1214,7 +1500,7 @@ msgstr ""
"Impossible de supprimer l'appareil: veuillez retirer toutes les séances créées "
"par cet appareil au préalable."
-#: modules/FlaskModule/API/user/UserQueryDevices.py:445
+#: modules/FlaskModule/API/user/UserQueryDevices.py:450
msgid ""
"Can't delete device: please delete all assets created by that device before "
"deleting."
@@ -1222,7 +1508,7 @@ msgstr ""
"Impossible de supprimer l'appareil: veuillez supprimer toutes les ressources "
"crées par cet appareil au préalable."
-#: modules/FlaskModule/API/user/UserQueryDevices.py:448
+#: modules/FlaskModule/API/user/UserQueryDevices.py:453
msgid ""
"Can't delete device: please delete all tests created by that device before "
"deleting."
@@ -1230,7 +1516,7 @@ msgstr ""
"Impossible de supprimer l'appareil: veuillez supprimer tous les tests créés par "
"cet appareil au préalable."
-#: modules/FlaskModule/API/user/UserQueryDevices.py:451
+#: modules/FlaskModule/API/user/UserQueryDevices.py:456
msgid ""
"Can't delete device: please remove all related sessions, assets and tests before "
"deleting."
@@ -1238,52 +1524,52 @@ msgstr ""
"Impossible de supprimer l'appareil: veuillez retirer toutes les séances, "
"ressources et tests associés au préalable."
-#: modules/FlaskModule/API/user/UserQueryDevices.py:469
+#: modules/FlaskModule/API/user/UserQueryDevices.py:474
msgid "Device successfully deleted"
msgstr "Appareil supprimé avec succès"
-#: modules/FlaskModule/API/user/UserQueryDisconnect.py:49
-#: modules/FlaskModule/API/user/UserQueryDisconnect.py:57
+#: modules/FlaskModule/API/user/UserQueryDisconnect.py:50
+#: modules/FlaskModule/API/user/UserQueryDisconnect.py:58
msgid "Use Logout instead to disconnect current user"
msgstr "Utilisez l'API de Logout pour déconnecter l'utilisateur actuel"
-#: modules/FlaskModule/API/user/UserQueryForms.py:82
+#: modules/FlaskModule/API/user/UserQueryForms.py:95
msgid "Missing type"
msgstr "Champ type manquant"
-#: modules/FlaskModule/API/user/UserQueryForms.py:137
+#: modules/FlaskModule/API/user/UserQueryForms.py:150
msgid "Missing session type id"
msgstr "Champ id_session_type manquant"
-#: modules/FlaskModule/API/user/UserQueryForms.py:148
+#: modules/FlaskModule/API/user/UserQueryForms.py:161
msgid "No reply from service while querying session type config"
msgstr ""
"Aucune réponse du service lors de la requête de la configuration du type de "
"séance"
-#: modules/FlaskModule/API/user/UserQueryForms.py:182
+#: modules/FlaskModule/API/user/UserQueryForms.py:195
msgid "Invalid service specified"
msgstr "Service non valide"
-#: modules/FlaskModule/API/user/UserQueryForms.py:195
+#: modules/FlaskModule/API/user/UserQueryForms.py:208
msgid "Unknown form type: "
msgstr "Formulaire inconnu: "
#: modules/FlaskModule/API/user/UserQueryOnlineDevices.py:60
-#: modules/FlaskModule/API/user/UserQueryOnlineParticipants.py:69
-#: modules/FlaskModule/API/user/UserQueryOnlineUsers.py:57
+#: modules/FlaskModule/API/user/UserQueryOnlineParticipants.py:70
+#: modules/FlaskModule/API/user/UserQueryOnlineUsers.py:59
msgid "Internal server error when making RPC call."
msgstr "Erreur interne du serveur pour RPC."
-#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:93
+#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:96
msgid "Missing group"
msgstr "Groupe manquant"
-#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:102
+#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:105
msgid "Missing id_participant_group or id_project"
msgstr "Champs id_participant_group ou id_project manquants"
-#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:170
+#: modules/FlaskModule/API/user/UserQueryParticipantGroup.py:175
msgid ""
"Can't delete participant group: please delete all sessions from all participants "
"before deleting."
@@ -1291,41 +1577,41 @@ msgstr ""
"Impossible de supprimer le groupe: veuillez supprimer toutes les séances de tous "
"les participants du groupe au préalable."
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:220
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:223
msgid "Missing participant"
msgstr "Participant manquant"
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:226
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:229
msgid "Missing id_participant"
msgstr "ID Participant manquant"
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:230
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:233
msgid "Missing id_project or id_participant_group"
msgstr "Champ manquants: id_participant, id_project ou id_participant_group"
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:245
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:248
msgid "No admin access to project"
msgstr "Aucun accès administrateur à ce projet"
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:253
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:256
msgid "No admin access to group"
msgstr "Aucun accès administrateur à ce groupe"
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:262
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:265
msgid "Participant group not found."
msgstr "Le groupe participant n'existe pas."
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:266
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:269
msgid "Mismatch between id_project and group's project"
msgstr "Aucune correspondance entre id_project et le projet"
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:383
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:388
msgid "Can't delete participant: please remove all related sessions beforehand."
msgstr ""
"Impossible de supprimer le participant: veuillez supprimer toutes les séances de "
"ce participant au préalable."
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:385
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:390
msgid ""
"Can't delete participant: please remove all sessions created by this participant "
"beforehand."
@@ -1333,19 +1619,19 @@ msgstr ""
"Impossible de supprimer le participant: veuillez supprimer toutes les séances "
"créées par ce participant au préalable."
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:388
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:393
msgid "Can't delete participant: please remove all related assets beforehand."
msgstr ""
"Impossible de supprimer le participant: veuillez supprimer toutes les ressources "
"de ce participant au préalable."
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:390
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:395
msgid "Can't delete participant: please remove all related tests beforehand."
msgstr ""
"Impossible de supprimer le participant: veuillez supprimer tous les tests "
"associés à ce participant au préalable."
-#: modules/FlaskModule/API/user/UserQueryParticipants.py:392
+#: modules/FlaskModule/API/user/UserQueryParticipants.py:397
msgid ""
"Can't delete participant: please remove all related sessions, assets and tests "
"before deleting."
@@ -1353,56 +1639,56 @@ msgstr ""
"Impossible de supprimer le participant: veuillez retirer toutes les séances, "
"ressources et tests associés à ce participant au préalable."
-#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:200
-#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:183
+#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:203
+#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:186
msgid "Missing role name or id"
msgstr "Nom du rôle ou ID manquant"
-#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:231
+#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:234
msgid "Invalid role name or id for that project"
msgstr "Nom du rôle ou ID invalide pour ce projet"
-#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:270
+#: modules/FlaskModule/API/user/UserQueryProjectAccess.py:275
msgid "No project access to delete."
msgstr "Aucun accès au projet pour supprimer."
-#: modules/FlaskModule/API/user/UserQueryProjects.py:169
+#: modules/FlaskModule/API/user/UserQueryProjects.py:173
msgid "No access to a session type for at least one of it"
msgstr "Pas d'accès à ce type de session pour au moins un projet"
-#: modules/FlaskModule/API/user/UserQueryProjects.py:181
+#: modules/FlaskModule/API/user/UserQueryProjects.py:185
msgid "At least one session type is not associated to the project site"
msgstr "Au moins un type de séance n’est pas associé au site du projet"
-#: modules/FlaskModule/API/user/UserQueryServiceAccessToken.py:35
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:189
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:142
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:134
-#: modules/FlaskModule/API/user/UserQueryServices.py:128
+#: modules/FlaskModule/API/user/UserQueryServiceAccessToken.py:37
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:192
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:146
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:138
+#: modules/FlaskModule/API/user/UserQueryServices.py:131
msgid "Missing id_service"
msgstr "Champ id_service manquant"
-#: modules/FlaskModule/API/user/UserQueryServiceAccessToken.py:38
+#: modules/FlaskModule/API/user/UserQueryServiceAccessToken.py:40
msgid "No access to specified service"
msgstr "Aucun accès au service spécifié"
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:72
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:146
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:73
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:149
msgid "Can't combine id_user, id_participant and id_device in request"
msgstr "Ne peut pas combiner id_user, id_participant et id_device dans la requête"
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:121
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:124
msgid "Missing service_config"
msgstr "Champ service_config manquant"
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:175
-#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:198
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:178
+#: modules/FlaskModule/API/user/UserQueryServiceConfigs.py:201
msgid "Invalid config format provided"
msgstr "Le format de la configuration est invalide"
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:170
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:211
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:339
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:174
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:215
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:345
msgid ""
"Can't delete service-project: please remove all related sessions, assets and "
"tests before deleting."
@@ -1410,21 +1696,22 @@ msgstr ""
"Impossible de retirer ce service de ce projet: veuillez supprimer toutes les "
"séances, ressources et tests en lien avec ce service au préalable."
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:181
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:166
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:185
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:170
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:115
msgid "Missing services"
msgstr "Services manquants"
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:249
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:253
msgid ""
"At least one service is not part of the allowed service for that project site"
msgstr "Au moins un service n'est pas permis pour ce projet pour ce site"
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:311
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:317
msgid "Operation not completed"
msgstr "Opération non complétée"
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:332
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:338
msgid ""
"Can't delete service-project: please remove all sessions involving a session "
"type using this project beforehand."
@@ -1432,21 +1719,21 @@ msgstr ""
"Impossible de retirer ce service de ce projet: veuillez supprimer toutes les "
"séances impliquant un type de séance en lien avec ce service au préalable."
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:335
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:341
msgid "Can't delete service-project: please remove all related assets beforehand."
msgstr ""
"Impossible de retirer ce service de ce projet: veuillez supprimer toutes les "
"ressources associées au préalable."
-#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:337
+#: modules/FlaskModule/API/user/UserQueryServiceProjects.py:343
msgid "Can't delete service-project: please remove all related tests beforehand."
msgstr ""
"Impossible de retirer ce service de ce projet: veuillez supprimer tous les tests "
"associés à ce service au préalable."
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:155
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:185
-#: modules/FlaskModule/API/user/UserQueryServiceSites.py:278
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:159
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:189
+#: modules/FlaskModule/API/user/UserQueryServiceSites.py:284
msgid ""
"Can't delete service from site: please delete all sessions, assets and tests "
"related to that service beforehand."
@@ -1454,24 +1741,24 @@ msgstr ""
"Impossible de retirer le service de ce site: veuillez supprimer toutes les "
"séances, ressources et tests reliés à ce service dans ce site au préalable."
-#: modules/FlaskModule/API/user/UserQueryServices.py:150
+#: modules/FlaskModule/API/user/UserQueryServices.py:153
msgid "OpenTera service can't be updated using this API"
msgstr "Les services ne peuvent pas être mis-à-jour par cet API"
-#: modules/FlaskModule/API/user/UserQueryServices.py:164
-#: modules/FlaskModule/API/user/UserQueryServices.py:184
+#: modules/FlaskModule/API/user/UserQueryServices.py:167
+#: modules/FlaskModule/API/user/UserQueryServices.py:187
msgid "Invalid config json schema"
msgstr "Schéma JSON invalide pour la configuration"
-#: modules/FlaskModule/API/user/UserQueryServices.py:168
+#: modules/FlaskModule/API/user/UserQueryServices.py:171
msgid "Missing service_key"
msgstr "Clé du service manquante"
-#: modules/FlaskModule/API/user/UserQueryServices.py:236
+#: modules/FlaskModule/API/user/UserQueryServices.py:241
msgid "Invalid service"
msgstr "Service invalide"
-#: modules/FlaskModule/API/user/UserQueryServices.py:251
+#: modules/FlaskModule/API/user/UserQueryServices.py:256
msgid ""
"Can't delete service: please delete all sessions, assets and tests related to "
"that service beforehand."
@@ -1479,8 +1766,8 @@ msgstr ""
"Impossible de supprimer le service: veuillez supprimer toutes les séances, "
"ressources et tests reliés à ce service au préalable."
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:154
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:186
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:158
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:190
msgid ""
"Can't delete session type from project: please delete all sessions using that "
"type in that project before deleting."
@@ -1488,26 +1775,47 @@ msgstr ""
"Impossible de retirer le type de séance du projet: veuillez supprimer toutes les "
"séances de ce type dans ce projet avant de supprimer."
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:163
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:160
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:167
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:148
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:164
msgid "Missing session types"
msgstr "Champ session_type manquant"
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:212
+#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:216
msgid "At least one session type is not associated to the site of its project"
msgstr "Au moins un type de session n'est pas associé au site de ce projet"
-#: modules/FlaskModule/API/user/UserQuerySessionTypeProjects.py:283
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:138
msgid ""
-"Can't delete session type from project: please delete all sessions of that type "
-"in the project before deleting."
+"Can't remove associated service from session type: please delete all sessions "
+"using that type before deleting."
msgstr ""
-"Impossible de retirer le type de séance du projet: veuillez supprimer toutes les "
-"séances de ce type dans ce projet avant de supprimer."
+"Impossible de retirer le service du type de séance: veuillez supprimer toutes "
+"les séances de ce type au préalable."
+
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:146
+msgid "Missing service ID"
+msgstr "ID Service manquant"
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:150
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:185
-#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:278
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:171
+msgid ""
+"Can't remove session type association from service: please delete all sessions "
+"using that type before deleting."
+msgstr ""
+"Impossible de retirer le type de séance du service: veuillez supprimer toutes "
+"les séances de ce type avant de supprimer."
+
+#: modules/FlaskModule/API/user/UserQuerySessionTypeServices.py:254
+msgid ""
+"Can't remove associated service from session type: please delete all sessions of "
+"that type before deleting."
+msgstr ""
+"Impossible de retirer le service du type de séance: veuillez supprimer toutes "
+"les séances de ce type au préalable."
+
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:154
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:189
+#: modules/FlaskModule/API/user/UserQuerySessionTypeSites.py:284
msgid ""
"Can't delete session type from site: please delete all sessions of that type in "
"the site before deleting."
@@ -1515,53 +1823,57 @@ msgstr ""
"Impossible de retirer le type de séance du site: veuillez supprimer toutes les "
"séances de ce type dans ce site au préalable."
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:95
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:101
msgid "Missing session_type"
msgstr "Champ session_type manquant"
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:111
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:117
msgid "Missing site(s) to associate that session type to"
msgstr "Site(s) manquant(s) pour l'association avec un type de session"
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:136
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:142
msgid "Missing id_service for session type of type service"
msgstr "Champ id_service pour la session manquant"
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:151
-#: modules/FlaskModule/API/user/UserQueryTestType.py:183
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:157
+#: modules/FlaskModule/API/user/UserQueryTestType.py:200
msgid "No site admin access for at least one site in the list"
msgstr "Aucun accès administrateur de site pour au moins un site dans la liste"
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:161
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:167
msgid "At least one site isn't associated with the service of that session type"
msgstr "Au moins un site n'est pas associé avec ce type de session pour ce service"
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:178
-#: modules/FlaskModule/API/user/UserQueryTestType.py:206
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:184
+#: modules/FlaskModule/API/user/UserQueryTestType.py:223
msgid "No project admin access for at a least one project in the list"
msgstr "Pas administrateur pour au moins un projet da la liste"
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:264
-#: modules/FlaskModule/API/user/UserQueryTestType.py:286
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:201
+msgid "No service admin access for at a least one service in the list"
+msgstr "Aucun administrateur de site pour au moins un site dans la liste"
+
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:287
+#: modules/FlaskModule/API/user/UserQueryTestType.py:303
msgid "Session type not associated to project site"
msgstr "Type de séance non associé au site du projet"
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:292
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:332
msgid "Session type has a a service not associated to its site"
msgstr ""
"Tentative d'association avec un type de séance qui a un service non associé à "
"son site"
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:316
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:359
msgid "Cannot delete because you are not admin in all projects."
msgstr ""
"Impossible de supprimer: vous n'êtes pas administrateur dans tous les projets."
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:321
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:364
msgid "Unable to delete - not admin in at least one project"
msgstr "Impossible de supprimer - pas administrateur dans au moins un projet"
-#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:334
+#: modules/FlaskModule/API/user/UserQuerySessionTypes.py:377
msgid ""
"Can't delete session type: please delete all sessions with that type before "
"deleting."
@@ -1569,97 +1881,103 @@ msgstr ""
"Impossible de supprimer le type de séance: veuillez supprimer toutes les séances "
"de ce type au préalable."
-#: modules/FlaskModule/API/user/UserQuerySessions.py:135
+#: modules/FlaskModule/API/user/UserQuerySessions.py:139
msgid "Missing session participants and users"
msgstr "Usagers ou participants manquants pour la session"
-#: modules/FlaskModule/API/user/UserQuerySessions.py:141
+#: modules/FlaskModule/API/user/UserQuerySessions.py:145
msgid "No access to session."
msgstr "Aucun accès à la séance."
-#: modules/FlaskModule/API/user/UserQuerySessions.py:156
-#: modules/FlaskModule/API/user/UserQuerySessions.py:247
+#: modules/FlaskModule/API/user/UserQuerySessions.py:160
+#: modules/FlaskModule/API/user/UserQuerySessions.py:253
msgid "User doesn't have access to at least one participant of that session."
msgstr "L'utilisateur n'a pas accès à au moins un participant de la séance."
-#: modules/FlaskModule/API/user/UserQuerySessions.py:161
-#: modules/FlaskModule/API/user/UserQuerySessions.py:252
+#: modules/FlaskModule/API/user/UserQuerySessions.py:165
+#: modules/FlaskModule/API/user/UserQuerySessions.py:258
msgid "User doesn't have access to at least one user of that session."
msgstr "L'utilisateur n'a pas accès à au moins un utilisateur de la séance."
-#: modules/FlaskModule/API/user/UserQuerySessions.py:166
-#: modules/FlaskModule/API/user/UserQuerySessions.py:257
+#: modules/FlaskModule/API/user/UserQuerySessions.py:170
+#: modules/FlaskModule/API/user/UserQuerySessions.py:263
msgid "User doesn't have access to at least one device of that session."
msgstr "L'utilisateur n'a pas accès à au moins un appareil de la séance."
-#: modules/FlaskModule/API/user/UserQuerySessions.py:261
+#: modules/FlaskModule/API/user/UserQuerySessions.py:267
msgid "Session is in progress: can't delete that session."
msgstr "La séance est en cours: impossible de supprimer."
-#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:181
+#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:184
msgid "Missing id_site"
msgstr "Champ id_site manquant"
-#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:230
+#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:233
msgid "Invalid role name or id for that site"
msgstr "Nom de rôle ou id invalide(s) pour ce site"
-#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:271
+#: modules/FlaskModule/API/user/UserQuerySiteAccess.py:276
msgid "No site access to delete"
msgstr "Pas d'accès à effacer"
-#: modules/FlaskModule/API/user/UserQuerySites.py:112
+#: modules/FlaskModule/API/user/UserQuerySites.py:116
msgid "Missing site"
msgstr "Site manquant"
-#: modules/FlaskModule/API/user/UserQuerySites.py:119
+#: modules/FlaskModule/API/user/UserQuerySites.py:123
msgid "Missing id_site field"
msgstr "Champ id_site manquant"
-#: modules/FlaskModule/API/user/UserQuerySites.py:187
+#: modules/FlaskModule/API/user/UserQuerySites.py:194
msgid ""
"Can't delete site: please delete all participants with sessions before deleting."
msgstr ""
"Impossible de supprimer le site: veuillez supprimer tous les participants au "
"préalable."
-#: modules/FlaskModule/API/user/UserQueryStats.py:94
+#: modules/FlaskModule/API/user/UserQueryStats.py:96
msgid "Missing id argument"
msgstr "Champ id manquant"
-#: modules/FlaskModule/API/user/UserQueryTestType.py:151
+#: modules/FlaskModule/API/user/UserQueryTestType.py:58
+#: modules/FlaskModule/API/user/UserQueryTestType.py:63
+#: modules/FlaskModule/API/user/UserQueryTestType.py:68
+msgid "Test type not found"
+msgstr "Type de test non trouvé"
+
+#: modules/FlaskModule/API/user/UserQueryTestType.py:168
msgid "Missing project(s) to associate that test type to"
msgstr "Projet(s) manquant(s) pour l'association avec ce type de test"
-#: modules/FlaskModule/API/user/UserQueryTestType.py:192
+#: modules/FlaskModule/API/user/UserQueryTestType.py:209
msgid "At least one site isn't associated with the service of that test type"
msgstr "Au moins un site n'est pas associé avec le service de ce type de test"
-#: modules/FlaskModule/API/user/UserQueryTestType.py:306
+#: modules/FlaskModule/API/user/UserQueryTestType.py:323
msgid "Test type has a a service not associated to its site"
msgstr ""
"Tentative d'association avec un type de test qui a un service non associé à son "
"site"
-#: modules/FlaskModule/API/user/UserQueryTestType.py:328
+#: modules/FlaskModule/API/user/UserQueryTestType.py:347
msgid "Unable to delete - not admin in the related test type service"
msgstr ""
"Impossible de supprimer - pas administrateur pour le type de test du service"
-#: modules/FlaskModule/API/user/UserQueryTestType.py:338
+#: modules/FlaskModule/API/user/UserQueryTestType.py:357
msgid ""
"Can't delete test type: please delete all tests of that type before deleting."
msgstr ""
"Impossible de supprimer le type de test: veuillez supprimer tous les tests de ce "
"type au préalable."
-#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:213
+#: modules/FlaskModule/API/user/UserQueryTestTypeProjects.py:217
msgid "At least one test type is not associated to the site of its project"
msgstr "Au moins un type de test n'est pas associé au site de ce projet"
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:143
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:176
-#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:266
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:147
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:180
+#: modules/FlaskModule/API/user/UserQueryTestTypeSites.py:272
msgid ""
"Can't delete test type from site: please delete all tests of that type in the "
"site before deleting."
@@ -1667,7 +1985,7 @@ msgstr ""
"Impossible de retirer ce type de test du site: veuillez supprimer tous les tests "
"de ce type dans ce site au préalable."
-#: modules/FlaskModule/API/user/UserQueryTests.py:123
+#: modules/FlaskModule/API/user/UserQueryTests.py:127
msgid ""
"Test information update and creation must be done directly into a service (such "
"as Test service)"
@@ -1675,97 +1993,106 @@ msgstr ""
"La création et la mise à jour d'information sur les tests doivent être fait "
"directement dans un service"
-#: modules/FlaskModule/API/user/UserQueryUndelete.py:30
+#: modules/FlaskModule/API/user/UserQueryUndelete.py:32
msgid "No access to this API"
msgstr "Aucun accès à cet API"
-#: modules/FlaskModule/API/user/UserQueryUndelete.py:46
+#: modules/FlaskModule/API/user/UserQueryUndelete.py:48
msgid "Item to undelete not found"
msgstr "L'élément à restaurer est introuvable"
-#: modules/FlaskModule/API/user/UserQueryUndelete.py:49
+#: modules/FlaskModule/API/user/UserQueryUndelete.py:51
msgid "Item can't be undeleted"
msgstr "L'élément ne peut pas être restauré"
-#: modules/FlaskModule/API/user/UserQueryUndelete.py:52
+#: modules/FlaskModule/API/user/UserQueryUndelete.py:54
msgid "Item isn't deleted"
msgstr "L'élément n'est pas supprimé"
-#: modules/FlaskModule/API/user/UserQueryUserGroups.py:142
+#: modules/FlaskModule/API/user/UserQueryUserGroups.py:145
msgid "Missing user group name"
msgstr "Nom du groupe utilisateur manquant"
-#: modules/FlaskModule/API/user/UserQueryUserPreferences.py:89
+#: modules/FlaskModule/API/user/UserQueryUserPreferences.py:92
msgid "Missing app tag"
msgstr "Champ App Tag manquant"
-#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:57
+#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:59
msgid "At least one id must be specified"
msgstr "Au moins un ID doit être spécifié"
-#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:89
+#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:93
msgid "Missing user user group"
msgstr "Champ user_user_group manquant"
-#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:100
-#: modules/FlaskModule/API/user/UserQueryUsers.py:183
+#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:104
+#: modules/FlaskModule/API/user/UserQueryUsers.py:198
msgid "Missing id_user"
msgstr "Champ id_user manquant"
-#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:106
+#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:110
msgid "No access to specified user"
msgstr "Aucun accès à l'utilisateur"
-#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:108
+#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:112
msgid "No access to specified user group"
msgstr "Aucun accès au groupe utilisateurs"
-#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:113
+#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:117
msgid "Super admins can't be associated to an user group"
msgstr ""
"Les super administrateurs ne peuvent pas être associés à un groupe d'utilisateurs"
-#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:158
+#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:164
msgid "Can't delete specified relationship"
msgstr "Impossible de supprimer cette relation"
-#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:161
+#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:167
msgid "No access to relationship's user"
msgstr "Aucun accès à l'utilisateur"
-#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:163
+#: modules/FlaskModule/API/user/UserQueryUserUserGroups.py:169
msgid "No access to relationship's user group"
msgstr "Aucun accès au groupe"
-#: modules/FlaskModule/API/user/UserQueryUsers.py:176
+#: modules/FlaskModule/API/user/UserQueryUsers.py:57
+msgid "Password not long enough"
+msgstr "Mot de passe pas assez long"
+
+#: modules/FlaskModule/API/user/UserQueryUsers.py:191
msgid "Missing user"
msgstr "Champ user manquant"
-#: modules/FlaskModule/API/user/UserQueryUsers.py:198
+#: modules/FlaskModule/API/user/UserQueryUsers.py:213
msgid "No access for at a least one user group in the list"
msgstr "Aucun accès pour au moins un groupe d'utilisateurs dans la liste"
-#: modules/FlaskModule/API/user/UserQueryUsers.py:229
+#: modules/FlaskModule/API/user/UserQueryUsers.py:244
msgid "Username can't be modified"
msgstr "Le code utilisateur ne peut pas être modifié"
-#: modules/FlaskModule/API/user/UserQueryUsers.py:250
+#: modules/FlaskModule/API/user/UserQueryUsers.py:257
+#: modules/FlaskModule/API/user/UserQueryUsers.py:295
+msgid "Password not strong enough"
+msgstr "Mot de passe insécure"
+
+#: modules/FlaskModule/API/user/UserQueryUsers.py:271
msgid "Missing required fields: "
msgstr "Champs manquants: "
-#: modules/FlaskModule/API/user/UserQueryUsers.py:253
+#: modules/FlaskModule/API/user/UserQueryUsers.py:274
msgid "Invalid password"
msgstr "Mot de passe incorrect"
-#: modules/FlaskModule/API/user/UserQueryUsers.py:257
+#: modules/FlaskModule/API/user/UserQueryUsers.py:278
msgid "Username unavailable."
msgstr "Nom d'utilisateur non disponible."
-#: modules/FlaskModule/API/user/UserQueryUsers.py:336
+#: modules/FlaskModule/API/user/UserQueryUsers.py:344
msgid "Sorry, you can't delete yourself!"
msgstr "Désolé, vous ne pouvez pas vous supprimer!"
-#: modules/FlaskModule/API/user/UserQueryUsers.py:366
+#: modules/FlaskModule/API/user/UserQueryUsers.py:374
msgid ""
"Can't delete user: please remove all sessions that this user is part of before "
"deleting."
@@ -1773,7 +2100,7 @@ msgstr ""
"Impossible de supprimer l'utilisateur: veuillez supprimer toutes les séances "
"dont cet utilisateur fait partie au préalable."
-#: modules/FlaskModule/API/user/UserQueryUsers.py:369
+#: modules/FlaskModule/API/user/UserQueryUsers.py:377
msgid ""
"Can't delete user: please remove all sessions created by this user before "
"deleting."
@@ -1781,90 +2108,109 @@ msgstr ""
"Impossible de supprimer l'utilisateur: veuillez supprimer toutes les séances "
"créées par cet utilisateur au préalable."
-#: modules/FlaskModule/API/user/UserQueryUsers.py:372
+#: modules/FlaskModule/API/user/UserQueryUsers.py:380
msgid ""
"Can't delete user: please remove all tests created by this user before deleting."
msgstr ""
"Impossible de supprimer l'utilisateur: veuillez supprimer tous les tests créés "
"par cet utilisateur au préalable."
-#: modules/FlaskModule/API/user/UserQueryUsers.py:375
+#: modules/FlaskModule/API/user/UserQueryUsers.py:383
msgid ""
"Can't delete user: please remove all assets created by this user before deleting."
msgstr ""
"Impossible de supprimer l'utilisateur: veuillez supprimer toutes les ressources "
"créées par cet utilisateur au préalable."
-#: modules/FlaskModule/API/user/UserQueryUsers.py:378
+#: modules/FlaskModule/API/user/UserQueryUsers.py:386
msgid ""
"Can't delete user: please delete all assets created by this user before deleting."
msgstr ""
"Impossible de supprimer l'utilisateur: veuillez supprimer toutes les ressources "
"créées par cet utilisateur au préalable."
-#: modules/FlaskModule/API/user/UserQueryVersions.py:40
+#: modules/FlaskModule/API/user/UserQueryVersions.py:42
msgid "No version information found"
msgstr "Aucune information de version disponible"
-#: modules/FlaskModule/API/user/UserQueryVersions.py:71
+#: modules/FlaskModule/API/user/UserQueryVersions.py:75
msgid "Wrong ClientVersions"
msgstr "Mauvais version du client (ClientVersions)"
-#: modules/FlaskModule/API/user/UserQueryVersions.py:77
+#: modules/FlaskModule/API/user/UserQueryVersions.py:81
msgid "Not authorized"
msgstr "Non autorisé"
-#: modules/FlaskModule/API/user/UserSessionManager.py:129
+#: modules/FlaskModule/API/user/UserSessionManager.py:130
msgid "User doesn't have access to that session"
msgstr "L'utilisateur n'a pas accès à cette session"
-#: modules/FlaskModule/API/user/UserSessionManager.py:160
+#: modules/FlaskModule/API/user/UserSessionManager.py:161
msgid "User doesn't have access to that service."
msgstr "L'utilisateur n'a pas accès à ce service."
-#: modules/FlaskModule/API/user/UserSessionManager.py:165
+#: modules/FlaskModule/API/user/UserSessionManager.py:166
msgid "Missing parameters"
msgstr "Paramètres manquants"
-#: modules/FlaskModule/API/user/UserSessionManager.py:168
+#: modules/FlaskModule/API/user/UserSessionManager.py:169
msgid "Missing reply code in parameters"
msgstr "Manque le reply code dans les paramètres"
-#: modules/FlaskModule/API/user/UserSessionManager.py:181
-#: modules/FlaskModule/API/user/UserSessionManager.py:184
+#: modules/FlaskModule/API/user/UserSessionManager.py:182
+#: modules/FlaskModule/API/user/UserSessionManager.py:185
msgid "Invalid reply code"
msgstr "Le champ reply code est invalide"
-#: modules/LoginModule/LoginModule.py:619 modules/LoginModule/LoginModule.py:652
+#: modules/FlaskModule/Views/LoginChangePasswordView.py:38
+msgid "Missing information"
+msgstr "Information manquante"
+
+#: modules/FlaskModule/Views/LoginChangePasswordView.py:53
+msgid "New password must be different from current"
+msgstr "Le nouveau mot de passe doit être différent de l'actuel"
+
+#: modules/LoginModule/LoginModule.py:219
+msgid "Unauthorized - User must login first to change password"
+msgstr ""
+"Non-autorisé - L'utilisateur doit se connecter pour changer son mot de passe"
+
+#: modules/LoginModule/LoginModule.py:222
+msgid "Unauthorized - 2FA is enabled, must login first and use token"
+msgstr ""
+"Non-autorisé - Authentification multi-facteurs activée, doit se connecter et "
+"utiliser un jeton"
+
+#: modules/LoginModule/LoginModule.py:633 modules/LoginModule/LoginModule.py:666
msgid "Disabled device"
msgstr "Appareil désactivé"
-#: modules/LoginModule/LoginModule.py:629
+#: modules/LoginModule/LoginModule.py:643
msgid "Invalid token"
msgstr "Jeton invalide"
-#: modules/LoginModule/LoginModule.py:729
+#: modules/LoginModule/LoginModule.py:743
msgid "Invalid Token"
msgstr "Jeton invalide"
-#: opentera/db/models/TeraSessionType.py:151 opentera/forms/TeraSessionForm.py:105
+#: opentera/db/models/TeraSessionType.py:152 opentera/forms/TeraSessionForm.py:105
msgid "Unknown"
msgstr "Inconnue"
-#: opentera/db/models/TeraSessionType.py:153
+#: opentera/db/models/TeraSessionType.py:154
#: opentera/forms/TeraSessionTypeForm.py:35 opentera/forms/TeraTestTypeForm.py:24
msgid "Service"
msgstr "Service"
-#: opentera/db/models/TeraSessionType.py:155
+#: opentera/db/models/TeraSessionType.py:156
msgid "File Transfer"
msgstr "Transfert de fichiers"
-#: opentera/db/models/TeraSessionType.py:157
+#: opentera/db/models/TeraSessionType.py:158
msgid "Data Collect"
msgstr "Collecte de données"
-#: opentera/db/models/TeraSessionType.py:159
+#: opentera/db/models/TeraSessionType.py:160
msgid "Protocol"
msgstr "Protocole"
@@ -1910,7 +2256,7 @@ msgid "Device Onlineable?"
msgstr "Se met en ligne?"
#: opentera/forms/TeraDeviceForm.py:44 opentera/forms/TeraParticipantForm.py:50
-#: opentera/forms/TeraUserForm.py:30
+#: opentera/forms/TeraUserForm.py:48
msgid "Last Connection"
msgstr "Dernière connexion"
@@ -1919,8 +2265,8 @@ msgstr "Dernière connexion"
#: opentera/forms/TeraParticipantGroupForm.py:18
#: opentera/forms/TeraProjectForm.py:18 opentera/forms/TeraServiceConfigForm.py:18
#: opentera/forms/TeraServiceForm.py:18 opentera/forms/TeraSessionForm.py:120
-#: opentera/forms/TeraSessionTypeForm.py:25 opentera/forms/TeraSiteForm.py:12
-#: opentera/forms/TeraUserForm.py:13 opentera/forms/TeraUserGroupForm.py:18
+#: opentera/forms/TeraSessionTypeForm.py:25 opentera/forms/TeraSiteForm.py:13
+#: opentera/forms/TeraUserForm.py:14 opentera/forms/TeraUserGroupForm.py:18
#: opentera/forms/TeraVersionsForm.py:18
msgid "Information"
msgstr "Information"
@@ -1938,7 +2284,7 @@ msgid "Device Configuration"
msgstr "Configuration de l'appareil"
#: opentera/forms/TeraDeviceForm.py:55 opentera/forms/TeraDeviceForm.py:57
-#: opentera/forms/TeraUserForm.py:28
+#: opentera/forms/TeraUserForm.py:50
msgid "Notes"
msgstr "Notes"
@@ -2044,7 +2390,7 @@ msgstr "Site"
msgid "Role"
msgstr "Rôle"
-#: opentera/forms/TeraProjectForm.py:28 opentera/forms/TeraSiteForm.py:17
+#: opentera/forms/TeraProjectForm.py:28 opentera/forms/TeraSiteForm.py:18
msgid "Site Name"
msgstr "Nom Site"
@@ -2061,7 +2407,7 @@ msgstr "ID Service"
msgid "Service Config ID"
msgstr "ID Configuration Service"
-#: opentera/forms/TeraServiceConfigForm.py:27 opentera/forms/TeraUserForm.py:17
+#: opentera/forms/TeraServiceConfigForm.py:27 opentera/forms/TeraUserForm.py:18
msgid "User ID"
msgstr "ID Usager"
@@ -2081,43 +2427,49 @@ msgstr "Caméra"
msgid "Mirrored image"
msgstr "Image miroir"
-#: opentera/forms/TeraServiceConfigForm.py:54
+#: opentera/forms/TeraServiceConfigForm.py:53
+msgid "Blurred background"
+msgstr "Flouter l'arrière-plan"
+
+#: opentera/forms/TeraServiceConfigForm.py:55
msgid "URL"
msgstr "Adresse web"
-#: opentera/forms/TeraServiceConfigForm.py:58
+#: opentera/forms/TeraServiceConfigForm.py:59
msgid "Pan-Tilt-Zoom Camera"
msgstr "Caméra Pan-Tilt-Zoom"
-#: opentera/forms/TeraServiceConfigForm.py:59
+#: opentera/forms/TeraServiceConfigForm.py:60
msgid "Control Type"
msgstr "Type de Contrôle"
-#: opentera/forms/TeraServiceConfigForm.py:60
+#: opentera/forms/TeraServiceConfigForm.py:61
msgid "Vivotek"
msgstr "Vivotek"
-#: opentera/forms/TeraServiceConfigForm.py:65
+#: opentera/forms/TeraServiceConfigForm.py:66
msgid "Network Address"
msgstr "Adresse Réseau"
-#: opentera/forms/TeraServiceConfigForm.py:68
+#: opentera/forms/TeraServiceConfigForm.py:69
msgid "Port"
msgstr "Port"
-#: opentera/forms/TeraServiceConfigForm.py:71 opentera/forms/TeraUserForm.py:20
+#: opentera/forms/TeraServiceConfigForm.py:72 opentera/forms/TeraUserForm.py:21
+#: templates/login.html:125
msgid "Username"
msgstr "Code utilisateur"
-#: opentera/forms/TeraServiceConfigForm.py:74 opentera/forms/TeraUserForm.py:26
+#: opentera/forms/TeraServiceConfigForm.py:75 opentera/forms/TeraUserForm.py:46
+#: templates/login.html:131
msgid "Password"
msgstr "Mot de passe"
-#: opentera/forms/TeraServiceConfigForm.py:78
+#: opentera/forms/TeraServiceConfigForm.py:79
msgid "Microphone"
msgstr "Microphone"
-#: opentera/forms/TeraServiceConfigForm.py:79
+#: opentera/forms/TeraServiceConfigForm.py:80
msgid "Secondary Camera"
msgstr "Caméra Secondaire"
@@ -2170,10 +2522,18 @@ msgid "Service Enabled"
msgstr "Service Activé"
#: opentera/forms/TeraServiceForm.py:43
+msgid "Service Contains Assets"
+msgstr "Le service contient des données"
+
+#: opentera/forms/TeraServiceForm.py:44
+msgid "Service Contains Tests"
+msgstr "Le service contient des tests"
+
+#: opentera/forms/TeraServiceForm.py:45
msgid "Service has editable config"
msgstr "Le service peut être configuré"
-#: opentera/forms/TeraServiceForm.py:45
+#: opentera/forms/TeraServiceForm.py:47
msgid "Service Default Configuration"
msgstr "Configuration par défaut du Service"
@@ -2281,11 +2641,15 @@ msgstr "Couleur du Type de Séance"
msgid "Session Type Configuration"
msgstr "Configuration du Type de Séance"
-#: opentera/forms/TeraSiteForm.py:16
+#: opentera/forms/TeraSiteForm.py:17
msgid "Site ID"
msgstr "ID Site"
-#: opentera/forms/TeraSiteForm.py:18
+#: opentera/forms/TeraSiteForm.py:20
+msgid "Users Require 2FA"
+msgstr "Authentification Multi-Facteurs (MFA) requise"
+
+#: opentera/forms/TeraSiteForm.py:22
msgid "Site Role"
msgstr "Rôle du Site"
@@ -2317,35 +2681,47 @@ msgstr "A une interface web"
msgid "Expose Web editor"
msgstr "A un éditeur web"
-#: opentera/forms/TeraUserForm.py:18
+#: opentera/forms/TeraUserForm.py:19
msgid "User UUID"
msgstr "UUID Utilisateur"
-#: opentera/forms/TeraUserForm.py:19
+#: opentera/forms/TeraUserForm.py:20
msgid "User Full Name"
msgstr "Nom complet Utilisateur"
-#: opentera/forms/TeraUserForm.py:21
+#: opentera/forms/TeraUserForm.py:22
msgid "User Enabled"
msgstr "Utilisateur activé"
-#: opentera/forms/TeraUserForm.py:22
+#: opentera/forms/TeraUserForm.py:26
+msgid "Force password change"
+msgstr "Imposer changement de mot de passe"
+
+#: opentera/forms/TeraUserForm.py:28
+msgid "2FA Enabled"
+msgstr "Double authentification (2FA) activée"
+
+#: opentera/forms/TeraUserForm.py:31
+msgid "2FA OTP Enabled"
+msgstr "2FA OTP activé"
+
+#: opentera/forms/TeraUserForm.py:42
msgid "First Name"
msgstr "Prénom"
-#: opentera/forms/TeraUserForm.py:23
+#: opentera/forms/TeraUserForm.py:43
msgid "Last Name"
msgstr "Nom"
-#: opentera/forms/TeraUserForm.py:24
+#: opentera/forms/TeraUserForm.py:44
msgid "Email"
msgstr "Courriel"
-#: opentera/forms/TeraUserForm.py:27
+#: opentera/forms/TeraUserForm.py:47
msgid "User Is Super Administrator"
msgstr "Utilisateur est Super Administrateur"
-#: opentera/forms/TeraUserForm.py:29
+#: opentera/forms/TeraUserForm.py:51
msgid "Profile"
msgstr "Profil"
@@ -2374,7 +2750,6 @@ msgid "OpenTeraServer patch version number"
msgstr "Version OpenTeraServeur (Patch)"
#: opentera/forms/TeraVersionsForm.py:43 templates/about.html:26
-#: templates/disabled_doc.html:26
msgid "Versions"
msgstr "Versions"
@@ -2410,86 +2785,92 @@ msgstr "Version Linux du Client"
msgid "Join me!"
msgstr "Joins-toi à moi!"
-#: opentera/services/BaseWebRTCService.py:383
+#: opentera/services/BaseWebRTCService.py:382
msgid "Cannot create session event"
msgstr "Impossible de créer l'événement de séance"
-#: opentera/services/BaseWebRTCService.py:423
+#: opentera/services/BaseWebRTCService.py:422
msgid "Cannot create process"
msgstr "Impossible de démarrer le processus"
-#: opentera/services/BaseWebRTCService.py:437
+#: opentera/services/BaseWebRTCService.py:436
msgid "Cannot create session"
msgstr "Impossible de créer la séance"
-#: opentera/services/BaseWebRTCService.py:460
+#: opentera/services/BaseWebRTCService.py:459
msgid "Cannot create STOP session event"
msgstr "Impossible de créer l'événement de séance \"Arrêt de séance\""
-#: opentera/services/BaseWebRTCService.py:517
+#: opentera/services/BaseWebRTCService.py:516
msgid "Error stopping session - check server logs"
msgstr ""
"Erreur lors de l'arrêt de la séance - veuillez vérifier les journaux système"
-#: opentera/services/BaseWebRTCService.py:519
+#: opentera/services/BaseWebRTCService.py:518
msgid "No matching session to stop"
msgstr "Aucune séance correspondante n'a pu être arrêtée"
-#: opentera/services/BaseWebRTCService.py:543
-#: opentera/services/BaseWebRTCService.py:620
-#: opentera/services/BaseWebRTCService.py:707
+#: opentera/services/BaseWebRTCService.py:542
+#: opentera/services/BaseWebRTCService.py:619
+#: opentera/services/BaseWebRTCService.py:712
msgid "User"
msgstr "Utilisateur"
-#: opentera/services/BaseWebRTCService.py:545
+#: opentera/services/BaseWebRTCService.py:544
msgid "Error creating user invited session event"
msgstr "Erreur lors de la création de l'événement de séance 'Utilisateur invité\""
-#: opentera/services/BaseWebRTCService.py:557
-#: opentera/services/BaseWebRTCService.py:635
-#: opentera/services/BaseWebRTCService.py:722
+#: opentera/services/BaseWebRTCService.py:556
+#: opentera/services/BaseWebRTCService.py:634
+#: opentera/services/BaseWebRTCService.py:733
msgid "Participant"
msgstr "Participant"
-#: opentera/services/BaseWebRTCService.py:559
+#: opentera/services/BaseWebRTCService.py:558
msgid "Error creating participant invited session event"
msgstr "Erreur lors de la création de l'événement de séance 'Participant invité\""
-#: opentera/services/BaseWebRTCService.py:572
-#: opentera/services/BaseWebRTCService.py:651
-#: opentera/services/BaseWebRTCService.py:737
+#: opentera/services/BaseWebRTCService.py:571
+#: opentera/services/BaseWebRTCService.py:650
+#: opentera/services/BaseWebRTCService.py:755
msgid "Device"
msgstr "Appareil"
-#: opentera/services/BaseWebRTCService.py:574
+#: opentera/services/BaseWebRTCService.py:573
msgid "Error creating device invited session event"
msgstr "Erreur lors de la création de l'événement de séance 'Appareil invité\""
-#: opentera/services/BaseWebRTCService.py:590
+#: opentera/services/BaseWebRTCService.py:589
msgid "Error updating session"
msgstr "Erreur de mise à jour de la séance"
-#: opentera/services/BaseWebRTCService.py:622
+#: opentera/services/BaseWebRTCService.py:621
msgid "Error creating user left session event"
msgstr ""
"Erreur lors de la création de l'événement de séance 'Utilisateur a quitté la "
"séance\""
-#: opentera/services/BaseWebRTCService.py:637
+#: opentera/services/BaseWebRTCService.py:636
msgid "Error creating participant left session event"
msgstr ""
"Erreur lors de la création de l'événement de séance 'Participant a quitté la "
"séance\""
-#: opentera/services/BaseWebRTCService.py:653
+#: opentera/services/BaseWebRTCService.py:652
msgid "Error creating device left session event"
msgstr ""
"Erreur lors de la création de l'événement de séance 'Appareil a quitté la "
"séance\""
-#: opentera/services/BaseWebRTCService.py:710
+#: opentera/services/BaseWebRTCService.py:704
#: opentera/services/BaseWebRTCService.py:725
-#: opentera/services/BaseWebRTCService.py:740
+#: opentera/services/BaseWebRTCService.py:746
+msgid "Creator refused to join session - session ended"
+msgstr "Le créateur de la séance a refusé de se joindre - la séance a été terminée"
+
+#: opentera/services/BaseWebRTCService.py:715
+#: opentera/services/BaseWebRTCService.py:736
+#: opentera/services/BaseWebRTCService.py:758
msgid "Cannot create refused session event"
msgstr "Erreur lors de la création de l'événement de séance 'Invitation refusée\""
@@ -2501,22 +2882,121 @@ msgstr "Téléchargements"
msgid "Latest version: "
msgstr "Dernière version: "
-#: templates/about.html:63 templates/disabled_doc.html:35
+#: templates/about.html:64
msgid "License"
msgstr "Licence"
-#: templates/about.html:77 templates/disabled_doc.html:53
+#: templates/about.html:78
msgid "Authors"
msgstr "Auteurs"
-#: templates/about.html:87 templates/disabled_doc.html:63
+#: templates/about.html:88
msgid "Contributors"
msgstr "Collaborateurs"
-#: templates/disabled_doc.html:48
+#: templates/disabled_doc.html:27
msgid "Documentation is disabled!"
msgstr "La documentation d’API est désactivée!"
+#: templates/login.html:5
+msgid "OpenTera Login Page"
+msgstr "OpenTera - Page de connexion"
+
+#: templates/login.html:89
+msgid "Invalid username or password"
+msgstr "Code utilisateur ou mot de passe incorrect"
+
+#: templates/login.html:139
+msgid "Login"
+msgstr "Connecter"
+
+#: templates/login_change_password.html:5
+msgid "OpenTera - Change Password"
+msgstr "OpenTera - Changement de mot de passe"
+
+#: templates/login_change_password.html:96
+msgid "Password successfully changed!"
+msgstr "Mot de passe changé avec succès!"
+
+#: templates/login_change_password.html:97
+msgid "Redirecting to login screen..."
+msgstr "Redirection vers la page de connexion..."
+
+#: templates/login_change_password.html:104
+msgid "Password change required"
+msgstr "Changement de mot de passe requis"
+
+#: templates/login_change_password.html:110
+msgid "New Password"
+msgstr "Nouveau"
+
+#: templates/login_change_password.html:115
+msgid "Confirm Password"
+msgstr "Confirmation"
+
+#: templates/login_change_password.html:122
+msgid "Change Password"
+msgstr "Changer mot de passe"
+
+#: templates/login_setup_2fa.html:107
+msgid "You need to setup multi-factor authentication before continuing."
+msgstr ""
+"Vous devez configurer l'authentification multi-facteurs avant de poursuivre."
+
+#: templates/login_setup_2fa.html:121
+msgid "OTP configuration"
+msgstr "Configuration OTP"
+
+#: templates/login_setup_2fa.html:127
+msgid "Scan the QR code with your authenticator app"
+msgstr "Numérisez le code QR avec votre application d'authentification"
+
+#: templates/login_setup_2fa.html:128
+msgid "Enter the generated code:"
+msgstr "Entrez le code généré:"
+
+#: templates/login_setup_2fa.html:134 templates/login_setup_2fa.html:151
+#: templates/login_validate_2fa.html:162
+msgid "Validate"
+msgstr "Valider"
+
+#: templates/login_setup_2fa.html:144
+msgid "Use email for authentication"
+msgstr "Utiliser courriel pour authentification"
+
+#: templates/login_setup_2fa.html:149
+msgid "No email address configured. Please contact your administrator."
+msgstr ""
+"Aucune adresse courriel configurée. Veuillez contacter votre administrateur."
+
+#: templates/login_validate_2fa.html:32
+msgid "Code validation timeout. Redirecting to login."
+msgstr "Délai de validation expiré. Veuillez vous connecter à nouveau."
+
+#: templates/login_validate_2fa.html:117
+msgid "Too many attempts - returning to login"
+msgstr "Trop d'essais - veuillez attendre et vous reconnecter à nouveau"
+
+#: templates/login_validate_2fa.html:120
+msgid "Invalid code"
+msgstr "Code invalide"
+
+#: templates/login_validate_2fa.html:120
+msgid "attempts left"
+msgstr "essais restants"
+
+#: templates/login_validate_2fa.html:144
+msgid "Multi Factor Authentication"
+msgstr "Authentification Multi-Facteurs"
+
+#: templates/login_validate_2fa.html:151
+msgid "Authentication code"
+msgstr "Code d'authentification"
+
+#: templates/login_validate_2fa.html:171
+msgid "Successfully authenticated"
+msgstr "Authentification complétée"
+
#~ msgid "Missing id_project or id_site arguments"
#~ msgstr "Champs : id_projet oou id_site manquants"
@@ -2534,3 +3014,12 @@ msgstr "La documentation d’API est désactivée!"
#~ msgid "Invalid client name :"
#~ msgstr "Nom du client invalide :"
+
+#~ msgid "Invalid client version handler"
+#~ msgstr "Mauvaise version du client"
+
+#~ msgid "Invalid old password"
+#~ msgstr "Ancien mot de passe incorrect"
+
+#~ msgid "2FA Email Enabled"
+#~ msgstr "2FA par courriel activée"