Skip to content

Commit

Permalink
services: Add elevated permissions check for publish and community re…
Browse files Browse the repository at this point in the history
…moval
  • Loading branch information
sakshamarora1 committed Oct 2, 2024
1 parent c983d91 commit 871fe66
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 61 deletions.
13 changes: 7 additions & 6 deletions invenio_rdm_records/services/communities/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,22 +203,23 @@ def _remove(self, identity, community_id, record):
if community_id not in record.parent.communities.ids:
raise RecordCommunityMissing(record.id, community_id)

# If config is true and there is only 1 communities left to remove
# Check for permission to remove a community from a record
self.require_permission(
identity, "remove_community", record=record, community_id=community_id
)
is_community_required = current_app.config["RDM_RECORD_ALWAYS_IN_COMMUNITY"]
is_last_community = len(record.parent.communities.ids) == 1
# Then, check for permissions to remove last community
# If community is required for a record and if it is the last community to remove
# Then, only users with special permissions can remove
can_remove_last_community = self.check_permission(
identity, "remove_community", record=record, community_id=community_id
identity, "remove_community_elevated", record=record, community_id=community_id
)
if (
is_community_required
and is_last_community
and not can_remove_last_community
):
raise CannotRemoveCommunityError()
# check permission here, per community: curator cannot remove another community
elif not can_remove_last_community:
raise PermissionDeniedError("remove_community")

# Default community is deleted when the exact same community is removed from the record
record.parent.communities.remove(community_id)
Expand Down
8 changes: 5 additions & 3 deletions invenio_rdm_records/services/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,16 +205,18 @@ def description(self):
class RecordSubmissionClosedCommunityError(PermissionDenied):
"""Record submission policy forbids non-members from submitting records to community."""

description = "Submission to this community is only allowed to community members."
description = _(
"Submission to this community is only allowed to community members."
)


class CommunityNotSelectedError(Exception):
"""Error thrown when a record is being created/updated with less than 1 community."""

description = "Cannot publish without selecting a community."
description = _("Cannot publish without selecting a community.")


class CannotRemoveCommunityError(Exception):
"""Error thrown when the last community is being removed from the record."""

description = "Cannot remove. A record should be part of atleast 1 community."
description = _("Cannot remove. A record should be part of at least 1 community.")
22 changes: 0 additions & 22 deletions invenio_rdm_records/services/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,25 +414,3 @@ def needs(self, request=None, **kwargs):
return [AccessRequestTokenNeed(request["payload"]["token"])]

return []


class IfOneCommunity(ConditionalGenerator):
"""Conditional generator for records always in communities case."""

def _condition(self, record=None, **kwargs):
"""Check if the record is associated with zero or one community."""
if record is None:
return True
rec_communities = record.parent.communities.ids
return len(rec_communities) == 1


class IfAtleastOneCommunity(ConditionalGenerator):
"""Conditional generator for records always in communities case."""

def _condition(self, record=None, **kwargs):
"""Check if the record is associated with zero or one community."""
if record is None:
return True
rec_communities = record.parent.communities.ids
return len(rec_communities) > 0
36 changes: 9 additions & 27 deletions invenio_rdm_records/services/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,11 @@
AccessGrant,
CommunityInclusionReviewers,
GuestAccessRequestToken,
IfAtleastOneCommunity,
IfCreate,
IfDeleted,
IfExternalDOIRecord,
IfFileIsLocal,
IfNewRecord,
IfOneCommunity,
IfRecordDeleted,
IfRequestType,
IfRestricted,
Expand Down Expand Up @@ -68,6 +66,7 @@ class RDMRecordPermissionPolicy(RecordPermissionPolicy):
RecordOwners(),
RecordCommunitiesAction("curate"),
AccessGrant("manage"),
Administration(),
SystemProcess(),
]
can_curate = can_manage + [AccessGrant("edit"), SecretLinks("edit")]
Expand Down Expand Up @@ -201,17 +200,9 @@ class RDMRecordPermissionPolicy(RecordPermissionPolicy):
),
]
# Allow publishing a new record or changes to an existing record.
can_publish = [
IfConfig(
"RDM_RECORD_ALWAYS_IN_COMMUNITY",
then_=[
IfAtleastOneCommunity(
then_=can_review, else_=[Administration(), SystemProcess()]
)
],
else_=can_review,
)
]
can_publish = can_review
# Permission to allow special users to publish a record in special cases
can_publish_elevated = [Administration(), SystemProcess()]
# Allow lifting a record or draft.
can_lift_embargo = can_manage

Expand All @@ -221,25 +212,16 @@ class RDMRecordPermissionPolicy(RecordPermissionPolicy):
# Who can add record to a community
can_add_community = can_manage
# Who can remove a community from a record
can_remove_community_ = [
can_remove_community = [
RecordOwners(),
CommunityCurators(),
Administration(),
SystemProcess(),
]
can_remove_community = [
IfConfig(
"RDM_RECORD_ALWAYS_IN_COMMUNITY",
then_=[
IfOneCommunity(
then_=[Administration(), SystemProcess()],
else_=can_remove_community_,
)
],
else_=can_remove_community_,
)
]
# Permission to allow special users to remove community in special cases
can_remove_community_elevated = [Administration(), SystemProcess()]
# Who can remove records from a community
can_remove_record = [CommunityCurators()]
can_remove_record = [CommunityCurators(), Administration()]
# Who can add records to a community in bulk
can_bulk_add = [SystemProcess()]

Expand Down
9 changes: 6 additions & 3 deletions invenio_rdm_records/services/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,14 +415,17 @@ def publish(self, identity, id_, uow=None, expand=False):
# Get the draft
draft = self.draft_cls.pid.resolve(id_, registered_only=False)

# If config is true and there are no communities selected
is_community_required = current_app.config["RDM_RECORD_ALWAYS_IN_COMMUNITY"]
is_community_missing = len(draft.parent.communities.ids) == 0
# Then, check for permissions to upload without community
# If community is required for a record and there are no communities selected
# Then, check for permissions to publish without community
can_publish_without_community = self.check_permission(
identity, "publish_elevated", record=draft
)
if (
is_community_required
and is_community_missing
and not self.check_permission(identity, "publish", record=draft)
and not can_publish_without_community
):
raise CommunityNotSelectedError()

Expand Down

0 comments on commit 871fe66

Please sign in to comment.