Skip to content

Commit

Permalink
documents: add permissions for documents with registred URN
Browse files Browse the repository at this point in the history
 * prevent deleting documents with registered URN
 * prevent deleting pdf files of documents with registered URN

Co-Authored-by: Valeria Granata <[email protected]>
  • Loading branch information
2 people authored and PascalRepond committed Aug 22, 2023
1 parent deed13b commit 291079d
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 12 deletions.
21 changes: 20 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ charset-normalizer = "<2.1.0"
python-levenshtein = "<0.20.0"
jsonschema = "<4.0.0"
pydocstyle = ">=6.1.1,<6.2"
requests-mock = "^1.9.3"
requests-mock = "^1.11.0"

[tool.poetry.dev-dependencies]
Flask-Debugtoolbar = ">=0.10.1"
Expand Down
1 change: 1 addition & 0 deletions sonar/modules/documents/extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

from invenio_pidstore.models import PersistentIdentifier, PIDDoesNotExistError
from invenio_records.extensions import RecordExtension

from sonar.modules.documents.tasks import register_urn_code_from_document
from sonar.modules.documents.urn import Urn

Expand Down
42 changes: 37 additions & 5 deletions sonar/modules/documents/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
"""Permissions for documents."""

from flask import request
from invenio_files_rest.models import Bucket
from invenio_files_rest.models import Bucket, ObjectVersion
from invenio_pidstore.errors import PIDDoesNotExistError
from invenio_pidstore.models import PersistentIdentifier

from sonar.modules.documents.api import DocumentRecord
from sonar.modules.organisations.api import current_organisation
Expand Down Expand Up @@ -104,7 +106,6 @@ def update(cls, user, record):
return False

user = user.replace_refs()

return document.has_subdivision(user.get('subdivision', {}).get('pid'))

@classmethod
Expand All @@ -119,8 +120,33 @@ def delete(cls, user, record):
if not user or not user.is_admin:
return False

# Same rules as update
return cls.update(user, record)
# Check delete conditions and consider same rules as update
return cls.can_delete(record) and cls.update(user, record)

@classmethod
def can_delete(cls, record):
"""Delete permission conditions.
:param record: Record to check.
:returns: True if action can be done.
"""
# Delete only documents with no URN or no registred URN
document = DocumentRecord.get_record_by_pid(record['pid'])

if document:
# check if document has urn
try:
urn_identifier = PersistentIdentifier\
.get_by_object('urn', 'rec', document.id)
except PIDDoesNotExistError:
return True

# check if urn is registered
if urn_identifier.is_registered():
return False

return True


class DocumentFilesPermission(FilesPermission):
"""Documents files permissions.
Expand Down Expand Up @@ -193,6 +219,12 @@ def delete(cls, user, record, pid, parent_record):
:param pid: The :class:`invenio_pidstore.models.PersistentIdentifier`
instance.
:param parent_record: the record related to the bucket.
:returns: True is action can be done.
:returns: True if action can be done.
"""
document = cls.get_document(parent_record)
if isinstance(record, ObjectVersion):
file_type = document.files[record.key]['type']
if file_type == 'file' and record.mimetype == 'application/pdf':
return DocumentPermission.can_delete(parent_record)\
and cls.update(user, record, pid, parent_record)
return cls.update(user, record, pid, parent_record)
11 changes: 7 additions & 4 deletions sonar/modules/documents/urn.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,13 @@ def create_urn(cls, record):
object_uuid=record.id,
status=PIDStatus.NEW,
)
record["identifiedBy"].append(
{"type": "bf:Urn", "value": urn_code}
)
if "identifiedBy" in record:
record["identifiedBy"].append(
{"type": "bf:Urn", "value": urn_code}
)
else:
record["identifiedBy"] = \
[{"type": "bf:Urn", "value": urn_code}]
except PIDAlreadyExists:
current_app.logger.error(
'generated urn already exist for document: '
Expand Down Expand Up @@ -177,7 +181,6 @@ def register_urn_pid(cls, urn=None):
pid.status = PIDStatus.REGISTERED
db.session.commit()


@classmethod
def get_documents_to_generate_urns(cls):
"""Get documents that need a URN code..
Expand Down
32 changes: 31 additions & 1 deletion tests/api/documents/test_documents_files_permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

"""Test documents files permissions."""

from io import BytesIO

from flask import url_for
from flask_security import url_for_security
Expand Down Expand Up @@ -46,7 +47,6 @@ def test_update_delete(client, superuser, admin, moderator,
assert res.status_code == status



def test_read_metadata(client, superuser, admin, moderator,
submitter, user, document_with_file):
"""Test read files permissions."""
Expand Down Expand Up @@ -74,6 +74,7 @@ def test_read_metadata(client, superuser, admin, moderator,
res = client.get(url_files)
assert res.status_code == status


def test_read_content(client, superuser, admin, moderator,
submitter, user, user_without_org, document_with_file):
"""Test read documents permissions."""
Expand Down Expand Up @@ -120,3 +121,32 @@ def test_read_content(client, superuser, admin, moderator,
client.get(url_for_security('logout'))
res = client.get(url_file_content)
assert res.status_code == status


def test_file_of_document_with_urn_delete(client, superuser,
minimal_thesis_document):
"""Test delete file of document with registered URN identifier."""
# Logged as superuser
login_user_via_session(client, email=superuser['email'])

# Add pdf file to document
minimal_thesis_document.files['test.pdf'] = BytesIO(b'File content')
minimal_thesis_document.files['test.pdf']['type'] = 'file'
minimal_thesis_document.commit()

url_file_content = url_for(
'invenio_records_files.doc_object_api',
pid_value=minimal_thesis_document['pid'], key='test.pdf')
res = client.delete(url_file_content)
assert res.status_code == 403

# Add png file to document
minimal_thesis_document.files['test.png'] = BytesIO(b'File content')
minimal_thesis_document.files['test.png']['type'] = 'file'
minimal_thesis_document.commit()

url_file_content = url_for(
'invenio_records_files.doc_object_api',
pid_value=minimal_thesis_document['pid'], key='test.png')
res = client.delete(url_file_content)
assert res.status_code == 204
16 changes: 16 additions & 0 deletions tests/api/documents/test_documents_permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"""Test documents permissions."""

import json
from io import BytesIO

import mock
from flask import url_for
Expand Down Expand Up @@ -403,3 +404,18 @@ def test_delete(client, document, make_document, make_user, superuser, admin,
assert res.status_code == 204
assert PersistentIdentifier.get('ark', ark_id).status == \
PIDStatus.DELETED


def test_document_with_urn_delete(client, superuser, minimal_thesis_document):
"""Test delete document with registered URN identifier."""
# Add file to document
minimal_thesis_document.files['test.pdf'] = BytesIO(b'File content')
minimal_thesis_document.files['test.pdf']['type'] = 'file'
minimal_thesis_document.commit()

# Logged as superuser
login_user_via_session(client, email=superuser['email'])
pid = minimal_thesis_document['pid']
res = client.delete(url_for('invenio_records_rest.doc_item',
pid_value=pid))
assert res.status_code == 403

0 comments on commit 291079d

Please sign in to comment.