From 59f63b90fa6879e61c9d26b8b52f8566966e83c5 Mon Sep 17 00:00:00 2001 From: Corey Krewson Date: Mon, 28 Oct 2024 17:09:16 -0500 Subject: [PATCH 1/4] added ability for ENABLE_RESTRICTED_APP_ACCESS to be a list and maintain original functionality with boolean if desired if an app is in the given list, then it cannot be access unless given explicit permissions --- tethys_apps/utilities.py | 10 ++++++++-- tethys_portal/settings.py | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/tethys_apps/utilities.py b/tethys_apps/utilities.py index 06444aec5..afaa39cc0 100644 --- a/tethys_apps/utilities.py +++ b/tethys_apps/utilities.py @@ -585,11 +585,17 @@ def get_service_model_from_type(service_type): def user_can_access_app(user, app): from django.conf import settings + RESTRICTED_APP_ACCESS = getattr(settings, "ENABLE_RESTRICTED_APP_ACCESS", False) if getattr(settings, "ENABLE_OPEN_PORTAL", False): return True - elif getattr(settings, "ENABLE_RESTRICTED_APP_ACCESS", False): - return user.has_perm(f"{app.package}:access_app", app) + elif RESTRICTED_APP_ACCESS: + if isinstance(RESTRICTED_APP_ACCESS, bool): + return user.has_perm(f"{app.package}:access_app", app) + elif app.package in RESTRICTED_APP_ACCESS: + return user.has_perm(f"{app.package}:access_app", app) + else: + return True else: return True diff --git a/tethys_portal/settings.py b/tethys_portal/settings.py index ab0829104..b42444c81 100644 --- a/tethys_portal/settings.py +++ b/tethys_portal/settings.py @@ -82,7 +82,7 @@ # Set to True to allow Open Portal mode. This mode supersedes any specific user/group app access permissions ENABLE_OPEN_PORTAL = TETHYS_PORTAL_CONFIG.pop("ENABLE_OPEN_PORTAL", False) -# Set to True to allow Open Portal mode. This mode supersedes any specific user/group app access permissions +# Set to True to allow restricted app access. This mode removes access to apps for nonadmins unless given explicit permission ENABLE_RESTRICTED_APP_ACCESS = TETHYS_PORTAL_CONFIG.pop( "ENABLE_RESTRICTED_APP_ACCESS", False ) From 85a8e8864ff460d06a234b9894ceff42cbdd6fb7 Mon Sep 17 00:00:00 2001 From: Corey Krewson Date: Mon, 28 Oct 2024 17:27:11 -0500 Subject: [PATCH 2/4] black format added tests --- .../test_tethys_apps/test_utilities.py | 28 ++++++++++++++++--- tethys_apps/utilities.py | 1 + 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/tests/unit_tests/test_tethys_apps/test_utilities.py b/tests/unit_tests/test_tethys_apps/test_utilities.py index 4ac09bbbc..2e91a30b4 100644 --- a/tests/unit_tests/test_tethys_apps/test_utilities.py +++ b/tests/unit_tests/test_tethys_apps/test_utilities.py @@ -858,16 +858,36 @@ def test_user_can_access_app(self, mock_settings): result1 = utilities.user_can_access_app(user, app) self.assertFalse(result1) + # test restricted access with restricted apps list + mock_settings.ENABLE_RESTRICTED_APP_ACCESS = [app.package] + result2 = utilities.user_can_access_app(user, app) + self.assertFalse(result2) + + # test restricted access with no restricted apps list + mock_settings.ENABLE_RESTRICTED_APP_ACCESS = [] + result3 = utilities.user_can_access_app(user, app) + self.assertTrue(result3) + + # test restricted access with restricted apps list of a different app + mock_settings.ENABLE_RESTRICTED_APP_ACCESS = ["some_other_app"] + result4 = utilities.user_can_access_app(user, app) + self.assertTrue(result4) + # test with permission assign_perm(f"{app.package}:access_app", user, app) - result2 = utilities.user_can_access_app(user, app) - self.assertTrue(result2) + result5 = utilities.user_can_access_app(user, app) + self.assertTrue(result5) # test open portal mode case mock_settings.ENABLE_OPEN_PORTAL = True - result3 = utilities.user_can_access_app(user, app) - self.assertTrue(result3) + result6 = utilities.user_can_access_app(user, app) + self.assertTrue(result6) + + # test restricted access with restricted apps list + mock_settings.ENABLE_RESTRICTED_APP_ACCESS = [app.package] + result7 = utilities.user_can_access_app(user, app) + self.assertTrue(result7) def test_get_installed_tethys_items_apps(self): # Get list of apps installed in the tethysapp directory diff --git a/tethys_apps/utilities.py b/tethys_apps/utilities.py index afaa39cc0..4bdd60469 100644 --- a/tethys_apps/utilities.py +++ b/tethys_apps/utilities.py @@ -585,6 +585,7 @@ def get_service_model_from_type(service_type): def user_can_access_app(user, app): from django.conf import settings + RESTRICTED_APP_ACCESS = getattr(settings, "ENABLE_RESTRICTED_APP_ACCESS", False) if getattr(settings, "ENABLE_OPEN_PORTAL", False): From 6ca6e86921f49bbb46d83fe81989950e6ea89bcf Mon Sep 17 00:00:00 2001 From: Corey Krewson Date: Mon, 28 Oct 2024 17:35:35 -0500 Subject: [PATCH 3/4] added more comments --- tethys_portal/settings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tethys_portal/settings.py b/tethys_portal/settings.py index b42444c81..98b825ab5 100644 --- a/tethys_portal/settings.py +++ b/tethys_portal/settings.py @@ -82,7 +82,8 @@ # Set to True to allow Open Portal mode. This mode supersedes any specific user/group app access permissions ENABLE_OPEN_PORTAL = TETHYS_PORTAL_CONFIG.pop("ENABLE_OPEN_PORTAL", False) -# Set to True to allow restricted app access. This mode removes access to apps for nonadmins unless given explicit permission +# Set to True to allow restricted app access. This mode removes access to apps for nonadmins unless given explicit permission. +# A list can also be provided to restrict specific applications unless users are given explicit permission ENABLE_RESTRICTED_APP_ACCESS = TETHYS_PORTAL_CONFIG.pop( "ENABLE_RESTRICTED_APP_ACCESS", False ) From 1bdf3190af7bc6cf27de37a6ffa3da527c545067 Mon Sep 17 00:00:00 2001 From: Corey Krewson Date: Tue, 29 Oct 2024 09:55:19 -0500 Subject: [PATCH 4/4] updated docs --- docs/tethys_portal/configuration.rst | 2 +- scripts/generate_portal_config_tables.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tethys_portal/configuration.rst b/docs/tethys_portal/configuration.rst index 6de3161ef..6ded214ed 100644 --- a/docs/tethys_portal/configuration.rst +++ b/docs/tethys_portal/configuration.rst @@ -81,7 +81,7 @@ Setting Description ENABLE_OPEN_SIGNUP anyone can create a Tethys Portal account using a "Sign Up" link on the home page when ``True``. Defaults to ``False``. REGISTER_CONTROLLER override the default registration page with a custom controller. The value should be the dot-path to the controller function/class (e.g. ``tethysext.my_extension.controllers.custom_registration``) ENABLE_OPEN_PORTAL no login required for Tethys Portal when ``True``. Defaults to ``False``. Controllers in apps need to use the ``controller`` decorator from the Tethys SDK, rather than Django's ``login_required`` decorator. -ENABLE_RESTRICTED_APP_ACCESS app access can be restricted based on user object permissions when ``True``. Defaults to ``False``. If ``ENABLE_OPEN_PORTAL`` is set to ``True`` this setting has no effect. That is, users will have unrestricted access to apps independently of the value of this setting. +ENABLE_RESTRICTED_APP_ACCESS app access can be restricted based on user object permissions when ``True``. Defaults to ``False``. A list can also be provided to restrict specific applications. If ``ENABLE_OPEN_PORTAL`` is set to ``True`` this setting has no effect. That is, users will have unrestricted access to apps independently of the value of this setting. TETHYS_WORKSPACES_ROOT location to where app/user workspaces will be created. Defaults to :file:`/workspaces`. STATIC_ROOT the Django `STATIC_ROOT `_ setting. Defaults to :file:`/static`. MEDIA_URL the Django `MEDIA_URL `_ setting. Defaults to ``'/media/'``. diff --git a/scripts/generate_portal_config_tables.py b/scripts/generate_portal_config_tables.py index ddab9f54e..69ecc97b0 100644 --- a/scripts/generate_portal_config_tables.py +++ b/scripts/generate_portal_config_tables.py @@ -20,7 +20,7 @@ "ENABLE_OPEN_SIGNUP": 'anyone can create a Tethys Portal account using a "Sign Up" link on the home page when ``True``. Defaults to ``False``.', "REGISTER_CONTROLLER": "override the default registration page with a custom controller. The value should be the dot-path to the controller function/class (e.g. ``tethysext.my_extension.controllers.custom_registration``)", "ENABLE_OPEN_PORTAL": "no login required for Tethys Portal when ``True``. Defaults to ``False``. Controllers in apps need to use the ``controller`` decorator from the Tethys SDK, rather than Django's ``login_required`` decorator.", - "ENABLE_RESTRICTED_APP_ACCESS": "app access can be restricted based on user object permissions when ``True``. Defaults to ``False``. If ``ENABLE_OPEN_PORTAL`` is set to ``True`` this setting has no effect. That is, users will have unrestricted access to apps independently of the value of this setting.", + "ENABLE_RESTRICTED_APP_ACCESS": "app access can be restricted based on user object permissions when ``True``. Defaults to ``False``. A list can also be provided to restrict specific applications. If ``ENABLE_OPEN_PORTAL`` is set to ``True`` this setting has no effect. That is, users will have unrestricted access to apps independently of the value of this setting.", "TETHYS_WORKSPACES_ROOT": "location to which app workspaces will be synced when ``tethys manage collectworkspaces`` is executed. Gathering all workspaces to one location is recommended for production deployments to allow for easier updating and backing up of app data. Defaults to :file:`/workspaces`.", "STATIC_ROOT": f"the Django `STATIC_ROOT `_ setting. Defaults to :file:`/static`.", "STATICFILES_USE_NPM": "serves JavaScript dependencies through Tethys rather than using a content delivery network (CDN) when ``True``. Defaults to ``False``. When set to ``True`` then you must run ``tethys gen package_json`` to npm install the JS dependencies locally so they can be served by Tethys.",