Skip to content

Commit

Permalink
moderation: delete a user's records when blocking them
Browse files Browse the repository at this point in the history
  • Loading branch information
max-moser authored and zzacharo committed Sep 11, 2023
1 parent 7a1353d commit 69eca6c
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 5 deletions.
87 changes: 82 additions & 5 deletions invenio_rdm_records/requests/user_moderation/actions.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,101 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2023 CERN.
# Copyright (C) 2023 TU Wien.
#
# Invenio-RDM-Records is free software; you can redistribute it and/or modify
# it under the terms of the MIT License; see LICENSE file for more details.
"""RDM user moderation action."""

from invenio_access.permissions import system_identity
from invenio_pidstore.errors import PIDDoesNotExistError
from invenio_vocabularies.proxies import current_service

from invenio_rdm_records.proxies import current_rdm_records_service
from ...proxies import current_rdm_records_service
from ...records.systemfields.deletion_status import RecordDeletionStatusEnum


def _get_records_for_user(user_id):
"""Helper function for getting all the records of the user.
Note: This function performs DB queries yielding all records for a given
user (which is not hard-limited in the system) and performs service calls
on each of them. Thus, this function has the potential of being a very
heavy operation, and should not be called as part of the handling of an
HTTP request!
"""
record_cls = current_rdm_records_service.record_cls
model_cls = record_cls.model_cls
parent_cls = record_cls.parent_record_cls
parent_model_cls = parent_cls.model_cls

# get all the parent records owned by the blocked user
parent_recs = [
parent_cls(m.data, model=m)
for m in parent_model_cls.query.filter(
parent_model_cls.json["access"]["owned_by"]["user"].as_string() == user_id
).all()
]

# get all child records of the chosen parent records
recs = [
record_cls(m.data, model=m)
for m in model_cls.query.filter(
model_cls.parent_id.in_([p.id for p in parent_recs])
).all()
]

return recs


def on_block(user_id, uow=None, **kwargs):
"""Removes records that belong to a user."""
pass
"""Removes records that belong to a user.
Note: This function operates on all records of a user and thus has the potential
to be a very heavy operation! Thus it should not be called as part of the handling
of an HTTP request!
"""
user_id = str(user_id)
tombstone_data = {"note": "User was blocked"}

# set the removal reason if the vocabulary item exists
try:
removal_reason_id = kwargs.get("removal_reason_id", "misconduct")
vocab = current_service.read(
identity=system_identity, id_=("removalreasons", removal_reason_id)
)
tombstone_data["removal_reason"] = {"id": vocab.id}
except PIDDoesNotExistError:
pass

# soft-delete all the published records of that user
for rec in _get_records_for_user(user_id):
if not rec.deletion_status.is_deleted:
current_rdm_records_service.delete_record(
system_identity,
rec.pid.pid_value,
tombstone_data,
uow=uow,
)


def on_restore(user_id, uow=None, **kwargs):
"""Restores records that belong to a user."""
pass
"""Restores records that belong to a user.
Note: This function operates on all records of a user and thus has the potential
to be a very heavy operation! Thus it should not be called as part of the handling
of an HTTP request!
"""
user_id = str(user_id)

# restore all the deleted records of that user
for rec in _get_records_for_user(user_id):
if rec.deletion_status == RecordDeletionStatusEnum.DELETED:
current_rdm_records_service.restore_record(
system_identity,
rec.pid.pid_value,
uow=uow,
)


def on_approve(user_id, uow=None, **kwargs):
Expand Down
32 changes: 32 additions & 0 deletions tests/requests/test_user_moderation_actions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# # -*- coding: utf-8 -*-
# #
# # Copyright (C) 2023 CERN.
# # Copyright (C) 2023 TU Wien.
# #
# # Invenio-RDM is free software; you can redistribute it and/or modify
# # it under the terms of the MIT License; see LICENSE file for more details.
Expand Down Expand Up @@ -94,3 +95,34 @@ def test_user_moderation_approve(
hits = post_approval_records.to_dict()["hits"]["hits"]
is_verified = all([hit["parent"]["is_verified"] for hit in hits])
assert is_verified == True


def test_user_moderation_decline(
running_app, mod_identity, unverified_user, es_clear, minimal_record, mocker
):
"""Tests user moderation action after decline.
All of the user's records should be deleted.
"""
# Create a record
draft = records_service.create(unverified_user.identity, minimal_record)
record = records_service.publish(id_=draft.id, identity=unverified_user.identity)
assert not record._record.deletion_status.is_deleted
assert record._record.tombstone is None

# Fetch moderation request that was created on publish and decline the user
res = current_requests_service.search(
system_identity, params={"q": f"topic.user:{unverified_user.id}"}
)
assert res.total == 1
mod_request = res.to_dict()["hits"]["hits"][0]
current_requests_service.execute_action(
mod_identity, id_=mod_request["id"], action="decline"
)

# The user's record should now be deleted
record = records_service.read(
id_=draft.id, identity=system_identity, with_deleted=True
)
assert record._record.deletion_status.is_deleted
assert record._record.tombstone is not None

0 comments on commit 69eca6c

Please sign in to comment.