Skip to content

Commit 6d9d9d2

Browse files
committed
moderation: add self-action prevention
* Implement prevent_self_action decorator * Prevent self-block, deactivate, impersonate * Update tests for self-action prevention * closes <inveniosoftware/invenio-administration#203>
1 parent 4c31af2 commit 6d9d9d2

File tree

3 files changed

+55
-0
lines changed

3 files changed

+55
-0
lines changed

invenio_users_resources/decorators.py

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
# Copyright (C) 2024 CERN.
4+
# Copyright (C) 2024 KTH Royal Institute of Technology
5+
#
6+
# Invenio-Users-Resources is free software; you can redistribute it and/or
7+
# modify it under the terms of the MIT License; see LICENSE file for more
8+
# details.
9+
10+
"""Users decorators."""
11+
from functools import wraps
12+
13+
from flask import g
14+
from flask_resources import resource_requestctx
15+
from invenio_records_resources.resources.errors import PermissionDeniedError
16+
17+
18+
def prevent_self_action():
19+
"""Decorator that prevents users from taking actions on their own account."""
20+
21+
def decorator(func):
22+
@wraps(func)
23+
def wrapper(*args, **kwargs):
24+
user_id = str(resource_requestctx.view_args["id"])
25+
current_user_id = str(g.identity.id)
26+
27+
if user_id == current_user_id:
28+
raise PermissionDeniedError()
29+
30+
return func(*args, **kwargs)
31+
32+
return wrapper
33+
34+
return decorator

invenio_users_resources/resources/users/resource.py

+6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# Copyright (C) 2022 TU Wien.
44
# Copyright (C) 2022 CERN.
55
# Copyright (C) 2022 European Union.
6+
# Copyright (C) 2024 KTH Royal Institute of Technology
67
#
78
# Invenio-Users-Resources is free software; you can redistribute it and/or
89
# modify it under the terms of the MIT License; see LICENSE file for more
@@ -20,6 +21,8 @@
2021
)
2122
from invenio_records_resources.resources.records.utils import search_preference
2223

24+
from invenio_users_resources.decorators import prevent_self_action
25+
2326

2427
#
2528
# Resource
@@ -108,6 +111,7 @@ def approve(self):
108111
return "", 200
109112

110113
@request_view_args
114+
@prevent_self_action()
111115
def block(self):
112116
"""Block user."""
113117
self.service.block(
@@ -126,6 +130,7 @@ def restore(self):
126130
return "", 200
127131

128132
@request_view_args
133+
@prevent_self_action()
129134
def deactivate(self):
130135
"""Deactive user."""
131136
self.service.deactivate(
@@ -144,6 +149,7 @@ def activate(self):
144149
return "", 200
145150

146151
@request_view_args
152+
@prevent_self_action()
147153
def impersonate(self):
148154
"""Impersonate the user."""
149155
user = self.service.can_impersonate(

tests/resources/test_resources_users.py

+15
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#
33
# Copyright (C) 2022 European Union.
44
# Copyright (C) 2022 CERN.
5+
# Copyright (C) 2024 KTH Royal Institute of Technology
56
#
67
# Invenio-Users-Resources is free software; you can redistribute it and/or
78
# modify it under the terms of the MIT License; see LICENSE file for more
@@ -137,6 +138,13 @@ def test_block_user(client, headers, user_pub, user_moderator, db):
137138
assert res.status_code == 200
138139
assert res.json["blocked_at"] is not None
139140

141+
# Test user tries to block themselves
142+
res = client.post(f"/users/{user_moderator.id}/block", headers=headers)
143+
assert res.status_code == 403
144+
145+
res = client.get(f"/users/{user_moderator.id}")
146+
assert res.status_code == 200
147+
140148

141149
def test_deactivate_user(client, headers, user_pub, user_moderator, db):
142150
"""Tests deactivate user endpoint."""
@@ -148,6 +156,13 @@ def test_deactivate_user(client, headers, user_pub, user_moderator, db):
148156
assert res.status_code == 200
149157
assert res.json["active"] == False
150158

159+
# Test user tries to deactivate themselves
160+
res = client.post(f"/users/{user_moderator.id}/deactivate", headers=headers)
161+
assert res.status_code == 403
162+
163+
res = client.get(f"/users/{user_moderator.id}")
164+
assert res.status_code == 200
165+
151166

152167
def test_management_permissions(client, headers, user_pub, db):
153168
"""Test permissions at the resource level."""

0 commit comments

Comments
 (0)