Skip to content

Commit

Permalink
permissions: disable edit and delete buttons for librarians
Browse files Browse the repository at this point in the history
* Creates separate permission files for patron, library, location resources.
* Disables location/library edit and delete buttons for librarians from external libraries.
* Closes #488

Co-Authored-by: Aly Badr <aly.badr@rero.ch>
  • Loading branch information
Aly Badr committed Oct 17, 2019
1 parent fa62e43 commit e804cd3
Show file tree
Hide file tree
Showing 8 changed files with 211 additions and 61 deletions.
16 changes: 10 additions & 6 deletions rero_ils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,19 @@
from .modules.item_types.api import ItemType
from .modules.items.api import Item, ItemsIndexer
from .modules.libraries.api import Library
from .modules.libraries.permissions import can_update_delete_library_factory
from .modules.loans.api import Loan
from .modules.loans.utils import can_be_requested, get_default_loan_duration, \
get_extension_params, is_item_available_for_checkout, \
loan_satisfy_circ_policies
from .modules.locations.api import Location
from .modules.locations.permissions import can_update_delete_location_factory
from .modules.notifications.api import Notification
from .modules.organisations.api import Organisation
from .modules.patron_types.api import PatronType
from .modules.patrons.api import Patron
from .modules.patrons.permissions import can_delete_patron_factory, \
can_update_patron_factory
from .permissions import can_access_organisation_patrons_factory, \
can_access_organisation_records_factory, \
can_create_organisation_records_factory, \
Expand Down Expand Up @@ -491,8 +495,8 @@ def _(x):
list_permission_factory_imp=can_access_organisation_patrons_factory,
read_permission_factory_imp=can_access_organisation_records_factory,
create_permission_factory_imp=can_create_organisation_records_factory,
update_permission_factory_imp=can_update_organisation_records_factory,
delete_permission_factory_imp=can_delete_organisation_records_factory,
update_permission_factory_imp=can_update_patron_factory,
delete_permission_factory_imp=can_delete_patron_factory,
),
ptty=dict(
pid_type='ptty',
Expand Down Expand Up @@ -588,8 +592,8 @@ def _(x):
list_permission_factory_imp=can_access_organisation_patrons_factory,
read_permission_factory_imp=can_access_organisation_records_factory,
create_permission_factory_imp=can_create_organisation_records_factory,
update_permission_factory_imp=can_update_organisation_records_factory,
delete_permission_factory_imp=can_delete_organisation_records_factory,
update_permission_factory_imp=can_update_delete_library_factory,
delete_permission_factory_imp=can_update_delete_library_factory,
),
loc=dict(
pid_type='loc',
Expand Down Expand Up @@ -620,8 +624,8 @@ def _(x):
search_factory_imp='rero_ils.query:organisation_search_factory',
read_permission_factory_imp=can_access_organisation_records_factory,
create_permission_factory_imp=can_create_organisation_records_factory,
update_permission_factory_imp=can_update_organisation_records_factory,
delete_permission_factory_imp=can_delete_organisation_records_factory,
update_permission_factory_imp=can_update_delete_location_factory,
delete_permission_factory_imp=can_update_delete_location_factory,
),
pers=dict(
pid_type='pers',
Expand Down
39 changes: 39 additions & 0 deletions rero_ils/modules/libraries/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
#
# RERO ILS
# Copyright (C) 2019 RERO
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""Library permissions."""


from ...permissions import staffer_is_authenticated


def can_update_delete_library_factory(record, *args, **kwargs):
"""Checks if logged user can update or delete its organisation libraries.
user must have librarian or system_librarian role
User can only update or delete its affiliated library.
"""
def can(self):
patron = staffer_is_authenticated()
if patron and patron.organisation_pid == record.organisation_pid:
if not patron.is_system_librarian:
if patron.library_pid and \
record.pid != patron.library_pid:
return False
return True
return False
return type('Check', (), {'can': can})()
39 changes: 39 additions & 0 deletions rero_ils/modules/locations/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
#
# RERO ILS
# Copyright (C) 2019 RERO
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""Location permissions."""


from ...permissions import staffer_is_authenticated


def can_update_delete_location_factory(record, *args, **kwargs):
"""Checks if logged user can update or delete its organisation locations.
user must have librarian or system_librarian role
User can only update or delete its affiliated library locations.
"""
def can(self):
patron = staffer_is_authenticated()
if patron and patron.organisation_pid == record.organisation_pid:
if not patron.is_system_librarian:
if patron.library_pid and \
record.library_pid != patron.library_pid:
return False
return True
return False
return type('Check', (), {'can': can})()
73 changes: 73 additions & 0 deletions rero_ils/modules/patrons/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# -*- coding: utf-8 -*-
#
# RERO ILS
# Copyright (C) 2019 RERO
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""Patron permissions."""

from flask import request

from ...permissions import staffer_is_authenticated


def can_update_patron_factory(record, *args, **kwargs):
"""Checks if the logged user can update its organisations patrons.
user must have librarian or system_librarian role
returns False if a librarian tries to update a system_librarian
returns False if a librarian tries to add the system_librarian role.
"""
def can(self):
incoming_record = request.get_json(silent=True) or {}
patron = staffer_is_authenticated()
if patron and patron.organisation_pid == record.organisation_pid:
if not patron.is_system_librarian:
if (
'system_librarian' in incoming_record.get(
'roles', []) or
'system_librarian' in record.get('roles', [])
):
return False
if patron.library_pid and \
record.library_pid and \
record.library_pid != patron.library_pid:
return False
return True
return False
return type('Check', (), {'can': can})()


def can_delete_patron_factory(record, *args, **kwargs):
"""Checks if the logged user can delete records of its organisation.
user must have librarian or system_librarian role
returns False if a librarian tries to delete a system_librarian and if
librarian tries to delete a librarian from another library.
"""
def can(self):
patron = staffer_is_authenticated()
if patron and patron.organisation_pid == record.organisation_pid:
if patron.is_system_librarian:
return True
if patron.is_librarian:
if 'system_librarian' in record.get('roles', []):
return False
if patron.library_pid and \
record.library_pid and \
record.library_pid != patron.library_pid:
return False
return True
return False
return type('Check', (), {'can': can})()
39 changes: 6 additions & 33 deletions rero_ils/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"""Permissions for this module."""


from flask import abort, request
from flask import abort
from flask_login import current_user
from flask_principal import RoleNeed
from invenio_access.permissions import DynamicPermission
Expand All @@ -34,7 +34,7 @@
def user_is_authenticated(user=None):
"""Checks if user is authenticated.
returns True if user is logged in and authenticated.
returns True if user is logged in and authenticated
returns False if user is not logged or not authenticated.
"""
if not user:
Expand Down Expand Up @@ -69,7 +69,7 @@ def can(self):
patron = staffer_is_authenticated()
if patron and patron.organisation_pid == record.organisation_pid:
if patron.is_librarian or patron.is_system_librarian:
return True
return True
return False
return type('Check', (), {'can': can})()

Expand All @@ -78,23 +78,11 @@ def can_delete_organisation_records_factory(record, *args, **kwargs):
"""Checks if the logged user can delete records of its organisation.
user must have librarian or system_librarian role.
returns False if a librarian tries to delete a system_librarian and if
librarian tries to delete a librarian from another library.
"""
def can(self):
patron = staffer_is_authenticated()
if patron and patron.organisation_pid == record.organisation_pid:
if patron.is_system_librarian:
return True
if patron.is_librarian:
if 'system_librarian' in record.get('roles', []):
return False
if patron.library_pid and \
isinstance(record, Patron) and \
record.library_pid and \
record.library_pid != patron.library_pid:
return False
return True
return True
return False
return type('Check', (), {'can': can})()

Expand All @@ -103,25 +91,10 @@ def can_update_organisation_records_factory(record, *args, **kwargs):
"""Checks if the logged user can update records of its organisation.
user must have librarian or system_librarian role.
returns False if a librarian tries to update a system_librarian.
returns False if a librarian tries to add the system_librarian role.
"""
def can(self):
incoming_record = request.get_json(silent=True) or {}
patron = staffer_is_authenticated()
if patron and patron.organisation_pid == record.organisation_pid:
if not patron.is_system_librarian:
if (
'system_librarian' in incoming_record.get(
'roles', []) or
'system_librarian' in record.get('roles', [])
):
return False
if patron.library_pid and \
isinstance(record, Patron) and \
record.library_pid and \
record.library_pid != patron.library_pid:
return False
return True
return False
return type('Check', (), {'can': can})()
Expand All @@ -130,7 +103,7 @@ def can(self):
def can_create_organisation_records_factory(record, *args, **kwargs):
"""Checks if the logged user can create records of its organisation.
user must have librarian or system_librarian role.
user must have librarian or system_librarian role
returns False if a librarian tries to create a system_librarian.
"""
def can(self):
Expand All @@ -139,7 +112,7 @@ def can(self):
return True
if patron and patron.organisation_pid == record.organisation_pid:
if patron.is_system_librarian:
return True
return True
if patron.is_librarian:
if 'system_librarian' in record.get('roles', []):
return False
Expand Down
6 changes: 3 additions & 3 deletions tests/api/test_libraries_rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ def test_library_secure_api_update(client, lib_fully,
data=json.dumps(data),
headers=json_header
)
assert res.status_code == 200
assert res.status_code == 403

# Sion
login_user_via_session(client, librarian_sion_no_email.user)
Expand All @@ -326,10 +326,10 @@ def test_library_secure_api_delete(client, lib_fully,
pid_value=lib_fully.pid)

res = client.delete(record_url)
assert res.status_code == 204
assert res.status_code == 403

# Sion
login_user_via_session(client, librarian_sion_no_email.user)

res = client.delete(record_url)
assert res.status_code == 410
assert res.status_code == 403
13 changes: 6 additions & 7 deletions tests/api/test_locations_rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ def test_filtered_locations_get(
loc_public_fully, loc_restricted_fully,
librarian_sion_no_email,
loc_public_sion, loc_restricted_sion
):
):
"""Test location filter by organisation."""
# Martigny
login_user_via_session(client, librarian_martigny_no_email.user)
Expand Down Expand Up @@ -264,7 +264,7 @@ def test_location_secure_api_update(client, loc_restricted_saxon,
data=json.dumps(data),
headers=json_header
)
assert res.status_code == 200
assert res.status_code == 403

# Sion
login_user_via_session(client, librarian_sion_no_email.user)
Expand All @@ -279,19 +279,18 @@ def test_location_secure_api_update(client, loc_restricted_saxon,

def test_location_secure_api_delete(client, loc_restricted_saxon,
librarian_martigny_no_email,
librarian_sion_no_email,
loc_restricted_saxon_data,
json_header):
librarian_sion_no_email):
"""Test location secure api delete."""
login_user_via_session(client, librarian_martigny_no_email.user)
record_url = url_for('invenio_records_rest.loc_item',
pid_value=loc_restricted_saxon.pid)

# Martigny
res = client.delete(record_url)
assert res.status_code == 204
assert res.status_code == 403

# Sion
login_user_via_session(client, librarian_sion_no_email.user)

res = client.delete(record_url)
assert res.status_code == 410
assert res.status_code == 403
Loading

0 comments on commit e804cd3

Please sign in to comment.