diff --git a/api/funkwhale_api/users/models.py b/api/funkwhale_api/users/models.py index 9e2a185c8..a739c1e38 100644 --- a/api/funkwhale_api/users/models.py +++ b/api/funkwhale_api/users/models.py @@ -72,9 +72,12 @@ class User(AbstractUser): perms[p] = v return perms - def has_permissions(self, *perms): + def has_permissions(self, *perms, operator='and'): + if operator not in ['and', 'or']: + raise ValueError('Invalid operator {}'.format(operator)) permissions = self.get_permissions() - return all([permissions[p] for p in perms]) + checker = all if operator == 'and' else any + return checker([permissions[p] for p in perms]) def get_absolute_url(self): return reverse('users:detail', kwargs={'username': self.username}) diff --git a/api/funkwhale_api/users/permissions.py b/api/funkwhale_api/users/permissions.py index 2ff49ff3f..146bc5e1c 100644 --- a/api/funkwhale_api/users/permissions.py +++ b/api/funkwhale_api/users/permissions.py @@ -16,4 +16,6 @@ class HasUserPermission(BasePermission): return False if request.user.is_anonymous: return False - return request.user.has_permissions(*view.required_permissions) + operator = getattr(view, 'permission_operator', 'and') + return request.user.has_permissions( + *view.required_permissions, operator=operator) diff --git a/api/tests/conftest.py b/api/tests/conftest.py index d6b1c0204..7caff2009 100644 --- a/api/tests/conftest.py +++ b/api/tests/conftest.py @@ -231,8 +231,9 @@ def authenticated_actor(factories, mocker): @pytest.fixture def assert_user_permission(): - def inner(view, permissions): + def inner(view, permissions, operator='and'): assert HasUserPermission in view.permission_classes + assert getattr(view, 'permission_operator', 'and') == operator assert set(view.required_permissions) == set(permissions) return inner diff --git a/api/tests/users/test_models.py b/api/tests/users/test_models.py index 49199e0a7..445744802 100644 --- a/api/tests/users/test_models.py +++ b/api/tests/users/test_models.py @@ -47,6 +47,17 @@ def test_get_permissions_regular(factories): ({'permission_library': True}, ['library'], True), ({'permission_library': True}, ['library', 'federation'], False), ]) -def test_has_permissions(args, perms, expected, factories): +def test_has_permissions_and(args, perms, expected, factories): user = factories['users.User'](**args) - assert user.has_permissions(*perms) is expected + assert user.has_permissions(*perms, operator='and') is expected + + +@pytest.mark.parametrize('args,perms,expected', [ + ({'is_superuser': True}, ['federation', 'library'], True), + ({'is_superuser': False}, ['federation'], False), + ({'permission_library': True}, ['library', 'federation'], True), + ({'permission_library': True}, ['federation'], False), +]) +def test_has_permissions_or(args, perms, expected, factories): + user = factories['users.User'](**args) + assert user.has_permissions(*perms, operator='or') is expected diff --git a/api/tests/users/test_permissions.py b/api/tests/users/test_permissions.py index 1564c761d..518ccd1c8 100644 --- a/api/tests/users/test_permissions.py +++ b/api/tests/users/test_permissions.py @@ -39,7 +39,7 @@ def test_has_user_permission_logged_in_single(value, factories, api_request): (False, False, False), (True, True, True), ]) -def test_has_user_permission_logged_in_single( +def test_has_user_permission_logged_in_multiple_and( federation, library, expected, factories, api_request): user = factories['users.User']( permission_federation=federation, @@ -48,9 +48,35 @@ def test_has_user_permission_logged_in_single( class View(APIView): required_permissions = ['federation', 'library'] + permission_operator = 'and' view = View() permission = permissions.HasUserPermission() request = api_request.get('/') setattr(request, 'user', user) result = permission.has_permission(request, view) assert result == user.has_permissions('federation', 'library') == expected + + +@pytest.mark.parametrize('federation,library,expected', [ + (True, False, True), + (False, True, True), + (False, False, False), + (True, True, True), +]) +def test_has_user_permission_logged_in_multiple_or( + federation, library, expected, factories, api_request): + user = factories['users.User']( + permission_federation=federation, + permission_library=library, + ) + + class View(APIView): + required_permissions = ['federation', 'library'] + permission_operator = 'or' + view = View() + permission = permissions.HasUserPermission() + request = api_request.get('/') + setattr(request, 'user', user) + result = permission.has_permission(request, view) + assert result == user.has_permissions( + 'federation', 'library', operator='or') == expected