Skip to content

Commit 69f47cd

Browse files
JakobMiesnerkpsherva
authored andcommitted
permissions: add backoffice readonly action
* add `backoffice_readonly_access_action` and have it replace `backoffice_access_action` in read operations * Users with the `backoffice_readonly_access_action` can read everything that users with the `backoffice_access_action` could read before * create `PatronOwnerReadPermission` and `backoffice_read_permission` that make use of `backoffice_readonly_access_action`
1 parent 839aad8 commit 69f47cd

36 files changed

+266
-58
lines changed

invenio_app_ils/acquisition/config.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@
1010
from invenio_records_rest.facets import terms_filter
1111

1212
from invenio_app_ils.config import RECORDS_REST_MAX_RESULT_WINDOW
13-
from invenio_app_ils.permissions import backoffice_permission, superuser_permission
13+
from invenio_app_ils.permissions import (
14+
backoffice_permission,
15+
backoffice_read_permission,
16+
superuser_permission,
17+
)
1418

1519
from .api import ORDER_PID_FETCHER, ORDER_PID_MINTER, ORDER_PID_TYPE, Order
1620
from .search import OrderSearch
@@ -44,8 +48,8 @@
4448
default_media_type="application/json",
4549
max_result_window=RECORDS_REST_MAX_RESULT_WINDOW,
4650
error_handlers=dict(),
47-
read_permission_factory_imp=backoffice_permission,
48-
list_permission_factory_imp=backoffice_permission,
51+
read_permission_factory_imp=backoffice_read_permission,
52+
list_permission_factory_imp=backoffice_read_permission,
4953
create_permission_factory_imp=backoffice_permission,
5054
update_permission_factory_imp=backoffice_permission,
5155
delete_permission_factory_imp=superuser_permission,

invenio_app_ils/circulation/config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
)
4242
from invenio_app_ils.patrons.api import patron_exists
4343
from invenio_app_ils.permissions import (
44-
PatronOwnerPermission,
44+
PatronOwnerReadPermission,
4545
authenticated_user_permission,
4646
backoffice_permission,
4747
loan_extend_circulation_permission,
@@ -254,7 +254,7 @@
254254
links_factory_imp="invenio_circulation.links:loan_links_factory",
255255
max_result_window=RECORDS_REST_MAX_RESULT_WINDOW,
256256
error_handlers=dict(),
257-
read_permission_factory_imp=PatronOwnerPermission,
257+
read_permission_factory_imp=PatronOwnerReadPermission,
258258
# auth via search_factory
259259
list_permission_factory_imp=authenticated_user_permission,
260260
create_permission_factory_imp=superuser_permission,

invenio_app_ils/cli.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,6 +1308,7 @@ def data(
13081308
# Create roles to restrict access
13091309
_run_command("roles create admin", verbose)
13101310
_run_command("roles create librarian", verbose)
1311+
_run_command("roles create librarian-readonly", verbose)
13111312

13121313
# Create users
13131314
patron1_profile = {"full_name": "Yannic Vilma"}
@@ -1328,6 +1329,12 @@ def data(
13281329
verbose,
13291330
)
13301331

1332+
readonly_profile = {"full_name": "Ro Only"}
1333+
_run_command(
1334+
f"users create [email protected] -a --password=123456 --profile '{json.dumps(readonly_profile)}'",
1335+
verbose,
1336+
)
1337+
13311338
patron3_profile = {"full_name": "Medrod Tara"}
13321339
_run_command(
13331340
f"users create [email protected] -a --password=123456 --profile '{json.dumps(patron3_profile)}'",
@@ -1350,6 +1357,7 @@ def data(
13501357

13511358
# assign roles
13521359
_run_command("roles add [email protected] librarian", verbose)
1360+
_run_command("roles add [email protected] librarian-readonly", verbose)
13531361

13541362
# Index vocabularies
13551363
vocabularies_dir = os.path.join(CURRENT_DIR, "vocabularies", "data")
@@ -1368,6 +1376,9 @@ def data(
13681376
# Assign actions
13691377
_run_command("access allow superuser-access role admin", verbose)
13701378
_run_command("access allow ils-backoffice-access role librarian", verbose)
1379+
_run_command(
1380+
"access allow ils-backoffice-readonly-access role librarian-readonly", verbose
1381+
)
13711382

13721383
# Create demo locations
13731384
click.echo("Creating locations and internal locations...")

invenio_app_ils/closures/views.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from invenio_app_ils.locations.api import LOCATION_PID_TYPE
1717
from invenio_app_ils.closures.serializers import closure_periods_response
1818
from invenio_app_ils.closures.api import get_closure_periods
19-
from invenio_app_ils.permissions import backoffice_permission
19+
from invenio_app_ils.permissions import backoffice_read_permission
2020
from invenio_app_ils.records.permissions import RecordPermission
2121

2222

@@ -53,7 +53,7 @@ def get(self, pid, record, year, **kwargs):
5353
"""Get the date ranges for which a location is closure based in the specified year"""
5454

5555
factory = RecordPermission(record, "read")
56-
if not factory.is_public() and not backoffice_permission().can():
56+
if not factory.is_public() and not backoffice_read_permission().can():
5757
if not current_user.is_authenticated:
5858
abort(401)
5959
abort(403)

invenio_app_ils/config.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,10 @@
9191
)
9292
from .patrons.search import PatronsSearch
9393
from .permissions import (
94-
PatronOwnerPermission,
94+
PatronOwnerReadPermission,
9595
authenticated_user_permission,
9696
backoffice_permission,
97+
backoffice_read_permission,
9798
views_permissions_factory,
9899
)
99100
from .records.permissions import record_read_permission_factory
@@ -395,8 +396,8 @@ def _(x):
395396
default_media_type="application/json",
396397
max_result_window=RECORDS_REST_MAX_RESULT_WINDOW,
397398
error_handlers=dict(),
398-
read_permission_factory_imp=backoffice_permission,
399-
list_permission_factory_imp=backoffice_permission,
399+
read_permission_factory_imp=backoffice_read_permission,
400+
list_permission_factory_imp=backoffice_read_permission,
400401
create_permission_factory_imp=backoffice_permission,
401402
update_permission_factory_imp=backoffice_permission,
402403
delete_permission_factory_imp=backoffice_permission,
@@ -427,8 +428,8 @@ def _(x):
427428
default_media_type="application/json",
428429
max_result_window=RECORDS_REST_MAX_RESULT_WINDOW,
429430
error_handlers=dict(),
430-
read_permission_factory_imp=backoffice_permission,
431-
list_permission_factory_imp=backoffice_permission,
431+
read_permission_factory_imp=backoffice_read_permission,
432+
list_permission_factory_imp=backoffice_read_permission,
432433
create_permission_factory_imp=backoffice_permission,
433434
update_permission_factory_imp=backoffice_permission,
434435
delete_permission_factory_imp=backoffice_permission,
@@ -522,8 +523,8 @@ def _(x):
522523
default_media_type="application/json",
523524
max_result_window=RECORDS_REST_MAX_RESULT_WINDOW,
524525
error_handlers=dict(),
525-
read_permission_factory_imp=backoffice_permission,
526-
list_permission_factory_imp=backoffice_permission,
526+
read_permission_factory_imp=backoffice_read_permission,
527+
list_permission_factory_imp=backoffice_read_permission,
527528
create_permission_factory_imp=backoffice_permission,
528529
update_permission_factory_imp=backoffice_permission,
529530
delete_permission_factory_imp=backoffice_permission,
@@ -552,7 +553,7 @@ def _(x):
552553
max_result_window=RECORDS_REST_MAX_RESULT_WINDOW,
553554
error_handlers=dict(),
554555
read_permission_factory_imp=deny_all,
555-
list_permission_factory_imp=backoffice_permission,
556+
list_permission_factory_imp=backoffice_read_permission,
556557
create_permission_factory_imp=deny_all,
557558
update_permission_factory_imp=deny_all,
558559
delete_permission_factory_imp=deny_all,
@@ -584,7 +585,7 @@ def _(x):
584585
default_media_type="application/json",
585586
max_result_window=RECORDS_REST_MAX_RESULT_WINDOW,
586587
error_handlers=dict(),
587-
read_permission_factory_imp=PatronOwnerPermission,
588+
read_permission_factory_imp=PatronOwnerReadPermission,
588589
# auth via search_factory
589590
list_permission_factory_imp=authenticated_user_permission,
590591
create_permission_factory_imp=authenticated_user_permission,

invenio_app_ils/ill/config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
from invenio_app_ils.config import RECORDS_REST_MAX_RESULT_WINDOW
1313
from invenio_app_ils.permissions import (
14-
PatronOwnerPermission,
14+
PatronOwnerReadPermission,
1515
authenticated_user_permission,
1616
backoffice_permission,
1717
superuser_permission,
@@ -76,7 +76,7 @@
7676
default_media_type="application/json",
7777
max_result_window=RECORDS_REST_MAX_RESULT_WINDOW,
7878
error_handlers=dict(),
79-
read_permission_factory_imp=PatronOwnerPermission,
79+
read_permission_factory_imp=PatronOwnerReadPermission,
8080
# auth via search_factory
8181
list_permission_factory_imp=authenticated_user_permission,
8282
create_permission_factory_imp=backoffice_permission,

invenio_app_ils/patrons/anonymization.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,9 @@ def anonymize_patron_data(patron_pid, force=False):
170170
borrowing_request["patron"] = anonymous_patron_fields
171171
borrowing_request["patron_pid"] = anonymous_patron_fields["pid"]
172172
borrowing_request.commit()
173-
anonymized_records[BORROWING_REQUEST_PID_TYPE]["records"].append(borrowing_request)
173+
anonymized_records[BORROWING_REQUEST_PID_TYPE]["records"].append(
174+
borrowing_request
175+
)
174176
indices += 1
175177

176178
DocumentRequestSearch = current_app_ils.document_request_search_cls

invenio_app_ils/permissions.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from invenio_app_ils.proxies import current_app_ils
2121

2222
backoffice_access_action = action_factory("ils-backoffice-access")
23+
backoffice_readonly_access_action = action_factory("ils-backoffice-readonly-access")
2324

2425

2526
def need_permissions(action):
@@ -62,6 +63,11 @@ def backoffice_permission(*args, **kwargs):
6263
return Permission(backoffice_access_action)
6364

6465

66+
def backoffice_read_permission(*args, **kwargs):
67+
"""Return permission for backoffice (full or read-only) read access."""
68+
return Permission(backoffice_access_action, backoffice_readonly_access_action)
69+
70+
6571
def superuser_permission(*args, **kwargs):
6672
"""Return permission to allow only admins."""
6773
return Permission(superuser_access)
@@ -160,6 +166,18 @@ def loan_checkout_permission(*args, **kwargs):
160166
raise LoanCheckoutByPatronForbidden(int(loan["patron_pid"]), current_user.id)
161167

162168

169+
class PatronOwnerReadPermission(Permission):
170+
"""Return Permission to evaluate if the current user owns the record or has backoffice read access."""
171+
172+
def __init__(self, record):
173+
"""Constructor."""
174+
super().__init__(
175+
UserNeed(int(record["patron_pid"])),
176+
backoffice_access_action,
177+
backoffice_readonly_access_action,
178+
)
179+
180+
163181
class PatronOwnerPermission(Permission):
164182
"""Return Permission to evaluate if the current user owns the record."""
165183

@@ -179,12 +197,14 @@ def __init__(self, record):
179197
"circulation-loan-update-dates",
180198
"relations-create",
181199
"relations-delete",
182-
"stats-most-loaned",
183200
"document-request-actions",
184201
"bucket-create",
185202
"ill-brwreq-patron-loan-create",
186203
"ill-brwreq-patron-loan-extension-accept",
187204
"ill-brwreq-patron-loan-extension-decline",
205+
]
206+
_is_backoffice_read_permission = [
207+
"stats-most-loaned",
188208
"send-notification-to-patron",
189209
]
190210
_is_patron_owner_permission = [
@@ -199,6 +219,8 @@ def views_permissions_factory(action):
199219
return authenticated_user_permission()
200220
elif action in _is_backoffice_permission:
201221
return backoffice_permission()
222+
elif action in _is_backoffice_read_permission:
223+
return backoffice_read_permission()
202224
elif action in _is_patron_owner_permission:
203225
return PatronOwnerPermission
204226
elif action == "circulation-loan-checkout":

invenio_app_ils/providers/config.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@
1010
from invenio_records_rest.facets import terms_filter
1111

1212
from invenio_app_ils.config import RECORDS_REST_MAX_RESULT_WINDOW
13-
from invenio_app_ils.permissions import backoffice_permission
13+
from invenio_app_ils.permissions import (
14+
backoffice_permission,
15+
backoffice_read_permission,
16+
)
1417

1518
from .api import PROVIDER_PID_FETCHER, PROVIDER_PID_MINTER, PROVIDER_PID_TYPE, Provider
1619
from .indexer import ProviderIndexer
@@ -47,8 +50,8 @@
4750
default_media_type="application/json",
4851
max_result_window=RECORDS_REST_MAX_RESULT_WINDOW,
4952
error_handlers=dict(),
50-
read_permission_factory_imp=backoffice_permission,
51-
list_permission_factory_imp=backoffice_permission,
53+
read_permission_factory_imp=backoffice_read_permission,
54+
list_permission_factory_imp=backoffice_read_permission,
5255
create_permission_factory_imp=backoffice_permission,
5356
update_permission_factory_imp=backoffice_permission,
5457
delete_permission_factory_imp=backoffice_permission,

invenio_app_ils/records/permissions.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212
from invenio_access import Permission, any_user
1313
from six import string_types
1414

15-
from invenio_app_ils.permissions import backoffice_access_action
15+
from invenio_app_ils.permissions import (
16+
backoffice_access_action,
17+
backoffice_readonly_access_action,
18+
)
1619

1720
create_records_action = ActionNeed("create-records")
1821

@@ -68,7 +71,10 @@ def read_permissions(self):
6871
if self.is_public():
6972
return [any_user]
7073
else:
71-
return self.record_needs() + [backoffice_access_action]
74+
return self.record_needs() + [
75+
backoffice_readonly_access_action,
76+
backoffice_access_action,
77+
]
7278

7379
def record_explicit_restrictions(self):
7480
"""Return the list of user ids/roles allowed for the given action."""

0 commit comments

Comments
 (0)