funkwhale/api/tests/users/oauth/test_permissions.py

264 wiersze
9.0 KiB
Python

import pytest
from funkwhale_api.users.oauth import scopes
from funkwhale_api.users.oauth import permissions
@pytest.mark.parametrize(
"required_scope, request_scopes, expected",
[
(None, {}, True),
("write:profile", {"write"}, True),
("write:profile", {"read"}, False),
("write:profile", {"read:profile"}, False),
("write:profile", {"write:profile"}, True),
("read:profile", {"read"}, True),
("read:profile", {"write"}, False),
("read:profile", {"read:profile"}, True),
("read:profile", {"write:profile"}, False),
("write:profile", {"write"}, True),
("write:profile", {"read:profile"}, False),
("write:profile", {"write:profile"}, True),
("write:profile", {"write"}, True),
("write:profile", {"read:profile"}, False),
("write:profile", {"write:profile"}, True),
("write:profile", {"write"}, True),
("write:profile", {"read:profile"}, False),
("write:profile", {"write:profile"}, True),
],
)
def test_should_allow(required_scope, request_scopes, expected):
assert (
permissions.should_allow(
required_scope=required_scope, request_scopes=request_scopes
)
is expected
)
@pytest.mark.parametrize("method", ["OPTIONS", "HEAD"])
def test_scope_permission_safe_methods(method, mocker, factories):
view = mocker.Mock(required_scope="write:profile", anonymous_policy=False)
request = mocker.Mock(method=method)
p = permissions.ScopePermission()
assert p.has_permission(request, view) is True
@pytest.mark.parametrize(
"policy, preference, expected",
[
(True, False, True),
(False, False, False),
("setting", True, False),
("setting", False, True),
],
)
def test_scope_permission_anonymous_policy(
policy, preference, expected, preferences, mocker, anonymous_user
):
preferences["common__api_authentication_required"] = preference
view = mocker.Mock(
required_scope="libraries", anonymous_policy=policy, anonymous_scopes=set()
)
request = mocker.Mock(method="GET", user=anonymous_user, actor=None, scopes=None)
p = permissions.ScopePermission()
assert p.has_permission(request, view) is expected
def test_scope_permission_dict_no_required(mocker, anonymous_user):
view = mocker.Mock(
required_scope={"read": None, "write": "write:profile"},
anonymous_policy=True,
action="read",
anonymous_scopes=set(),
)
request = mocker.Mock(method="GET", user=anonymous_user, actor=None, scopes=None)
p = permissions.ScopePermission()
assert p.has_permission(request, view) is True
@pytest.mark.parametrize(
"required_scope, method, action, expected_scope",
[
("profile", "GET", "read", "read:profile"),
("profile", "POST", "write", "write:profile"),
({"read": "read:profile"}, "GET", "read", "read:profile"),
({"write": "write:profile"}, "POST", "write", "write:profile"),
],
)
def test_scope_permission_user(
required_scope, method, action, expected_scope, mocker, factories
):
user = factories["users.User"]()
should_allow = mocker.patch.object(permissions, "should_allow")
request = mocker.Mock(method=method, user=user, actor=None, scopes=None)
view = mocker.Mock(
required_scope=required_scope, anonymous_policy=False, action=action
)
p = permissions.ScopePermission()
assert p.has_permission(request, view) == should_allow.return_value
should_allow.assert_called_once_with(
required_scope=expected_scope,
request_scopes=scopes.get_from_permissions(**user.get_permissions()),
)
def test_scope_permission_token(mocker, factories):
token = factories["users.AccessToken"](
scope="write:profile read:playlists",
application__scope="write:profile read:playlists",
)
should_allow = mocker.patch.object(permissions, "should_allow")
request = mocker.Mock(method="POST", auth=token)
view = mocker.Mock(required_scope="profile", anonymous_policy=False)
p = permissions.ScopePermission()
assert p.has_permission(request, view) == should_allow.return_value
should_allow.assert_called_once_with(
required_scope="write:profile",
request_scopes={"write:profile", "read:playlists"},
)
def test_scope_permission_request_scopes(mocker, factories):
should_allow = mocker.patch.object(permissions, "should_allow")
request = mocker.Mock(method="POST", scopes=["write:profile", "read:playlists"])
view = mocker.Mock(required_scope="profile", anonymous_policy=False)
p = permissions.ScopePermission()
assert p.has_permission(request, view) == should_allow.return_value
should_allow.assert_called_once_with(
required_scope="write:profile",
request_scopes={"write:profile", "read:playlists"},
)
def test_scope_permission_actor(mocker, factories, anonymous_user):
should_allow = mocker.patch.object(permissions, "should_allow")
request = mocker.Mock(
method="POST",
actor=factories["federation.Actor"](),
user=anonymous_user,
scopes=None,
)
view = mocker.Mock(required_scope="profile", anonymous_policy=False)
p = permissions.ScopePermission()
assert p.has_permission(request, view) == should_allow.return_value
should_allow.assert_called_once_with(
required_scope="write:profile", request_scopes=scopes.FEDERATION_REQUEST_SCOPES
)
def test_scope_permission_token_anonymous_user_auth_required(
mocker, factories, anonymous_user, preferences
):
preferences["common__api_authentication_required"] = True
should_allow = mocker.patch.object(permissions, "should_allow")
request = mocker.Mock(method="POST", user=anonymous_user, actor=None, scopes=None)
view = mocker.Mock(required_scope="profile", anonymous_policy=False)
p = permissions.ScopePermission()
assert p.has_permission(request, view) is False
should_allow.assert_not_called()
def test_scope_permission_token_anonymous_user_auth_not_required(
mocker, factories, anonymous_user, preferences
):
preferences["common__api_authentication_required"] = False
should_allow = mocker.patch.object(permissions, "should_allow")
request = mocker.Mock(method="POST", user=anonymous_user, actor=None, scopes=None)
view = mocker.Mock(
required_scope="profile", anonymous_policy="setting", anonymous_scopes=set()
)
p = permissions.ScopePermission()
assert p.has_permission(request, view) == should_allow.return_value
should_allow.assert_called_once_with(
required_scope="write:profile", request_scopes=scopes.ANONYMOUS_SCOPES
)
def test_scope_permission_token_expired(mocker, factories, now):
token = factories["users.AccessToken"](
scope="profile:write playlists:read", expires=now
)
should_allow = mocker.patch.object(permissions, "should_allow")
request = mocker.Mock(method="POST", auth=token)
view = mocker.Mock(required_scope="profile", anonymous_policy=False)
p = permissions.ScopePermission()
assert p.has_permission(request, view) is False
should_allow.assert_not_called()
def test_scope_permission_token_no_user(mocker, factories, now):
token = factories["users.AccessToken"](
scope="profile:write playlists:read", user=None
)
should_allow = mocker.patch.object(permissions, "should_allow")
request = mocker.Mock(method="POST", auth=token)
view = mocker.Mock(required_scope="profile", anonymous_policy=False)
p = permissions.ScopePermission()
assert p.has_permission(request, view) is False
should_allow.assert_not_called()
def test_scope_permission_token_honor_app_scopes(mocker, factories, now):
# token contains read access, but app scope only allows profile:write
token = factories["users.AccessToken"](
scope="write:profile read", application__scope="write:profile"
)
should_allow = mocker.patch.object(permissions, "should_allow")
request = mocker.Mock(method="POST", auth=token)
view = mocker.Mock(required_scope="profile", anonymous_policy=False)
p = permissions.ScopePermission()
assert p.has_permission(request, view) == should_allow.return_value
should_allow.assert_called_once_with(
required_scope="write:profile", request_scopes={"write:profile"}
)
def test_scope_permission_token_honor_allowed_app_scopes(mocker, factories, now):
mocker.patch.object(scopes, "OAUTH_APP_SCOPES", {"read:profile"})
token = factories["users.AccessToken"](
scope="write:profile read:profile read",
application__scope="write:profile read:profile read",
)
should_allow = mocker.patch.object(permissions, "should_allow")
request = mocker.Mock(method="POST", auth=token)
view = mocker.Mock(required_scope="profile", anonymous_policy=False)
p = permissions.ScopePermission()
assert p.has_permission(request, view) == should_allow.return_value
should_allow.assert_called_once_with(
required_scope="write:profile", request_scopes={"read:profile"}
)