diff --git a/rero_ils/modules/patrons/jsonschemas/patrons/patron-v0.0.1.json b/rero_ils/modules/patrons/jsonschemas/patrons/patron-v0.0.1.json index 3c59e70373..13a91263b1 100644 --- a/rero_ils/modules/patrons/jsonschemas/patrons/patron-v0.0.1.json +++ b/rero_ils/modules/patrons/jsonschemas/patrons/patron-v0.0.1.json @@ -225,6 +225,9 @@ } ] } + }, + "form": { + "fieldMap": "roles" } }, "communication_channel": { diff --git a/rero_ils/modules/patrons/permissions.py b/rero_ils/modules/patrons/permissions.py index a04c8c8a32..9e00513c47 100644 --- a/rero_ils/modules/patrons/permissions.py +++ b/rero_ils/modules/patrons/permissions.py @@ -58,6 +58,9 @@ def can_delete_patron_factory(record, *args, **kwargs): """ def can(self): patron = staffer_is_authenticated() + # It should be not possible to remove itself ! + if patron and patron.pid == record.pid: + return False if patron and patron.organisation_pid == record.organisation_pid: if patron.is_system_librarian: return True @@ -71,3 +74,18 @@ def can(self): return True return False return type('Check', (), {'can': can})() + + +def get_allowed_roles_management(): + """Get the roles that current logged user could manage. + + :return An array of allowed role management. + """ + allowed_roles = [] + patron = staffer_is_authenticated() + if patron: + if patron.is_librarian: + allowed_roles.extend(['patron', 'librarian']) + if patron.is_system_librarian: + allowed_roles.append('system_librarian') + return allowed_roles diff --git a/rero_ils/modules/patrons/views.py b/rero_ils/modules/patrons/views.py index 1de3d4cad5..8c45dabfc0 100644 --- a/rero_ils/modules/patrons/views.py +++ b/rero_ils/modules/patrons/views.py @@ -32,6 +32,7 @@ from werkzeug.exceptions import NotFound from .api import Patron, PatronsSearch +from .permissions import get_allowed_roles_management from .utils import user_has_patron from ..items.api import Item from ..libraries.api import Library @@ -217,6 +218,15 @@ def profile(viewcode): ) +@api_blueprint.route('/roles_management_permissions', methods=['GET']) +@check_permission +def get_roles_management_permissions(): + """Get the roles that current logged user could manage.""" + return jsonify({ + 'allowed_roles': get_allowed_roles_management() + }) + + @blueprint.app_template_filter('get_patron_from_barcode') def get_patron_from_barcode(value): """Get patron from barcode.""" diff --git a/tests/api/test_patrons_rest.py b/tests/api/test_patrons_rest.py index 4b8bc1b723..05c018f07c 100644 --- a/tests/api/test_patrons_rest.py +++ b/tests/api/test_patrons_rest.py @@ -356,10 +356,9 @@ def test_patron_secure_api(client, json_header, # assert res.status_code == 403 -def test_patron_secure_api_create(client, json_header, +def test_patron_secure_api_create(client, patron_martigny_data, - librarian_martigny_no_email, - librarian_sion_no_email): + librarian_martigny_no_email): """Test patron secure api create.""" # Martigny login_user_via_session(client, librarian_martigny_no_email.user) @@ -427,6 +426,12 @@ def test_patron_secure_api_delete(client, json_header, res = client.delete(record_url) assert res.status_code == 204 + # try to delete itself + record_url = url_for('invenio_records_rest.ptrn_item', + pid_value=librarian_martigny_no_email.pid) + res = client.delete(record_url) + assert res.status_code == 403 + # Sion # login_user_via_session(client, librarian_sion_no_email.user) diff --git a/tests/api/test_permissions_librarian.py b/tests/api/test_permissions_librarian.py index 4f08781663..d9fb6f5612 100644 --- a/tests/api/test_permissions_librarian.py +++ b/tests/api/test_permissions_librarian.py @@ -70,6 +70,14 @@ def test_librarian_permissions( data = get_json(res) assert data['hits']['total'] == 3 + # can manage all types of patron roles + role_url = url_for('api_patrons.get_roles_management_permissions') + res = client.get(role_url) + assert res.status_code == 200 + data = get_json(res) + assert 'librarian' in data['allowed_roles'] + assert 'system_librarian' not in data['allowed_roles'] + # can create all type of users except system_librarians post_entrypoint = 'invenio_records_rest.ptrn_list' system_librarian = deepcopy(record) diff --git a/tests/api/test_permissions_patron.py b/tests/api/test_permissions_patron.py index 2b8c55b97b..4f276a1397 100644 --- a/tests/api/test_permissions_patron.py +++ b/tests/api/test_permissions_patron.py @@ -50,6 +50,11 @@ def test_patron_permissions( res = client.get(list_url) assert res.status_code == 403 + # can not manage any types of patron roles + role_url = url_for('api_patrons.get_roles_management_permissions') + res = client.get(role_url) + assert res.status_code == 403 + # can not create any type of users. system_librarian = deepcopy(record) librarian = deepcopy(record) diff --git a/tests/api/test_permissions_sys_librarian.py b/tests/api/test_permissions_sys_librarian.py index b66bd8a478..c614d87c7f 100644 --- a/tests/api/test_permissions_sys_librarian.py +++ b/tests/api/test_permissions_sys_librarian.py @@ -52,6 +52,13 @@ def test_system_librarian_permissions( data = get_json(res) assert data['hits']['total'] == 3 + # can manage all types of patron roles + role_url = url_for('api_patrons.get_roles_management_permissions') + res = client.get(role_url) + assert res.status_code == 200 + data = get_json(res) + assert 'system_librarian' in data['allowed_roles'] + # can create all type of users. system_librarian = deepcopy(record) librarian = deepcopy(record) diff --git a/tests/api/test_record_permissions.py b/tests/api/test_record_permissions.py index a8d4d8e120..43b29a9e8b 100644 --- a/tests/api/test_record_permissions.py +++ b/tests/api/test_record_permissions.py @@ -92,10 +92,11 @@ def test_patrons_permissions( librarian2_martigny_no_email, librarian_saxon_no_email, system_librarian_martigny_no_email, + system_librarian_martigny2_no_email, system_librarian_sion_no_email, librarian_sion_no_email ): - """Test serializers for patrons.""" + """Test permissions for patrons.""" # simple librarian ----------------------------------------------- login_user(client, librarian_martigny_no_email) @@ -130,8 +131,9 @@ def test_patrons_permissions( assert data['update']['can'] # should update and delete a system librarian of the same organisation + # but not itself data = call_api_permissions(client, 'patrons', - system_librarian_martigny_no_email.pid) + system_librarian_martigny2_no_email.pid) assert data['delete']['can'] assert data['update']['can'] diff --git a/tests/data/data.json b/tests/data/data.json index eb4e903a1c..cf08469ba1 100644 --- a/tests/data/data.json +++ b/tests/data/data.json @@ -2319,6 +2319,26 @@ "blocked": true, "blocked_note": "Lost card" }, + "ptrn12": { + "$schema": "https://ils.rero.ch/schema/patrons/patron-v0.0.1.json", + "pid": "ptrn12", + "first_name": "Joella", + "last_name": "Dosimonta", + "street": "Grand place, 123", + "postal_code": "1920", + "city": "Martigny", + "birth_date": "1980-06-07", + "library": { + "$ref": "https://ils.rero.ch/api/libraries/lib1" + }, + "email": "joellalibri07@gmail.com", + "phone": "+32324993585", + "roles": [ + "system_librarian", + "librarian" + ], + "barcode": "sys_ptrn12" + }, "dummy_notif": { "$schema": "https://ils.rero.ch/schema/notifications/notification-v0.0.1.json", "pid": "notif1", diff --git a/tests/fixtures/circulation.py b/tests/fixtures/circulation.py index af94b97955..70bf29553c 100644 --- a/tests/fixtures/circulation.py +++ b/tests/fixtures/circulation.py @@ -89,6 +89,51 @@ def system_librarian_martigny_no_email( return ptrn +@pytest.fixture(scope="module") +def system_librarian_martigny2_data(data): + """Load Martigny system librarian data.""" + return deepcopy(data.get('ptrn12')) + + +@pytest.fixture(scope="function") +def system_librarian_martigny2_data_tmp(data): + """Load Martigny system librarian data scope function.""" + return deepcopy(data.get('ptrn12')) + + +@pytest.fixture(scope="module") +def system_librarian_martigny2( + app, + roles, + lib_martigny, + system_librarian_martigny2_data): + """Create Martigny system librarian record.""" + ptrn = Patron.create( + data=system_librarian_martigny2_data, + delete_pid=False, + dbcommit=True, + reindex=True) + flush_index(PatronsSearch.Meta.index) + return ptrn + + +@pytest.fixture(scope="module") +@mock.patch('rero_ils.modules.patrons.api.send_reset_password_instructions') +def system_librarian_martigny2_no_email( + app, + roles, + lib_martigny, + system_librarian_martigny2_data): + """Create Martigny system librarian without sending reset password.""" + ptrn = Patron.create( + data=system_librarian_martigny2_data, + delete_pid=False, + dbcommit=True, + reindex=True) + flush_index(PatronsSearch.Meta.index) + return ptrn + + # ------------ Org: Martigny, Lib: Martigny, Librarian 1 ---------- @pytest.fixture(scope="module") def librarian_martigny_data(data):