264 lines
9.0 KiB
Python
264 lines
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"}
|
|
)
|