From da85c311be2513c2c21170d74d8acf6809933164 Mon Sep 17 00:00:00 2001 From: Maximilian Moser Date: Wed, 20 Mar 2024 16:15:24 +0100 Subject: [PATCH 1/3] generators: add ConditionalGenerator.query_filter() * it seems like it was missing before --- invenio_records_permissions/generators.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/invenio_records_permissions/generators.py b/invenio_records_permissions/generators.py index a94376f..33bf4b3 100644 --- a/invenio_records_permissions/generators.py +++ b/invenio_records_permissions/generators.py @@ -221,7 +221,7 @@ def query_filter(self, identity=None, **kwargs): **{ "internal.access_levels.{}".format(access_level): { "scheme": "person", - "id": id_need.value + "id": id_need.value, # TODO: Implement other schemes } } @@ -296,6 +296,11 @@ def excludes(self, record=None, **kwargs): ] return set(chain.from_iterable(excludes)) + def query_filter(self, **kwargs): + """Create query filter based on the condition.""" + record = kwargs.pop("record", None) + return self._make_query(self._generators(record, **kwargs)) or [] + @staticmethod def _make_query(generators, **kwargs): """Make a query for one set of generators.""" From 7782fde69314b11adb21c77943eb042bc1af0a7a Mon Sep 17 00:00:00 2001 From: Maximilian Moser Date: Fri, 28 Oct 2022 15:27:51 +0200 Subject: [PATCH 2/3] generators: introduce conditional DisableIf generator * this can be used to implement a conditional read-only mode, e.g. enabled temporarily to make system migrations smoother --- invenio_records_permissions/config.py | 4 ++++ invenio_records_permissions/generators.py | 18 ++++++++++++++++++ .../policies/records.py | 12 +++++++++--- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/invenio_records_permissions/config.py b/invenio_records_permissions/config.py index c22b14d..c885cb6 100644 --- a/invenio_records_permissions/config.py +++ b/invenio_records_permissions/config.py @@ -2,6 +2,7 @@ # # Copyright (C) 2019-2020 CERN. # Copyright (C) 2019-2020 Northwestern University. +# Copyright (C) 2022-2024 TU Wien. # # Invenio-Records-Permissions is free software; you can redistribute it # and/or modify it under the terms of the MIT License; see LICENSE file for @@ -13,3 +14,6 @@ "invenio_records_permissions.policies.RecordPermissionPolicy" ) """PermissionPolicy for records.""" + +RECORDS_PERMISSIONS_READ_ONLY = False +"""Condition to trigger the ``DisableIfReadOnly`` permission generator.""" diff --git a/invenio_records_permissions/generators.py b/invenio_records_permissions/generators.py index 33bf4b3..21c3782 100644 --- a/invenio_records_permissions/generators.py +++ b/invenio_records_permissions/generators.py @@ -2,6 +2,7 @@ # # Copyright (C) 2019-2023 CERN. # Copyright (C) 2019-2020 Northwestern University. +# Copyright (C) 2022-2024 TU Wien. # # Invenio-Records-Permissions is free software; you can redistribute it # and/or modify it under the terms of the MIT License; see LICENSE file for @@ -23,6 +24,7 @@ superuser_access, system_process, ) +from invenio_base.utils import obj_or_import_string from invenio_search.engine import dsl @@ -323,6 +325,22 @@ def _condition(self, **_): return current_app.config.get(self.config_key) in self.accept_values +class DisableIfReadOnly(IfConfig): + """Disable action for ALL users if the system is in read-only mode. + + This generator uses the ``RECORDS_PERMISSIONS_READ_ONLY`` configuration item + to determine if the read-only mode is set. + """ + + def __init__(self): + """Initialize generator.""" + super().__init__( + config_key="RECORDS_PERMISSIONS_READ_ONLY", + then_=[Disable()], + else_=[], + ) + + # # | Meta Restricted | Files Restricted | Access Right | Result | # |-----------------|------------------|--------------|--------| diff --git a/invenio_records_permissions/policies/records.py b/invenio_records_permissions/policies/records.py index aea5c14..adb7dac 100644 --- a/invenio_records_permissions/policies/records.py +++ b/invenio_records_permissions/policies/records.py @@ -14,7 +14,13 @@ from werkzeug.utils import import_string from ..errors import UnknownGeneratorError -from ..generators import AnyUser, AnyUserIfPublic, Disable, RecordOwners +from ..generators import ( + AnyUser, + AnyUserIfPublic, + Disable, + DisableIfReadOnly, + RecordOwners, +) from .base import BasePermissionPolicy @@ -59,13 +65,13 @@ class RecordPermissionPolicy(BasePermissionPolicy): # Read access given to everyone if public record/files and owners always. can_read = [AnyUserIfPublic(), RecordOwners()] # Update access given to record owners. - can_update = [RecordOwners()] + can_update = [RecordOwners(), DisableIfReadOnly()] # Delete access given to superuser-access action only # (superuser-access is added by default by base policy) can_delete = [] # Associated files permissions (which are really bucket permissions) can_read_files = [AnyUserIfPublic(), RecordOwners()] - can_update_files = [RecordOwners()] + can_update_files = [RecordOwners(), DisableIfReadOnly()] can_read_deleted_files = [] def __init__(self, action, **over): From a6c3afa9169cf90b1e61cc9080032748ab52f2dc Mon Sep 17 00:00:00 2001 From: Maximilian Moser Date: Fri, 11 Nov 2022 16:16:53 +0100 Subject: [PATCH 3/3] tests: add tests for the conditional disable generators --- tests/test_generators.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/test_generators.py b/tests/test_generators.py index 57a2f44..7257a18 100644 --- a/tests/test_generators.py +++ b/tests/test_generators.py @@ -2,7 +2,8 @@ # # Copyright (C) 2019 CERN. # Copyright (C) 2019 Northwestern University. -# Copyright (C) 2023 Graz University of Technology +# Copyright (C) 2023 Graz University of Technology. +# Copyright (C) 2024 TU Wien. # # Invenio-Records-Permissions is free software; you can redistribute it # and/or modify it under the terms of the MIT License; see LICENSE file for @@ -22,6 +23,7 @@ AuthenticatedUser, ConditionalGenerator, Disable, + DisableIfReadOnly, Generator, IfConfig, RecordOwners, @@ -266,3 +268,18 @@ def test_ifconfig(app, create_record): UserNeed(2), UserNeed(3), } + + +def test_disable_if_read_only(app): + generator = DisableIfReadOnly() + + # Normal operation + app.config["RECORDS_PERMISSIONS_READ_ONLY"] = False + assert generator.excludes(record=None) == set() + assert generator.query_filter(record=None) == [] + + # System is in read-only mode + app.config["RECORDS_PERMISSIONS_READ_ONLY"] = True + assert generator.excludes(record=None) == {any_user} + query_filter = generator.query_filter(record=None) + assert query_filter.to_dict() == {"match_none": {}}