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.
* Creates complete units tests for library and location permissions.
* Removes duplicates imports.
* Closes #488

Co-Authored-by: Aly Badr <aly.badr@rero.ch>
  • Loading branch information
Aly Badr committed Oct 23, 2019
1 parent 2493ca6 commit bad0367
Show file tree
Hide file tree
Showing 8 changed files with 540 additions and 85 deletions.
24 changes: 14 additions & 10 deletions rero_ils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,24 +43,28 @@
from invenio_search import RecordsSearch

from rero_ils.modules.api import IlsRecordIndexer
from rero_ils.modules.loans.api import Loan
from rero_ils.modules.organisations.api import Organisation

from .modules.circ_policies.api import CircPolicy
from .modules.documents.api import Document
from .modules.holdings.api import Holding, HoldingsIndexer
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_create_library_factory, \
can_delete_library_factory, can_update_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_create_location_factory, \
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 @@ -587,9 +591,9 @@ def _(x):
search_factory_imp='rero_ils.query:organisation_search_factory',
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,
create_permission_factory_imp=can_create_library_factory,
update_permission_factory_imp=can_update_library_factory,
delete_permission_factory_imp=can_delete_library_factory,
),
loc=dict(
pid_type='loc',
Expand Down Expand Up @@ -619,9 +623,9 @@ def _(x):
max_result_window=10000,
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,
create_permission_factory_imp=can_create_location_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
74 changes: 74 additions & 0 deletions rero_ils/modules/libraries/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# -*- 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_library_factory(record, *args, **kwargs):
"""Checks if logged user can update its organisation libraries.
librarian must have librarian or system_librarian role.
librarian can only update its affiliated library.
sys_librarian can update any library of its organisation only.
"""
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})()


def can_delete_library_factory(record, *args, **kwargs):
"""Checks if logged user can delete its organisation libraries.
librarian must have system_librarian role.
librarian can not delete any library.
sys_librarian can delete any library of its organisation only.
"""
def can(self):
patron = staffer_is_authenticated()
if patron and patron.organisation_pid == record.organisation_pid:
if patron.is_system_librarian:
return True
return False
return type('Check', (), {'can': can})()


def can_create_library_factory(record, *args, **kwargs):
"""Checks if the logged user can create libraries of its organisation.
user must have a system_librarian role.
returns False if a librarian tries to create a library.
returns False if a system_librarian tries to create a library in other org.
"""
def can(self):
patron = staffer_is_authenticated()
if patron and not record:
return True
if patron and patron.organisation_pid == record.organisation_pid:
if patron.is_system_librarian:
return True
return False
return type('Check', (), {'can': can})()
61 changes: 61 additions & 0 deletions rero_ils/modules/locations/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# -*- 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
librarian can only update or delete its affiliated library locations.
sys_librarian can update or delete any location of its organisation.
"""
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})()


def can_create_location_factory(record, *args, **kwargs):
"""Checks if the logged user can create locations of its organisation.
librarian can create locations for its library only.
system_librarian can create locations at any library of its org.
system_librarian or librarian can create locations at another org.
"""
def can(self):
patron = staffer_is_authenticated()
if patron and not record:
return True
if patron and patron.organisation_pid == record.organisation_pid:
if patron.is_system_librarian:
return True
if patron.is_librarian and \
record.library_pid == patron.library_pid:
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})()
35 changes: 4 additions & 31 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 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 @@ -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
Loading

0 comments on commit bad0367

Please sign in to comment.