diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 17696141..d0a36568 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -19,6 +19,12 @@ Added - App setting ``user_modifiable`` validation (#1536) - ``AppSettingAPI.get_all_by_scope()`` helper (#1534) - ``removeroles`` management command (#1391, #1541) + - Site read only mode (#24) + - ``site_read_only`` site app setting (#24) + - ``is_site_writable()`` rule predicate (#24) + - ``PermissionTestMixin.set_site_read_only()`` helper (#24) + - ``PROJECTROLES_READ_ONLY_MSG`` setting (#24) + - ``SiteReadOnlySettingAjaxView`` Ajax view (#24) Changed ------- @@ -33,6 +39,10 @@ Changed - Deprecate ``AppSettingAPI.get_all()`` (#1534) - Allow no role for old owner in ``RoleAssignmentOwnerTransferMixin`` (#836, #1391) - Allow no role for old owner in ``perform_owner_transfer()`` (#836, #1391) +- **Tokens** + - Update UI for site read-only mode (#24) +- **Userprofile** + - Update UI for site read-only mode (#24) Removed ------- diff --git a/bgjobs/rules.py b/bgjobs/rules.py index 908d33f2..1f12f0a8 100644 --- a/bgjobs/rules.py +++ b/bgjobs/rules.py @@ -41,24 +41,31 @@ # Allow creating background jobs rules.add_perm( 'bgjobs.create_bgjob', - pr_rules.is_project_owner - | pr_rules.is_project_delegate - | pr_rules.is_project_contributor, + ( + pr_rules.is_project_owner + | pr_rules.is_project_delegate + | pr_rules.is_project_contributor + ) + & pr_rules.is_site_writable, ) # Allow modifying or deleting the user's background jobs rules.add_perm( 'bgjobs.update_bgjob_own', - pr_rules.is_project_owner - | pr_rules.is_project_delegate - | pr_rules.is_project_contributor - | pr_rules.is_project_guest, + ( + pr_rules.is_project_owner + | pr_rules.is_project_delegate + | pr_rules.is_project_contributor + | pr_rules.is_project_guest + ) + & pr_rules.is_site_writable, ) # Allow modifying or deleting all background jobs rules.add_perm( 'bgjobs.update_bgjob_all', - pr_rules.is_project_owner | pr_rules.is_project_delegate, + (pr_rules.is_project_owner | pr_rules.is_project_delegate) + & pr_rules.is_site_writable, ) # Allow viewing site-global background jobs (not project-specific). diff --git a/config/settings/base.py b/config/settings/base.py index 1708ecc9..e5ef8977 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -626,15 +626,17 @@ def set_logging(level=None): ) # Optional projectroles settings -# Sidebar icon size. Minimum=18, maximum=42. +# Sidebar icon size (must be between 18-42) PROJECTROLES_SIDEBAR_ICON_SIZE = env.int('PROJECTROLES_SIDEBAR_ICON_SIZE', 36) # PROJECTROLES_SECRET_LENGTH = 32 # PROJECTROLES_HELP_HIGHLIGHT_DAYS = 7 # PROJECTROLES_SEARCH_PAGINATION = 5 -# Support for viewing the site in "kiosk mode" (under work, experimental) +# Support for viewing the site in "kiosk mode" (experimental) # PROJECTROLES_KIOSK_MODE = env.bool('PROJECTROLES_KIOSK_MODE', False) # Scroll project navigation with page content if set False # PROJECTROLES_BREADCRUMB_STICKY = True +# Custom message to be displayed if site read-only mode is enabled +PROJECTROLES_READ_ONLY_MSG = env.str('PROJECTROLES_READ_ONLY_MSG', None) # Hide project apps from the UI (sidebar, dropdown menus and project details) PROJECTROLES_HIDE_PROJECT_APPS = env.list( diff --git a/docs/source/app_projectroles_api_django.rst b/docs/source/app_projectroles_api_django.rst index bf8949a5..ec431f3f 100644 --- a/docs/source/app_projectroles_api_django.rst +++ b/docs/source/app_projectroles_api_django.rst @@ -106,6 +106,8 @@ General utility functions are stored in ``utils.py``. :members: +.. _app_projectroles_api_django_ajax_common: + Common Use Ajax Views ===================== @@ -113,11 +115,16 @@ Ajax views intended to be used in a SODAR Core based site are described here. .. currentmodule:: projectroles.views_ajax +.. autoclass:: CurrentUserRetrieveAjaxView + +.. autoclass:: SiteReadOnlySettingAjaxView + .. autoclass:: SidebarContentAjaxView .. autoclass:: UserDropdownContentAjaxView + .. _app_projectroles_api_django_rest: Base REST API View Classes diff --git a/docs/source/app_projectroles_usage.rst b/docs/source/app_projectroles_usage.rst index 8c7bc2f2..fdeaefbe 100644 --- a/docs/source/app_projectroles_usage.rst +++ b/docs/source/app_projectroles_usage.rst @@ -569,6 +569,5 @@ name and/or description. REST API ======== -Several SODAR Core functionalities are also available via a HTTP REST API -starting in version 0.8. See :ref:`app_projectroles_api_rest` for instructions -on REST API usage. +Many SODAR Core features are also available via a REST API. See +:ref:`app_projectroles_api_rest` for instructions on REST API usage. diff --git a/docs/source/dev_project_app.rst b/docs/source/dev_project_app.rst index df71dc87..4e1ad6e7 100644 --- a/docs/source/dev_project_app.rst +++ b/docs/source/dev_project_app.rst @@ -165,9 +165,18 @@ app if needed. .. hint:: For permissions dealing with modifying data, you are strongly recommend to - use the ``can_modify_project_data`` predicate. For more, see + use the ``can_modify_project_data`` predicate. For more information, see :ref:`dev_project_app_archiving`. +.. hint:: + + To support the site read-only mode introduced in SODAR Core v1.1, the rules + for your app's views need to be implemented accordingly. A check for the + read-only mode is contained in the ``can_modify_project_data()`` predicate. + If your view already uses that predicate, no further steps are necessary. + For site views, ``is_site_writable`` should be used. For more information, + see :ref:`dev_resources_read_only`. + ProjectAppPlugin ================ diff --git a/docs/source/dev_resource.rst b/docs/source/dev_resource.rst index 5adc6f6a..24ac9730 100644 --- a/docs/source/dev_resource.rst +++ b/docs/source/dev_resource.rst @@ -469,6 +469,85 @@ when creating multi-plugin apps: This, again, ensures apps are correctly detected and highlighted in the UI. +.. _dev_resources_read_only: + +Site Read-Only Mode +=================== + +A superuser can temporarily set the site into read-only mode. When the mode is +enabled, all data on the site is only accessible for reading. No project or user +data should be modifiable, except for superusers who still have full access. + +SODAR Core apps enforce this mode by prohibiting access to views and/or UI +elements which enable the user to modify data. Apps developed for a SODAR Core +based site must implement this within their rule and UI logic. + +If your data modifying view is in a project app and uses the +``can_modify_project_data()`` rule predicate, checks for view access are already +performed for that view in the permission checks. Example of this in a +``rules.py`` file: + +.. code-block:: python + + import rules + from projectroles import rules as pr_rules + + rules.add_perm( + 'your_project_app.update_data', + pr_rules.can_modify_project_data + & ( + pr_rules.is_project_owner + | pr_rules.is_project_delegate + | pr_rules.is_project_contributor + ), + ) + +For site views, you can use the ``is_site_writable()`` predicate. Example: + +.. code-block:: python + + import rules + from projectroles import rules as pr_rules + + rules.add_perm( + 'your_site_app.update_data', + rules.is_authenticated & pr_rules.is_site_writable, + ) + +To check for the mode in your Python code, you should use the app settings API +as follows: + +.. code-block:: python + + from projectroles.app_settings import AppSettingAPI + app_settings = AppSettingAPI() + + if app_settings.get('projectroles', 'site_read_only'): + pass # Add logic for read-only mode here + +In templates, the same can be done using the ``get_app_setting()`` template tag. +Example: + +.. code-block:: django + + {% load projectroles_common_tags %} + {% get_app_setting 'projectroles' 'site_read_only' as site_read_only %} + {% if site_read_only %} + {# ... #} + {% endif %} + +If you need to check the site read-only status in client-side apps, you can +query the ``SiteReadOnlySettingAjaxView`` Ajax view. See +:ref:`app_projectroles_api_django_ajax_common` for more information. + +.. note:: + + It is assumed that in read-only mode, superusers are still able to access + data modifying views and operations. The rule settings also allow this. + Actions within management commands should thus also be allowed in read-only + mode. + + Management Command Logger ========================= diff --git a/docs/source/major_changes.rst b/docs/source/major_changes.rst index bbdf0ec3..328c5f31 100644 --- a/docs/source/major_changes.rst +++ b/docs/source/major_changes.rst @@ -16,6 +16,7 @@ v1.1.0 (WIP) Release Highlights ================== +- Add site read-only mode - Add removeroles management command - Add app setting type constants - Add app setting definition as objects @@ -26,6 +27,14 @@ Release Highlights Breaking Changes ================ +Site Read-Only Mode +------------------- + +This release adds the site-wide read-only mode, which is intended to temporarily +prohibit modifying all data on the site. Rules, logic and/or UI of your apps' +views may have to be changed to support this functionality. For more +information, see :ref:`dev_resources_read_only`. + AppSettingAPI Definition Getter Return Data ------------------------------------------- diff --git a/filesfolders/tests/test_permissions.py b/filesfolders/tests/test_permissions.py index cabb47ed..7b632ca6 100644 --- a/filesfolders/tests/test_permissions.py +++ b/filesfolders/tests/test_permissions.py @@ -77,7 +77,7 @@ def make_test_link(self): # Test Cases ------------------------------------------------------------------- -class TestProjectFileViewPermissions( +class TestProjectFileView( FilesfoldersPermissionTestMixin, ProjectPermissionTestBase ): """Tests for ProjectFileView permissions""" @@ -87,10 +87,7 @@ def setUp(self): self.url = reverse( 'filesfolders:list', kwargs={'project': self.project.sodar_uuid} ) - - def test_get(self): - """Test ProjectFileView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, @@ -101,9 +98,16 @@ def test_get(self): self.user_contributor, self.user_guest, ] - bad_users = [self.user_finder_cat, self.user_no_roles, self.anonymous] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.bad_users = [ + self.user_finder_cat, + self.user_no_roles, + self.anonymous, + ] + + def test_get(self): + """Test ProjectFileView GET""" + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) # Test public project self.project.set_public() self.assert_response(self.url, self.user_no_roles, 200) @@ -118,24 +122,18 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - ] - bad_users = [self.user_finder_cat, self.user_no_roles, self.anonymous] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 200) self.assert_response(self.url, self.anonymous, 302) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) + class TestFolderCreateView( FilesfoldersPermissionTestMixin, ProjectPermissionTestBase @@ -181,24 +179,16 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [self.superuser] - bad_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) self.project.set_public() - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.non_superusers, 302) + + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) def test_get_category(self): """Test GET with folder under category (should fail)""" @@ -206,21 +196,7 @@ def test_get_category(self): 'filesfolders:folder_create', kwargs={'project': self.category.sodar_uuid}, ) - bad_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(url, bad_users, 302) + self.assert_response(url, self.all_users, 302) class TestFolderUpdateView( @@ -268,24 +244,16 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [self.superuser] - bad_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) self.project.set_public() - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.non_superusers, 302) + + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) class TestFolderDeleteView( @@ -333,24 +301,16 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [self.superuser] - bad_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) self.project.set_public() - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.non_superusers, 302) + + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) class TestFileCreateView( @@ -394,27 +354,19 @@ def test_get_anon(self): self.project.set_public() self.assert_response(self.url, self.anonymous, 302) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) + def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [self.superuser] - bad_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) self.project.set_public() - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.non_superusers, 302) def test_get_category(self): """Test GET under category (should fail)""" @@ -422,21 +374,7 @@ def test_get_category(self): 'filesfolders:file_create', kwargs={'project': self.category.sodar_uuid}, ) - bad_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(url, bad_users, 302) + self.assert_response(url, self.all_users, 302) class TestFileUpdateView( @@ -483,24 +421,16 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [self.superuser] - bad_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) self.project.set_public() - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.non_superusers, 302) + + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) class TestFileDeleteView( @@ -547,24 +477,16 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [self.superuser] - bad_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) self.project.set_public() - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.non_superusers, 302) + + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) class TestFilePublicLinkView( @@ -579,10 +501,7 @@ def setUp(self): 'filesfolders:file_public_link', kwargs={'file': file.sodar_uuid}, ) - - def test_get(self): - """Test FilePublicLinkView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, @@ -591,17 +510,20 @@ def test_get(self): self.user_delegate, self.user_contributor, ] - bad_users = [ + self.bad_users = [ self.user_guest_cat, self.user_finder_cat, self.user_guest, self.user_no_roles, self.anonymous, ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + + def test_get(self): + """Test FilePublicLinkView GET""" + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.bad_users, 302) @override_settings(PROJECTROLES_ALLOW_ANONYMOUS=True) def test_get_anon(self): @@ -610,28 +532,18 @@ def test_get_anon(self): self.assert_response(self.url, self.anonymous, 302) def test_get_archive(self): - """Test get with archived project""" + """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - ] - bad_users = [ - self.user_guest_cat, - self.user_finder_cat, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.bad_users, 302) + + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) class TestFileServeView( @@ -646,10 +558,7 @@ def setUp(self): 'filesfolders:file_serve', kwargs={'file': file.sodar_uuid, 'file_name': file.name}, ) - - def test_get(self): - """Test FileServeView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, @@ -660,9 +569,16 @@ def test_get(self): self.user_contributor, self.user_guest, ] - bad_users = [self.user_finder_cat, self.user_no_roles, self.anonymous] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.bad_users = [ + self.user_finder_cat, + self.user_no_roles, + self.anonymous, + ] + + def test_get(self): + """Test FileServeView GET""" + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 200) self.assert_response(self.url, self.anonymous, 302) @@ -676,24 +592,18 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - ] - bad_users = [self.user_finder_cat, self.user_no_roles, self.anonymous] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 200) self.assert_response(self.url, self.anonymous, 302) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) + class TestFileServePublicView( FilesfoldersPermissionTestMixin, ProjectPermissionTestBase @@ -710,21 +620,7 @@ def setUp(self): def test_get(self): """Test FileServePublicView GET""" - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, good_users, 200) + self.assert_response(self.url, self.all_users, 200) self.project.set_public() self.assert_response( self.url, [self.user_no_roles, self.anonymous], 200 @@ -737,23 +633,9 @@ def test_get_anon(self): self.assert_response(self.url, self.anonymous, 200) def test_get_archived(self): - """Test FileServePublicView GET with archived project""" + """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, good_users, 200) + self.assert_response(self.url, self.all_users, 200) self.project.set_public() self.assert_response( self.url, [self.user_no_roles, self.anonymous], 200 @@ -764,21 +646,12 @@ def test_get_disabled(self): app_settings.set( APP_NAME, 'allow_public_links', False, project=self.project ) - bad_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, bad_users, 400) + self.assert_response(self.url, self.all_users, 400) + + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.all_users, 200) class TestHyperLinkCreateView( @@ -825,24 +698,16 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [self.superuser] - bad_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) self.project.set_public() - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.non_superusers, 302) + + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) def test_get_category(self): """Test GET under category (should fail)""" @@ -850,21 +715,7 @@ def test_get_category(self): 'filesfolders:hyperlink_create', kwargs={'project': self.category.sodar_uuid}, ) - bad_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(url, bad_users, 302) + self.assert_response(url, self.all_users, 302) class TestHyperLinkUpdateView( @@ -912,24 +763,16 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [self.superuser] - bad_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) self.project.set_public() - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.non_superusers, 302) + + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) class TestHyperLinkDeleteView( @@ -977,24 +820,16 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [self.superuser] - bad_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) self.project.set_public() - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.non_superusers, 302) + + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) class TestBatchEditView( @@ -1053,26 +888,43 @@ def test_post_anon(self): def test_post_archive(self): """Test POST with archived project""" self.project.set_archive() - good_users = [self.superuser] - bad_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response( - self.url, good_users, 200, method='POST', data=self.post_data + self.url, self.superuser, 200, method='POST', data=self.post_data ) self.assert_response( - self.url, bad_users, 302, method='POST', data=self.post_data + self.url, + self.non_superusers, + 302, + method='POST', + data=self.post_data, ) self.project.set_public() self.assert_response( - self.url, bad_users, 302, method='POST', data=self.post_data + self.url, + self.non_superusers, + 302, + method='POST', + data=self.post_data, + ) + + def test_post_read_only(self): + """Test POST with site read-only mode""" + self.set_site_read_only() + self.assert_response( + self.url, self.superuser, 200, method='POST', data=self.post_data + ) + self.assert_response( + self.url, + self.non_superusers, + 302, + method='POST', + data=self.post_data, + ) + self.project.set_public() + self.assert_response( + self.url, + self.non_superusers, + 302, + method='POST', + data=self.post_data, ) diff --git a/filesfolders/tests/test_permissions_api.py b/filesfolders/tests/test_permissions_api.py index 5d0f4098..5a61d586 100644 --- a/filesfolders/tests/test_permissions_api.py +++ b/filesfolders/tests/test_permissions_api.py @@ -47,10 +47,7 @@ def setUp(self): 'flag': 'IMPORTANT', 'description': 'Description', } - - def test_get(self): - """Test FolderListCreateAPIView GET""" - good_users = [ + self.good_users_get = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, @@ -61,11 +58,14 @@ def test_get(self): self.user_contributor, self.user_guest, ] - bad_users = [self.user_finder_cat, self.user_no_roles] - self.assert_response_api(self.url, good_users, 200) - self.assert_response_api(self.url, bad_users, 403) + self.bad_users_get = [self.user_finder_cat, self.user_no_roles] + + def test_get(self): + """Test FolderListCreateAPIView GET""" + self.assert_response_api(self.url, self.good_users_get, 200) + self.assert_response_api(self.url, self.bad_users_get, 403) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.good_users_get, 200, knox=True) self.project.set_public() self.assert_response_api(self.url, self.user_no_roles, 200) @@ -78,25 +78,20 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - ] - bad_users = [self.user_finder_cat, self.user_no_roles] - self.assert_response_api(self.url, good_users, 200) - self.assert_response_api(self.url, bad_users, 403) + self.assert_response_api(self.url, self.good_users_get, 200) + self.assert_response_api(self.url, self.bad_users_get, 403) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.good_users_get, 200, knox=True) self.project.set_public() self.assert_response_api(self.url, self.user_no_roles, 200) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response_api(self.url, self.good_users_get, 200) + self.assert_response_api(self.url, self.bad_users_get, 403) + self.assert_response_api(self.url, self.anonymous, 401) + def test_post(self): """Test FolderListCreateAPIView POST""" good_users = [ @@ -151,31 +146,22 @@ def test_post_anon(self): def test_post_archive(self): """Test POST with archived project""" self.project.set_archive() - good_users = [self.superuser] - bad_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( - self.url, good_users, 201, method='POST', data=self.post_data + self.url, self.superuser, 201, method='POST', data=self.post_data ) self.assert_response_api( - self.url, bad_users, 403, method='POST', data=self.post_data + self.url, + self.auth_non_superusers, + 403, + method='POST', + data=self.post_data, ) self.assert_response_api( self.url, self.anonymous, 401, method='POST', data=self.post_data ) self.assert_response_api( self.url, - good_users, + self.superuser, 201, method='POST', data=self.post_data, @@ -190,6 +176,23 @@ def test_post_archive(self): data=self.post_data, ) + def test_post_read_only(self): + """Test POST with site read-only mode""" + self.set_site_read_only() + self.assert_response_api( + self.url, self.superuser, 201, method='POST', data=self.post_data + ) + self.assert_response_api( + self.url, + self.auth_non_superusers, + 403, + method='POST', + data=self.post_data, + ) + self.assert_response_api( + self.url, self.anonymous, 401, method='POST', data=self.post_data + ) + class TestFolderRetrieveUpdateDestroyAPIView(FilesfoldersAPIPermissionTestBase): """Tests for FolderRetrieveUpdateDestroyAPIView permissions""" @@ -213,10 +216,7 @@ def setUp(self): 'description': 'UPDATED Description', } self.patch_data = {'name': 'UPDATED Folder'} - - def test_get(self): - """Test FolderRetrieveUpdateDestroyAPIView GET""" - good_users = [ + self.good_users_get = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, @@ -227,11 +227,32 @@ def test_get(self): self.user_contributor, self.user_guest, ] - bad_users = [self.user_finder_cat, self.user_no_roles] - self.assert_response_api(self.url, good_users, 200, method='GET') - self.assert_response_api(self.url, bad_users, 403) + self.bad_users_get = [self.user_finder_cat, self.user_no_roles] + self.good_users_update = [ + self.superuser, + self.user_owner_cat, + self.user_delegate_cat, + self.user_owner, + self.user_delegate, + ] + self.bad_users_update = [ + self.user_contributor_cat, + self.user_guest_cat, + self.user_finder_cat, + self.user_contributor, + self.user_guest, + self.user_no_roles, + ] + + def test_get(self): + """Test FolderRetrieveUpdateDestroyAPIView GET""" + + self.assert_response_api( + self.url, self.good_users_get, 200, method='GET' + ) + self.assert_response_api(self.url, self.bad_users_get, 403) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.good_users_get, 200, knox=True) self.project.set_public() self.assert_response_api(self.url, self.user_no_roles, 200) @@ -244,53 +265,45 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - ] - bad_users = [self.user_finder_cat, self.user_no_roles] - self.assert_response_api(self.url, good_users, 200, method='GET') - self.assert_response_api(self.url, bad_users, 403) + self.assert_response_api( + self.url, self.good_users_get, 200, method='GET' + ) + self.assert_response_api(self.url, self.bad_users_get, 403) self.assert_response_api(self.url, self.anonymous, 401) self.project.set_public() self.assert_response_api(self.url, self.user_no_roles, 200) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response_api( + self.url, self.good_users_get, 200, method='GET' + ) + self.assert_response_api(self.url, self.bad_users_get, 403) + self.assert_response_api(self.url, self.anonymous, 401) + def test_put(self): """Test FolderRetrieveUpdateDestroyAPIView PUT""" - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( - self.url, good_users, 200, method='PUT', data=self.put_data + self.url, + self.good_users_update, + 200, + method='PUT', + data=self.put_data, ) self.assert_response_api( - self.url, bad_users, 403, method='PUT', data=self.put_data + self.url, + self.bad_users_update, + 403, + method='PUT', + data=self.put_data, ) self.assert_response_api( self.url, self.anonymous, 401, method='PUT', data=self.put_data ) self.assert_response_api( self.url, - good_users, + self.good_users_update, 200, method='PUT', data=self.put_data, @@ -303,33 +316,24 @@ def test_put(self): @override_settings(PROJECTROLES_ALLOW_ANONYMOUS=True) def test_put_anon(self): - """Test permissions for folder updating with anonymous access""" + """Test PUT with anonymous access""" self.project.set_public() self.assert_response_api( self.url, self.anonymous, 401, method='PUT', data=self.put_data ) def test_put_archive(self): - """Test permissions for folder updating with PUT and archived project""" + """Test PUT with archived project""" self.project.set_archive() - good_users = [self.superuser] - bad_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( - self.url, good_users, 200, method='PUT', data=self.put_data + self.url, self.superuser, 200, method='PUT', data=self.put_data ) self.assert_response_api( - self.url, bad_users, 403, method='PUT', data=self.put_data + self.url, + self.auth_non_superusers, + 403, + method='PUT', + data=self.put_data, ) self.assert_response_api( self.url, self.anonymous, 401, method='PUT', data=self.put_data @@ -339,35 +343,45 @@ def test_put_archive(self): self.url, self.user_no_roles, 403, method='PUT', data=self.put_data ) + def test_put_read_only(self): + """Test PUT with site read-only mode""" + self.set_site_read_only() + self.assert_response_api( + self.url, self.superuser, 200, method='PUT', data=self.put_data + ) + self.assert_response_api( + self.url, + self.auth_non_superusers, + 403, + method='PUT', + data=self.put_data, + ) + self.assert_response_api( + self.url, self.anonymous, 401, method='PUT', data=self.put_data + ) + def test_patch(self): """Test FolderRetrieveUpdateDestroyAPIView PATCH""" - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( - self.url, good_users, 200, method='PATCH', data=self.patch_data + self.url, + self.good_users_update, + 200, + method='PATCH', + data=self.patch_data, ) self.assert_response_api( - self.url, bad_users, 403, method='PATCH', data=self.patch_data + self.url, + self.bad_users_update, + 403, + method='PATCH', + data=self.patch_data, ) self.assert_response_api( self.url, self.anonymous, 401, method='PATCH', data=self.patch_data ) self.assert_response_api( self.url, - good_users, + self.good_users_update, 200, method='PATCH', data=self.patch_data, @@ -393,24 +407,15 @@ def test_patch_anon(self): def test_patch_archive(self): """Test PATCH with archived project""" self.project.set_archive() - good_users = [self.superuser] - bad_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( - self.url, good_users, 200, method='PATCH', data=self.patch_data + self.url, self.superuser, 200, method='PATCH', data=self.patch_data ) self.assert_response_api( - self.url, bad_users, 403, method='PATCH', data=self.patch_data + self.url, + self.auth_non_superusers, + 403, + method='PATCH', + data=self.patch_data, ) self.assert_response_api( self.url, self.anonymous, 401, method='PATCH', data=self.patch_data @@ -424,35 +429,39 @@ def test_patch_archive(self): data=self.patch_data, ) + def test_patch_read_only(self): + """Test PATCH with site read-only mode""" + self.set_site_read_only() + self.assert_response_api( + self.url, self.superuser, 200, method='PATCH', data=self.patch_data + ) + self.assert_response_api( + self.url, + self.auth_non_superusers, + 403, + method='PATCH', + data=self.patch_data, + ) + self.assert_response_api( + self.url, self.anonymous, 401, method='PATCH', data=self.patch_data + ) + def test_delete(self): """Test FolderRetrieveUpdateDestroyAPIView DELETE""" - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( self.url, - good_users, + self.good_users_update, 204, method='DELETE', cleanup_method=self._make_folder, ) - self.assert_response_api(self.url, bad_users, 403, method='DELETE') + self.assert_response_api( + self.url, self.bad_users_update, 403, method='DELETE' + ) self.assert_response_api(self.url, self.anonymous, 401, method='DELETE') self.assert_response_api( self.url, - good_users, + self.good_users_update, 204, method='DELETE', cleanup_method=self._make_folder, @@ -470,35 +479,39 @@ def test_delete_anon(self): self.assert_response_api(self.url, self.anonymous, 401, method='DELETE') def test_delete_archive(self): - """Test DELETEwith archived project""" + """Test DELETE with archived project""" self.project.set_archive() - good_users = [self.superuser] - bad_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( self.url, - good_users, + self.superuser, 204, method='DELETE', cleanup_method=self._make_folder, ) - self.assert_response_api(self.url, bad_users, 403, method='DELETE') + self.assert_response_api( + self.url, self.auth_non_superusers, 403, method='DELETE' + ) self.assert_response_api(self.url, self.anonymous, 401, method='DELETE') self.project.set_public() self.assert_response_api( self.url, self.user_no_roles, 403, method='DELETE' ) + def test_delete_read_only(self): + """Test DELETE with site read-only mode""" + self.set_site_read_only() + self.assert_response_api( + self.url, + self.superuser, + 204, + method='DELETE', + cleanup_method=self._make_folder, + ) + self.assert_response_api( + self.url, self.auth_non_superusers, 403, method='DELETE' + ) + self.assert_response_api(self.url, self.anonymous, 401, method='DELETE') + class TestFileListCreateAPIView(FilesfoldersAPIPermissionTestBase): """Tests for FileListCreateAPIView permissions""" @@ -524,15 +537,7 @@ def setUp(self): 'filesfolders:api_file_list_create', kwargs={'project': self.project.sodar_uuid}, ) - - def tearDown(self): - if hasattr(self, 'post_data'): - self.post_data['file'].close() - super().tearDown() - - def test_get(self): - """Test FileListCreateAPIView GET""" - good_users = [ + self.good_users_get = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, @@ -543,39 +548,45 @@ def test_get(self): self.user_contributor, self.user_guest, ] - bad_users = [self.user_finder_cat, self.user_no_roles] - self.assert_response_api(self.url, good_users, 200) - self.assert_response_api(self.url, bad_users, 403) + self.bad_users_get = [self.user_finder_cat, self.user_no_roles] + + def tearDown(self): + if hasattr(self, 'post_data'): + self.post_data['file'].close() + super().tearDown() + + def test_get(self): + """Test FileListCreateAPIView GET""" + self.assert_response_api(self.url, self.good_users_get, 200) + self.assert_response_api(self.url, self.bad_users_get, 403) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.good_users_get, 200, knox=True) self.project.set_public() self.assert_response_api(self.url, self.user_no_roles, 200) @override_settings(PROJECTROLES_ALLOW_ANONYMOUS=True) def test_get_anon(self): - """Test permissions for file listing with anonymous access""" + """Test GET with anonymous access""" self.project.set_public() self.assert_response_api(self.url, self.anonymous, 200) def test_get_archive(self): - """Test permissions for file listing with archived project""" + """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - ] - bad_users = [self.user_finder_cat, self.user_no_roles] - self.assert_response_api(self.url, good_users, 200) - self.assert_response_api(self.url, bad_users, 403) + self.assert_response_api(self.url, self.good_users_get, 200) + self.assert_response_api(self.url, self.bad_users_get, 403) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.good_users_get, 200, knox=True) + self.project.set_public() + self.assert_response_api(self.url, self.user_no_roles, 200) + + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response_api(self.url, self.good_users_get, 200) + self.assert_response_api(self.url, self.bad_users_get, 403) + self.assert_response_api(self.url, self.anonymous, 401) + self.assert_response_api(self.url, self.good_users_get, 200, knox=True) self.project.set_public() self.assert_response_api(self.url, self.user_no_roles, 200) @@ -645,7 +656,6 @@ def test_post(self): data=self.post_data, cleanup_method=self._cleanup, ) - # self.request_data['file'].close() @override_settings(PROJECTROLES_ALLOW_ANONYMOUS=True) def test_post_anon(self): @@ -661,46 +671,67 @@ def test_post_anon(self): data=self.post_data, cleanup_method=self._cleanup, ) - - def test_post_archive(self): - """Test POST with archived project""" - self._make_post_data() - self.project.set_archive() - good_users = [self.superuser] - bad_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] + + def test_post_archive(self): + """Test POST with archived project""" + self._make_post_data() + self.project.set_archive() + self.assert_response_api( + self.url, + self.superuser, + 201, + method='POST', + format='multipart', + data=self.post_data, + cleanup_method=self._cleanup, + ) + self.assert_response_api( + self.url, + self.auth_non_superusers, + 403, + method='POST', + format='multipart', + data=self.post_data, + cleanup_method=self._cleanup, + ) + self.assert_response_api( + self.url, + self.anonymous, + 401, + method='POST', + format='multipart', + data=self.post_data, + cleanup_method=self._cleanup, + ) self.assert_response_api( self.url, - good_users, + self.superuser, 201, method='POST', format='multipart', data=self.post_data, cleanup_method=self._cleanup, + knox=True, ) + self.project.set_public() self.assert_response_api( self.url, - bad_users, + self.user_no_roles, 403, method='POST', format='multipart', data=self.post_data, cleanup_method=self._cleanup, ) + + def test_post_read_only(self): + """Test POST with site read-only mode""" + self._make_post_data() + self.set_site_read_only() self.assert_response_api( self.url, - self.anonymous, - 401, + self.superuser, + 201, method='POST', format='multipart', data=self.post_data, @@ -708,19 +739,17 @@ def test_post_archive(self): ) self.assert_response_api( self.url, - good_users, - 201, + self.auth_non_superusers, + 403, method='POST', format='multipart', data=self.post_data, cleanup_method=self._cleanup, - knox=True, ) - self.project.set_public() self.assert_response_api( self.url, - self.user_no_roles, - 403, + self.anonymous, + 401, method='POST', format='multipart', data=self.post_data, @@ -758,10 +787,7 @@ def setUp(self): 'filesfolders:api_file_retrieve_update_destroy', kwargs={'file': file.sodar_uuid}, ) - - def test_get(self): - """Test FileRetrieveUpdateDestroyAPIView GET""" - good_users = [ + self.good_users_get = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, @@ -772,11 +798,29 @@ def test_get(self): self.user_contributor, self.user_guest, ] - bad_users = [self.user_finder_cat, self.user_no_roles] - self.assert_response_api(self.url, good_users, 200) - self.assert_response_api(self.url, bad_users, 403) + self.bad_users_get = [self.user_finder_cat, self.user_no_roles] + self.good_users_update = [ + self.superuser, + self.user_owner_cat, + self.user_delegate_cat, + self.user_owner, + self.user_delegate, + ] + self.bad_users_update = [ + self.user_contributor_cat, + self.user_guest_cat, + self.user_finder_cat, + self.user_contributor, + self.user_guest, + self.user_no_roles, + ] + + def test_get(self): + """Test FileRetrieveUpdateDestroyAPIView GET""" + self.assert_response_api(self.url, self.good_users_get, 200) + self.assert_response_api(self.url, self.bad_users_get, 403) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.good_users_get, 200, knox=True) self.project.set_public() self.assert_response_api(self.url, self.user_no_roles, 200) @@ -789,46 +833,26 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - ] - bad_users = [self.user_finder_cat, self.user_no_roles] - self.assert_response_api(self.url, good_users, 200) - self.assert_response_api(self.url, bad_users, 403) + self.assert_response_api(self.url, self.good_users_get, 200) + self.assert_response_api(self.url, self.bad_users_get, 403) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.good_users_get, 200, knox=True) self.project.set_public() self.assert_response_api(self.url, self.user_no_roles, 200) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response_api(self.url, self.good_users_get, 200) + self.assert_response_api(self.url, self.bad_users_get, 403) + self.assert_response_api(self.url, self.anonymous, 401) + def test_put(self): """Test FileRetrieveUpdateDestroyAPIView PUT""" self._make_put_data() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( self.url, - good_users, + self.good_users_update, 200, method='PUT', format='multipart', @@ -837,7 +861,7 @@ def test_put(self): ) self.assert_response_api( self.url, - bad_users, + self.bad_users_update, 403, method='PUT', format='multipart', @@ -855,7 +879,7 @@ def test_put(self): ) self.assert_response_api( self.url, - good_users, + self.good_users_update, 200, method='PUT', format='multipart', @@ -891,22 +915,9 @@ def test_put_archive(self): """Test PUT with archived project""" self._make_put_data() self.project.set_archive() - good_users = [self.superuser] - bad_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( self.url, - good_users, + self.superuser, 200, method='PUT', format='multipart', @@ -915,7 +926,7 @@ def test_put_archive(self): ) self.assert_response_api( self.url, - bad_users, + self.auth_non_superusers, 403, method='PUT', format='multipart', @@ -941,26 +952,43 @@ def test_put_archive(self): data=self.put_data, ) + def test_put_read_only(self): + """Test PUT with site read-only mode""" + self._make_put_data() + self.set_site_read_only() + self.assert_response_api( + self.url, + self.superuser, + 200, + method='PUT', + format='multipart', + data=self.put_data, + cleanup_method=self._cleanup_put, + ) + self.assert_response_api( + self.url, + self.auth_non_superusers, + 403, + method='PUT', + format='multipart', + data=self.put_data, + cleanup_method=self._cleanup_put, + ) + self.assert_response_api( + self.url, + self.anonymous, + 401, + method='PUT', + format='multipart', + data=self.put_data, + cleanup_method=self._cleanup_put, + ) + def test_patch(self): """Test FileRetrieveUpdateDestroyAPIView PATCH""" - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( self.url, - good_users, + self.good_users_update, 200, method='PATCH', format='multipart', @@ -968,7 +996,7 @@ def test_patch(self): ) self.assert_response_api( self.url, - bad_users, + self.bad_users_update, 403, method='PATCH', format='multipart', @@ -984,7 +1012,7 @@ def test_patch(self): ) self.assert_response_api( self.url, - good_users, + self.good_users_update, 200, method='PATCH', format='multipart', @@ -1017,22 +1045,9 @@ def test_patch_anon(self): def test_patch_archive(self): """Test PATCH with archived project""" self.project.set_archive() - good_users = [self.superuser] - bad_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( self.url, - good_users, + self.superuser, 200, method='PATCH', format='multipart', @@ -1040,7 +1055,7 @@ def test_patch_archive(self): ) self.assert_response_api( self.url, - bad_users, + self.auth_non_superusers, 403, method='PATCH', format='multipart', @@ -1065,35 +1080,50 @@ def test_patch_archive(self): data=self.patch_data, ) + def test_patch_read_only(self): + """Test PATCH with site read-only mode""" + self.set_site_read_only() + self.assert_response_api( + self.url, + self.superuser, + 200, + method='PATCH', + format='multipart', + data=self.patch_data, + ) + self.assert_response_api( + self.url, + self.auth_non_superusers, + 403, + method='PATCH', + format='multipart', + data=self.patch_data, + ) + self.assert_response_api( + self.url, + self.anonymous, + 401, + method='PATCH', + format='multipart', + data=self.patch_data, + ) + def test_delete(self): """Test FileRetrieveUpdateDestroyAPIView DELETE""" - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( self.url, - good_users, + self.good_users_update, 204, method='DELETE', cleanup_method=self._make_file, ) - self.assert_response_api(self.url, bad_users, 403, method='DELETE') + self.assert_response_api( + self.url, self.bad_users_update, 403, method='DELETE' + ) self.assert_response_api(self.url, self.anonymous, 401, method='DELETE') self.assert_response_api( self.url, - good_users, + self.good_users_update, 204, method='DELETE', cleanup_method=self._make_file, @@ -1116,33 +1146,37 @@ def test_delete_anon(self): def test_delete_archive(self): """Test DELETE with archived project""" self.project.set_archive() - good_users = [self.superuser] - bad_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( self.url, - good_users, + self.superuser, 204, method='DELETE', cleanup_method=self._make_file, ) - self.assert_response_api(self.url, bad_users, 403, method='DELETE') + self.assert_response_api( + self.url, self.auth_non_superusers, 403, method='DELETE' + ) self.assert_response_api(self.url, self.anonymous, 401, method='DELETE') self.project.set_public() self.assert_response_api( self.url, self.user_no_roles, 403, method='DELETE' ) + def test_delete_read_only(self): + """Test DELETE with site read-only mode""" + self.set_site_read_only() + self.assert_response_api( + self.url, + self.superuser, + 204, + method='DELETE', + cleanup_method=self._make_file, + ) + self.assert_response_api( + self.url, self.auth_non_superusers, 403, method='DELETE' + ) + self.assert_response_api(self.url, self.anonymous, 401, method='DELETE') + class TestFileServeAPIView(FilesfoldersAPIPermissionTestBase): """Tests for FileServeAPIView permissions""" @@ -1153,10 +1187,7 @@ def setUp(self): self.url = reverse( 'filesfolders:api_file_serve', kwargs={'file': file.sodar_uuid} ) - - def test_get(self): - """Test FileServeAPIView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, @@ -1167,11 +1198,14 @@ def test_get(self): self.user_contributor, self.user_guest, ] - bad_users = [self.user_finder_cat, self.user_no_roles] - self.assert_response_api(self.url, good_users, 200, method='GET') - self.assert_response_api(self.url, bad_users, 403) + self.bad_users = [self.user_finder_cat, self.user_no_roles] + + def test_get(self): + """Test FileServeAPIView GET""" + self.assert_response_api(self.url, self.good_users, 200, method='GET') + self.assert_response_api(self.url, self.bad_users, 403) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.good_users, 200, knox=True) self.project.set_public() self.assert_response_api(self.url, self.user_no_roles, 200) @@ -1184,25 +1218,20 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - ] - bad_users = [self.user_finder_cat, self.user_no_roles] - self.assert_response_api(self.url, good_users, 200, method='GET') - self.assert_response_api(self.url, bad_users, 403) + self.assert_response_api(self.url, self.good_users, 200, method='GET') + self.assert_response_api(self.url, self.bad_users, 403) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.good_users, 200, knox=True) self.project.set_public() self.assert_response_api(self.url, self.user_no_roles, 200) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response_api(self.url, self.good_users, 200, method='GET') + self.assert_response_api(self.url, self.bad_users, 403) + self.assert_response_api(self.url, self.anonymous, 401) + class TestHyperLinkListCreateAPIView(FilesfoldersAPIPermissionTestBase): """Tests for HyperLinkListCreateAPIView permissions""" @@ -1223,10 +1252,7 @@ def setUp(self): 'description': 'Description', 'url': 'https://www.cubi.bihealth.org', } - - def test_get(self): - """Test HyperLinkListCreateAPIView GET""" - good_users = [ + self.good_users_get = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, @@ -1237,11 +1263,14 @@ def test_get(self): self.user_contributor, self.user_guest, ] - bad_users = [self.user_finder_cat, self.user_no_roles] - self.assert_response_api(self.url, good_users, 200) - self.assert_response_api(self.url, bad_users, 403) + self.bad_users_get = [self.user_finder_cat, self.user_no_roles] + + def test_get(self): + """Test HyperLinkListCreateAPIView GET""" + self.assert_response_api(self.url, self.good_users_get, 200) + self.assert_response_api(self.url, self.bad_users_get, 403) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.good_users_get, 200, knox=True) self.project.set_public() self.assert_response_api(self.url, self.user_no_roles, 200) @@ -1254,25 +1283,20 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - ] - bad_users = [self.user_finder_cat, self.user_no_roles] - self.assert_response_api(self.url, good_users, 200) - self.assert_response_api(self.url, bad_users, 403) + self.assert_response_api(self.url, self.good_users_get, 200) + self.assert_response_api(self.url, self.bad_users_get, 403) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.good_users_get, 200, knox=True) self.project.set_public() self.assert_response_api(self.url, self.user_no_roles, 200) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response_api(self.url, self.good_users_get, 200) + self.assert_response_api(self.url, self.bad_users_get, 403) + self.assert_response_api(self.url, self.anonymous, 401) + def test_post(self): """Test HyperLinkListCreateAPIView POST""" good_users = [ @@ -1334,36 +1358,27 @@ def test_post_anon(self): def test_post_archive(self): """Test POST with archived project""" self.project.set_archive() - good_users = [self.superuser] - bad_users = [ - self.user_owner, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( self.url, - good_users, + self.superuser, 201, method='POST', data=self.post_data, cleanup_method=self._cleanup, ) self.assert_response_api( - self.url, bad_users, 403, method='POST', data=self.post_data + self.url, + self.auth_non_superusers, + 403, + method='POST', + data=self.post_data, ) self.assert_response_api( self.url, self.anonymous, 401, method='POST', data=self.post_data ) self.assert_response_api( self.url, - good_users, + self.superuser, 201, method='POST', data=self.post_data, @@ -1379,6 +1394,28 @@ def test_post_archive(self): data=self.post_data, ) + def test_post_read_only(self): + """Test POST with site read-only mode""" + self.set_site_read_only() + self.assert_response_api( + self.url, + self.superuser, + 201, + method='POST', + data=self.post_data, + cleanup_method=self._cleanup, + ) + self.assert_response_api( + self.url, + self.auth_non_superusers, + 403, + method='POST', + data=self.post_data, + ) + self.assert_response_api( + self.url, self.anonymous, 401, method='POST', data=self.post_data + ) + class TestHyperLinkRetrieveUpdateDestroyAPIView( FilesfoldersAPIPermissionTestBase @@ -1408,10 +1445,7 @@ def setUp(self): 'url': 'https://www.bihealth.org', } self.patch_data = {'name': 'UPDATED Hyperlink'} - - def test_get(self): - """Test HyperLinkRetrieveUpdateDestroyAPIView GET""" - good_users = [ + self.good_users_get = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, @@ -1422,11 +1456,31 @@ def test_get(self): self.user_contributor, self.user_guest, ] - bad_users = [self.user_finder_cat, self.user_no_roles] - self.assert_response_api(self.url, good_users, 200, method='GET') - self.assert_response_api(self.url, bad_users, 403) + self.bad_users_get = [self.user_finder_cat, self.user_no_roles] + self.good_users_update = [ + self.superuser, + self.user_owner_cat, + self.user_delegate_cat, + self.user_owner, # Owner of link + self.user_delegate, + ] + self.bad_users_update = [ + self.user_contributor_cat, + self.user_guest_cat, + self.user_finder_cat, + self.user_contributor, + self.user_guest, + self.user_no_roles, + ] + + def test_get(self): + """Test HyperLinkRetrieveUpdateDestroyAPIView GET""" + self.assert_response_api( + self.url, self.good_users_get, 200, method='GET' + ) + self.assert_response_api(self.url, self.bad_users_get, 403) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.good_users_get, 200, knox=True) self.project.set_public() self.assert_response_api(self.url, self.user_no_roles, 200) @@ -1439,54 +1493,46 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - ] - bad_users = [self.user_finder_cat, self.user_no_roles] - self.assert_response_api(self.url, good_users, 200, method='GET') - self.assert_response_api(self.url, bad_users, 403) + self.assert_response_api( + self.url, self.good_users_get, 200, method='GET' + ) + self.assert_response_api(self.url, self.bad_users_get, 403) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.good_users_get, 200, knox=True) self.project.set_public() self.assert_response_api(self.url, self.user_no_roles, 200) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response_api( + self.url, self.good_users_get, 200, method='GET' + ) + self.assert_response_api(self.url, self.bad_users_get, 403) + self.assert_response_api(self.url, self.anonymous, 401) + def test_put(self): """Test HyperLinkRetrieveUpdateDestroyAPIView PUT""" - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, # Owner of link - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( - self.url, good_users, 200, method='PUT', data=self.put_data + self.url, + self.good_users_update, + 200, + method='PUT', + data=self.put_data, ) self.assert_response_api( - self.url, bad_users, 403, method='PUT', data=self.put_data + self.url, + self.bad_users_update, + 403, + method='PUT', + data=self.put_data, ) self.assert_response_api( self.url, self.anonymous, 401, method='PUT', data=self.put_data ) self.assert_response_api( self.url, - good_users, + self.good_users_update, 200, method='PUT', data=self.put_data, @@ -1508,31 +1554,22 @@ def test_put_anon(self): def test_put_archive(self): """Test PUT with archived project""" self.project.set_archive() - good_users = [self.superuser] - bad_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( - self.url, good_users, 200, method='PUT', data=self.put_data + self.url, self.superuser, 200, method='PUT', data=self.put_data ) self.assert_response_api( - self.url, bad_users, 403, method='PUT', data=self.put_data + self.url, + self.auth_non_superusers, + 403, + method='PUT', + data=self.put_data, ) self.assert_response_api( self.url, self.anonymous, 401, method='PUT', data=self.put_data ) self.assert_response_api( self.url, - good_users, + self.superuser, 200, method='PUT', data=self.put_data, @@ -1543,35 +1580,45 @@ def test_put_archive(self): self.url, self.user_no_roles, 403, method='PUT', data=self.put_data ) + def test_put_read_only(self): + """Test PUT with site read-only mode""" + self.set_site_read_only() + self.assert_response_api( + self.url, self.superuser, 200, method='PUT', data=self.put_data + ) + self.assert_response_api( + self.url, + self.auth_non_superusers, + 403, + method='PUT', + data=self.put_data, + ) + self.assert_response_api( + self.url, self.anonymous, 401, method='PUT', data=self.put_data + ) + def test_patch(self): """Test HyperLinkRetrieveUpdateDestroyAPIView PATCH""" - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( - self.url, good_users, 200, method='PATCH', data=self.patch_data + self.url, + self.good_users_update, + 200, + method='PATCH', + data=self.patch_data, ) self.assert_response_api( - self.url, bad_users, 403, method='PATCH', data=self.patch_data + self.url, + self.bad_users_update, + 403, + method='PATCH', + data=self.patch_data, ) self.assert_response_api( self.url, self.anonymous, 401, method='PATCH', data=self.patch_data ) self.assert_response_api( self.url, - good_users, + self.good_users_update, 200, method='PATCH', data=self.patch_data, @@ -1597,31 +1644,22 @@ def test_patch_anon(self): def test_patch_archive(self): """Test PATCH with archived project""" self.project.set_archive() - good_users = [self.superuser] - bad_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( - self.url, good_users, 200, method='PATCH', data=self.patch_data + self.url, self.superuser, 200, method='PATCH', data=self.patch_data ) self.assert_response_api( - self.url, bad_users, 403, method='PATCH', data=self.patch_data + self.url, + self.auth_non_superusers, + 403, + method='PATCH', + data=self.patch_data, ) self.assert_response_api( self.url, self.anonymous, 401, method='PATCH', data=self.patch_data ) self.assert_response_api( self.url, - good_users, + self.superuser, 200, method='PATCH', data=self.patch_data, @@ -1636,36 +1674,39 @@ def test_patch_archive(self): data=self.patch_data, ) + def test_patch_read_only(self): + """Test PATCH with site read-only mode""" + self.set_site_read_only() + self.assert_response_api( + self.url, self.superuser, 200, method='PATCH', data=self.patch_data + ) + self.assert_response_api( + self.url, + self.auth_non_superusers, + 403, + method='PATCH', + data=self.patch_data, + ) + self.assert_response_api( + self.url, self.anonymous, 401, method='PATCH', data=self.patch_data + ) + def test_delete(self): """Test HyperLinkRetrieveUpdateDestroyAPIView DELETE""" - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] - self.assert_response_api( self.url, - good_users, + self.good_users_update, 204, method='DELETE', cleanup_method=self._cleanup_delete, ) - self.assert_response_api(self.url, bad_users, 403, method='DELETE') + self.assert_response_api( + self.url, self.bad_users_update, 403, method='DELETE' + ) self.assert_response_api(self.url, self.anonymous, 401, method='DELETE') self.assert_response_api( self.url, - good_users, + self.good_users_update, 204, method='DELETE', cleanup_method=self._cleanup_delete, @@ -1689,31 +1730,20 @@ def test_delete_anon(self): def test_delete_archive(self): """Test DELETE with archived project""" self.project.set_archive() - good_users = [self.superuser] - bad_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( self.url, - good_users, + self.superuser, 204, method='DELETE', cleanup_method=self._cleanup_delete, ) - self.assert_response_api(self.url, bad_users, 403, method='DELETE') + self.assert_response_api( + self.url, self.auth_non_superusers, 403, method='DELETE' + ) self.assert_response_api(self.url, self.anonymous, 401, method='DELETE') self.assert_response_api( self.url, - good_users, + self.superuser, 204, method='DELETE', cleanup_method=self._cleanup_delete, @@ -1723,3 +1753,18 @@ def test_delete_archive(self): self.assert_response_api( self.url, self.user_no_roles, 403, method='DELETE' ) + + def test_delete_read_only(self): + """Test DELETE with site read-only mode""" + self.set_site_read_only() + self.assert_response_api( + self.url, + self.superuser, + 204, + method='DELETE', + cleanup_method=self._cleanup_delete, + ) + self.assert_response_api( + self.url, self.auth_non_superusers, 403, method='DELETE' + ) + self.assert_response_api(self.url, self.anonymous, 401, method='DELETE') diff --git a/projectroles/app_settings.py b/projectroles/app_settings.py index 17c3100f..a8215900 100644 --- a/projectroles/app_settings.py +++ b/projectroles/app_settings.py @@ -131,6 +131,18 @@ user_modifiable=True, global_edit=True, ), + PluginAppSettingDef( + name='site_read_only', + scope=APP_SETTING_SCOPE_SITE, + type=APP_SETTING_TYPE_BOOLEAN, + default=False, + label='Site read-only mode', + description='Set site in read-only mode. Data altering operations will ' + 'be prohibited. Mode must be explicitly unset to allow data' + 'modification.', + user_modifiable=True, + global_edit=False, + ), ] diff --git a/projectroles/rules.py b/projectroles/rules.py index 37a30fbb..2732246b 100644 --- a/projectroles/rules.py +++ b/projectroles/rules.py @@ -2,8 +2,13 @@ from django.conf import settings +from projectroles.app_settings import AppSettingAPI from projectroles.models import RoleAssignment, SODAR_CONSTANTS + +app_settings = AppSettingAPI() + + # SODAR constants PROJECT_ROLE_OWNER = SODAR_CONSTANTS['PROJECT_ROLE_OWNER'] PROJECT_ROLE_DELEGATE = SODAR_CONSTANTS['PROJECT_ROLE_DELEGATE'] @@ -108,7 +113,9 @@ def has_roles(user): @rules.predicate def is_modifiable_project(user, obj): """Whether or not project metadata is modifiable""" - return False if obj.is_remote() else True + if obj.is_remote() or app_settings.get('projectroles', 'site_read_only'): + return False + return True @rules.predicate @@ -117,16 +124,20 @@ def can_modify_project_data(user, obj): Whether or not project app data can be modified, due to e.g. project archiving status. """ - return not obj.archive + return not obj.archive and not app_settings.get( + 'projectroles', 'site_read_only' + ) @rules.predicate def can_create_projects(user, obj): - """Whether or not new projects can be generated on the site""" + """Whether or not new projects can be created on the site""" if settings.PROJECTROLES_SITE_MODE == SITE_MODE_TARGET and ( not settings.PROJECTROLES_TARGET_CREATE or (obj and obj.is_remote()) ): return False + if app_settings.get('projectroles', 'site_read_only'): + return False return True @@ -154,6 +165,12 @@ def is_target_site(): return settings.PROJECTROLES_SITE_MODE == SITE_MODE_TARGET +@rules.predicate +def is_site_writable(): + """Return True if site has not been set in read-only mode""" + return not app_settings.get('projectroles', 'site_read_only') + + # Combined predicates ---------------------------------------------------------- @@ -186,7 +203,7 @@ def is_target_site(): # Allow project updating rules.add_perm( 'projectroles.update_project', - is_project_update_user, + is_project_update_user & is_site_writable, ) # Allow creation of projects @@ -194,10 +211,13 @@ def is_target_site(): 'projectroles.create_project', is_project_create_user & can_create_projects ) +# Allow viewing PROJECT scope settings +rules.add_perm('projectroles.view_project_settings', is_project_update_user) + # Allow updating project settings rules.add_perm( 'projectroles.update_project_settings', - is_role_update_user & is_modifiable_project, + is_project_update_user & is_modifiable_project, ) # Allow viewing project roles @@ -241,3 +261,9 @@ def is_target_site(): rules.add_perm( 'projectroles.view_hidden_projects', rules.is_superuser | is_project_owner ) + +# Allow starring/unstarring a project +rules.add_perm( + 'projectroles.star_project', + (can_view_project | has_category_child_role) & is_site_writable, +) diff --git a/projectroles/static/projectroles/js/projectroles.js b/projectroles/static/projectroles/js/projectroles.js index 640eded9..afaf53fd 100644 --- a/projectroles/static/projectroles/js/projectroles.js +++ b/projectroles/static/projectroles/js/projectroles.js @@ -329,7 +329,42 @@ $(document).ready(function () { $(this).prepend( ''); - $("body").css("cursor", "progress"); + $('body').css('cursor', 'progress'); $(this).closest('form').submit(); }); }); + + +/* Update site read-only mode alert ----------------------------------------- */ + +function updateReadOnlyAlert(url, alert) { + $.ajax({ + url: url, + method: 'GET', + }).done(function (data) { + var siteReadOnly = data['site_read_only']; + if (siteReadOnly === true) { + setTimeout(function() { + updateReadOnlyAlert(url, alert); + }, 5000); + } else { + alert.addClass('alert-success') + .removeClass('alert-danger') + .addClass('sodar-alert-site-read-only-updated'); + alert.find('.sodar-alert-top-content').html( + ' ' + + 'Site read-only mode disabled. Please ' + + 'reload ' + + 'your browser tab.' + ) + } + }); +} + +$(document).ready(function () { + var readOnlyAlert = $(document).find('#sodar-alert-site-read-only'); + if (readOnlyAlert) { + var url = readOnlyAlert.attr('data-url'); + updateReadOnlyAlert(url, readOnlyAlert); + } +}); diff --git a/projectroles/templates/projectroles/_messages.html b/projectroles/templates/projectroles/_messages.html index d9394451..4564ee14 100644 --- a/projectroles/templates/projectroles/_messages.html +++ b/projectroles/templates/projectroles/_messages.html @@ -5,39 +5,56 @@ {% load projectroles_common_tags %} {% get_site_app_messages request.user as site_app_messages %} +{% get_app_setting 'projectroles' 'site_read_only' as site_read_only %} -{% if messages or site_app_messages %} +{% if site_read_only or messages or site_app_messages %}
- - {# Site app messages #} - {% for message in site_app_messages %} - {% if request.user.is_authenticated or not message.require_auth %} -
+ {# Site read-only mode message #} + {% if site_read_only %} + {% get_django_setting 'PROJECTROLES_READ_ONLY_MSG' as read_only_msg %} +
- {% comment %} - {% if message.dismissable %} - - - + + {% if read_only_msg %} + {{ read_only_msg }} + {% else %} + This site is currently in read-only mode. Modifying data is not + permitted. {% endif %} - {% endcomment %} - {{ message.content | safe }}
{% endif %} - {% endfor %} - {# Regular Django messages #} - {% for message in messages %} -
-
- {{ message }} - - - -
-
- {% endfor %} + {# Site app messages #} + {% for message in site_app_messages %} + {% if request.user.is_authenticated or not message.require_auth %} +
+
+ {% comment %} + {% if message.dismissable %} + + + + {% endif %} + {% endcomment %} + {{ message.content | safe }} +
+
+ {% endif %} + {% endfor %} + {# Regular Django messages #} + {% for message in messages %} +
+
+ {{ message }} + + + +
+
+ {% endfor %}
{% endif %} diff --git a/projectroles/templates/projectroles/_project_header.html b/projectroles/templates/projectroles/_project_header.html index a47e7a05..5981e833 100644 --- a/projectroles/templates/projectroles/_project_header.html +++ b/projectroles/templates/projectroles/_project_header.html @@ -5,6 +5,7 @@ {% load projectroles_common_tags %} {% get_app_setting 'userprofile' 'enable_project_uuid_copy' user=request.user as enable_uuid_copy %} +{% get_app_setting 'projectroles' 'site_read_only' as site_read_only %} {% get_django_setting 'PROJECTROLES_KIOSK_MODE' as kiosk_mode %}
@@ -16,7 +17,7 @@

{{ project.title }}

{# Project starring/unstarring #} - {% if request.user.is_authenticated %} + {% if request.user.is_authenticated and not site_read_only %} -
-
- {% endblock content %} {% block javascript %} diff --git a/projectroles/templates/projectroles/base_site.html b/projectroles/templates/projectroles/base_site.html index 131fa8e2..76f367a7 100644 --- a/projectroles/templates/projectroles/base_site.html +++ b/projectroles/templates/projectroles/base_site.html @@ -33,10 +33,8 @@ {% endif %} {% block head_javascript %} - {# NOTE: The rest are included under the "javascript" block at the end #} - @@ -46,17 +44,14 @@ - - - {% endif %} @@ -65,68 +60,52 @@ {% for js_inc in custom_js_includes %} {% endfor %} - {% endblock head_javascript %} {% block css %} - - - {% if not disable_cdn_includes %} - {% endif %} - {# Custom CSS includes #} {% for css_inc in custom_css_includes %} {% endfor %} - - - - {% endblock css %} {% block head_extend %} {# Extended head stuff from apps goes here #} {% endblock head_extend %} - -
-
{# Projectroles site title bar #} {% include 'projectroles/_site_titlebar.html' %}
-
{% block content %}

Use this document as a way to quick start any new project.

{% endblock content %}
-
{# Custom template for Bootstrap4 modal #} @@ -142,14 +121,10 @@ {% get_django_setting name='PROJECTROLES_BROWSER_WARNING' js=True as browser_warning %} window.sodarBrowserWarning = {{ browser_warning }}; - - {% include 'projectroles/_appalerts_include.html' %} {% endblock javascript %} - - diff --git a/projectroles/tests/test_permissions.py b/projectroles/tests/test_permissions.py index 31180c1b..beed9b0f 100644 --- a/projectroles/tests/test_permissions.py +++ b/projectroles/tests/test_permissions.py @@ -8,6 +8,7 @@ from test_plus.test import TestCase +from projectroles.app_settings import AppSettingAPI from projectroles.models import SODAR_CONSTANTS from projectroles.utils import build_secret from projectroles.tests.test_models import ( @@ -21,6 +22,9 @@ ) +app_settings = AppSettingAPI() + + # SODAR constants PROJECT_ROLE_OWNER = SODAR_CONSTANTS['PROJECT_ROLE_OWNER'] PROJECT_ROLE_DELEGATE = SODAR_CONSTANTS['PROJECT_ROLE_DELEGATE'] @@ -44,6 +48,14 @@ class PermissionTestMixin: """Helper class for permission tests""" + def set_site_read_only(self, value=True): + """ + Helper to set site read only mode to the desired value. + + :param value: Boolean + """ + app_settings.set('projectroles', 'site_read_only', value) + def send_request(self, url, method, req_kwargs): req_method = getattr(self.client, method.lower(), None) if not req_method: @@ -222,6 +234,27 @@ def setUp(self): self.guest_as = self.make_assignment( self.project, self.user_guest, self.role_guest ) + # User helpers + self.all_users = [ + self.superuser, + self.user_owner_cat, + self.user_delegate_cat, + self.user_contributor_cat, + self.user_guest_cat, + self.user_finder_cat, + self.user_owner, + self.user_delegate, + self.user_contributor, + self.user_guest, + self.user_no_roles, + self.anonymous, + ] # All users + # All authenticated users + self.auth_users = self.all_users[:-1] + # All users except for superuser + self.non_superusers = self.all_users[1:] + # All authenticated non-superusers + self.auth_non_superusers = self.non_superusers[:-1] class SiteAppPermissionTestBase( @@ -287,6 +320,27 @@ def test_get_home_anon(self): ] self.assert_response(url, good_users, 200) + def test_get_home_read_only(self): + """Test HomeView GET with site read-only mode""" + self.set_site_read_only() + url = reverse('home') + good_users = [ + self.superuser, + self.user_owner_cat, + self.user_delegate_cat, + self.user_contributor_cat, + self.user_guest_cat, + self.user_finder_cat, + self.user_owner, + self.user_delegate, + self.user_contributor, + self.user_guest, + self.user_no_roles, + ] + bad_users = [self.anonymous] + self.assert_response(url, good_users, 200) + self.assert_response(url, bad_users, 302) + def test_get_search(self): """Test ProjectSearchResultsView GET""" url = reverse('projectroles:search') + '?' + urlencode({'s': 'test'}) @@ -327,6 +381,27 @@ def test_get_search_anon(self): ] self.assert_response(url, good_users, 200) + def test_get_search_read_only(self): + """Test ProjectSearchResultsView GET with site read-only mode""" + self.set_site_read_only() + url = reverse('projectroles:search') + '?' + urlencode({'s': 'test'}) + good_users = [ + self.superuser, + self.user_owner_cat, + self.user_delegate_cat, + self.user_contributor_cat, + self.user_guest_cat, + self.user_finder_cat, + self.user_owner, + self.user_delegate, + self.user_contributor, + self.user_guest, + self.user_no_roles, + ] + bad_users = [self.anonymous] + self.assert_response(url, good_users, 200) + self.assert_response(reverse('home'), bad_users, 302) + def test_get_search_advanced(self): """Test ProjectAdvancedSearchView GET""" url = reverse('projectroles:search_advanced') @@ -474,10 +549,7 @@ def setUp(self): self.url_cat = reverse( 'projectroles:detail', kwargs={'project': self.category.sodar_uuid} ) - - def test_get(self): - """Test ProjectDetailView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, @@ -488,91 +560,73 @@ def test_get(self): self.user_contributor, self.user_guest, ] - bad_users = [self.user_finder_cat, self.user_no_roles, self.anonymous] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) - # Test public project - self.project.set_public() - self.assert_response(self.url, self.user_no_roles, 200) - - @override_settings(PROJECTROLES_ALLOW_ANONYMOUS=True) - def test_get_anon(self): - """Test GET with anonymous access""" - good_users = [ + self.bad_users = [ + self.user_finder_cat, + self.user_no_roles, + self.anonymous, + ] + self.good_users_cat = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, self.user_contributor_cat, self.user_guest_cat, + self.user_finder_cat, self.user_owner, self.user_delegate, self.user_contributor, self.user_guest, ] - bad_users = [self.user_finder_cat, self.user_no_roles, self.anonymous] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.bad_users_cat = [self.user_no_roles, self.anonymous] + + def test_get(self): + """Test ProjectDetailView GET""" + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) + # Test public project + self.project.set_public() + self.assert_response(self.url, self.user_no_roles, 200) + + @override_settings(PROJECTROLES_ALLOW_ANONYMOUS=True) + def test_get_anon(self): + """Test GET with anonymous access""" + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() - self.assert_response(self.url, bad_users, 200) + self.assert_response(self.url, self.bad_users, 200) def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - ] - bad_users = [self.user_finder_cat, self.user_no_roles, self.anonymous] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 200) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) + def test_get_category(self): """Test GET with category""" - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - ] - bad_users = [self.user_no_roles, self.anonymous] - self.assert_response(self.url_cat, good_users, 200) - self.assert_response(self.url_cat, bad_users, 302) + self.assert_response(self.url_cat, self.good_users_cat, 200) + self.assert_response(self.url_cat, self.bad_users_cat, 302) self.project.set_public() self.assert_response(self.url_cat, self.user_no_roles, 200) @override_settings(PROJECTROLES_ALLOW_ANONYMOUS=True) def test_get_category_anon(self): """Test GET with category and anonymous access""" - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - ] - bad_users = [self.user_no_roles, self.anonymous] - self.assert_response(self.url_cat, good_users, 200) - self.assert_response(self.url_cat, bad_users, 302) + self.assert_response(self.url_cat, self.good_users_cat, 200) + self.assert_response(self.url_cat, self.bad_users_cat, 302) + + def test_get_category_read_only(self): + """Test GET with category and site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url_cat, self.good_users_cat, 200) + self.assert_response(self.url_cat, self.bad_users_cat, 302) class TestProjectCreateView(ProjectPermissionTestBase): @@ -584,11 +638,8 @@ def setUp(self): self.url_sub = reverse( 'projectroles:create', kwargs={'project': self.category.sodar_uuid} ) - - def test_get_top(self): - """Test ProjectCreateView GET for top level creation""" - good_users = [self.superuser] - bad_users = [ + self.good_users_top = [self.superuser] + self.bad_users_top = [ self.user_owner_cat, self.user_delegate_cat, self.user_contributor_cat, @@ -601,28 +652,13 @@ def test_get_top(self): self.user_no_roles, self.anonymous, ] - self.assert_response(self.url_top, good_users, 200) - self.assert_response(self.url_top, bad_users, 302) - self.project.set_public() - self.assert_response(self.url_top, self.user_no_roles, 302) - - @override_settings(PROJECTROLES_ALLOW_ANONYMOUS=True) - def test_get_top_anon(self): - """Test GET for top level creation with anonymous access""" - self.project.set_public() - self.assert_response( - self.url_top, [self.user_no_roles, self.anonymous], 302 - ) - - def test_get_sub(self): - """Test GET for subproject creation""" - good_users = [ + self.good_users_sub = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, self.user_contributor_cat, ] - bad_users = [ + self.bad_users_sub = [ self.user_guest_cat, self.user_finder_cat, self.user_owner, @@ -632,8 +668,34 @@ def test_get_sub(self): self.user_no_roles, self.anonymous, ] - self.assert_response(self.url_sub, good_users, 200) - self.assert_response(self.url_sub, bad_users, 302) + + def test_get_top(self): + """Test ProjectCreateView GET for top level creation""" + self.assert_response(self.url_top, self.good_users_top, 200) + self.assert_response(self.url_top, self.bad_users_top, 302) + self.project.set_public() + self.assert_response(self.url_top, self.user_no_roles, 302) + + @override_settings(PROJECTROLES_ALLOW_ANONYMOUS=True) + def test_get_top_anon(self): + """Test GET for top level creation with anonymous access""" + self.project.set_public() + self.assert_response( + self.url_top, [self.user_no_roles, self.anonymous], 302 + ) + + def test_get_top_read_only(self): + """Test GET for top level with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url_top, self.good_users_top, 200) + self.assert_response(self.url_top, self.bad_users_top, 302) + self.project.set_public() + self.assert_response(self.url_top, self.user_no_roles, 302) + + def test_get_sub(self): + """Test GET for subproject creation""" + self.assert_response(self.url_sub, self.good_users_sub, 200) + self.assert_response(self.url_sub, self.bad_users_sub, 302) self.project.set_public() self.assert_response(self.url_sub, self.user_no_roles, 302) @@ -645,6 +707,15 @@ def test_get_sub_anon(self): self.url_sub, [self.user_no_roles, self.anonymous], 302 ) + def test_get_sub_read_only(self): + """Test GET for subproject with site read-only mode""" + self.set_site_read_only() + # Only superuser should have access in read-only mode + self.assert_response(self.url_sub, self.superuser, 200) + self.assert_response(self.url_sub, self.non_superusers, 302) + self.project.set_public() + self.assert_response(self.url_sub, self.user_no_roles, 302) + class TestProjectUpdateView(ProjectPermissionTestBase): """Tests for ProjectUpdateView permissions""" @@ -657,17 +728,14 @@ def setUp(self): self.url_cat = reverse( 'projectroles:update', kwargs={'project': self.category.sodar_uuid} ) - - def test_get(self): - """Test ProjectUpdateView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, self.user_owner, self.user_delegate, ] - bad_users = [ + self.bad_users = [ self.user_contributor_cat, self.user_guest_cat, self.user_finder_cat, @@ -676,8 +744,26 @@ def test_get(self): self.user_no_roles, self.anonymous, ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.good_users_cat = [ + self.superuser, + self.user_owner_cat, + self.user_delegate_cat, + ] + self.bad_users_cat = [ + self.user_contributor_cat, + self.user_guest_cat, + self.user_finder_cat, + self.user_owner, + self.user_delegate, + self.user_contributor, + self.user_guest, + self.anonymous, + ] + + def test_get(self): + """Test ProjectUpdateView GET""" + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 302) @@ -692,46 +778,21 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 302) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) + def test_get_category(self): """Test GET with category""" - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.anonymous, - ] - self.assert_response(self.url_cat, good_users, 200) - self.assert_response(self.url_cat, bad_users, 302) + self.assert_response(self.url_cat, self.good_users_cat, 200) + self.assert_response(self.url_cat, self.bad_users_cat, 302) self.project.set_public() self.assert_response(self.url_cat, self.user_no_roles, 302) @@ -743,6 +804,12 @@ def test_get_category_anon(self): self.url_cat, [self.user_no_roles, self.anonymous], 302 ) + def test_get_category_read_only(self): + """Test GET with category and site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url_cat, self.superuser, 200) + self.assert_response(self.url_cat, self.non_superusers, 302) + class TestProjectArchiveView(ProjectPermissionTestBase): """Tests for ProjectArchiveView permissions""" @@ -755,17 +822,14 @@ def setUp(self): self.url_cat = reverse( 'projectroles:archive', kwargs={'project': self.category.sodar_uuid} ) - - def test_get(self): - """Test ProjectArchiveView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, self.user_owner, self.user_delegate, ] - bad_users = [ + self.bad_users = [ self.user_contributor_cat, self.user_guest_cat, self.user_finder_cat, @@ -774,8 +838,11 @@ def test_get(self): self.user_no_roles, self.anonymous, ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + + def test_get(self): + """Test ProjectArchiveView GET""" + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 302) @@ -788,27 +855,17 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 302) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) + def test_get_category(self): """Test GET with category""" bad_users_cat = [ @@ -862,10 +919,7 @@ def setUp(self): self.url_cat = reverse( 'projectroles:roles', kwargs={'project': self.category.sodar_uuid} ) - - def test_get(self): - """Test ProjectRoleView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, @@ -876,12 +930,35 @@ def test_get(self): self.user_contributor, self.user_guest, ] - bad_users = [self.user_finder_cat, self.user_no_roles, self.anonymous] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) - self.project.set_public() - self.assert_response(self.url, self.user_no_roles, 200) - + self.bad_users = [ + self.user_finder_cat, + self.user_no_roles, + self.anonymous, + ] + self.good_users_cat = [ + self.superuser, + self.user_owner_cat, + self.user_delegate_cat, + self.user_contributor_cat, + self.user_guest_cat, + self.user_finder_cat, + ] + self.bad_users_cat = [ + self.user_owner, + self.user_delegate, + self.user_contributor, + self.user_guest, + self.user_no_roles, + self.anonymous, + ] + + def test_get(self): + """Test ProjectRoleView GET""" + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) + self.project.set_public() + self.assert_response(self.url, self.user_no_roles, 200) + @override_settings(PROJECTROLES_ALLOW_ANONYMOUS=True) def test_get_anon(self): """Test GET with anonymous access""" @@ -893,47 +970,34 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - ] - bad_users = [self.user_finder_cat, self.user_no_roles, self.anonymous] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) + self.project.set_public() + self.assert_response(self.url, self.user_no_roles, 200) + + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + # View should still be browseable + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 200) def test_get_category(self): """Test GET with category""" - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - ] - bad_users = [ - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url_cat, good_users, 200) - self.assert_response(self.url_cat, bad_users, 302) + self.assert_response(self.url_cat, self.good_users_cat, 200) + self.assert_response(self.url_cat, self.bad_users_cat, 302) # Public guest access is disabled for categories with self.assertRaises(ValidationError): self.category.set_public() + def test_get_category_read_only(self): + """Test GET with category and site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url_cat, self.good_users_cat, 200) + self.assert_response(self.url_cat, self.bad_users_cat, 302) + class TestRoleAssignmentCreateView(ProjectPermissionTestBase): """Tests for RoleAssignmentCreateView permissions""" @@ -948,17 +1012,14 @@ def setUp(self): 'projectroles:role_create', kwargs={'project': self.category.sodar_uuid}, ) - - def test_get(self): - """Test RoleAssignmentCreateView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, self.user_owner, self.user_delegate, ] - bad_users = [ + self.bad_users = [ self.user_contributor_cat, self.user_guest_cat, self.user_finder_cat, @@ -966,8 +1027,11 @@ def test_get(self): self.user_no_roles, self.anonymous, ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + + def test_get(self): + """Test RoleAssignmentCreateView GET""" + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 302) @@ -982,27 +1046,17 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 302) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) + def test_get_category(self): """Test GET with category""" good_users = [ @@ -1027,6 +1081,12 @@ def test_get_category(self): with self.assertRaises(ValidationError): self.category.set_public() + def test_get_category_read_only(self): + """Test GET with category and site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url_cat, self.superuser, 200) + self.assert_response(self.url_cat, self.non_superusers, 302) + class TestRoleAssignmentUpdateView(ProjectPermissionTestBase): """Tests for RoleAssignmentUpdateView permissions""" @@ -1041,17 +1101,14 @@ def setUp(self): 'projectroles:role_update', kwargs={'roleassignment': self.contributor_as_cat.sodar_uuid}, ) - - def test_get(self): - """Test RoleAssignmentUpdateView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, self.user_owner, self.user_delegate, ] - bad_users = [ + self.bad_users = [ self.user_contributor_cat, self.user_guest_cat, self.user_finder_cat, @@ -1060,8 +1117,11 @@ def test_get(self): self.user_no_roles, self.anonymous, ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + + def test_get(self): + """Test RoleAssignmentUpdateView GET""" + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 302) @@ -1076,27 +1136,17 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_guest, - self.user_contributor, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 302) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) + def test_get_category(self): """Test GET with category""" good_users = [ @@ -1128,6 +1178,12 @@ def test_get_category_anon(self): self.url_cat, [self.user_no_roles, self.anonymous], 302 ) + def test_get_category_read_only(self): + """Test GET with category and site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url_cat, self.superuser, 200) + self.assert_response(self.url_cat, self.non_superusers, 302) + def test_get_owner(self): """Test GET with owner role (should fail)""" url = reverse( @@ -1202,17 +1258,14 @@ def setUp(self): 'projectroles:role_delete', kwargs={'roleassignment': self.contributor_as_cat.sodar_uuid}, ) - - def test_get(self): - """Test RoleAssignmentDeleteView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, self.user_owner, self.user_delegate, ] - bad_users = [ + self.bad_users = [ self.user_contributor_cat, self.user_guest_cat, self.user_finder_cat, @@ -1221,8 +1274,11 @@ def test_get(self): self.user_no_roles, self.anonymous, ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + + def test_get(self): + """Test RoleAssignmentDeleteView GET""" + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 302) @@ -1237,29 +1293,19 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 302) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) + def test_get_category(self): - """Test RoleAssignmentDeleteView GET""" + """Test GET with category""" good_users = [ self.superuser, self.user_owner_cat, @@ -1289,6 +1335,12 @@ def test_get_category_anon(self): self.url_cat, [self.user_no_roles, self.anonymous], 302 ) + def test_get_category_read_only(self): + """Test GET with category and site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url_cat, self.superuser, 200) + self.assert_response(self.url_cat, self.non_superusers, 302) + def test_get_owner(self): """Test GET with owner role (should fail)""" url = reverse( @@ -1360,15 +1412,16 @@ def setUp(self): 'projectroles:role_owner_transfer', kwargs={'project': self.project.sodar_uuid}, ) - - def test_get(self): - """Test RoleAssignmentOwnerTransferView GET""" - good_users = [ + self.url_cat = reverse( + 'projectroles:role_owner_transfer', + kwargs={'project': self.category.sodar_uuid}, + ) + self.good_users = [ self.superuser, self.user_owner_cat, self.user_owner, ] - bad_users = [ + self.bad_users = [ self.user_delegate_cat, self.user_contributor_cat, self.user_guest_cat, @@ -1379,8 +1432,11 @@ def test_get(self): self.user_no_roles, self.anonymous, ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + + def test_get(self): + """Test RoleAssignmentOwnerTransferView GET""" + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 302) @@ -1395,26 +1451,45 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) + self.project.set_public() + self.assert_response(self.url, self.user_no_roles, 302) + + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) + + def test_get_category(self): + """Test GET with category""" good_users = [ self.superuser, self.user_owner_cat, - self.user_owner, ] bad_users = [ self.user_delegate_cat, + self.user_owner, + self.user_delegate, self.user_contributor_cat, self.user_guest_cat, self.user_finder_cat, - self.user_delegate, self.user_contributor, self.user_guest, self.user_no_roles, self.anonymous, ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url_cat, good_users, 200) + self.assert_response(self.url_cat, bad_users, 302) self.project.set_public() - self.assert_response(self.url, self.user_no_roles, 302) + self.assert_response(self.url_cat, self.user_no_roles, 302) + + def test_get_category_read_only(self): + """Test GET with category and site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url_cat, self.superuser, 200) + self.assert_response(self.url_cat, self.non_superusers, 302) class TestProjectInviteView(ProjectPermissionTestBase): @@ -1428,17 +1503,14 @@ def setUp(self): self.url_cat = reverse( 'projectroles:invites', kwargs={'project': self.category.sodar_uuid} ) - - def test_get(self): - """Test ProjectInviteView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, self.user_owner, self.user_delegate, ] - bad_users = [ + self.bad_users = [ self.user_contributor_cat, self.user_guest_cat, self.user_finder_cat, @@ -1447,8 +1519,11 @@ def test_get(self): self.user_no_roles, self.anonymous, ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + + def test_get(self): + """Test ProjectInviteView GET""" + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 302) @@ -1463,27 +1538,17 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 302) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) + def test_get_category(self): """Test GET with category""" good_users = [ @@ -1508,6 +1573,12 @@ def test_get_category(self): with self.assertRaises(ValidationError): self.category.set_public() + def test_get_category_read_only(self): + """Test GET with category and site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url_cat, self.superuser, 200) + self.assert_response(self.url_cat, self.non_superusers, 302) + class TestProjectInviteCreateView(ProjectPermissionTestBase): """Tests for ProjectInviteCreateView permissions""" @@ -1522,17 +1593,14 @@ def setUp(self): 'projectroles:invite_create', kwargs={'project': self.category.sodar_uuid}, ) - - def test_get(self): - """Test ProjectInviteCreateView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, self.user_owner, self.user_delegate, ] - bad_users = [ + self.bad_users = [ self.user_contributor_cat, self.user_guest_cat, self.user_finder_cat, @@ -1541,8 +1609,11 @@ def test_get(self): self.user_no_roles, self.anonymous, ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + + def test_get(self): + """Test ProjectInviteCreateView GET""" + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 302) @@ -1557,27 +1628,17 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 302) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) + def test_get_category(self): """Test GET with category""" good_users = [ @@ -1598,8 +1659,12 @@ def test_get_category(self): ] self.assert_response(self.url_cat, good_users, 200) self.assert_response(self.url_cat, bad_users, 302) - self.project.set_public() - self.assert_response(self.url_cat, self.user_no_roles, 302) + + def test_get_category_read_only(self): + """Test GET with category and site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url_cat, self.superuser, 200) + self.assert_response(self.url_cat, self.non_superusers, 302) class TestProjectInviteResendView(ProjectPermissionTestBase): @@ -1619,17 +1684,14 @@ def setUp(self): 'projectroles:invite_resend', kwargs={'projectinvite': invite.sodar_uuid}, ) - - def test_get(self): - """Test ProjectInviteResendView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, self.user_owner, self.user_delegate, ] - bad_users = [ + self.bad_users = [ self.user_contributor_cat, self.user_guest_cat, self.user_finder_cat, @@ -1638,16 +1700,19 @@ def test_get(self): self.user_no_roles, self.anonymous, ] + + def test_get(self): + """Test ProjectInviteResendView GET""" self.assert_response( self.url, - good_users, + self.good_users, 302, redirect_user=reverse( 'projectroles:invites', kwargs={'project': self.project.sodar_uuid}, ), ) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 302) @@ -1662,32 +1727,16 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] self.assert_response( self.url, - good_users, + self.good_users, 302, redirect_user=reverse( 'projectroles:invites', kwargs={'project': self.project.sodar_uuid}, ), ) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 302) @@ -1709,17 +1758,14 @@ def setUp(self): 'projectroles:invite_revoke', kwargs={'projectinvite': invite.sodar_uuid}, ) - - def test_get(self): - """Test ProjectInviteRevokeView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, self.user_owner, self.user_delegate, ] - bad_users = [ + self.bad_users = [ self.user_contributor_cat, self.user_guest_cat, self.user_finder_cat, @@ -1728,8 +1774,11 @@ def test_get(self): self.user_no_roles, self.anonymous, ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + + def test_get(self): + """Test ProjectInviteRevokeView GET""" + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 302) @@ -1744,27 +1793,17 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 302) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) + class TestRemoteSiteViews(RemoteSiteMixin, SiteAppPermissionTestBase): """Tests for UI view permissions in remote site views""" diff --git a/projectroles/tests/test_permissions_ajax.py b/projectroles/tests/test_permissions_ajax.py index b9c14d73..1eab5fb8 100644 --- a/projectroles/tests/test_permissions_ajax.py +++ b/projectroles/tests/test_permissions_ajax.py @@ -24,20 +24,7 @@ class TestProjectListAjaxViews(ProjectPermissionTestBase): def test_get_project_list(self): """Test ProjectListAjaxView GET""" url = reverse('projectroles:ajax_project_list') - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] - self.assert_response(url, good_users, 200) + self.assert_response(url, self.auth_users, 200) self.assert_response(url, self.anonymous, 403) self.project.set_public() self.assert_response(url, self.anonymous, 403) @@ -50,27 +37,23 @@ def test_get_project_list_anon(self): self.project.set_public() self.assert_response(url, self.anonymous, 200) + def test_get_project_list_read_only(self): + """Test ProjectListAjaxView GET with site read-only mode""" + self.set_site_read_only() + url = reverse('projectroles:ajax_project_list') + self.assert_response(url, self.auth_users, 200) + self.assert_response(url, self.anonymous, 403) + self.project.set_public() + self.assert_response(url, self.anonymous, 403) + def test_get_project_list_column(self): """Test ProjectListColumnAjaxView GET""" url = reverse('projectroles:ajax_project_list_columns') data = {'projects': [str(self.project.sodar_uuid)]} req_kwargs = {'content_type': 'application/json'} - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response( url, - good_users, + self.auth_users, 200, method='POST', data=data, @@ -110,27 +93,37 @@ def test_get_project_list_column_anon(self): req_kwargs=req_kwargs, ) + def test_get_project_list_column_read_only(self): + """Test ProjectListColumnAjaxView GET with site read-only mode""" + self.set_site_read_only() + url = reverse('projectroles:ajax_project_list_columns') + data = {'projects': [str(self.project.sodar_uuid)]} + req_kwargs = {'content_type': 'application/json'} + self.assert_response( + url, + self.auth_users, + 200, + method='POST', + data=data, + req_kwargs=req_kwargs, + ) + self.assert_response( + url, + self.anonymous, + 403, + method='POST', + data=data, + req_kwargs=req_kwargs, + ) + def test_get_project_list_role(self): """Test ProjectListRoleAjaxView GET""" url = reverse('projectroles:ajax_project_list_roles') data = {'projects': [str(self.project.sodar_uuid)]} req_kwargs = {'content_type': 'application/json'} - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response( url, - good_users, + self.auth_users, 200, method='POST', data=data, @@ -170,6 +163,38 @@ def test_get_project_list_role_anon(self): req_kwargs=req_kwargs, ) + def test_get_project_list_role_read_only(self): + """Test ProjectListRoleAjaxView GET with site read-only mode""" + self.set_site_read_only() + url = reverse('projectroles:ajax_project_list_roles') + data = {'projects': [str(self.project.sodar_uuid)]} + req_kwargs = {'content_type': 'application/json'} + self.assert_response( + url, + self.auth_users, + 200, + method='POST', + data=data, + req_kwargs=req_kwargs, + ) + self.assert_response( + url, + self.anonymous, + 403, + method='POST', + data=data, + req_kwargs=req_kwargs, + ) + self.project.set_public() + self.assert_response( + url, + self.anonymous, + 403, + method='POST', + data=data, + req_kwargs=req_kwargs, + ) + class TestProjectStarringAjaxView(ProjectPermissionTestBase): """Tests for ProjectStarringAjaxView permissions""" @@ -185,8 +210,8 @@ def setUp(self): kwargs={'project': self.category.sodar_uuid}, ) - def test_get(self): - """Test ProjectStarringAjaxView GET""" + def test_post(self): + """Test ProjectStarringAjaxView POST""" good_users = [ self.superuser, self.user_owner_cat, @@ -205,13 +230,19 @@ def test_get(self): self.assert_response(self.url, self.user_no_roles, 200, method='POST') @override_settings(PROJECTROLES_ALLOW_ANONYMOUS=True) - def test_get_anon(self): - """Test GET with anonymous access""" + def test_post_anon(self): + """Test POST with anonymous access""" self.project.set_public() self.assert_response(self.url, self.anonymous, 401, method='POST') - def test_get_category(self): - """Test GET with category""" + def test_post_read_only(self): + """Test POST with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.superuser, 200, method='POST') + self.assert_response(self.url, self.non_superusers, 403, method='POST') + + def test_post_category(self): + """Test POST with category""" good_users = [ self.superuser, self.user_owner_cat, @@ -233,11 +264,19 @@ def test_get_category(self): ) @override_settings(PROJECTROLES_ALLOW_ANONYMOUS=True) - def test_get_category_anon(self): - """Test GET with category and anonymous access""" + def test_post_category_anon(self): + """Test POST with category and anonymous access""" self.project.set_public() self.assert_response(self.url_cat, self.anonymous, 401, method='POST') + def test_post_category_read_only(self): + """Test POST with category and site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url_cat, self.superuser, 200, method='POST') + self.assert_response( + self.url_cat, self.non_superusers, 403, method='POST' + ) + class TestRemoteProjectAccessAjaxView( RemoteSiteMixin, RemoteProjectMixin, ProjectPermissionTestBase @@ -261,10 +300,7 @@ def setUp(self): 'projectroles:ajax_remote_access', kwargs={'project': self.project.sodar_uuid}, ) + '?rp={}'.format(self.remote_project.sodar_uuid) - - def test_get(self): - """Test RemoteProjectAccessAjaxView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, @@ -275,9 +311,16 @@ def test_get(self): self.user_contributor, self.user_guest, ] - bad_users = [self.user_finder_cat, self.user_no_roles, self.anonymous] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 403) + self.bad_users = [ + self.user_finder_cat, + self.user_no_roles, + self.anonymous, + ] + + def test_get(self): + """Test RemoteProjectAccessAjaxView GET""" + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 403) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 200) @@ -290,20 +333,14 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - ] - bad_users = [self.user_finder_cat, self.user_no_roles, self.anonymous] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 403) + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 403) + + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 403) class TestSidebarContentAjaxView(ProjectPermissionTestBase): @@ -319,10 +356,7 @@ def setUp(self): 'projectroles:ajax_sidebar', kwargs={'project': self.category.sodar_uuid}, ) - - def test_get(self): - """Test SidebarContentAjaxView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, @@ -333,9 +367,29 @@ def test_get(self): self.user_contributor, self.user_guest, ] - bad_users = [self.user_finder_cat, self.user_no_roles, self.anonymous] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 403) + self.bad_users = [ + self.user_finder_cat, + self.user_no_roles, + self.anonymous, + ] + self.good_users_cat = [ + self.superuser, + self.user_owner_cat, + self.user_delegate_cat, + self.user_contributor_cat, + self.user_guest_cat, + self.user_finder_cat, + self.user_owner, + self.user_delegate, + self.user_contributor, + self.user_guest, + ] + self.bad_users_cat = [self.user_no_roles, self.anonymous] + + def test_get(self): + """Test SidebarContentAjaxView GET""" + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 403) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 200) @@ -348,38 +402,19 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - ] - bad_users = [self.user_finder_cat, self.user_no_roles, self.anonymous] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 403) + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 403) + + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 403) def test_get_category(self): """Test GET with category""" - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - ] - bad_users = [self.user_no_roles, self.anonymous] - self.assert_response(self.url_cat, good_users, 200) - self.assert_response(self.url_cat, bad_users, 403) + self.assert_response(self.url_cat, self.good_users_cat, 200) + self.assert_response(self.url_cat, self.bad_users_cat, 403) self.project.set_public() self.assert_response(self.url_cat, self.user_no_roles, 200) @@ -389,6 +424,30 @@ def test_get_category_anon(self): self.project.set_public() self.assert_response(self.url_cat, self.anonymous, 200) + def test_get_category_read_only(self): + """Test GET with category and site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url_cat, self.good_users_cat, 200) + self.assert_response(self.url_cat, self.bad_users_cat, 403) + + +class TestSiteReadOnlySettingAjaxView(ProjectPermissionTestBase): + """Tests for SiteReadOnlySettingAjaxView permissions""" + + def setUp(self): + super().setUp() + self.url = reverse('projectroles:ajax_settings_site_read_only') + + def test_get(self): + """Test SiteReadOnlySettingAjaxView GET""" + self.assert_response(self.url, self.auth_users, 200) + self.assert_response(self.url, self.anonymous, 403) + + @override_settings(PROJECTROLES_ALLOW_ANONYMOUS=True) + def test_get_anon(self): + """Test GET with anonymous access""" + self.assert_response(self.url, self.anonymous, 200) + class TestUserDropdownContentAjaxView(ProjectPermissionTestBase): """Tests for UserDropdownContentAjaxView permissions""" @@ -399,22 +458,14 @@ def setUp(self): def test_get(self): """Test UserDropdownContentAjaxView GET""" - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_finder_cat, - self.user_no_roles, - ] - bad_users = [self.anonymous] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 403) + self.assert_response(self.url, self.auth_users, 200) + self.assert_response(self.url, self.anonymous, 403) + + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.auth_users, 200) + self.assert_response(self.url, self.anonymous, 403) class TestUserAjaxViews(ProjectPermissionTestBase): @@ -423,40 +474,27 @@ class TestUserAjaxViews(ProjectPermissionTestBase): def test_get_current_user(self): """Test CurrentUserRetrieveAjaxView GET""" url = reverse('projectroles:ajax_user_current') - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] - bad_users = [self.anonymous] - self.assert_response(url, good_users, 200) - self.assert_response(url, bad_users, 403) + self.assert_response(url, self.auth_users, 200) + self.assert_response(url, self.anonymous, 403) + + def test_get_current_user_read_only(self): + """Test CurrentUserRetrieveAjaxView GET with site read-only mode""" + self.set_site_read_only() + url = reverse('projectroles:ajax_user_current') + self.assert_response(url, self.auth_users, 200) + self.assert_response(url, self.anonymous, 403) @override_settings(PROJECTROLES_ALLOW_LOCAL_USERS=True) def test_get_autocomplete_ajax(self): """Test UserAutocompleteAjaxView GET""" url = reverse('projectroles:ajax_autocomplete_user') - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] - bad_users = [self.anonymous] - self.assert_response(url, good_users, 200) - self.assert_response(url, bad_users, 403) + self.assert_response(url, self.auth_users, 200) + self.assert_response(url, self.anonymous, 403) + + @override_settings(PROJECTROLES_ALLOW_LOCAL_USERS=True) + def test_get_autocomplete_ajax_read_only(self): + """Test UserAutocompleteAjaxView GET with site read-only mode""" + self.set_site_read_only() + url = reverse('projectroles:ajax_autocomplete_user') + self.assert_response(url, self.auth_users, 200) + self.assert_response(url, self.anonymous, 403) diff --git a/projectroles/tests/test_permissions_api.py b/projectroles/tests/test_permissions_api.py index c82f8386..505f9436 100644 --- a/projectroles/tests/test_permissions_api.py +++ b/projectroles/tests/test_permissions_api.py @@ -125,25 +125,21 @@ class ProjectrolesAPIPermissionTestBase(ProjectAPIPermissionTestBase): class TestProjectListAPIView(ProjectrolesAPIPermissionTestBase): """Tests for ProjectListAPIView permissions""" + def setUp(self): + super().setUp() + self.url = reverse('projectroles:api_project_list') + def test_get(self): """Test ProjectListAPIView GET""" - url = reverse('projectroles:api_project_list') - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] - self.assert_response_api(url, good_users, 200) - self.assert_response_api(url, self.anonymous, 401) - self.assert_response_api(url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.auth_users, 200) + self.assert_response_api(self.url, self.anonymous, 401) + self.assert_response_api(self.url, self.auth_users, 200, knox=True) + + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response_api(self.url, self.auth_users, 200) + self.assert_response_api(self.url, self.anonymous, 401) class TestProjectRetrieveAPIView(ProjectrolesAPIPermissionTestBase): @@ -155,10 +151,7 @@ def setUp(self): 'projectroles:api_project_retrieve', kwargs={'project': self.project.sodar_uuid}, ) - - def test_get(self): - """Test ProjectRetrieveAPIView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, @@ -169,12 +162,15 @@ def test_get(self): self.user_contributor, self.user_guest, ] - bad_users = [self.user_finder_cat, self.user_no_roles] - self.assert_response_api(self.url, good_users, 200) - self.assert_response_api(self.url, bad_users, 403) + self.bad_users = [self.user_finder_cat, self.user_no_roles] + + def test_get(self): + """Test ProjectRetrieveAPIView GET""" + self.assert_response_api(self.url, self.good_users, 200) + self.assert_response_api(self.url, self.bad_users, 403) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) - self.assert_response_api(self.url, bad_users, 403, knox=True) + self.assert_response_api(self.url, self.good_users, 200, knox=True) + self.assert_response_api(self.url, self.bad_users, 403, knox=True) self.project.set_public() self.assert_response_api(self.url, self.user_no_roles, 200) @@ -187,26 +183,21 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - ] - bad_users = [self.user_finder_cat, self.user_no_roles] - self.assert_response_api(self.url, good_users, 200) - self.assert_response_api(self.url, bad_users, 403) + self.assert_response_api(self.url, self.good_users, 200) + self.assert_response_api(self.url, self.bad_users, 403) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) - self.assert_response_api(self.url, bad_users, 403, knox=True) + self.assert_response_api(self.url, self.good_users, 200, knox=True) + self.assert_response_api(self.url, self.bad_users, 403, knox=True) self.project.set_public() self.assert_response_api(self.url, self.user_no_roles, 200) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response_api(self.url, self.good_users, 200) + self.assert_response_api(self.url, self.bad_users, 403) + self.assert_response_api(self.url, self.anonymous, 401) + class TestProjectCreateAPIView(ProjectrolesAPIPermissionTestBase): """Tests for ProjectCreateAPIView permissions""" @@ -234,21 +225,10 @@ def setUp(self): 'owner': str(self.user_owner.sodar_uuid), } - def test_post(self): - """Test ProjectCreateAPIView POST""" + def test_post_top(self): + """Test ProjectCreateAPIView POST on top level""" good_users = [self.superuser] - bad_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] + bad_users = self.auth_non_superusers self.assert_response_api( self.url, good_users, @@ -290,13 +270,33 @@ def test_post(self): ) @override_settings(PROJECTROLES_ALLOW_ANONYMOUS=True) - def test_post_anon(self): - """Test POST with anonymous access""" + def test_post_top_anon(self): + """Test POST with top level and anonymous access""" self.project.set_public() self.assert_response_api( self.url, self.anonymous, 401, method='POST', data=self.post_data ) + def test_post_top_read_only(self): + """Test POST on top level with site read-only mode""" + self.set_site_read_only() + good_users = [self.superuser] + bad_users = self.auth_non_superusers + self.assert_response_api( + self.url, + good_users, + 201, + method='POST', + data=self.post_data, + cleanup_method=self._cleanup, + ) + self.assert_response_api( + self.url, bad_users, 403, method='POST', data=self.post_data + ) + self.assert_response_api( + self.url, self.anonymous, 401, method='POST', data=self.post_data + ) + def test_post_parent(self): """Test POST with parent category""" good_users = [ @@ -370,6 +370,30 @@ def test_post_parent_anon(self): data=self.post_data_parent, ) + def test_post_parent_read_only(self): + """Test POST with parent category and site read-only mode""" + self.set_site_read_only() + good_users = [self.superuser] + bad_users = self.auth_non_superusers + self.assert_response_api( + self.url, + good_users, + 201, + method='POST', + data=self.post_data_parent, + cleanup_method=self._cleanup, + ) + self.assert_response_api( + self.url, bad_users, 403, method='POST', data=self.post_data_parent + ) + self.assert_response_api( + self.url, + self.anonymous, + 401, + method='POST', + data=self.post_data_parent, + ) + class TestProjectUpdateAPIView(ProjectrolesAPIPermissionTestBase): """Tests for ProjectUpdateAPIView permissions""" @@ -388,17 +412,14 @@ def setUp(self): 'readme': 'readme', 'owner': str(self.user_owner.sodar_uuid), } - - def test_put(self): - """Test ProjectUpdateAPIView PUT""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, self.user_owner, self.user_delegate, ] - bad_users = [ + self.bad_users = [ self.user_contributor_cat, self.user_guest_cat, self.user_finder_cat, @@ -406,18 +427,21 @@ def test_put(self): self.user_guest, self.user_no_roles, ] + + def test_put(self): + """Test ProjectUpdateAPIView PUT""" self.assert_response_api( - self.url, good_users, 200, method='PUT', data=self.put_data + self.url, self.good_users, 200, method='PUT', data=self.put_data ) self.assert_response_api( - self.url, bad_users, 403, method='PUT', data=self.put_data + self.url, self.bad_users, 403, method='PUT', data=self.put_data ) self.assert_response_api( self.url, self.anonymous, 401, method='PUT', data=self.put_data ) self.assert_response_api( self.url, - good_users, + self.good_users, 200, method='PUT', data=self.put_data, @@ -425,7 +449,7 @@ def test_put(self): ) self.assert_response_api( self.url, - bad_users, + self.bad_users, 403, method='PUT', data=self.put_data, @@ -446,33 +470,18 @@ def test_put_anon(self): def test_put_archive(self): """Test PUT with archived project""" - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( - self.url, good_users, 200, method='PUT', data=self.put_data + self.url, self.good_users, 200, method='PUT', data=self.put_data ) self.assert_response_api( - self.url, bad_users, 403, method='PUT', data=self.put_data + self.url, self.bad_users, 403, method='PUT', data=self.put_data ) self.assert_response_api( self.url, self.anonymous, 401, method='PUT', data=self.put_data ) self.assert_response_api( self.url, - good_users, + self.good_users, 200, method='PUT', data=self.put_data, @@ -480,7 +489,7 @@ def test_put_archive(self): ) self.assert_response_api( self.url, - bad_users, + self.bad_users, 403, method='PUT', data=self.put_data, @@ -491,6 +500,19 @@ def test_put_archive(self): self.url, self.user_no_roles, 403, method='PUT', data=self.put_data ) + def test_put_read_only(self): + """Test PUT with site read-only mode""" + self.set_site_read_only() + self.assert_response_api( + self.url, self.superuser, 200, method='PUT', data=self.put_data + ) + self.assert_response_api( + self.url, self.bad_users, 403, method='PUT', data=self.put_data + ) + self.assert_response_api( + self.url, self.anonymous, 401, method='PUT', data=self.put_data + ) + class TestRoleAssignmentCreateAPIView(ProjectrolesAPIPermissionTestBase): """Tests for RoleAssignmentCreateAPIView permissions""" @@ -513,17 +535,14 @@ def setUp(self): 'role': SODAR_CONSTANTS['PROJECT_ROLE_CONTRIBUTOR'], 'user': str(self.assign_user.sodar_uuid), } - - def test_post(self): - """Test RoleAssignmentCreateAPIView POST""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, self.user_owner, self.user_delegate, ] - bad_users = [ + self.bad_users = [ self.user_contributor_cat, self.user_guest_cat, self.user_finder_cat, @@ -531,23 +550,26 @@ def test_post(self): self.user_guest, self.user_no_roles, ] + + def test_post(self): + """Test RoleAssignmentCreateAPIView POST""" self.assert_response_api( self.url, - good_users, + self.good_users, 201, method='POST', data=self.post_data, cleanup_method=self._cleanup, ) self.assert_response_api( - self.url, bad_users, 403, method='POST', data=self.post_data + self.url, self.bad_users, 403, method='POST', data=self.post_data ) self.assert_response_api( self.url, self.anonymous, 401, method='POST', data=self.post_data ) self.assert_response_api( self.url, - good_users, + self.good_users, 201, method='POST', data=self.post_data, @@ -556,7 +578,7 @@ def test_post(self): ) self.assert_response_api( self.url, - bad_users, + self.bad_users, 403, method='POST', data=self.post_data, @@ -582,38 +604,23 @@ def test_post_anon(self): def test_post_archive(self): """Test POST with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( self.url, - good_users, + self.good_users, 201, method='POST', data=self.post_data, cleanup_method=self._cleanup, ) self.assert_response_api( - self.url, bad_users, 403, method='POST', data=self.post_data + self.url, self.bad_users, 403, method='POST', data=self.post_data ) self.assert_response_api( self.url, self.anonymous, 401, method='POST', data=self.post_data ) self.assert_response_api( self.url, - good_users, + self.good_users, 201, method='POST', data=self.post_data, @@ -622,7 +629,7 @@ def test_post_archive(self): ) self.assert_response_api( self.url, - bad_users, + self.bad_users, 403, method='POST', data=self.post_data, @@ -637,6 +644,28 @@ def test_post_archive(self): data=self.post_data, ) + def test_post_read_only(self): + """Test POST with site read-only mode""" + self.set_site_read_only() + self.assert_response_api( + self.url, + self.superuser, + 201, + method='POST', + data=self.post_data, + cleanup_method=self._cleanup, + ) + self.assert_response_api( + self.url, + self.auth_non_superusers, + 403, + method='POST', + data=self.post_data, + ) + self.assert_response_api( + self.url, self.anonymous, 401, method='POST', data=self.post_data + ) + class TestRoleAssignmentUpdateAPIView(ProjectrolesAPIPermissionTestBase): """Tests for RoleAssignmentUpdateAPIView permissions""" @@ -655,17 +684,14 @@ def setUp(self): 'role': self.role_guest.name, 'user': str(self.assign_user.sodar_uuid), } - - def test_put(self): - """Test RoleAssignmentUpdateAPIView PUT""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, self.user_owner, self.user_delegate, ] - bad_users = [ + self.bad_users = [ self.user_contributor_cat, self.user_guest_cat, self.user_finder_cat, @@ -673,18 +699,21 @@ def test_put(self): self.user_guest, self.user_no_roles, ] + + def test_put(self): + """Test RoleAssignmentUpdateAPIView PUT""" self.assert_response_api( - self.url, good_users, 200, method='PUT', data=self.put_data + self.url, self.good_users, 200, method='PUT', data=self.put_data ) self.assert_response_api( - self.url, bad_users, 403, method='PUT', data=self.put_data + self.url, self.bad_users, 403, method='PUT', data=self.put_data ) self.assert_response_api( self.url, self.anonymous, 401, method='PUT', data=self.put_data ) self.assert_response_api( self.url, - good_users, + self.good_users, 200, method='PUT', data=self.put_data, @@ -692,7 +721,7 @@ def test_put(self): ) self.assert_response_api( self.url, - bad_users, + self.bad_users, 403, method='PUT', data=self.put_data, @@ -714,33 +743,18 @@ def test_put_anon(self): def test_put_archive(self): """Test PUT with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( - self.url, good_users, 200, method='PUT', data=self.put_data + self.url, self.good_users, 200, method='PUT', data=self.put_data ) self.assert_response_api( - self.url, bad_users, 403, method='PUT', data=self.put_data + self.url, self.bad_users, 403, method='PUT', data=self.put_data ) self.assert_response_api( self.url, self.anonymous, 401, method='PUT', data=self.put_data ) self.assert_response_api( self.url, - good_users, + self.good_users, 200, method='PUT', data=self.put_data, @@ -748,7 +762,7 @@ def test_put_archive(self): ) self.assert_response_api( self.url, - bad_users, + self.bad_users, 403, method='PUT', data=self.put_data, @@ -759,6 +773,23 @@ def test_put_archive(self): self.url, self.user_no_roles, 403, method='PUT', data=self.put_data ) + def test_put_read_only(self): + """Test PUT with site read-only mode""" + self.set_site_read_only() + self.assert_response_api( + self.url, self.superuser, 200, method='PUT', data=self.put_data + ) + self.assert_response_api( + self.url, + self.auth_non_superusers, + 403, + method='PUT', + data=self.put_data, + ) + self.assert_response_api( + self.url, self.anonymous, 401, method='PUT', data=self.put_data + ) + class TestRoleAssignmentDestroyAPIView(ProjectrolesAPIPermissionTestBase): """Tests for RoleAssignmentDestroyAPIView permissions""" @@ -779,17 +810,14 @@ def setUp(self): 'projectroles:api_role_destroy', kwargs={'roleassignment': self.role_uuid}, ) - - def test_delete(self): - """Test RoleAssignmentDestroyAPIView DELETE""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, self.user_owner, self.user_delegate, ] - bad_users = [ + self.bad_users = [ self.user_contributor_cat, self.user_guest_cat, self.user_finder_cat, @@ -797,25 +825,28 @@ def test_delete(self): self.user_guest, self.user_no_roles, ] + + def test_delete(self): + """Test RoleAssignmentDestroyAPIView DELETE""" self.assert_response_api( self.url, - good_users, + self.good_users, 204, method='DELETE', cleanup_method=self._make_as, ) - self.assert_response_api(self.url, bad_users, 403, method='DELETE') + self.assert_response_api(self.url, self.bad_users, 403, method='DELETE') self.assert_response_api(self.url, self.anonymous, 401, method='DELETE') self.assert_response_api( self.url, - good_users, + self.good_users, 204, method='DELETE', cleanup_method=self._make_as, knox=True, ) self.assert_response_api( - self.url, bad_users, 403, method='DELETE', knox=True + self.url, self.bad_users, 403, method='DELETE', knox=True ) self.project.set_public() self.assert_response_api( @@ -831,46 +862,46 @@ def test_delete_anon(self): def test_delete_archive(self): """Test DELETE with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( self.url, - good_users, + self.good_users, 204, method='DELETE', cleanup_method=self._make_as, ) - self.assert_response_api(self.url, bad_users, 403, method='DELETE') + self.assert_response_api(self.url, self.bad_users, 403, method='DELETE') self.assert_response_api(self.url, self.anonymous, 401, method='DELETE') self.assert_response_api( self.url, - good_users, + self.good_users, 204, method='DELETE', cleanup_method=self._make_as, knox=True, ) self.assert_response_api( - self.url, bad_users, 403, method='DELETE', knox=True + self.url, self.bad_users, 403, method='DELETE', knox=True ) self.project.set_public() self.assert_response_api( self.url, self.user_no_roles, 403, method='DELETE' ) + def test_delete_read_only(self): + """Test DELETE with site read-only mode""" + self.set_site_read_only() + self.assert_response_api( + self.url, + self.superuser, + 204, + method='DELETE', + cleanup_method=self._make_as, + ) + self.assert_response_api( + self.url, self.auth_non_superusers, 403, method='DELETE' + ) + self.assert_response_api(self.url, self.anonymous, 401, method='DELETE') + class TestRoleAssignmentOwnerTransferAPIView(ProjectrolesAPIPermissionTestBase): """Tests for RoleAssignmentOwnerTransferAPIView permissions""" @@ -897,15 +928,12 @@ def setUp(self): 'new_owner': self.new_owner.username, 'old_owner_role': SODAR_CONSTANTS['PROJECT_ROLE_CONTRIBUTOR'], } - - def test_post(self): - """Test RoleAssignmentOwnerTransferAPIView POST""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_owner, ] - bad_users = [ + self.bad_users = [ self.user_delegate_cat, self.user_contributor_cat, self.user_guest_cat, @@ -915,23 +943,26 @@ def test_post(self): self.user_guest, self.user_no_roles, ] + + def test_post(self): + """Test RoleAssignmentOwnerTransferAPIView POST""" self.assert_response_api( self.url, - good_users, + self.good_users, 200, method='POST', data=self.post_data, cleanup_method=self._cleanup, ) self.assert_response_api( - self.url, bad_users, 403, method='POST', data=self.post_data + self.url, self.bad_users, 403, method='POST', data=self.post_data ) self.assert_response_api( self.url, self.anonymous, 401, method='POST', data=self.post_data ) self.assert_response_api( self.url, - good_users, + self.good_users, 200, method='POST', data=self.post_data, @@ -940,7 +971,7 @@ def test_post(self): ) self.assert_response_api( self.url, - bad_users, + self.bad_users, 403, method='POST', data=self.post_data, @@ -966,38 +997,23 @@ def test_post_anon(self): def test_post_archive(self): """Test POST with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_owner, - ] - bad_users = [ - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( self.url, - good_users, + self.good_users, 200, method='POST', data=self.post_data, cleanup_method=self._cleanup, ) self.assert_response_api( - self.url, bad_users, 403, method='POST', data=self.post_data + self.url, self.bad_users, 403, method='POST', data=self.post_data ) self.assert_response_api( self.url, self.anonymous, 401, method='POST', data=self.post_data ) self.assert_response_api( self.url, - good_users, + self.good_users, 200, method='POST', data=self.post_data, @@ -1006,7 +1022,7 @@ def test_post_archive(self): ) self.assert_response_api( self.url, - bad_users, + self.bad_users, 403, method='POST', data=self.post_data, @@ -1021,6 +1037,28 @@ def test_post_archive(self): data=self.post_data, ) + def test_post_read_only(self): + """Test POST with site read-only mode""" + self.set_site_read_only() + self.assert_response_api( + self.url, + self.superuser, + 200, + method='POST', + data=self.post_data, + cleanup_method=self._cleanup, + ) + self.assert_response_api( + self.url, + self.auth_non_superusers, + 403, + method='POST', + data=self.post_data, + ) + self.assert_response_api( + self.url, self.anonymous, 401, method='POST', data=self.post_data + ) + class TestProjectInviteListAPIView(ProjectrolesAPIPermissionTestBase): """Tests for ProjectInviteListAPIView permissions""" @@ -1031,17 +1069,14 @@ def setUp(self): 'projectroles:api_invite_list', kwargs={'project': self.project.sodar_uuid}, ) - - def test_get(self): - """Test ProjectInviteListAPIView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, self.user_owner, self.user_delegate, ] - bad_users = [ + self.bad_users = [ self.user_contributor_cat, self.user_guest_cat, self.user_finder_cat, @@ -1049,10 +1084,13 @@ def test_get(self): self.user_guest, self.user_no_roles, ] - self.assert_response_api(self.url, good_users, 200) - self.assert_response_api(self.url, bad_users, 403) + + def test_get(self): + """Test ProjectInviteListAPIView GET""" + self.assert_response_api(self.url, self.good_users, 200) + self.assert_response_api(self.url, self.bad_users, 403) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.good_users, 200, knox=True) self.project.set_public() self.assert_response_api(self.url, self.user_no_roles, 403) @@ -1065,28 +1103,20 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] - self.assert_response_api(self.url, good_users, 200) - self.assert_response_api(self.url, bad_users, 403) + self.assert_response_api(self.url, self.good_users, 200) + self.assert_response_api(self.url, self.bad_users, 403) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.good_users, 200, knox=True) self.project.set_public() self.assert_response_api(self.url, self.user_no_roles, 403) + def test_get_read_only(self): + """Test GET with archived project and site read-only mode""" + self.set_site_read_only() + self.assert_response_api(self.url, self.superuser, 200) + self.assert_response_api(self.url, self.auth_non_superusers, 403) + self.assert_response_api(self.url, self.anonymous, 401) + class TestProjectInviteCreateAPIView(ProjectrolesAPIPermissionTestBase): """Tests for ProjectInviteCreateAPIView permissions""" @@ -1105,17 +1135,14 @@ def setUp(self): 'email': self.email, 'role': SODAR_CONSTANTS['PROJECT_ROLE_CONTRIBUTOR'], } - - def test_post(self): - """Test ProjectInviteCreateAPIView POST""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, self.user_owner, self.user_delegate, ] - bad_users = [ + self.bad_users = [ self.user_contributor_cat, self.user_guest_cat, self.user_finder_cat, @@ -1123,23 +1150,26 @@ def test_post(self): self.user_guest, self.user_no_roles, ] + + def test_post(self): + """Test ProjectInviteCreateAPIView POST""" self.assert_response_api( self.url, - good_users, + self.good_users, 201, method='POST', data=self.post_data, cleanup_method=self._cleanup, ) self.assert_response_api( - self.url, bad_users, 403, method='POST', data=self.post_data + self.url, self.bad_users, 403, method='POST', data=self.post_data ) self.assert_response_api( self.url, self.anonymous, 401, method='POST', data=self.post_data ) self.assert_response_api( self.url, - good_users, + self.good_users, 201, method='POST', data=self.post_data, @@ -1148,7 +1178,7 @@ def test_post(self): ) self.assert_response_api( self.url, - bad_users, + self.bad_users, 403, method='POST', data=self.post_data, @@ -1174,38 +1204,23 @@ def test_post_anon(self): def test_post_archive(self): """Test POST with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( self.url, - good_users, + self.good_users, 201, method='POST', data=self.post_data, cleanup_method=self._cleanup, ) self.assert_response_api( - self.url, bad_users, 403, method='POST', data=self.post_data + self.url, self.bad_users, 403, method='POST', data=self.post_data ) self.assert_response_api( self.url, self.anonymous, 401, method='POST', data=self.post_data ) self.assert_response_api( self.url, - good_users, + self.good_users, 201, method='POST', data=self.post_data, @@ -1214,7 +1229,7 @@ def test_post_archive(self): ) self.assert_response_api( self.url, - bad_users, + self.bad_users, 403, method='POST', data=self.post_data, @@ -1229,9 +1244,31 @@ def test_post_archive(self): data=self.post_data, ) + def test_post_read_only(self): + """Test POST with site read-only mode""" + self.set_site_read_only() + self.assert_response_api( + self.url, + self.superuser, + 201, + method='POST', + data=self.post_data, + cleanup_method=self._cleanup, + ) + self.assert_response_api( + self.url, + self.auth_non_superusers, + 403, + method='POST', + data=self.post_data, + ) + self.assert_response_api( + self.url, self.anonymous, 401, method='POST', data=self.post_data + ) + class TestProjectInviteRevokeAPIView(ProjectrolesAPIPermissionTestBase): - """Tests for ProjectInviteRevokeAPIView( permissions""" + """Tests for ProjectInviteRevokeAPIView permissions""" def _cleanup(self): self.invite.active = True @@ -1249,17 +1286,14 @@ def setUp(self): 'projectroles:api_invite_revoke', kwargs={'projectinvite': self.invite.sodar_uuid}, ) - - def test_post(self): - """Test ProjectInviteRevokeAPIView POST""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, self.user_owner, self.user_delegate, ] - bad_users = [ + self.bad_users = [ self.user_contributor_cat, self.user_guest_cat, self.user_finder_cat, @@ -1267,25 +1301,28 @@ def test_post(self): self.user_guest, self.user_no_roles, ] + + def test_post(self): + """Test ProjectInviteRevokeAPIView POST""" self.assert_response_api( self.url, - good_users, + self.good_users, 200, method='POST', cleanup_method=self._cleanup, ) - self.assert_response_api(self.url, bad_users, 403, method='POST') + self.assert_response_api(self.url, self.bad_users, 403, method='POST') self.assert_response_api(self.url, self.anonymous, 401, method='POST') self.assert_response_api( self.url, - good_users, + self.good_users, 200, method='POST', knox=True, cleanup_method=self._cleanup, ) self.assert_response_api( - self.url, bad_users, 403, method='POST', knox=True + self.url, self.bad_users, 403, method='POST', knox=True ) self.project.set_public() self.assert_response_api( @@ -1301,46 +1338,46 @@ def test_post_anon(self): def test_post_archive(self): """Test POST with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( self.url, - good_users, + self.good_users, 200, method='POST', cleanup_method=self._cleanup, ) - self.assert_response_api(self.url, bad_users, 403, method='POST') + self.assert_response_api(self.url, self.bad_users, 403, method='POST') self.assert_response_api(self.url, self.anonymous, 401, method='POST') self.assert_response_api( self.url, - good_users, + self.good_users, 200, method='POST', knox=True, cleanup_method=self._cleanup, ) self.assert_response_api( - self.url, bad_users, 403, method='POST', knox=True + self.url, self.bad_users, 403, method='POST', knox=True ) self.project.set_public() self.assert_response_api( self.url, self.user_no_roles, 403, method='POST' ) + def test_post_read_only(self): + """Test POST with site read-only mode""" + self.set_site_read_only() + self.assert_response_api( + self.url, + self.superuser, + 200, + method='POST', + cleanup_method=self._cleanup, + ) + self.assert_response_api( + self.url, self.auth_non_superusers, 403, method='POST' + ) + self.assert_response_api(self.url, self.anonymous, 401, method='POST') + class TestProjectInviteResendAPIView(ProjectrolesAPIPermissionTestBase): """Tests for ProjectInviteResendAPIView permissions""" @@ -1357,17 +1394,14 @@ def setUp(self): 'projectroles:api_invite_resend', kwargs={'projectinvite': self.invite.sodar_uuid}, ) - - def test_post(self): - """Test ProjectInviteResendAPIView POST""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, self.user_owner, self.user_delegate, ] - bad_users = [ + self.bad_users = [ self.user_contributor_cat, self.user_guest_cat, self.user_finder_cat, @@ -1375,18 +1409,21 @@ def test_post(self): self.user_guest, self.user_no_roles, ] - self.assert_response_api(self.url, good_users, 200, method='POST') - self.assert_response_api(self.url, bad_users, 403, method='POST') + + def test_post(self): + """Test ProjectInviteResendAPIView POST""" + self.assert_response_api(self.url, self.good_users, 200, method='POST') + self.assert_response_api(self.url, self.bad_users, 403, method='POST') self.assert_response_api(self.url, self.anonymous, 401, method='POST') self.assert_response_api( self.url, - good_users, + self.good_users, 200, method='POST', knox=True, ) self.assert_response_api( - self.url, bad_users, 403, method='POST', knox=True + self.url, self.bad_users, 403, method='POST', knox=True ) self.project.set_public() self.assert_response_api( @@ -1402,39 +1439,33 @@ def test_post_anon(self): def test_post_archive(self): """Test POST with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] - self.assert_response_api(self.url, good_users, 200, method='POST') - self.assert_response_api(self.url, bad_users, 403, method='POST') + self.assert_response_api(self.url, self.good_users, 200, method='POST') + self.assert_response_api(self.url, self.bad_users, 403, method='POST') self.assert_response_api(self.url, self.anonymous, 401, method='POST') self.assert_response_api( self.url, - good_users, + self.good_users, 200, method='POST', knox=True, ) self.assert_response_api( - self.url, bad_users, 403, method='POST', knox=True + self.url, self.bad_users, 403, method='POST', knox=True ) self.project.set_public() self.assert_response_api( self.url, self.user_no_roles, 403, method='POST' ) + def test_post_read_only(self): + """Test POST with site read-only mode""" + self.set_site_read_only() + self.assert_response_api(self.url, self.superuser, 200, method='POST') + self.assert_response_api( + self.url, self.auth_non_superusers, 403, method='POST' + ) + self.assert_response_api(self.url, self.anonymous, 401, method='POST') + class TestProjectSettingRetrieveAPIView(ProjectrolesAPIPermissionTestBase): """Tests for ProjectSettingRetrieveAPIView permissions""" @@ -1450,17 +1481,14 @@ def setUp(self): 'plugin_name': 'example_project_app', 'setting_name': 'project_str_setting', } - - def test_get_project_setting(self): - """Test ProjectSettingRetrieveAPIView GET with PROJECT scope""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, self.user_owner, self.user_delegate, ] - bad_users = [ + self.bad_users = [ self.user_contributor_cat, self.user_guest_cat, self.user_finder_cat, @@ -1468,20 +1496,27 @@ def test_get_project_setting(self): self.user_guest, self.user_no_roles, ] - self.assert_response_api(self.url, good_users, 200, data=self.get_data) - self.assert_response_api(self.url, bad_users, 403, data=self.get_data) + + def test_get_project_setting(self): + """Test ProjectSettingRetrieveAPIView GET with PROJECT scope""" + self.assert_response_api( + self.url, self.good_users, 200, data=self.get_data + ) + self.assert_response_api( + self.url, self.bad_users, 403, data=self.get_data + ) self.assert_response_api( self.url, self.anonymous, 401, data=self.get_data ) self.assert_response_api( self.url, - good_users, + self.good_users, 200, data=self.get_data, knox=True, ) self.assert_response_api( - self.url, bad_users, 403, data=self.get_data, knox=True + self.url, self.bad_users, 403, data=self.get_data, knox=True ) self.project.set_public() self.assert_response_api( @@ -1501,41 +1536,43 @@ def test_get_project_setting_anon(self): def test_get_project_setting_archive(self): """Test GET with PROJECT scope and archived project""" - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] - self.assert_response_api(self.url, good_users, 200, data=self.get_data) - self.assert_response_api(self.url, bad_users, 403, data=self.get_data) + self.assert_response_api( + self.url, self.good_users, 200, data=self.get_data + ) + self.assert_response_api( + self.url, self.bad_users, 403, data=self.get_data + ) self.assert_response_api( self.url, self.anonymous, 401, data=self.get_data ) self.assert_response_api( self.url, - good_users, + self.good_users, 200, data=self.get_data, knox=True, ) self.assert_response_api( - self.url, bad_users, 403, data=self.get_data, knox=True + self.url, self.bad_users, 403, data=self.get_data, knox=True ) self.project.set_public() self.assert_response_api( self.url, self.user_no_roles, 403, data=self.get_data ) + def test_get_project_setting_read_only(self): + """Test GET with PROJECT scope and site read-only mode""" + self.set_site_read_only() + self.assert_response_api( + self.url, self.good_users, 200, data=self.get_data + ) + self.assert_response_api( + self.url, self.bad_users, 403, data=self.get_data + ) + self.assert_response_api( + self.url, self.anonymous, 401, data=self.get_data + ) + def test_get_project_user_setting(self): """Test GET with PROJECT_USER scope""" get_data = { @@ -1576,6 +1613,33 @@ def test_get_project_user_setting(self): self.url, self.user_no_roles, 403, data=get_data ) + def test_get_project_user_setting_read_only(self): + """Test GET with PROJECT_USER scope and site read-only mode""" + self.set_site_read_only() + get_data = { + 'plugin_name': 'example_project_app', + 'setting_name': 'project_user_str_setting', + 'user': str(self.user_owner.sodar_uuid), + } + good_users = [ + self.superuser, + self.user_owner, + ] + bad_users = [ + self.user_owner_cat, + self.user_delegate_cat, + self.user_contributor_cat, + self.user_guest_cat, + self.user_finder_cat, + self.user_delegate, + self.user_contributor, + self.user_guest, + self.user_no_roles, + ] + self.assert_response_api(self.url, good_users, 200, data=get_data) + self.assert_response_api(self.url, bad_users, 403, data=get_data) + self.assert_response_api(self.url, self.anonymous, 401, data=get_data) + class TestProjectSettingSetAPIView(ProjectrolesAPIPermissionTestBase): """Tests for ProjectSettingSetAPIView permissions""" @@ -1592,17 +1656,14 @@ def setUp(self): 'setting_name': 'project_str_setting', 'value': 'value', } - - def test_post_project_setting(self): - """Test ProjectSettingSetAPIView POST with PROJECT scope""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, self.user_owner, self.user_delegate, ] - bad_users = [ + self.bad_users = [ self.user_contributor_cat, self.user_guest_cat, self.user_finder_cat, @@ -1610,22 +1671,25 @@ def test_post_project_setting(self): self.user_guest, self.user_no_roles, ] + + def test_post_project_setting(self): + """Test ProjectSettingSetAPIView POST with PROJECT scope""" self.assert_response_api( self.url, - good_users, + self.good_users, 200, method='POST', data=self.post_data, ) self.assert_response_api( - self.url, bad_users, 403, method='POST', data=self.post_data + self.url, self.bad_users, 403, method='POST', data=self.post_data ) self.assert_response_api( self.url, self.anonymous, 401, method='POST', data=self.post_data ) self.assert_response_api( self.url, - good_users, + self.good_users, 200, method='POST', data=self.post_data, @@ -1633,7 +1697,7 @@ def test_post_project_setting(self): ) self.assert_response_api( self.url, - bad_users, + self.bad_users, 403, method='POST', data=self.post_data, @@ -1663,37 +1727,22 @@ def test_post_project_setting_anon(self): def test_post_project_setting_archive(self): """Test POST with PROJECT scope and archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( self.url, - good_users, + self.good_users, 200, method='POST', data=self.post_data, ) self.assert_response_api( - self.url, bad_users, 403, method='POST', data=self.post_data + self.url, self.bad_users, 403, method='POST', data=self.post_data ) self.assert_response_api( self.url, self.anonymous, 401, method='POST', data=self.post_data ) self.assert_response_api( self.url, - good_users, + self.good_users, 200, method='POST', data=self.post_data, @@ -1701,7 +1750,7 @@ def test_post_project_setting_archive(self): ) self.assert_response_api( self.url, - bad_users, + self.bad_users, 403, method='POST', data=self.post_data, @@ -1716,6 +1765,27 @@ def test_post_project_setting_archive(self): data=self.post_data, ) + def test_post_project_setting_read_only(self): + """Test POST with PROJECT scope and site read-only mode""" + self.set_site_read_only() + self.assert_response_api( + self.url, + self.superuser, + 200, + method='POST', + data=self.post_data, + ) + self.assert_response_api( + self.url, + self.auth_non_superusers, + 403, + method='POST', + data=self.post_data, + ) + self.assert_response_api( + self.url, self.anonymous, 401, method='POST', data=self.post_data + ) + def test_post_project_user_setting(self): """Test POST with PROJECT_USER scope""" post_data = { @@ -1765,6 +1835,33 @@ def test_post_project_user_setting(self): self.url, self.user_no_roles, 403, method='POST', data=post_data ) + def test_post_project_user_setting_read_only(self): + """Test POST with PROJECT_USER scope and site read-only mode""" + self.set_site_read_only() + post_data = { + 'plugin_name': 'example_project_app', + 'setting_name': 'project_user_str_setting', + 'value': 'value', + 'user': str(self.user_owner.sodar_uuid), + } + self.assert_response_api( + self.url, + self.superuser, + 200, + method='POST', + data=post_data, + ) + self.assert_response_api( + self.url, + self.auth_non_superusers, + 403, + method='POST', + data=post_data, + ) + self.assert_response_api( + self.url, self.anonymous, 401, method='POST', data=post_data + ) + class TestUserSettingRetrieveAPIView(ProjectrolesAPIPermissionTestBase): """Tests for UserSettingRetrieveAPIView permissions""" @@ -1777,27 +1874,17 @@ def setUp(self): 'setting_name': 'user_str_setting', } - def test_get_retrieve(self): + def test_get(self): """Test UserSettingRetrieveAPIView GET""" - good_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] - self.assert_response_api(self.url, good_users, 200, data=self.get_data) + self.assert_response_api( + self.url, self.auth_non_superusers, 200, data=self.get_data + ) self.assert_response_api( self.url, self.anonymous, 403, data=self.get_data ) self.assert_response_api( self.url, - good_users, + self.auth_non_superusers, 200, data=self.get_data, knox=True, @@ -1811,6 +1898,16 @@ def test_get_anon(self): self.url, [self.anonymous], 403, data=self.get_data ) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response_api( + self.url, self.auth_non_superusers, 200, data=self.get_data + ) + self.assert_response_api( + self.url, self.anonymous, 403, data=self.get_data + ) + class TestUserSettingSetAPIView(ProjectrolesAPIPermissionTestBase): """Tests for UserSettingSetAPIView permissions""" @@ -1826,27 +1923,19 @@ def setUp(self): def test_post(self): """Test UserSettingSetAPIView POST""" - good_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( - self.url, good_users, 200, method='POST', data=self.post_data + self.url, + self.auth_non_superusers, + 200, + method='POST', + data=self.post_data, ) self.assert_response_api( self.url, self.anonymous, 403, method='POST', data=self.post_data ) self.assert_response_api( self.url, - good_users, + self.auth_non_superusers, 200, method='POST', data=self.post_data, @@ -1865,9 +1954,23 @@ def test_post_anon(self): data=self.post_data, ) + def test_post_read_only(self): + """Test POST with site read-only mode""" + self.set_site_read_only() + self.assert_response_api( + self.url, + self.auth_non_superusers, + 200, + method='POST', + data=self.post_data, + ) + self.assert_response_api( + self.url, self.anonymous, 403, method='POST', data=self.post_data + ) + class TestUserListAPIView(ProjectrolesAPIPermissionTestBase): - """Tests for UserSettingSetAPIView permissions""" + """Tests for UserListAPIView permissions""" def setUp(self): super().setUp() @@ -1875,28 +1978,21 @@ def setUp(self): def test_get(self): """Test UserListAPIView GET""" - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] - self.assert_response_api(self.url, good_users, 200) + self.assert_response_api(self.url, self.auth_users, 200) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.auth_users, 200, knox=True) @override_settings(PROJECTROLES_ALLOW_ANONYMOUS=True) def test_get_anon(self): """Test GET with anonymous access""" self.assert_response_api(self.url, [self.anonymous], 401) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response_api(self.url, self.auth_users, 200) + self.assert_response_api(self.url, self.anonymous, 401) + class TestCurrentUserRetrieveAPIView(ProjectrolesAPIPermissionTestBase): """Tests for CurrentUserRetrieveAPIView permissions""" @@ -1907,24 +2003,17 @@ def setUp(self): def test_get(self): """Test CurrentUserRetrieveAPIView GET""" - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] - self.assert_response_api(self.url, good_users, 200) + self.assert_response_api(self.url, self.auth_users, 200) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.auth_users, 200, knox=True) @override_settings(PROJECTROLES_ALLOW_ANONYMOUS=True) def test_get_anon(self): """Test GET with anonymous access""" self.assert_response_api(self.url, [self.anonymous], 401) + + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response_api(self.url, self.auth_users, 200) + self.assert_response_api(self.url, self.anonymous, 401) diff --git a/projectroles/tests/test_templatetags.py b/projectroles/tests/test_templatetags.py index cd3a814a..f08411a5 100644 --- a/projectroles/tests/test_templatetags.py +++ b/projectroles/tests/test_templatetags.py @@ -684,7 +684,7 @@ def test_get_sidebar_links_timeline_view(self): else: self.assertEqual(app['active'], False) - def test_get_links_home_user(self): + def test_get_user_links_home(self): """Test get_user_links() on the home view""" url = reverse('home') request = self.req_factory.get(url) @@ -738,7 +738,7 @@ def test_get_links_home_user(self): ], ) - def test_get_links_home_superuser(self): + def test_get_user_links_home_superuser(self): """Test get_user_links() on the home view as superuser""" url = reverse('home') request = self.req_factory.get(url) diff --git a/projectroles/tests/test_ui.py b/projectroles/tests/test_ui.py index f610585d..2650cd92 100644 --- a/projectroles/tests/test_ui.py +++ b/projectroles/tests/test_ui.py @@ -81,6 +81,7 @@ PUBLIC_ACCESS_ID = 'id_public_guest_access' REMOTE_SITE_UUID = uuid.uuid4() REMOTE_SITE_ID = 'id_remote_site.{}'.format(REMOTE_SITE_UUID) +CUSTOM_READ_ONLY_MSG = 'This is a custom site read-only mode message.' class LiveUserMixin: @@ -724,6 +725,51 @@ def test_inline_head_include_disabled(self): with self.assertRaises(NoSuchElementException): self.selenium.find_element(By.XPATH, '//meta[@name="keywords"]') + def test_read_only_alert_disabled(self): + """Test site read-only mode alert with mode disabled""" + self.assertFalse(app_settings.get('projectroles', 'site_read_only')) + self.login_and_redirect(self.user_owner, self.url) + with self.assertRaises(NoSuchElementException): + self.selenium.find_element(By.ID, 'sodar-alert-site-read-only') + + def test_read_only_alert_enabled(self): + """Test site read-only mode alert with mode enabled""" + app_settings.set('projectroles', 'site_read_only', True) + self.login_and_redirect(self.user_owner, self.url) + elem = self.selenium.find_element(By.ID, 'sodar-alert-site-read-only') + self.assertIsNotNone(elem) + self.assertNotEqual(elem.text, CUSTOM_READ_ONLY_MSG) + + @override_settings(PROJECTROLES_READ_ONLY_MSG=CUSTOM_READ_ONLY_MSG) + def test_read_only_alert_custom(self): + """Test site read-only mode alert with custom message""" + app_settings.set('projectroles', 'site_read_only', True) + self.login_and_redirect(self.user_owner, self.url) + elem = self.selenium.find_element(By.ID, 'sodar-alert-site-read-only') + self.assertIsNotNone(elem) + self.assertEqual(elem.text, CUSTOM_READ_ONLY_MSG) + + def test_read_only_alert_disable(self): + """Test site read-only mode disabling enabled alert""" + app_settings.set('projectroles', 'site_read_only', True) + self.login_and_redirect(self.user_owner, self.url) + elem = self.selenium.find_element(By.ID, 'sodar-alert-site-read-only') + self.assertIsNotNone(elem) + self.assertIn('alert-danger', elem.get_attribute('class')) + self.assertNotIn('alert-success', elem.get_attribute('class')) + with self.assertRaises(NoSuchElementException): + elem.find_element(By.TAG_NAME, 'a') + + app_settings.set('projectroles', 'site_read_only', False) + WebDriverWait(self.selenium, 15).until( + ec.presence_of_element_located( + (By.CLASS_NAME, 'sodar-alert-site-read-only-updated') + ) + ) + self.assertNotIn('alert-danger', elem.get_attribute('class')) + self.assertIn('alert-success', elem.get_attribute('class')) + self.assertIsNotNone(elem.find_element(By.TAG_NAME, 'a')) + class TestProjectSidebar(ProjectInviteMixin, RemoteTargetMixin, UITestBase): """Tests for the project sidebar""" @@ -1313,6 +1359,22 @@ def test_project_links_category(self): ] self.assert_element_set(expected, PROJECT_LINK_IDS, self.url_cat) + def test_project_links_read_only(self): + """Test visibility of project links with site read-only mode""" + app_settings.set('projectroles', 'site_read_only', True) + expected = [ + (self.superuser, self._get_pr_links('roles', 'update')), + (self.user_owner_cat, self._get_pr_links('roles')), + (self.user_delegate_cat, self._get_pr_links('roles')), + (self.user_contributor_cat, self._get_pr_links('roles')), + (self.user_guest_cat, self._get_pr_links('roles')), + (self.user_owner, self._get_pr_links('roles')), + (self.user_delegate, self._get_pr_links('roles')), + (self.user_contributor, self._get_pr_links('roles')), + (self.user_guest, self._get_pr_links('roles')), + ] + self.assert_element_set(expected, PROJECT_LINK_IDS, self.url) + def test_copy_uuid_visibility_default(self): """Test default UUID copy button visibility (should not be visible)""" users = [ @@ -1982,6 +2044,23 @@ def test_role_ops_target(self): non_superusers, self.url, 'sodar-pr-role-ops-dropdown', False ) + def test_role_ops_read_only(self): + """Test rendering role operations dropdown with site read-only mode""" + app_settings.set('projectroles', 'site_read_only', True) + good_users = [self.superuser] + bad_users = [ + self.user_owner, + self.user_delegate, + self.user_contributor, + self.user_guest, + ] + self.assert_element_exists( + good_users, self.url, 'sodar-pr-role-ops-dropdown', True + ) + self.assert_element_exists( + bad_users, self.url, 'sodar-pr-role-ops-dropdown', False + ) + def test_role_ops_invite(self): """Test visibility of role operations invite item""" expected_true = [ @@ -2131,6 +2210,25 @@ def test_role_dropdown_guest_inherited(self): with self.assertRaises(NoSuchElementException): elem.find_element(By.CLASS_NAME, 'sodar-pr-role-item-delete') + def test_role_dropdown_read_only(self): + """Test role dropdown with site read-only mode""" + app_settings.set('projectroles', 'site_read_only', True) + expected = [ + (self.superuser, 6), + (self.user_owner_cat, 0), + (self.user_delegate_cat, 0), + (self.user_contributor_cat, 0), + (self.user_guest_cat, 0), + (self.user_finder_cat, 0), + (self.user_owner, 0), + (self.user_delegate, 0), + (self.user_contributor, 0), + (self.user_guest, 0), + ] + self.assert_element_count( + expected, self.url, 'sodar-pr-role-dropdown', attribute='class' + ) + class TestRoleAssignmentCreateView(UITestBase): """Tests for RoleAssignmentCreateView UI""" diff --git a/projectroles/tests/test_utils.py b/projectroles/tests/test_utils.py index 207905f7..107028b2 100644 --- a/projectroles/tests/test_utils.py +++ b/projectroles/tests/test_utils.py @@ -9,6 +9,7 @@ from test_plus import TestCase +from projectroles.app_settings import AppSettingAPI from projectroles.models import SODAR_CONSTANTS from projectroles.tests.test_models import ProjectMixin, RoleAssignmentMixin from projectroles.tests.test_views import ViewTestBase @@ -21,6 +22,7 @@ app_links = AppLinkContent() +app_settings = AppSettingAPI() # SODAR constants @@ -397,6 +399,35 @@ def test_get_project_links_category_target_disallow_superuser(self): link_names = [link['name'] for link in links] self.assertNotIn('project-create', link_names) + def test_get_project_links_read_only(self): + """Test get_project_links() with project and site read-only mode""" + app_settings.set('projectroles', 'site_read_only', True) + links = app_links.get_project_links(self.user_owner, self.project) + self.assertEqual(len(links), 6) + link_names = [link['name'] for link in links] + self.assertNotIn('project-update', link_names) + + def test_get_project_links_read_only_superuser(self): + """Test get_project_links() with project and site read-only mode as superuser""" + app_settings.set('projectroles', 'site_read_only', True) + links = app_links.get_project_links(self.user, self.project) + self.assertEqual(len(links), 7) + + def test_get_project_links_category_read_only(self): + """Test get_project_links() with category and site read-only mode""" + app_settings.set('projectroles', 'site_read_only', True) + links = app_links.get_project_links(self.user_owner, self.category) + self.assertEqual(len(links), 3) + link_names = [link['name'] for link in links] + self.assertNotIn('project-update', link_names) + self.assertNotIn('project-create', link_names) + + def test_get_project_links_category_read_only_superuser(self): + """Test get_project_links() with category and site read-only mode as superuser""" + app_settings.set('projectroles', 'site_read_only', True) + links = app_links.get_project_links(self.user, self.category) + self.assertEqual(len(links), 5) + def test_get_user_links(self): """Test get_user_links() as regular user""" expected = [ @@ -583,3 +614,14 @@ def test_get_user_links_anon_kiosk_mode(self): """Test get_user_links() as anonymous user and kiosk mode""" links = app_links.get_user_links(AnonymousUser()) self.assertEqual(len(links), 0) + + def test_get_user_links_read_only(self): + """Test get_user_links() with site read-only mode as regular user""" + app_settings.set('projectroles', 'site_read_only', True) + # All should be returned + self.assertEqual(len(app_links.get_user_links(self.user_owner)), 6) + + def test_get_user_links_read_only_superuser(self): + """Test get_user_links() with site read-only mode as superuser""" + app_settings.set('projectroles', 'site_read_only', True) + self.assertEqual(len(app_links.get_user_links(self.user)), 12) diff --git a/projectroles/tests/test_views.py b/projectroles/tests/test_views.py index 31f1710a..4c36968d 100644 --- a/projectroles/tests/test_views.py +++ b/projectroles/tests/test_views.py @@ -106,6 +106,7 @@ APP_SETTING_SCOPE_PROJECT_USER = SODAR_CONSTANTS[ 'APP_SETTING_SCOPE_PROJECT_USER' ] +APP_SETTING_SCOPE_SITE = SODAR_CONSTANTS['APP_SETTING_SCOPE_SITE'] APP_SETTING_TYPE_BOOLEAN = SODAR_CONSTANTS['APP_SETTING_TYPE_BOOLEAN'] APP_SETTING_TYPE_INTEGER = SODAR_CONSTANTS['APP_SETTING_TYPE_INTEGER'] APP_SETTING_TYPE_JSON = SODAR_CONSTANTS['APP_SETTING_TYPE_JSON'] @@ -168,6 +169,14 @@ default=False, global_edit=True, ), + PluginAppSettingDef( + name='site_read_only', + scope=APP_SETTING_SCOPE_SITE, + type=APP_SETTING_TYPE_BOOLEAN, + default=False, + user_modifiable=True, + global_edit=False, + ), ] EX_PROJECT_UI_SETTINGS = [ @@ -2027,7 +2036,7 @@ def test_post_category(self): class TestProjectForm( - AppSettingMixin, ViewTestBase, ProjectMixin, RoleAssignmentMixin + AppSettingMixin, ProjectMixin, RoleAssignmentMixin, ViewTestBase ): """Tests for ProjectForm""" @@ -2205,9 +2214,9 @@ class TestProjectFormTarget( RemoteSiteMixin, RemoteProjectMixin, AppSettingMixin, - ViewTestBase, ProjectMixin, RoleAssignmentMixin, + ViewTestBase, ): """Tests for project create/update form on a target site""" @@ -2390,9 +2399,9 @@ class TestProjectFormTargetLocal( RemoteSiteMixin, RemoteProjectMixin, AppSettingMixin, - ViewTestBase, ProjectMixin, RoleAssignmentMixin, + ViewTestBase, ): """ Tests for project create/update form on target site with local settings diff --git a/projectroles/tests/test_views_ajax.py b/projectroles/tests/test_views_ajax.py index c51f9bea..d91b04a4 100644 --- a/projectroles/tests/test_views_ajax.py +++ b/projectroles/tests/test_views_ajax.py @@ -678,6 +678,34 @@ def test_get_url_name(self): self.assertEqual(response.json(), expected) +class TestSiteReadOnlySettingAjaxView(SerializedObjectMixin, TestCase): + """Tests for SiteReadOnlySettingAjaxView""" + + def setUp(self): + self.user = self.make_user('user') + self.user.save() + + def test_get_disabled(self): + """Test SiteReadOnlySettingAjaxView GET with read-only mode disabled""" + self.assertFalse(app_settings.get('projectroles', 'site_read_only')) + with self.login(self.user): + response = self.client.get( + reverse('projectroles:ajax_settings_site_read_only') + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json(), {'site_read_only': False}) + + def test_get_enabled(self): + """Test GET with read-only mode enabled""" + app_settings.set('projectroles', 'site_read_only', True) + with self.login(self.user): + response = self.client.get( + reverse('projectroles:ajax_settings_site_read_only') + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json(), {'site_read_only': True}) + + class TestUserDropdownContentAjaxView(ViewTestBase): """Tests for UserDropdownContentAjaxView""" @@ -753,8 +781,8 @@ def setUp(self): self.user.save() self.reg_user = self.make_user('reg_user') - def test_regular_user(self): - """Test CurrentUserRetrieveAjaxView with regular user""" + def test_get_regular_user(self): + """Test CurrentUserRetrieveAjaxView GET with regular user""" with self.login(self.reg_user): response = self.client.get( reverse('projectroles:ajax_user_current') @@ -762,8 +790,8 @@ def test_regular_user(self): self.assertEqual(response.status_code, 200) self.assertEqual(response.data, self.get_serialized_user(self.reg_user)) - def test_superuser(self): - """Test CurrentUserRetrieveAjaxView with superuser""" + def test_get_superuser(self): + """Test GET with superuser""" with self.login(self.user): response = self.client.get( reverse('projectroles:ajax_user_current') diff --git a/projectroles/urls.py b/projectroles/urls.py index 18d27dc1..96ddbde6 100644 --- a/projectroles/urls.py +++ b/projectroles/urls.py @@ -200,6 +200,11 @@ view=views_ajax.UserDropdownContentAjaxView.as_view(), name='ajax_user_dropdown', ), + path( + route='ajax/settings/site-read-only', + view=views_ajax.SiteReadOnlySettingAjaxView.as_view(), + name='ajax_settings_site_read_only', + ), path( route='ajax/user/current', view=views_ajax.CurrentUserRetrieveAjaxView.as_view(), diff --git a/projectroles/views_ajax.py b/projectroles/views_ajax.py index bc50f8d5..0705a7f0 100644 --- a/projectroles/views_ajax.py +++ b/projectroles/views_ajax.py @@ -411,7 +411,7 @@ def post(self, request, *args, **kwargs): class ProjectStarringAjaxView(SODARBaseProjectAjaxView): """View to handle starring and unstarring a project""" - permission_required = 'projectroles.view_project' + permission_required = 'projectroles.star_project' def post(self, request, *args, **kwargs): # HACK: Manually refuse access to anonymous as this view is an exception @@ -491,6 +491,30 @@ def get(self, request, *args, **kwargs): return JsonResponse({'links': sidebar_links}) +class SiteReadOnlySettingAjaxView(SODARBaseAjaxView): + """ + Return the current status of the site read-only mode setting. + + Return data: + + - ``site_read_only``: Boolean + """ + + allow_anonymous = True + + def get(self, request, *args, **kwargs): + ret = app_settings.get('projectroles', 'site_read_only') + return JsonResponse({'site_read_only': ret}) + + +class CurrentUserRetrieveAjaxView( + SODARBaseAjaxMixin, CurrentUserRetrieveAPIView +): + """ + Return information of the requesting user for Ajax requests. + """ + + class UserDropdownContentAjaxView(SODARBaseAjaxView): """ Return user dropdown links to be displayed in a client-side application. @@ -522,14 +546,6 @@ def get(self, request, *args, **kwargs): return JsonResponse({'links': user_dropdown_links}) -class CurrentUserRetrieveAjaxView( - SODARBaseAjaxMixin, CurrentUserRetrieveAPIView -): - """ - Return information of the requesting user for Ajax requests. - """ - - class UserAutocompleteAjaxView(autocomplete.Select2QuerySetView): """User autocompletion widget view""" diff --git a/projectroles/views_api.py b/projectroles/views_api.py index 06b7fc08..b87b3496 100644 --- a/projectroles/views_api.py +++ b/projectroles/views_api.py @@ -882,21 +882,21 @@ def get_request_value(cls, request): return request.data['value'] @classmethod - def check_project_perms( - cls, setting_def, project, request_user, setting_user - ): + def check_project_perms(cls, setting_def, project, request, setting_user): """ Check permissions for project settings. :param setting_def: PluginAppSettingDef object :param project: Project object - :param request_user: User object for requesting user + :param request: HttpRequest object :param setting_user: User object for the setting user or None """ if setting_def.scope == APP_SETTING_SCOPE_PROJECT: - if not request_user.has_perm( - 'projectroles.update_project_settings', project - ): + if request.method == 'GET': + perm = 'projectroles.view_project_settings' + else: + perm = 'projectroles.update_project_settings' + if not request.user.has_perm(perm, project): raise PermissionDenied( 'User lacks permission to access PROJECT scope app ' 'settings in this project' @@ -906,10 +906,18 @@ def check_project_perms( raise serializers.ValidationError( 'No user given for PROJECT_USER setting' ) - if request_user != setting_user and not request_user.is_superuser: + if request.user != setting_user and not request.user.is_superuser: raise PermissionDenied( 'User is not allowed to update settings for other users' ) + if ( + request.method == 'POST' + and not request.user.is_superuser + and app_settings.get('projectroles', 'site_read_only') + ): + raise PermissionDenied( + 'Site in read-only mode, operation not allowed' + ) @classmethod def _get_setting_obj( @@ -1017,9 +1025,7 @@ def get_object(self): setting_user = User.objects.filter( sodar_uuid=self.request.GET['user'] ).first() - self.check_project_perms( - s_def, project, self.request.user, setting_user - ) + self.check_project_perms(s_def, project, self.request, setting_user) # Return new object with default setting if not set return self.get_setting_for_api( @@ -1081,7 +1087,7 @@ def post(self, request, *args, **kwargs): setting_user = User.objects.filter( sodar_uuid=request.data['user'] ).first() - self.check_project_perms(s_def, project, request.user, setting_user) + self.check_project_perms(s_def, project, request, setting_user) # Set setting value with validation, return possible errors try: diff --git a/sodarcache/tests/test_permissions_api.py b/sodarcache/tests/test_permissions_api.py index e1b540b8..017d8d7d 100644 --- a/sodarcache/tests/test_permissions_api.py +++ b/sodarcache/tests/test_permissions_api.py @@ -51,10 +51,7 @@ def setUp(self): 'item_name': ITEM_NAME, }, ) - - def test_get(self): - """Test CacheItemRetrieveAPIView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, @@ -65,11 +62,14 @@ def test_get(self): self.user_contributor, self.user_guest, ] - bad_users = [self.user_finder_cat, self.user_no_roles] - self.assert_response_api(self.url, good_users, 200) - self.assert_response_api(self.url, bad_users, 403) + self.bad_users = [self.user_finder_cat, self.user_no_roles] + + def test_get(self): + """Test CacheItemRetrieveAPIView GET""" + self.assert_response_api(self.url, self.good_users, 200) + self.assert_response_api(self.url, self.bad_users, 403) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.good_users, 200, knox=True) self.project.set_public() self.assert_response_api(self.url, self.user_no_roles, 200) @@ -82,25 +82,20 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - ] - bad_users = [self.user_finder_cat, self.user_no_roles] - self.assert_response_api(self.url, good_users, 200) - self.assert_response_api(self.url, bad_users, 403) + self.assert_response_api(self.url, self.good_users, 200) + self.assert_response_api(self.url, self.bad_users, 403) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.good_users, 200, knox=True) self.project.set_public() self.assert_response_api(self.url, self.user_no_roles, 200) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response_api(self.url, self.good_users, 200) + self.assert_response_api(self.url, self.bad_users, 403) + self.assert_response_api(self.url, self.anonymous, 401) + class TestCacheItemDateRetrieveAPIView(SodarcacheAPIPermissionTestBase): """Test CacheItemDateRetrieveAPIView permissions""" @@ -123,10 +118,7 @@ def setUp(self): 'item_name': ITEM_NAME, }, ) - - def test_get(self): - """Test CacheItemDateRetrieveAPIView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, @@ -137,11 +129,14 @@ def test_get(self): self.user_contributor, self.user_guest, ] - bad_users = [self.user_finder_cat, self.user_no_roles] - self.assert_response_api(self.url, good_users, 200) - self.assert_response_api(self.url, bad_users, 403) + self.bad_users = [self.user_finder_cat, self.user_no_roles] + + def test_get(self): + """Test CacheItemDateRetrieveAPIView GET""" + self.assert_response_api(self.url, self.good_users, 200) + self.assert_response_api(self.url, self.bad_users, 403) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.good_users, 200, knox=True) self.project.set_public() self.assert_response_api(self.url, self.user_no_roles, 200) @@ -154,25 +149,20 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - ] - bad_users = [self.user_finder_cat, self.user_no_roles] - self.assert_response_api(self.url, good_users, 200) - self.assert_response_api(self.url, bad_users, 403) + self.assert_response_api(self.url, self.good_users, 200) + self.assert_response_api(self.url, self.bad_users, 403) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.good_users, 200, knox=True) self.project.set_public() self.assert_response_api(self.url, self.user_no_roles, 200) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response_api(self.url, self.good_users, 200) + self.assert_response_api(self.url, self.bad_users, 403) + self.assert_response_api(self.url, self.anonymous, 401) + class TestCacheItemSetAPIView(SodarcacheAPIPermissionTestBase): """Test CacheItemSetAPIView permissions""" @@ -188,10 +178,7 @@ def setUp(self): }, ) self.post_data = {'data': {DATA_KEY: DATA_VAL}} - - def test_post(self): - """Test CacheItemSetAPIView POST""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, @@ -200,24 +187,27 @@ def test_post(self): self.user_delegate, self.user_contributor, ] - bad_users = [ + self.bad_users = [ self.user_guest_cat, self.user_finder_cat, self.user_guest, self.user_no_roles, ] + + def test_post(self): + """Test CacheItemSetAPIView POST""" self.assert_response_api( - self.url, good_users, 200, method='POST', data=self.post_data + self.url, self.good_users, 200, method='POST', data=self.post_data ) self.assert_response_api( - self.url, bad_users, 403, method='POST', data=self.post_data + self.url, self.bad_users, 403, method='POST', data=self.post_data ) self.assert_response_api( self.url, self.anonymous, 401, method='POST', data=self.post_data ) self.assert_response_api( self.url, - good_users, + self.good_users, 200, method='POST', data=self.post_data, @@ -243,33 +233,18 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - ] - bad_users = [ - self.user_guest_cat, - self.user_finder_cat, - self.user_guest, - self.user_no_roles, - ] self.assert_response_api( - self.url, good_users, 200, method='POST', data=self.post_data + self.url, self.good_users, 200, method='POST', data=self.post_data ) self.assert_response_api( - self.url, bad_users, 403, method='POST', data=self.post_data + self.url, self.bad_users, 403, method='POST', data=self.post_data ) self.assert_response_api( self.url, self.anonymous, 401, method='POST', data=self.post_data ) self.assert_response_api( self.url, - good_users, + self.good_users, 200, method='POST', data=self.post_data, @@ -283,3 +258,16 @@ def test_get_archive(self): method='POST', data=self.post_data, ) + + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response_api( + self.url, self.good_users, 200, method='POST', data=self.post_data + ) + self.assert_response_api( + self.url, self.bad_users, 403, method='POST', data=self.post_data + ) + self.assert_response_api( + self.url, self.anonymous, 401, method='POST', data=self.post_data + ) diff --git a/timeline/tests/test_permissions.py b/timeline/tests/test_permissions.py index 52204fab..30dcbc2f 100644 --- a/timeline/tests/test_permissions.py +++ b/timeline/tests/test_permissions.py @@ -15,10 +15,7 @@ def setUp(self): self.url = reverse( 'timeline:list_project', kwargs={'project': self.project.sodar_uuid} ) - - def test_get(self): - """Test ProjectTimelineView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, @@ -29,9 +26,16 @@ def test_get(self): self.user_contributor, self.user_guest, ] - bad_users = [self.user_finder_cat, self.user_no_roles, self.anonymous] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.bad_users = [ + self.user_finder_cat, + self.user_no_roles, + self.anonymous, + ] + + def test_get(self): + """Test ProjectTimelineView GET""" + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 200) self.assert_response(self.url, self.anonymous, 302) @@ -45,24 +49,18 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - ] - bad_users = [self.user_finder_cat, self.user_no_roles, self.anonymous] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 200) self.assert_response(self.url, self.anonymous, 302) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) + class TestSiteTimelineView(ProjectPermissionTestBase): """Tests for SiteTimelineView permissions""" @@ -73,20 +71,7 @@ def setUp(self): def test_get(self): """Test SiteTimelineView GET""" - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] - self.assert_response(self.url, good_users, 200) + self.assert_response(self.url, self.auth_users, 200) self.assert_response(self.url, self.anonymous, 302) @override_settings(PROJECTROLES_ALLOW_ANONYMOUS=True) @@ -94,6 +79,12 @@ def test_get_anon(self): """Test GET with anonynomus access""" self.assert_response(self.url, self.anonymous, 302) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.auth_users, 200) + self.assert_response(self.url, self.anonymous, 302) + class TestAdminTimelineView(ProjectPermissionTestBase): """Tests for AdminTimelineView permissions""" @@ -104,28 +95,20 @@ def setUp(self): def test_get(self): """Test AdminTimelineView GET""" - good_users = [self.superuser] - bad_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) @override_settings(PROJECTROLES_ALLOW_ANONYMOUS=True) def test_get_anon(self): """Test GET with anonymous access""" self.assert_response(self.url, self.anonymous, 302) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.superuser, 200) + self.assert_response(self.url, self.non_superusers, 302) + class TestProjectObjectTimelineView(ProjectPermissionTestBase): """Tests for ProjectObjectTimelineView permissions""" @@ -140,10 +123,7 @@ def setUp(self): 'object_uuid': self.user_owner.sodar_uuid, }, ) - - def test_get(self): - """Test ProjectObjectTimelineView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, @@ -154,9 +134,16 @@ def test_get(self): self.user_contributor, self.user_guest, ] - bad_users = [self.user_finder_cat, self.user_no_roles, self.anonymous] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.bad_users = [ + self.user_finder_cat, + self.user_no_roles, + self.anonymous, + ] + + def test_get(self): + """Test ProjectObjectTimelineView GET""" + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 200) self.assert_response(self.url, self.anonymous, 302) @@ -170,24 +157,18 @@ def test_project_event_object_list_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - ] - bad_users = [self.user_finder_cat, self.user_no_roles, self.anonymous] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 302) + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 200) self.assert_response(self.url, self.anonymous, 302) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 302) + class TestSiteObjectTimelineView(ProjectPermissionTestBase): """Tests for SiteObjectTimelineView permissions""" @@ -204,20 +185,7 @@ def setUp(self): def test_get(self): """Test SiteObjectTimelineView GET""" - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] - self.assert_response(self.url, good_users, 200) + self.assert_response(self.url, self.auth_users, 200) self.assert_response(self.url, self.anonymous, 302) self.project.set_public() self.assert_response(self.url, self.user_no_roles, 200) @@ -228,3 +196,9 @@ def test_get_anon(self): """Test GET with anonymous access""" self.project.set_public() self.assert_response(self.url, self.anonymous, 302) + + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.auth_users, 200) + self.assert_response(self.url, self.anonymous, 302) diff --git a/timeline/tests/test_permissions_ajax.py b/timeline/tests/test_permissions_ajax.py index b72f8885..5b52e1e0 100644 --- a/timeline/tests/test_permissions_ajax.py +++ b/timeline/tests/test_permissions_ajax.py @@ -27,10 +27,7 @@ def setUp(self): 'timeline:ajax_detail_project', kwargs={'timelineevent': self.event.sodar_uuid}, ) - - def test_get(self): - """Test ProjectEventDetailAjaxView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, @@ -41,13 +38,16 @@ def test_get(self): self.user_contributor, self.user_guest, ] - bad_users = [ + self.bad_users = [ self.user_finder_cat, self.user_no_roles, self.anonymous, ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 403) + + def test_get(self): + """Test ProjectEventDetailAjaxView GET""" + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 403) self.project.set_public() self.assert_response(self.url, self.anonymous, 403) @@ -60,27 +60,17 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - ] - bad_users = [ - self.user_finder_cat, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 403) + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 403) self.project.set_public() self.assert_response(self.url, self.anonymous, 403) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 403) + def test_get_classified(self): """Test GET with classified event""" self.event.classified = True @@ -138,6 +128,13 @@ def test_get(self): self.assert_response(self.url, good_users, 200) self.assert_response(self.url, self.anonymous, 403) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + good_users = [self.superuser, self.regular_user] + self.assert_response(self.url, good_users, 200) + self.assert_response(self.url, self.anonymous, 403) + @override_settings(PROJECTROLES_ALLOW_ANONYMOUS=True) def test_get_anon(self): """Test GET with anonymous access""" @@ -174,17 +171,14 @@ def setUp(self): 'timeline:ajax_extra_project', kwargs={'timelineevent': self.event.sodar_uuid}, ) - - def test_get(self): - """Test ProjectEventExtraAjaxView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, self.user_owner, self.user_delegate, ] - bad_users = [ + self.bad_users = [ self.user_contributor_cat, self.user_guest_cat, self.user_finder_cat, @@ -193,8 +187,11 @@ def test_get(self): self.user_no_roles, self.anonymous, ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 403) + + def test_get(self): + """Test ProjectEventExtraAjaxView GET""" + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 403) self.project.set_public() self.assert_response(self.url, self.anonymous, 403) @@ -207,49 +204,23 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 403) + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 403) self.project.set_public() self.assert_response(self.url, self.anonymous, 403) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 403) + def test_get_classified(self): """Test GET with classified event""" self.event.classified = True self.event.save() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 403) + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 403) self.project.set_public() self.assert_response(self.url, self.anonymous, 403) @@ -326,17 +297,14 @@ def setUp(self): 'timeline:ajax_extra_status', kwargs={'eventstatus': self.event_status.sodar_uuid}, ) - - def test_get(self): - """Test EventStatusExtraAjaxView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, self.user_owner, self.user_delegate, ] - bad_users = [ + self.bad_users = [ self.user_contributor_cat, self.user_guest_cat, self.user_finder_cat, @@ -345,8 +313,11 @@ def test_get(self): self.user_no_roles, self.anonymous, ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 403) + + def test_get(self): + """Test EventStatusExtraAjaxView GET""" + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 403) self.project.set_public() self.assert_response(self.url, self.anonymous, 403) @@ -356,28 +327,18 @@ def test_get_anon(self): self.project.set_public() self.assert_response(self.url, self.anonymous, 403) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 403) + def test_get_classified(self): """Test GET with classified event""" self.event.classified = True self.event.save() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_owner, - self.user_delegate, - ] - bad_users = [ - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_contributor, - self.user_guest, - self.user_no_roles, - self.anonymous, - ] - self.assert_response(self.url, good_users, 200) - self.assert_response(self.url, bad_users, 403) + self.assert_response(self.url, self.good_users, 200) + self.assert_response(self.url, self.bad_users, 403) self.project.set_public() self.assert_response(self.url, self.anonymous, 403) diff --git a/timeline/tests/test_permissions_api.py b/timeline/tests/test_permissions_api.py index b957d5de..f6bb6512 100644 --- a/timeline/tests/test_permissions_api.py +++ b/timeline/tests/test_permissions_api.py @@ -34,10 +34,7 @@ def setUp(self): 'timeline:api_list_project', kwargs={'project': self.project.sodar_uuid}, ) - - def test_get(self): - """Test ProjectTimelineEventListAPIView GET""" - good_users = [ + self.good_users = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, @@ -48,11 +45,15 @@ def test_get(self): self.user_contributor, self.user_guest, ] - bad_users = [self.user_finder_cat, self.user_no_roles] - self.assert_response_api(self.url, good_users, 200) - self.assert_response_api(self.url, bad_users, 403) + self.bad_users = [self.user_finder_cat, self.user_no_roles] + + def test_get(self): + """Test ProjectTimelineEventListAPIView GET""" + + self.assert_response_api(self.url, self.good_users, 200) + self.assert_response_api(self.url, self.bad_users, 403) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.good_users, 200, knox=True) self.project.set_public() self.assert_response_api(self.url, self.user_no_roles, 200) @@ -65,25 +66,20 @@ def test_get_anon(self): def test_get_archive(self): """Test GET with archived project""" self.project.set_archive() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - ] - bad_users = [self.user_finder_cat, self.user_no_roles] - self.assert_response_api(self.url, good_users, 200) - self.assert_response_api(self.url, bad_users, 403) + self.assert_response_api(self.url, self.good_users, 200) + self.assert_response_api(self.url, self.bad_users, 403) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.good_users, 200, knox=True) self.project.set_public() self.assert_response_api(self.url, self.user_no_roles, 200) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response_api(self.url, self.good_users, 200) + self.assert_response_api(self.url, self.bad_users, 403) + self.assert_response_api(self.url, self.anonymous, 401) + class TestSiteTimelineEventListAPIView(TimelineAPIPermissionTestBase): """Tests for SiteTimelineEventListAPIView""" @@ -94,28 +90,21 @@ def setUp(self): def test_get(self): """Test SiteTimelineEventListAPIView GET""" - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_finder_cat, - self.user_no_roles, - ] - self.assert_response_api(self.url, good_users, 200) + self.assert_response_api(self.url, self.auth_users, 200) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.auth_users, 200, knox=True) @override_settings(PROJECTROLES_ALLOW_ANONYMOUS=True) def test_get_anon(self): """Test GET with anonymous access""" self.assert_response_api(self.url, self.anonymous, 401) + def test_get_read_only(self): + """Test GET with site read-only mode""" + self.set_site_read_only() + self.assert_response_api(self.url, self.auth_users, 200) + self.assert_response_api(self.url, self.anonymous, 401) + class TestTimelineEventRetrieveAPIView( TimelineEventMixin, TimelineAPIPermissionTestBase @@ -137,10 +126,7 @@ def setUp(self): 'timeline:api_retrieve', kwargs={'timelineevent': self.event.sodar_uuid}, ) - - def test_get_project_event(self): - """Test TimelineEventRetrieveAPIView GET with project event""" - good_users = [ + self.good_users_project = [ self.superuser, self.user_owner_cat, self.user_delegate_cat, @@ -151,17 +137,29 @@ def test_get_project_event(self): self.user_contributor, self.user_guest, ] - bad_users = [self.user_finder_cat, self.user_no_roles] - self.assert_response_api(self.url, good_users, 200) - self.assert_response_api(self.url, bad_users, 403) + self.bad_users_project = [self.user_finder_cat, self.user_no_roles] + + def test_get_project_event(self): + """Test TimelineEventRetrieveAPIView GET with project event""" + self.assert_response_api(self.url, self.good_users_project, 200) + self.assert_response_api(self.url, self.bad_users_project, 403) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api( + self.url, self.good_users_project, 200, knox=True + ) @override_settings(PROJECTROLES_ALLOW_ANONYMOUS=True) def test_get_project_event_anon(self): """Test GET with project event and anonymous access""" self.assert_response_api(self.url, self.anonymous, 401) + def test_get_project_event_read_only(self): + """Test GET with project event and site read-only mode""" + self.set_site_read_only() + self.assert_response_api(self.url, self.good_users_project, 200) + self.assert_response_api(self.url, self.bad_users_project, 403) + self.assert_response_api(self.url, self.anonymous, 401) + def test_get_project_event_classified(self): """Test GET with classified project event""" self.event.classified = True @@ -190,22 +188,9 @@ def test_get_site_event(self): """Test GET with site-wide event""" self.event.project = None self.event.save() - good_users = [ - self.superuser, - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_finder_cat, - self.user_no_roles, - ] - self.assert_response_api(self.url, good_users, 200) + self.assert_response_api(self.url, self.auth_users, 200) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.auth_users, 200, knox=True) @override_settings(PROJECTROLES_ALLOW_ANONYMOUS=True) def test_get_site_event_anon(self): @@ -214,25 +199,20 @@ def test_get_site_event_anon(self): self.event.save() self.assert_response_api(self.url, self.anonymous, 401) + def test_get_site_event_read_only(self): + """Test GET with site-wide event and site read-only mode""" + self.set_site_read_only() + self.event.project = None + self.event.save() + self.assert_response_api(self.url, self.auth_users, 200) + self.assert_response_api(self.url, self.anonymous, 401) + def test_get_site_event_classified(self): """Test GET with classified site event""" self.event.project = None self.event.classified = True self.event.save() - good_users = [self.superuser] - bad_users = [ - self.user_owner_cat, - self.user_delegate_cat, - self.user_contributor_cat, - self.user_guest_cat, - self.user_finder_cat, - self.user_owner, - self.user_delegate, - self.user_contributor, - self.user_guest, - self.user_no_roles, - ] - self.assert_response_api(self.url, good_users, 200) - self.assert_response_api(self.url, bad_users, 403) + self.assert_response_api(self.url, self.superuser, 200) + self.assert_response_api(self.url, self.auth_non_superusers, 403) self.assert_response_api(self.url, self.anonymous, 401) - self.assert_response_api(self.url, good_users, 200, knox=True) + self.assert_response_api(self.url, self.superuser, 200, knox=True) diff --git a/tokens/plugins.py b/tokens/plugins.py index 910052f0..5c573f69 100644 --- a/tokens/plugins.py +++ b/tokens/plugins.py @@ -28,4 +28,4 @@ class ProjectAppPlugin(SiteAppPluginPoint): description = 'API Token Management' #: Required permission for accessing the app - app_permission = 'tokens.access' + app_permission = 'tokens.view_list' diff --git a/tokens/rules.py b/tokens/rules.py index 286aadc3..d0c87e09 100644 --- a/tokens/rules.py +++ b/tokens/rules.py @@ -1,7 +1,20 @@ -"""Permissions configured using django-rules.""" +"""Permissions for the tokens app""" import rules +# Projectroles dependency +from projectroles import rules as pr_rules # To access common predicates -# tokens.access -- Access to the tokens app. -rules.add_perm('tokens.access', rules.is_active) + +# View tokens list +rules.add_perm('tokens.view_list', rules.is_authenticated) + +# Create token +rules.add_perm( + 'tokens.create', rules.is_authenticated & pr_rules.is_site_writable +) + +# Delete token +rules.add_perm( + 'tokens.delete', rules.is_authenticated & pr_rules.is_site_writable +) diff --git a/tokens/templates/tokens/_token_item.html b/tokens/templates/tokens/_token_item.html index e147317b..9253513a 100644 --- a/tokens/templates/tokens/_token_item.html +++ b/tokens/templates/tokens/_token_item.html @@ -3,5 +3,9 @@ {{ item.created|date:'Y-m-d H:i' }} {{ token.expiry|date:'Y-m-d H:i'|default:'Never' }} {{ item.token_key }} - {% include 'tokens/_token_dropdown.html' %} + + {% if request.user.is_superuser or not site_read_only %} + {% include 'tokens/_token_dropdown.html' %} + {% endif %} + diff --git a/tokens/templates/tokens/token_list.html b/tokens/templates/tokens/token_list.html index 33ab2062..3eb178fa 100644 --- a/tokens/templates/tokens/token_list.html +++ b/tokens/templates/tokens/token_list.html @@ -1,5 +1,7 @@ {% extends 'projectroles/base.html' %} +{% load projectroles_common_tags %} + {% block title %} API Tokens in {{ project.title }} {% endblock title %} @@ -22,12 +24,16 @@ {% block projectroles %} +{% get_app_setting 'projectroles' 'site_read_only' as site_read_only %} +

API Tokens

-
- Create Token - + {% if request.user.is_superuser or not site_read_only %} + + Create Token + + {% endif %}
diff --git a/tokens/tests/test_permissions.py b/tokens/tests/test_permissions.py index dca73266..5a536892 100644 --- a/tokens/tests/test_permissions.py +++ b/tokens/tests/test_permissions.py @@ -13,7 +13,7 @@ class TestTokenPermissions(SiteAppPermissionTestBase): """Tests for token view permissions""" def test_get_list(self): - """Test tUserTokenListView GET""" + """Test UserTokenListView GET""" url = reverse('tokens:list') self.assert_response(url, [self.superuser, self.regular_user], 200) self.assert_response(url, self.anonymous, 302) @@ -25,6 +25,13 @@ def test_get_list_anon(self): self.assert_response(url, [self.superuser, self.regular_user], 200) self.assert_response(url, self.anonymous, 302) + def test_get_list_read_only(self): + """Test UserTokenListView GET with site read-only mode""" + self.set_site_read_only() + url = reverse('tokens:list') + self.assert_response(url, [self.superuser, self.regular_user], 200) + self.assert_response(url, self.anonymous, 302) + def test_get_create(self): """Test UserTokenCreateView GET""" url = reverse('tokens:create') @@ -38,9 +45,23 @@ def test_get_create_anon(self): self.assert_response(url, [self.superuser, self.regular_user], 200) self.assert_response(url, self.anonymous, 302) + def test_get_create_read_only(self): + """Test UserTokenCreateView GET with site read-only mode""" + self.set_site_read_only() + url = reverse('tokens:create') + self.assert_response(url, self.superuser, 200) + self.assert_response(url, [self.regular_user, self.anonymous], 302) + def test_get_delete(self): """Test UserTokenDeleteView GET""" token = AuthToken.objects.create(self.regular_user, None) url = reverse('tokens:delete', kwargs={'pk': token[0].pk}) self.assert_response(url, self.regular_user, 200) self.assert_response(url, self.anonymous, 302) + + def test_get_delete_read_only(self): + """Test UserTokenDeleteView GET with site read-only mode""" + self.set_site_read_only() + token = AuthToken.objects.create(self.regular_user, None) + url = reverse('tokens:delete', kwargs={'pk': token[0].pk}) + self.assert_response(url, [self.regular_user, self.anonymous], 302) diff --git a/tokens/tests/test_ui.py b/tokens/tests/test_ui.py index 68e93e0b..3f0bdac0 100644 --- a/tokens/tests/test_ui.py +++ b/tokens/tests/test_ui.py @@ -6,12 +6,17 @@ from django.utils import timezone from knox.models import AuthToken + from selenium.webdriver.common.by import By # Projectroles dependency +from projectroles.app_settings import AppSettingAPI from projectroles.tests.test_ui import UITestBase +app_settings = AppSettingAPI() + + class TestTokenList(UITestBase): """Tests for token list""" @@ -33,12 +38,47 @@ def setUp(self): )[0] self.url = reverse('tokens:list') + def test_create_button(self): + """Test visibility of create button""" + self.assertFalse(app_settings.get('projectroles', 'site_read_only')) + self.assert_element_exists( + [self.superuser], self.url, 'sodar-tk-create-btn', True + ) + self.assert_element_exists( + [self.regular_user], self.url, 'sodar-tk-create-btn', True + ) + + def test_create_button_read_only(self): + """Test visibility of create button with site read-only mode""" + app_settings.set('projectroles', 'site_read_only', True) + self.assert_element_exists( + [self.superuser], self.url, 'sodar-tk-create-btn', True + ) + self.assert_element_exists( + [self.regular_user], self.url, 'sodar-tk-create-btn', False + ) + def test_list_items(self): """Test visibility of items in token list""" expected = [(self.superuser, 0), (self.regular_user, 2)] self.assert_element_count( expected, self.url, 'sodar-tk-list-item', 'class' ) + self.assert_element_count( + expected, self.url, 'sodar-tk-item-dropdown', 'class' + ) + + def test_list_items_read_only(self): + """Test visibility of items in token list with site read-only mode""" + app_settings.set('projectroles', 'site_read_only', True) + expected = [(self.superuser, 0), (self.regular_user, 2)] + self.assert_element_count( + expected, self.url, 'sodar-tk-list-item', 'class' + ) + expected = [(self.superuser, 0), (self.regular_user, 0)] + self.assert_element_count( + expected, self.url, 'sodar-tk-item-dropdown', 'class' + ) def test_list_expiry(self): """Test token expiry dates in token list""" diff --git a/tokens/views.py b/tokens/views.py index 3fdaa153..9b074ec9 100644 --- a/tokens/views.py +++ b/tokens/views.py @@ -21,7 +21,7 @@ class UserTokenListView(LoginRequiredMixin, LoggedInPermissionMixin, ListView): model = AuthToken - permission_required = 'tokens.access' + permission_required = 'tokens.view_list' template_name = 'tokens/token_list.html' def get_queryset(self): @@ -33,7 +33,7 @@ class UserTokenCreateView( LoginRequiredMixin, LoggedInPermissionMixin, FormView ): form_class = UserTokenCreateForm - permission_required = 'tokens.access' + permission_required = 'tokens.create' template_name = 'tokens/token_create.html' def form_valid(self, form): @@ -48,7 +48,7 @@ class UserTokenDeleteView( LoginRequiredMixin, LoggedInPermissionMixin, DeleteView ): model = AuthToken - permission_required = 'tokens.access' + permission_required = 'tokens.delete' template_name = 'tokens/token_confirm_delete.html' def get_success_url(self): diff --git a/userprofile/rules.py b/userprofile/rules.py index d7640a37..8dd5594e 100644 --- a/userprofile/rules.py +++ b/userprofile/rules.py @@ -21,15 +21,25 @@ def can_delete_email(user, obj): # Permissions ------------------------------------------------------------ -# Allow viewing user detail +# Allow viewing user details rules.add_perm('userprofile.view_detail', rules.is_authenticated) +# Allow updating settings +rules.add_perm( + 'userprofile.update_settings', + rules.is_authenticated & pr_rules.is_site_writable, +) + # Allow creating additional email rules.add_perm( - 'userprofile.create_email', pr_rules.is_source_site & rules.is_authenticated + 'userprofile.create_email', + pr_rules.is_source_site + & rules.is_authenticated + & pr_rules.is_site_writable, ) # Allow deleting additional email rules.add_perm( - 'userprofile.delete_email', pr_rules.is_source_site & can_delete_email + 'userprofile.delete_email', + pr_rules.is_source_site & can_delete_email & pr_rules.is_site_writable, ) diff --git a/userprofile/templates/userprofile/detail.html b/userprofile/templates/userprofile/detail.html index dd8f0020..a0125a8b 100644 --- a/userprofile/templates/userprofile/detail.html +++ b/userprofile/templates/userprofile/detail.html @@ -31,6 +31,8 @@ {% block projectroles %} +{% get_app_setting 'projectroles' 'site_read_only' as site_read_only %} +

{{ request.user.get_full_name }}

@@ -43,7 +45,7 @@

{{ request.user.get_full_name }}

Details - {% if request.user.is_local %} + {% if request.user.is_local and not site_read_only %}

Settings - - - + {% if request.user.is_superuser or not site_read_only %} + + + + {% endif %}

@@ -128,14 +132,16 @@

Additional Email Addresses {% if site_mode == 'SOURCE' %} - - - Add Email - - + {% if request.user.is_superuser or not site_read_only %} + + + Add Email + + + {% endif %} {% endif %}

@@ -163,21 +169,23 @@

{{ email.date_modified | date:'Y-m-d H:i'}} {% if site_mode == 'SOURCE' %} - - + {% if request.user.is_superuser or not site_read_only %} + + + {% endif %} {% endif %} diff --git a/userprofile/tests/test_permissions.py b/userprofile/tests/test_permissions.py index 374dccf7..b5d09635 100644 --- a/userprofile/tests/test_permissions.py +++ b/userprofile/tests/test_permissions.py @@ -38,6 +38,13 @@ def test_get_profile_anon(self): self.assert_response(url, [self.superuser, self.regular_user], 200) self.assert_response(url, self.anonymous, 302) + def test_get_profile_read_only(self): + """Test UserDetailView GET with site read-only mode""" + self.set_site_read_only() + url = reverse('userprofile:detail') + self.assert_response(url, [self.superuser, self.regular_user], 200) + self.assert_response(url, self.anonymous, 302) + def test_get_settings_update(self): """Test UserSettingUpdateView GET""" url = reverse('userprofile:settings_update') @@ -51,6 +58,13 @@ def test_get_settings_update_anon(self): self.assert_response(url, [self.superuser, self.regular_user], 200) self.assert_response(url, self.anonymous, 302) + def test_get_settings_update_read_only(self): + """Test UserSettingUpdateView GET with site read-only mode""" + self.set_site_read_only() + url = reverse('userprofile:settings_update') + self.assert_response(url, self.superuser, 200) + self.assert_response(url, [self.regular_user, self.anonymous], 302) + def test_get_email_create(self): """Test UserEmailCreateView GET""" url = reverse('userprofile:email_create') @@ -64,6 +78,13 @@ def test_get_email_create_anon(self): self.assert_response(url, [self.superuser, self.regular_user], 200) self.assert_response(url, self.anonymous, 302) + def test_get_email_create_read_only(self): + """Test UserEmailCreateView GET with site read-only mode""" + self.set_site_read_only() + url = reverse('userprofile:email_create') + self.assert_response(url, self.superuser, 200) + self.assert_response(url, [self.regular_user, self.anonymous], 302) + @override_settings(PROJECTROLES_SITE_MODE=SITE_MODE_TARGET) def test_get_email_create_target(self): """Test UserEmailCreateView GET as target site""" @@ -92,6 +113,18 @@ def test_get_email_delete_anon(self): self.assert_response(url, [self.superuser, self.regular_user], 200) self.assert_response(url, [self.regular_user2, self.anonymous], 302) + def test_get_email_delete_read_only(self): + """Test UserEmailDeleteView GET with site read-only mode""" + self.set_site_read_only() + email = self.make_email(self.regular_user, ADD_EMAIL) + url = reverse( + 'userprofile:email_delete', + kwargs={'sodaruseradditionalemail': email.sodar_uuid}, + ) + bad_users = [self.regular_user, self.regular_user2, self.anonymous] + self.assert_response(url, self.superuser, 200) + self.assert_response(url, bad_users, 302) + @override_settings(PROJECTROLES_SITE_MODE=SITE_MODE_TARGET) def test_get_email_delete_target(self): """Test UserEmailDeleteView GET as target site""" diff --git a/userprofile/tests/test_ui.py b/userprofile/tests/test_ui.py index 9299e20c..d5782369 100644 --- a/userprofile/tests/test_ui.py +++ b/userprofile/tests/test_ui.py @@ -7,19 +7,23 @@ from selenium.webdriver.support.ui import WebDriverWait # Projectroles dependency +from projectroles.app_settings import AppSettingAPI from projectroles.forms import SETTING_DISABLE_LABEL from projectroles.models import SODAR_CONSTANTS from projectroles.tests.test_models import SODARUserAdditionalEmailMixin from projectroles.tests.test_ui import UITestBase +app_settings = AppSettingAPI() + + # SODAR constants SITE_MODE_TARGET = SODAR_CONSTANTS['SITE_MODE_TARGET'] @override_settings(AUTH_LDAP_USERNAME_DOMAIN='EXAMPLE') -class TestUserDetails(SODARUserAdditionalEmailMixin, UITestBase): - """Tests for user details page""" +class TestUserDetailView(SODARUserAdditionalEmailMixin, UITestBase): + """Tests for UserDetailView""" def setUp(self): super().setUp() @@ -33,14 +37,56 @@ def test_update_button(self): expected = [(self.local_user, 1), (self.ldap_user, 0)] self.assert_element_count(expected, self.url, 'sodar-user-btn-update') + def test_update_button_read_only(self): + """Test existence of user update button with site read-only mode""" + app_settings.set('projectroles', 'site_read_only', True) + expected = [(self.local_user, 0), (self.ldap_user, 0)] + self.assert_element_count(expected, self.url, 'sodar-user-btn-update') + + def test_settings_button(self): + """Test existence of settings update button""" + expected = [ + (self.superuser, 1), + (self.local_user, 1), + (self.ldap_user, 1), + ] + self.assert_element_count(expected, self.url, 'sodar-user-btn-settings') + + def test_settings_button_read_only(self): + """Test existence of settings update button with site read-only mode""" + app_settings.set('projectroles', 'site_read_only', True) + expected = [ + (self.superuser, 1), + (self.local_user, 0), + (self.ldap_user, 0), + ] + self.assert_element_count(expected, self.url, 'sodar-user-btn-settings') + + def test_add_email_button(self): + """Test existence of add email button""" + expected = [ + (self.superuser, 1), + (self.local_user, 1), + (self.ldap_user, 1), + ] + self.assert_element_count( + expected, self.url, 'sodar-user-btn-email-add' + ) + + def test_add_email_button_read_only(self): + """Test existence of add email button with site read-only mode""" + app_settings.set('projectroles', 'site_read_only', True) + expected = [ + (self.superuser, 1), + (self.local_user, 0), + (self.ldap_user, 0), + ] + self.assert_element_count( + expected, self.url, 'sodar-user-btn-email-add' + ) + def test_additional_email_unset(self): """Test existence of additional email elements without email""" - self.assert_element_exists( - [self.local_user], - self.url, - 'sodar-user-btn-email-add', - True, - ) self.assert_element_count( [(self.local_user, 0)], self.url, @@ -66,12 +112,6 @@ def test_additional_email_set(self): self.make_email(self.local_user, 'add2@example.com', verified=False) # Another user, should not be visible self.make_email(self.ldap_user, 'add3@example.com') - self.assert_element_exists( - [self.local_user], - self.url, - 'sodar-user-btn-email-add', - True, - ) self.assert_element_count( [(self.local_user, 2)], self.url, @@ -91,6 +131,42 @@ def test_additional_email_set(self): False, ) + def test_additional_email_set_read_only(self): + """Test additional email with site read-only mode""" + app_settings.set('projectroles', 'site_read_only', True) + self.make_email(self.local_user, 'add1@example.com') + self.make_email(self.local_user, 'add2@example.com', verified=False) + self.assert_element_count( + [(self.local_user, 2)], + self.url, + 'sodar-user-email-table-row', + 'class', + ) + self.assert_element_count( + [(self.local_user, 0)], + self.url, + 'sodar-user-email-dropdown', + 'class', + ) + + def test_additional_email_set_read_only_superuser(self): + """Test additional email with site read-only mode as superuser""" + app_settings.set('projectroles', 'site_read_only', True) + self.make_email(self.superuser, 'add1@example.com') + self.make_email(self.superuser, 'add2@example.com', verified=False) + self.assert_element_count( + [(self.superuser, 2)], + self.url, + 'sodar-user-email-table-row', + 'class', + ) + self.assert_element_count( + [(self.superuser, 2)], + self.url, + 'sodar-user-email-dropdown', + 'class', + ) + @override_settings(PROJECTROLES_SEND_EMAIL=False) def test_additional_email_disabled(self): """Test existence of email card with PROJECTROLES_SEND_EMAIL=False""" @@ -132,8 +208,8 @@ def test_additional_email_target(self): ) -class TestUserSettings(UITestBase): - """Tests for user settings page""" +class TestUserSettingsView(UITestBase): + """Tests for UserSettingsView""" def setUp(self): super().setUp() diff --git a/userprofile/views.py b/userprofile/views.py index 559ab68c..df05d06c 100644 --- a/userprofile/views.py +++ b/userprofile/views.py @@ -109,7 +109,7 @@ class UserSettingsView( """User settings update view""" form_class = UserSettingsForm - permission_required = 'userprofile.view_detail' + permission_required = 'userprofile.update_settings' template_name = 'userprofile/settings_form.html' success_url = reverse_lazy('userprofile:detail')