Skip to content

Commit

Permalink
authorization: create role management API
Browse files Browse the repository at this point in the history
Creates an API to expose which roles could be managed by the current
logged user.
This commit also introduces a restriction to disallow the current user
to delete itself.

Co-Authored-by: Renaud Michotte <[email protected]>
  • Loading branch information
zannkukai committed Jun 16, 2020
1 parent 2974aeb commit 6f6c533
Show file tree
Hide file tree
Showing 10 changed files with 128 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,9 @@
}
]
}
},
"form": {
"fieldMap": "roles"
}
},
"communication_channel": {
Expand Down
18 changes: 18 additions & 0 deletions rero_ils/modules/patrons/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
10 changes: 10 additions & 0 deletions rero_ils/modules/patrons/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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."""
Expand Down
11 changes: 8 additions & 3 deletions tests/api/test_patrons_rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)

Expand Down
8 changes: 8 additions & 0 deletions tests/api/test_permissions_librarian.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 5 additions & 0 deletions tests/api/test_permissions_patron.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 7 additions & 0 deletions tests/api/test_permissions_sys_librarian.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 4 additions & 2 deletions tests/api/test_record_permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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']

Expand Down
20 changes: 20 additions & 0 deletions tests/data/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "[email protected]",
"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",
Expand Down
45 changes: 45 additions & 0 deletions tests/fixtures/circulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down

0 comments on commit 6f6c533

Please sign in to comment.