Skip to content

Commit

Permalink
permissions: Refactor to use generator for accessing config permission
Browse files Browse the repository at this point in the history
  • Loading branch information
rekt-hard committed Oct 1, 2024
1 parent cde0aca commit 01abb30
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 54 deletions.
35 changes: 15 additions & 20 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -139,38 +139,31 @@ Since we only want to change the behaviour of these community submission request

.. code-block:: python
from invenio_rdm_records.requests import CommunitySubmission
from invenio_rdm_records.services.permissions import RDMRequestsPermissionPolicy
from invenio_requests.services.generators import Creator, Receiver
from invenio_curations.requests.curation import CurationRequest
from invenio_curations.services.generators import (
IfCurationRequestAccepted,
IfRequestTypes,
RequestTopicGenerators,
)
from invenio_rdm_records.requests import CommunitySubmission
from invenio_rdm_records.services.permissions import (
RDMRequestsPermissionPolicy,
RDMRecordPermissionPolicy,
TopicPermission,
)
from invenio_requests.services.generators import Creator, Receiver, Status
class CurationRDMRequestsPermissionPolicy(RDMRequestsPermissionPolicy):
"""Customized permission policy for sane handling of curation requests."""
rdm_policy = RDMRecordPermissionPolicy
curation_request_record_review = IfRequestTypes(
[CurationRequest],
then_=[RequestTopicGenerators(generators=rdm_policy.can_review)],
then_=[TopicPermission(permission_name="can_review")],
else_=[],
)
# Only allow community-submission requests to be accepted after
# the rdm-curation request has been accepted
# Only allow community-submission requests to be accepted after the rdm-curation request has been accepted
can_action_accept = [
IfRequestTypes(
request_types=[
CommunitySubmission,
],
request_types=[CommunitySubmission],
then_=[
IfCurationRequestAccepted(
then_=RDMRequestsPermissionPolicy.can_action_accept, else_=[]
Expand All @@ -182,27 +175,29 @@ Since we only want to change the behaviour of these community submission request
# Update can read and can comment with new states
can_read = [
# Have to explicitly check the request type and circumvent using status, as creator/receiver will add a query filter where one entity must be the user.
IfRequestTypes(
[CurationRequest],
then_=[
Creator(),
Receiver(),
RequestTopicGenerators(generators=rdm_policy.can_review),
TopicPermission(permission_name="can_review"),
],
else_=RDMRequestsPermissionPolicy.can_read,
)
]
can_create_comment = can_read
# Add new actions
can_action_review = RDMRequestsPermissionPolicy.can_action_accept
can_action_critique = RDMRequestsPermissionPolicy.can_action_accept
# Update submit to also allow record reviewers/managers for curation requests
can_action_submit = RDMRequestsPermissionPolicy.can_action_submit + [
curation_request_record_review
]
can_action_resubmit = can_action_submit
# Add new actions
can_action_review = RDMRequestsPermissionPolicy.can_action_accept
can_action_critique = RDMRequestsPermissionPolicy.can_action_accept
can_action_resubmit = can_action_submit
REQUESTS_PERMISSION_POLICY = CurationRDMRequestsPermissionPolicy
Expand Down
61 changes: 42 additions & 19 deletions invenio_curations/services/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,35 +62,58 @@ def _condition(self, request=None, **kwargs):
return False


class RequestTopicGenerators(Generator):
"""Request-oriented generator forwarding the request topic to the provided generators.
class EntityReferenceServicePermission(Generator):
"""Request-oriented generator accessing a named permission from the entity's service config."""

Knowing what a request topic is, it is possible to perform checks on the topic itself.
Helpful to allow record managers access to a request as well.
"""
entity_field = None

def __init__(self, generators=None, **kwargs):
self._generators = generators or set()
def __init__(self, permission_name, **kwargs):
"""Constructor specifying permission_name."""
self.permission_name = permission_name
assert self.entity_field is not None, "Subclass must define entity_field."
super().__init__()

def _get_permission(self, request):
"""Get the specified permission from the request entity service config."""
entity = getattr(request, self.entity_field)
permission_policy_cls = (
entity.get_resolver().get_service().config.permission_policy_cls
)

return getattr(permission_policy_cls, self.permission_name)

def needs(self, request=None, **kwargs):
"""Set of Needs granting permission."""
"""Set of needs granting permission."""
if request is None:
return set()

# popping as we will set this ourselves
kwargs.pop("record")

# we could also do: return request.topic.get_needs() but get_needs is not implemented on the RDM Record Proxy
# we could also
# - subclass the rdm record entity resolver
# - implement the get_needs() for our use case
# - register the new resolver for `record` entities in invenio.cfg
needs = [
g.needs(record=request.topic.resolve(), **kwargs) for g in self._generators
]
permission = self._get_permission(request)
record = request.topic.resolve()
popped_record = kwargs.pop("record")
needs = [g.needs(record=record, **kwargs) for g in permission]

kwargs["record"] = popped_record
return set(chain.from_iterable(needs))

def excludes(self, request=None, **kwargs):
"""Set of excludes denying permission."""
if request is None:
return set()

permission = self._get_permission(request)
record = request.topic.resolve()
popped_record = kwargs.pop("record")
excludes = [g.excludes(record=record, **kwargs) for g in permission]

kwargs["record"] = popped_record
return set(chain.from_iterable(excludes))


class TopicPermission(EntityReferenceServicePermission):
"""Request-oriented generator to get generators of specified permission name from the topic of the request."""

entity_field = "topic"


class CurationModerators(Generator):
"""Permission generator that allows users with the `moderation` role."""
Expand Down
26 changes: 11 additions & 15 deletions invenio_curations/services/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
IfCurationRequestAccepted,
IfCurationRequestExists,
IfRequestTypes,
RequestTopicGenerators,
TopicPermission,
)


Expand Down Expand Up @@ -79,22 +79,16 @@ class CurationRDMRecordPermissionPolicy(RDMRecordPermissionPolicy):
class CurationRDMRequestsPermissionPolicy(RDMRequestsPermissionPolicy):
"""Customized permission policy for sane handling of curation requests."""

# specifying the RDM policy as we use the `can_review` permission for viewing a request.
# if needs for viewing request.topic record can be resolved otherwise, it would be great (maybe through EntityNeeds/EntityGrants as is done for Creator/Receiver)
rdm_policy = RDMRecordPermissionPolicy
curation_request_record_review = IfRequestTypes(
[CurationRequest],
then_=[RequestTopicGenerators(generators=rdm_policy.can_review)],
then_=[TopicPermission(permission_name="can_review")],
else_=[],
)

# Only allow community-submission requests to be accepted after
# the rdm-curation request has been accepted
# Only allow community-submission requests to be accepted after the rdm-curation request has been accepted
can_action_accept = [
IfRequestTypes(
request_types=[
CommunitySubmission,
],
request_types=[CommunitySubmission],
then_=[
IfCurationRequestAccepted(
then_=RDMRequestsPermissionPolicy.can_action_accept, else_=[]
Expand All @@ -106,23 +100,25 @@ class CurationRDMRequestsPermissionPolicy(RDMRequestsPermissionPolicy):

# Update can read and can comment with new states
can_read = [
# Have to explicitly check the request type and circumvent using status, as creator/receiver will add a query filter where one entity must be the user.
IfRequestTypes(
[CurationRequest],
then_=[
Creator(),
Receiver(),
RequestTopicGenerators(generators=rdm_policy.can_review),
TopicPermission(permission_name="can_review"),
],
else_=RDMRequestsPermissionPolicy.can_read,
)
]

can_create_comment = can_read

# Add new actions
can_action_review = RDMRequestsPermissionPolicy.can_action_accept
can_action_critique = RDMRequestsPermissionPolicy.can_action_accept
# Update submit to also allow record reviewers/managers for curation requests
can_action_submit = RDMRequestsPermissionPolicy.can_action_submit + [
curation_request_record_review
]

# Add new actions
can_action_review = RDMRequestsPermissionPolicy.can_action_accept
can_action_critique = RDMRequestsPermissionPolicy.can_action_accept
can_action_resubmit = can_action_submit
1 change: 1 addition & 0 deletions invenio_curations/services/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"""Curation service."""

from flask import current_app
from invenio_access.permissions import system_identity
from invenio_accounts.models import Role
from invenio_accounts.proxies import current_datastore
from invenio_i18n import gettext as _
Expand Down

0 comments on commit 01abb30

Please sign in to comment.