diff --git a/geonode/resource/manager.py b/geonode/resource/manager.py
index f5322a46c39..4d8842c8a70 100644
--- a/geonode/resource/manager.py
+++ b/geonode/resource/manager.py
@@ -45,7 +45,12 @@
from geonode.thumbs.utils import ThumbnailAlgorithms
from geonode.documents.tasks import create_document_thumbnail
from geonode.security.permissions import PermSpecCompact, DATA_STYLABLE_RESOURCES_SUBTYPES
-from geonode.security.utils import perms_as_set, get_user_groups, skip_registered_members_common_group
+from geonode.security.utils import (
+ perms_as_set,
+ get_user_groups,
+ skip_registered_members_common_group,
+)
+from geonode.security.registry import permissions_registry
from . import settings as rm_settings
from .utils import update_resource, resourcebase_post_save
@@ -574,11 +579,12 @@ def set_permissions(
else:
_permissions = None
- # Fixup Advanced Workflow permissions
- _perm_spec = AdvancedSecurityWorkflowManager.get_permissions(
- _resource.uuid,
- instance=_resource,
- permissions=_permissions,
+ """
+ Align _perm_spec based on the permissions handlers
+ """
+ _perm_spec = permissions_registry.fixup_perms(
+ _resource,
+ _permissions,
created=created,
approval_status_changed=approval_status_changed,
group_status_changed=group_status_changed,
diff --git a/geonode/security/apps.py b/geonode/security/apps.py
index 3475f4d05f1..a5646d72bd0 100644
--- a/geonode/security/apps.py
+++ b/geonode/security/apps.py
@@ -22,3 +22,9 @@
class GeoNodeSecurityAppConfig(AppConfig):
name = "geonode.security"
verbose_name = "GeoNode Security"
+
+ def ready(self):
+ super().ready()
+ from geonode.security.registry import permissions_registry
+
+ permissions_registry.init_registry()
diff --git a/geonode/security/handlers.py b/geonode/security/handlers.py
new file mode 100644
index 00000000000..ddfba19a539
--- /dev/null
+++ b/geonode/security/handlers.py
@@ -0,0 +1,59 @@
+#########################################################################
+#
+# Copyright (C) 2024 OSGeo
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+#########################################################################
+from abc import ABC
+
+from geonode.security.utils import AdvancedSecurityWorkflowManager
+
+
+class BasePermissionsHandler(ABC):
+ """
+ Abstract permissions handler.
+ This is the base class, all the permissions instances should
+ inherit from this class.
+ All the flows that touches the permissions will use this class
+ (example advanced workflow)
+ """
+
+ def __str__(self):
+ return f"{self.__module__}.{self.__class__.__name__}"
+
+ def __repr__(self):
+ return self.__str__()
+
+ @staticmethod
+ def fixup_perms(instance, perms_payload, *args, **kwargs):
+ return perms_payload
+
+
+class AdvancedWorkflowPermissionsHandler(BasePermissionsHandler):
+ """
+ Handler that takes care of adjusting the permissions for the advanced workflow
+ """
+
+ @staticmethod
+ def fixup_perms(instance, perms_payload, *args, **kwargs):
+ # Fixup Advanced Workflow permissions
+ return AdvancedSecurityWorkflowManager.get_permissions(
+ instance.uuid,
+ instance=instance,
+ permissions=perms_payload,
+ created=kwargs.get("created"),
+ approval_status_changed=kwargs.get("approval_status_changed"),
+ group_status_changed=kwargs.get("group_status_changed"),
+ )
diff --git a/geonode/security/registry.py b/geonode/security/registry.py
new file mode 100644
index 00000000000..512315186ee
--- /dev/null
+++ b/geonode/security/registry.py
@@ -0,0 +1,65 @@
+#########################################################################
+#
+# Copyright (C) 2024 OSGeo
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+#########################################################################
+from django.conf import settings
+from django.utils.module_loading import import_string
+from geonode.security.handlers import BasePermissionsHandler
+
+
+class PermissionsHandlerRegistry:
+
+ REGISTRY = []
+
+ def init_registry(self):
+ self._register()
+ self.sanity_checks()
+
+ def add(self, module_path):
+ item = import_string(module_path)()
+ self.__check_item(item)
+ self.REGISTRY.append(item)
+
+ def reset(self):
+ self.REGISTRY = []
+
+ def _register(self):
+ for module_path in settings.PERMISSIONS_HANDLERS:
+ self.add(module_path)
+
+ def sanity_checks(self):
+ for item in self.REGISTRY:
+ self.__check_item(item)
+
+ def __check_item(self, item):
+ """
+ Ensure that the handler is a subclass of BasePermissionsHandler
+ """
+ if not isinstance(item, BasePermissionsHandler):
+ raise Exception(f"Handler {item} is not a subclass of BasePermissionsHandler")
+
+ def fixup_perms(self, instance, payload, *args, **kwargs):
+ for handler in self.REGISTRY:
+ payload = handler.fixup_perms(instance, payload, *args, **kwargs)
+ return payload
+
+ @classmethod
+ def get_registry(cls):
+ return PermissionsHandlerRegistry.REGISTRY
+
+
+permissions_registry = PermissionsHandlerRegistry()
diff --git a/geonode/security/tests.py b/geonode/security/tests.py
index 2c634ecabea..39671ab5b2d 100644
--- a/geonode/security/tests.py
+++ b/geonode/security/tests.py
@@ -47,6 +47,7 @@
from geonode.layers.models import Dataset
from geonode.documents.models import Document
from geonode.compat import ensure_string
+from geonode.security.handlers import BasePermissionsHandler
from geonode.upload.models import ResourceHandlerInfo
from geonode.utils import check_ogc_backend
from geonode.tests.utils import check_dataset
@@ -56,6 +57,7 @@
from geonode.groups.models import Group, GroupMember, GroupProfile
from geonode.layers.populate_datasets_data import create_dataset_data
from geonode.base.auth import create_auth_token, get_or_create_token
+from geonode.security.registry import permissions_registry
from geonode.base.models import Configuration, UserGeoLimit, GroupGeoLimit
from geonode.base.populate_test_data import (
@@ -2662,3 +2664,43 @@ def test_user_can_publish(self):
# setting back the owner to admin
self.dataset.owner = self.admin
self.dataset.save()
+
+
+class DummyPermissionsHandler(BasePermissionsHandler):
+ @staticmethod
+ def fixup_perms(instance, perms_payload, *args, **kwargs):
+ return {"perms": ["this", "is", "fake"]}
+
+
+@override_settings(PERMISSIONS_HANDLERS=["geonode.security.handlers.AdvancedWorkflowPermissionsHandler"])
+class TestPermissionsRegistry(GeoNodeBaseTestSupport):
+ """
+ Test to verify the permissions registry
+ """
+
+ def tearDown(self):
+ permissions_registry.reset()
+
+ def test_registry_is_correctly_initiated(self):
+ """
+ The permissions registry should initiated correctly
+ """
+ permissions_registry.init_registry()
+ self.assertIsNotNone(permissions_registry.get_registry())
+
+ def test_new_handler_is_registered(self):
+ permissions_registry.add("geonode.security.tests.DummyPermissionsHandler")
+ reg = permissions_registry.get_registry()
+ self.assertTrue("geonode.security.tests.DummyPermissionsHandler" in (str(r) for r in reg))
+
+ def test_should_raise_exception_if_is_not_subclass(self):
+ with self.assertRaises(Exception):
+ permissions_registry.add(int)
+
+ def test_handler_should_handle_the_perms_payload(self):
+ # create resource
+ instance = create_single_dataset("fake_dataset")
+ # adding the dummy at the end, means will win over the other handler
+ permissions_registry.add("geonode.security.tests.DummyPermissionsHandler")
+ perms = permissions_registry.fixup_perms(instance, instance.get_all_level_info())
+ self.assertDictEqual({"perms": ["this", "is", "fake"]}, perms)
diff --git a/geonode/settings.py b/geonode/settings.py
index 2be775af909..1d05645f1f8 100644
--- a/geonode/settings.py
+++ b/geonode/settings.py
@@ -2327,3 +2327,6 @@ def get_geonode_catalogue_service():
]
INSTALLED_APPS += ("geonode.assets",)
GEONODE_APPS += ("geonode.assets",)
+
+
+PERMISSIONS_HANDLERS = ["geonode.security.handlers.AdvancedWorkflowPermissionsHandler"]