Skip to content

Commit

Permalink
permissions: Allow record reviewers to also read/update request
Browse files Browse the repository at this point in the history
* introduce generator for accessing config permission
  • Loading branch information
rekt-hard committed Oct 2, 2024
1 parent 2d5c106 commit 163b581
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 31 deletions.
47 changes: 33 additions & 14 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -139,24 +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,
TopicPermission,
)
from invenio_rdm_records.requests import CommunityInclusion, CommunitySubmission
from invenio_rdm_records.services.permissions import RDMRequestsPermissionPolicy
from invenio_requests.services.generators import Creator, Receiver, Status
class CurationRDMRequestsPermissionPolicy(RDMRequestsPermissionPolicy):
"""Customized permission policy for sane handling of curation requests."""
# Only allow community-submission requests to be accepted after
# the rdm-curation request has been accepted
curation_request_record_review = IfRequestTypes(
[CurationRequest],
then_=[TopicPermission(permission_name="can_review")],
else_=[],
)
# 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 @@ -167,18 +174,30 @@ Since we only want to change the behaviour of these community submission request
]
# Update can read and can comment with new states
can_read = RDMRequestsPermissionPolicy.can_read + [
Status(
["review", "critiqued", "resubmitted"],
[Creator(), Receiver()],
),
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(),
TopicPermission(permission_name="can_review"),
],
else_=RDMRequestsPermissionPolicy.can_read,
)
]
can_create_comment = can_read
# 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 = RDMRequestsPermissionPolicy.can_action_submit
can_action_resubmit = can_action_submit
REQUESTS_PERMISSION_POLICY = CurationRDMRequestsPermissionPolicy
Expand Down
4 changes: 2 additions & 2 deletions invenio_curations/services/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

from invenio_curations.services import facets

from .permissions import CurationRDMRequestPermissionPolicy
from .permissions import CurationRDMRequestsPermissionPolicy


class CurationsSearchOptions(RequestSearchOptions):
Expand Down Expand Up @@ -43,7 +43,7 @@ class CurationsServiceConfig(RecordServiceConfig, ConfiguratorMixin):

# common configuration
permission_policy_cls = FromConfig(
"REQUESTS_PERMISSION_POLICY", default=CurationRDMRequestPermissionPolicy
"REQUESTS_PERMISSION_POLICY", default=CurationRDMRequestsPermissionPolicy
)
# TODO: update search options?
search = CurationsSearchOptions
55 changes: 55 additions & 0 deletions invenio_curations/services/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

"""Curations related generators."""

from itertools import chain

from flask_principal import RoleNeed
from invenio_access.permissions import system_identity
from invenio_records_permissions.generators import ConditionalGenerator, Generator
Expand Down Expand Up @@ -60,6 +62,59 @@ def _condition(self, request=None, **kwargs):
return False


class EntityReferenceServicePermission(Generator):
"""Request-oriented generator accessing a named permission from the entity's service config."""

entity_field = None

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."""
if request is None:
return set()

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
66 changes: 52 additions & 14 deletions invenio_curations/services/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,22 @@

"""Curations permissions."""

from invenio_rdm_records.requests import CommunitySubmission
from invenio_rdm_records.services.generators import IfFileIsLocal
from invenio_rdm_records.services.permissions import RDMRecordPermissionPolicy
from invenio_rdm_records.services.permissions import (
RDMRecordPermissionPolicy,
RDMRequestsPermissionPolicy,
)
from invenio_records_permissions.generators import SystemProcess
from invenio_requests.services.generators import Creator, Receiver, Status
from invenio_requests.services.permissions import (
PermissionPolicy as RequestPermissionPolicy,
)

from invenio_curations.requests.curation import CurationRequest
from invenio_curations.services.generators import (
CurationModerators,
IfCurationRequestAccepted,
IfCurationRequestExists,
IfRequestTypes,
TopicPermission,
)


Expand Down Expand Up @@ -71,16 +76,49 @@ class CurationRDMRecordPermissionPolicy(RDMRecordPermissionPolicy):
)


class CurationRDMRequestPermissionPolicy(RequestPermissionPolicy):
"""Request permission policy for curations."""
class CurationRDMRequestsPermissionPolicy(RDMRequestsPermissionPolicy):
"""Customized permission policy for sane handling of curation requests."""

can_read = RequestPermissionPolicy.can_read + [
Status(
["review", "critiqued", "resubmitted"],
[Creator(), Receiver()],
),
curation_request_record_review = IfRequestTypes(
[CurationRequest],
then_=[TopicPermission(permission_name="can_review")],
else_=[],
)

# Only allow community-submission requests to be accepted after the rdm-curation request has been accepted
can_action_accept = [
IfRequestTypes(
request_types=[CommunitySubmission],
then_=[
IfCurationRequestAccepted(
then_=RDMRequestsPermissionPolicy.can_action_accept, else_=[]
)
],
else_=RDMRequestsPermissionPolicy.can_action_accept,
)
]

# 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(),
TopicPermission(permission_name="can_review"),
],
else_=RDMRequestsPermissionPolicy.can_read,
)
]
can_create_comment = can_read
can_action_review = RequestPermissionPolicy.can_action_accept
can_action_critique = RequestPermissionPolicy.can_action_accept
can_action_resubmit = RequestPermissionPolicy.can_action_cancel

# 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
4 changes: 3 additions & 1 deletion 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 Expand Up @@ -127,7 +128,8 @@ def create(self, identity, data=None, uow=None, **kwargs):
),
}

if self.get_review(identity, topic):
# using system identity to ensure a request is fetched, if it exists. Even if the user would not have access.
if self.get_review(system_identity, topic):
raise OpenRecordCurationRequestAlreadyExists()

if data:
Expand Down

0 comments on commit 163b581

Please sign in to comment.