diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/plugs/oauth_scopes_plug_test.exs | 237 | ||||
-rw-r--r-- | test/support/factory.ex | 1 | ||||
-rw-r--r-- | test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs | 5 | ||||
-rw-r--r-- | test/web/oauth/oauth_controller_test.exs | 6 | ||||
-rw-r--r-- | test/web/twitter_api/util_controller_test.exs | 10 |
5 files changed, 184 insertions, 75 deletions
diff --git a/test/plugs/oauth_scopes_plug_test.exs b/test/plugs/oauth_scopes_plug_test.exs index 6a13ea811..be6d1340b 100644 --- a/test/plugs/oauth_scopes_plug_test.exs +++ b/test/plugs/oauth_scopes_plug_test.exs @@ -5,24 +5,48 @@ defmodule Pleroma.Plugs.OAuthScopesPlugTest do use Pleroma.Web.ConnCase, async: true + alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.Repo + import Mock import Pleroma.Factory - test "proceeds with no op if `assigns[:token]` is nil", %{conn: conn} do - conn = - conn - |> assign(:user, insert(:user)) - |> OAuthScopesPlug.call(%{scopes: ["read"]}) + setup_with_mocks([{EnsurePublicOrAuthenticatedPlug, [], [call: fn conn, _ -> conn end]}]) do + :ok + end - refute conn.halted - assert conn.assigns[:user] + describe "when `assigns[:token]` is nil, " do + test "with :skip_instance_privacy_check option, proceeds with no op", %{conn: conn} do + conn = + conn + |> assign(:user, insert(:user)) + |> OAuthScopesPlug.call(%{scopes: ["read"], skip_instance_privacy_check: true}) + + refute conn.halted + assert conn.assigns[:user] + + refute called(EnsurePublicOrAuthenticatedPlug.call(conn, :_)) + end + + test "without :skip_instance_privacy_check option, calls EnsurePublicOrAuthenticatedPlug", %{ + conn: conn + } do + conn = + conn + |> assign(:user, insert(:user)) + |> OAuthScopesPlug.call(%{scopes: ["read"]}) + + refute conn.halted + assert conn.assigns[:user] + + assert called(EnsurePublicOrAuthenticatedPlug.call(conn, :_)) + end end - test "proceeds with no op if `token.scopes` fulfill specified 'any of' conditions", %{ - conn: conn - } do + test "if `token.scopes` fulfills specified 'any of' conditions, " <> + "proceeds with no op", + %{conn: conn} do token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user) conn = @@ -35,9 +59,9 @@ defmodule Pleroma.Plugs.OAuthScopesPlugTest do assert conn.assigns[:user] end - test "proceeds with no op if `token.scopes` fulfill specified 'all of' conditions", %{ - conn: conn - } do + test "if `token.scopes` fulfills specified 'all of' conditions, " <> + "proceeds with no op", + %{conn: conn} do token = insert(:oauth_token, scopes: ["scope1", "scope2", "scope3"]) |> Repo.preload(:user) conn = @@ -50,73 +74,154 @@ defmodule Pleroma.Plugs.OAuthScopesPlugTest do assert conn.assigns[:user] end - test "proceeds with cleared `assigns[:user]` if `token.scopes` doesn't fulfill specified 'any of' conditions " <> - "and `fallback: :proceed_unauthenticated` option is specified", - %{conn: conn} do - token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user) + describe "with `fallback: :proceed_unauthenticated` option, " do + test "if `token.scopes` doesn't fulfill specified 'any of' conditions, " <> + "clears `assigns[:user]` and calls EnsurePublicOrAuthenticatedPlug", + %{conn: conn} do + token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user) + + conn = + conn + |> assign(:user, token.user) + |> assign(:token, token) + |> OAuthScopesPlug.call(%{scopes: ["follow"], fallback: :proceed_unauthenticated}) + + refute conn.halted + refute conn.assigns[:user] + + assert called(EnsurePublicOrAuthenticatedPlug.call(conn, :_)) + end + + test "if `token.scopes` doesn't fulfill specified 'all of' conditions, " <> + "clears `assigns[:user] and calls EnsurePublicOrAuthenticatedPlug", + %{conn: conn} do + token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user) + + conn = + conn + |> assign(:user, token.user) + |> assign(:token, token) + |> OAuthScopesPlug.call(%{ + scopes: ["read", "follow"], + op: :&, + fallback: :proceed_unauthenticated + }) + + refute conn.halted + refute conn.assigns[:user] + + assert called(EnsurePublicOrAuthenticatedPlug.call(conn, :_)) + end + + test "with :skip_instance_privacy_check option, " <> + "if `token.scopes` doesn't fulfill specified conditions, " <> + "clears `assigns[:user]` and does not call EnsurePublicOrAuthenticatedPlug", + %{conn: conn} do + token = insert(:oauth_token, scopes: ["read:statuses", "write"]) |> Repo.preload(:user) + + conn = + conn + |> assign(:user, token.user) + |> assign(:token, token) + |> OAuthScopesPlug.call(%{ + scopes: ["read"], + fallback: :proceed_unauthenticated, + skip_instance_privacy_check: true + }) + + refute conn.halted + refute conn.assigns[:user] + + refute called(EnsurePublicOrAuthenticatedPlug.call(conn, :_)) + end + end - conn = - conn - |> assign(:user, token.user) - |> assign(:token, token) - |> OAuthScopesPlug.call(%{scopes: ["follow"], fallback: :proceed_unauthenticated}) + describe "without :fallback option, " do + test "if `token.scopes` does not fulfill specified 'any of' conditions, " <> + "returns 403 and halts", + %{conn: conn} do + token = insert(:oauth_token, scopes: ["read", "write"]) + any_of_scopes = ["follow"] - refute conn.halted - refute conn.assigns[:user] - end + conn = + conn + |> assign(:token, token) + |> OAuthScopesPlug.call(%{scopes: any_of_scopes}) - test "proceeds with cleared `assigns[:user]` if `token.scopes` doesn't fulfill specified 'all of' conditions " <> - "and `fallback: :proceed_unauthenticated` option is specified", - %{conn: conn} do - token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user) + assert conn.halted + assert 403 == conn.status - conn = - conn - |> assign(:user, token.user) - |> assign(:token, token) - |> OAuthScopesPlug.call(%{ - scopes: ["read", "follow"], - op: :&, - fallback: :proceed_unauthenticated - }) + expected_error = "Insufficient permissions: #{Enum.join(any_of_scopes, ", ")}." + assert Jason.encode!(%{error: expected_error}) == conn.resp_body + end - refute conn.halted - refute conn.assigns[:user] - end + test "if `token.scopes` does not fulfill specified 'all of' conditions, " <> + "returns 403 and halts", + %{conn: conn} do + token = insert(:oauth_token, scopes: ["read", "write"]) + all_of_scopes = ["write", "follow"] - test "returns 403 and halts in case of no :fallback option and `token.scopes` not fulfilling specified 'any of' conditions", - %{conn: conn} do - token = insert(:oauth_token, scopes: ["read", "write"]) - any_of_scopes = ["follow"] + conn = + conn + |> assign(:token, token) + |> OAuthScopesPlug.call(%{scopes: all_of_scopes, op: :&}) - conn = - conn - |> assign(:token, token) - |> OAuthScopesPlug.call(%{scopes: any_of_scopes}) + assert conn.halted + assert 403 == conn.status - assert conn.halted - assert 403 == conn.status + expected_error = + "Insufficient permissions: #{Enum.join(all_of_scopes -- token.scopes, ", ")}." - expected_error = "Insufficient permissions: #{Enum.join(any_of_scopes, ", ")}." - assert Jason.encode!(%{error: expected_error}) == conn.resp_body + assert Jason.encode!(%{error: expected_error}) == conn.resp_body + end end - test "returns 403 and halts in case of no :fallback option and `token.scopes` not fulfilling specified 'all of' conditions", - %{conn: conn} do - token = insert(:oauth_token, scopes: ["read", "write"]) - all_of_scopes = ["write", "follow"] + describe "with hierarchical scopes, " do + test "if `token.scopes` fulfills specified 'any of' conditions, " <> + "proceeds with no op", + %{conn: conn} do + token = insert(:oauth_token, scopes: ["read", "write"]) |> Repo.preload(:user) + + conn = + conn + |> assign(:user, token.user) + |> assign(:token, token) + |> OAuthScopesPlug.call(%{scopes: ["read:something"]}) + + refute conn.halted + assert conn.assigns[:user] + end + + test "if `token.scopes` fulfills specified 'all of' conditions, " <> + "proceeds with no op", + %{conn: conn} do + token = insert(:oauth_token, scopes: ["scope1", "scope2", "scope3"]) |> Repo.preload(:user) + + conn = + conn + |> assign(:user, token.user) + |> assign(:token, token) + |> OAuthScopesPlug.call(%{scopes: ["scope1:subscope", "scope2:subscope"], op: :&}) + + refute conn.halted + assert conn.assigns[:user] + end + end - conn = - conn - |> assign(:token, token) - |> OAuthScopesPlug.call(%{scopes: all_of_scopes, op: :&}) + describe "filter_descendants/2" do + test "filters scopes which directly match or are ancestors of supported scopes" do + f = fn scopes, supported_scopes -> + OAuthScopesPlug.filter_descendants(scopes, supported_scopes) + end + + assert f.(["read", "follow"], ["write", "read"]) == ["read"] - assert conn.halted - assert 403 == conn.status + assert f.(["read", "write:something", "follow"], ["write", "read"]) == + ["read", "write:something"] - expected_error = - "Insufficient permissions: #{Enum.join(all_of_scopes -- token.scopes, ", ")}." + assert f.(["admin:read"], ["write", "read"]) == [] - assert Jason.encode!(%{error: expected_error}) == conn.resp_body + assert f.(["admin:read"], ["write", "admin"]) == ["admin:read"] + end end end diff --git a/test/support/factory.ex b/test/support/factory.ex index 4f3244025..b180844cd 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -324,6 +324,7 @@ defmodule Pleroma.Factory do %Pleroma.Web.OAuth.Token{ token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(), + scopes: ["read"], refresh_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64(), user: build(:user), app_id: oauth_app.id, diff --git a/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs b/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs index 599cd61c8..618031b40 100644 --- a/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs +++ b/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs @@ -272,7 +272,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do assert user_response["pleroma"]["background_image"] end - test "requires 'write' permission", %{conn: conn} do + test "requires 'write:accounts' permission", %{conn: conn} do token1 = insert(:oauth_token, scopes: ["read"]) token2 = insert(:oauth_token, scopes: ["write", "follow"]) @@ -283,7 +283,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do |> patch("/api/v1/accounts/update_credentials", %{}) if token == token1 do - assert %{"error" => "Insufficient permissions: write."} == json_response(conn, 403) + assert %{"error" => "Insufficient permissions: write:accounts."} == + json_response(conn, 403) else assert json_response(conn, 200) end diff --git a/test/web/oauth/oauth_controller_test.exs b/test/web/oauth/oauth_controller_test.exs index 4d0741d14..41aaf6189 100644 --- a/test/web/oauth/oauth_controller_test.exs +++ b/test/web/oauth/oauth_controller_test.exs @@ -557,7 +557,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do "password" => "test", "client_id" => app.client_id, "redirect_uri" => redirect_uri, - "scope" => "read write", + "scope" => "read:subscope write", "state" => "statepassed" } }) @@ -570,7 +570,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do assert %{"state" => "statepassed", "code" => code} = query auth = Repo.get_by(Authorization, token: code) assert auth - assert auth.scopes == ["read", "write"] + assert auth.scopes == ["read:subscope", "write"] end test "returns 401 for wrong credentials", %{conn: conn} do @@ -627,7 +627,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do assert result =~ "This action is outside the authorized scopes" end - test "returns 401 for scopes beyond app scopes", %{conn: conn} do + test "returns 401 for scopes beyond app scopes hierarchy", %{conn: conn} do user = insert(:user) app = insert(:oauth_app, scopes: ["read", "write"]) redirect_uri = OAuthController.default_redirect_uri(app) diff --git a/test/web/twitter_api/util_controller_test.exs b/test/web/twitter_api/util_controller_test.exs index 56e318182..9d4cb70f0 100644 --- a/test/web/twitter_api/util_controller_test.exs +++ b/test/web/twitter_api/util_controller_test.exs @@ -81,19 +81,21 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do assert response == "job started" end - test "requires 'follow' permission", %{conn: conn} do + test "requires 'follow' or 'write:follows' permissions", %{conn: conn} do token1 = insert(:oauth_token, scopes: ["read", "write"]) token2 = insert(:oauth_token, scopes: ["follow"]) + token3 = insert(:oauth_token, scopes: ["something"]) another_user = insert(:user) - for token <- [token1, token2] do + for token <- [token1, token2, token3] do conn = conn |> put_req_header("authorization", "Bearer #{token.token}") |> post("/api/pleroma/follow_import", %{"list" => "#{another_user.ap_id}"}) - if token == token1 do - assert %{"error" => "Insufficient permissions: follow."} == json_response(conn, 403) + if token == token3 do + assert %{"error" => "Insufficient permissions: follow | write:follows."} == + json_response(conn, 403) else assert json_response(conn, 200) end |