From 04ee877a20a849db53a307a1736e635229129b7a Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Tue, 19 Feb 2019 22:28:21 +0300 Subject: [PATCH] [#468] Added OAuth scopes-specific tests. --- .../mastodon_api_controller_test.exs | 18 ++++ test/web/oauth/authorization_test.exs | 50 ++++------ test/web/oauth/oauth_controller_test.exs | 96 ++++++++++++++++--- test/web/oauth/token_test.exs | 8 +- .../twitter_api_controller_test.exs | 18 ++++ test/web/twitter_api/util_controller_test.exs | 19 ++++ 6 files changed, 162 insertions(+), 47 deletions(-) diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index e43bc4508..8dcbde48b 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -1536,6 +1536,24 @@ test "updates the user's banner", %{conn: conn} do assert user_response = json_response(conn, 200) assert user_response["header"] != User.banner_url(user) end + + test "requires 'write' permission", %{conn: conn} do + token1 = insert(:oauth_token, scopes: ["read"]) + token2 = insert(:oauth_token, scopes: ["write", "follow"]) + + for token <- [token1, token2] do + conn = + conn + |> put_req_header("authorization", "Bearer #{token.token}") + |> patch("/api/v1/accounts/update_credentials", %{}) + + if token == token1 do + assert %{"error" => "Insufficient permissions: write."} == json_response(conn, 403) + else + assert json_response(conn, 200) + end + end + end end test "get instance information", %{conn: conn} do diff --git a/test/web/oauth/authorization_test.exs b/test/web/oauth/authorization_test.exs index b1a51e30e..306db2e62 100644 --- a/test/web/oauth/authorization_test.exs +++ b/test/web/oauth/authorization_test.exs @@ -8,36 +8,37 @@ defmodule Pleroma.Web.OAuth.AuthorizationTest do alias Pleroma.Web.OAuth.App import Pleroma.Factory - test "create an authorization token for a valid app" do + setup do {:ok, app} = Repo.insert( App.register_changeset(%App{}, %{ client_name: "client", - scopes: ["scope"], + scopes: ["read", "write"], redirect_uris: "url" }) ) - user = insert(:user) - - {:ok, auth} = Authorization.create_authorization(app, user) - - assert auth.user_id == user.id - assert auth.app_id == app.id - assert String.length(auth.token) > 10 - assert auth.used == false + %{app: app} end - test "use up a token" do - {:ok, app} = - Repo.insert( - App.register_changeset(%App{}, %{ - client_name: "client", - scopes: ["scope"], - redirect_uris: "url" - }) - ) + test "create an authorization token for a valid app", %{app: app} do + user = insert(:user) + {:ok, auth1} = Authorization.create_authorization(app, user) + assert auth1.scopes == app.scopes + + {:ok, auth2} = Authorization.create_authorization(app, user, ["read"]) + assert auth2.scopes == ["read"] + + for auth <- [auth1, auth2] do + assert auth.user_id == user.id + assert auth.app_id == app.id + assert String.length(auth.token) > 10 + assert auth.used == false + end + end + + test "use up a token", %{app: app} do user = insert(:user) {:ok, auth} = Authorization.create_authorization(app, user) @@ -61,16 +62,7 @@ test "use up a token" do assert {:error, "token expired"} == Authorization.use_token(expired_auth) end - test "delete authorizations" do - {:ok, app} = - Repo.insert( - App.register_changeset(%App{}, %{ - client_name: "client", - scopes: ["scope"], - redirect_uris: "url" - }) - ) - + test "delete authorizations", %{app: app} do user = insert(:user) {:ok, auth} = Authorization.create_authorization(app, user) diff --git a/test/web/oauth/oauth_controller_test.exs b/test/web/oauth/oauth_controller_test.exs index ca1c04319..53d83e6e8 100644 --- a/test/web/oauth/oauth_controller_test.exs +++ b/test/web/oauth/oauth_controller_test.exs @@ -12,7 +12,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do test "redirects with oauth authorization" do user = insert(:user) - app = insert(:oauth_app) + app = insert(:oauth_app, scopes: ["read", "write", "follow"]) conn = build_conn() @@ -22,7 +22,7 @@ test "redirects with oauth authorization" do "password" => "test", "client_id" => app.client_id, "redirect_uri" => app.redirect_uris, - "scope" => Enum.join(app.scopes, " "), + "scope" => "read write", "state" => "statepassed" } }) @@ -33,10 +33,12 @@ test "redirects with oauth authorization" do query = URI.parse(target).query |> URI.query_decoder() |> Map.new() assert %{"state" => "statepassed", "code" => code} = query - assert Repo.get_by(Authorization, token: code) + auth = Repo.get_by(Authorization, token: code) + assert auth + assert auth.scopes == ["read", "write"] end - test "correctly handles wrong credentials", %{conn: conn} do + test "returns 401 for wrong credentials", %{conn: conn} do user = insert(:user) app = insert(:oauth_app) @@ -48,7 +50,8 @@ test "correctly handles wrong credentials", %{conn: conn} do "password" => "wrong", "client_id" => app.client_id, "redirect_uri" => app.redirect_uris, - "state" => "statepassed" + "state" => "statepassed", + "scope" => Enum.join(app.scopes, " ") } }) |> html_response(:unauthorized) @@ -58,14 +61,66 @@ test "correctly handles wrong credentials", %{conn: conn} do assert result =~ app.redirect_uris # Error message - assert result =~ "Invalid" + assert result =~ "Invalid Username/Password" + end + + test "returns 401 for missing scopes", %{conn: conn} do + user = insert(:user) + app = insert(:oauth_app) + + result = + conn + |> post("/oauth/authorize", %{ + "authorization" => %{ + "name" => user.nickname, + "password" => "test", + "client_id" => app.client_id, + "redirect_uri" => app.redirect_uris, + "state" => "statepassed", + "scope" => "" + } + }) + |> html_response(:unauthorized) + + # Keep the details + assert result =~ app.client_id + assert result =~ app.redirect_uris + + # Error message + assert result =~ "Permissions not specified" + end + + test "returns 401 for scopes beyond app scopes", %{conn: conn} do + user = insert(:user) + app = insert(:oauth_app, scopes: ["read", "write"]) + + result = + conn + |> post("/oauth/authorize", %{ + "authorization" => %{ + "name" => user.nickname, + "password" => "test", + "client_id" => app.client_id, + "redirect_uri" => app.redirect_uris, + "state" => "statepassed", + "scope" => "read write follow" + } + }) + |> html_response(:unauthorized) + + # Keep the details + assert result =~ app.client_id + assert result =~ app.redirect_uris + + # Error message + assert result =~ "Permissions not specified" end test "issues a token for an all-body request" do user = insert(:user) - app = insert(:oauth_app) + app = insert(:oauth_app, scopes: ["read", "write"]) - {:ok, auth} = Authorization.create_authorization(app, user) + {:ok, auth} = Authorization.create_authorization(app, user, ["write"]) conn = build_conn() @@ -78,15 +133,19 @@ test "issues a token for an all-body request" do }) assert %{"access_token" => token} = json_response(conn, 200) - assert Repo.get_by(Token, token: token) + + token = Repo.get_by(Token, token: token) + assert token + assert token.scopes == auth.scopes end - test "issues a token for `password` grant_type with valid credentials" do + test "issues a token for `password` grant_type with valid credentials, with full permissions by default" do password = "testpassword" user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(password)) - app = insert(:oauth_app) + app = insert(:oauth_app, scopes: ["read", "write"]) + # Note: "scope" param is intentionally omitted conn = build_conn() |> post("/oauth/token", %{ @@ -98,14 +157,18 @@ test "issues a token for `password` grant_type with valid credentials" do }) assert %{"access_token" => token} = json_response(conn, 200) - assert Repo.get_by(Token, token: token) + + token = Repo.get_by(Token, token: token) + assert token + assert token.scopes == app.scopes end test "issues a token for request with HTTP basic auth client credentials" do user = insert(:user) - app = insert(:oauth_app) + app = insert(:oauth_app, scopes: ["scope1", "scope2"]) - {:ok, auth} = Authorization.create_authorization(app, user) + {:ok, auth} = Authorization.create_authorization(app, user, ["scope2"]) + assert auth.scopes == ["scope2"] app_encoded = (URI.encode_www_form(app.client_id) <> ":" <> URI.encode_www_form(app.client_secret)) @@ -121,7 +184,10 @@ test "issues a token for request with HTTP basic auth client credentials" do }) assert %{"access_token" => token} = json_response(conn, 200) - assert Repo.get_by(Token, token: token) + + token = Repo.get_by(Token, token: token) + assert token + assert token.scopes == ["scope2"] end test "rejects token exchange with invalid client credentials" do diff --git a/test/web/oauth/token_test.exs b/test/web/oauth/token_test.exs index a708e4991..62444a0fa 100644 --- a/test/web/oauth/token_test.exs +++ b/test/web/oauth/token_test.exs @@ -11,24 +11,26 @@ defmodule Pleroma.Web.OAuth.TokenTest do import Pleroma.Factory - test "exchanges a auth token for an access token" do + test "exchanges a auth token for an access token, preserving `scopes`" do {:ok, app} = Repo.insert( App.register_changeset(%App{}, %{ client_name: "client", - scopes: ["scope"], + scopes: ["read", "write"], redirect_uris: "url" }) ) user = insert(:user) - {:ok, auth} = Authorization.create_authorization(app, user) + {:ok, auth} = Authorization.create_authorization(app, user, ["read"]) + assert auth.scopes == ["read"] {:ok, token} = Token.exchange_token(app, auth) assert token.app_id == app.id assert token.user_id == user.id + assert token.scopes == auth.scopes assert String.length(token.token) > 10 assert String.length(token.refresh_token) > 10 diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs index 1571ab68e..27b1e878c 100644 --- a/test/web/twitter_api/twitter_api_controller_test.exs +++ b/test/web/twitter_api/twitter_api_controller_test.exs @@ -1690,6 +1690,24 @@ test "it lists friend requests" do assert [relationship] = json_response(conn, 200) assert other_user.id == relationship["id"] end + + test "requires 'read' permission", %{conn: conn} do + token1 = insert(:oauth_token, scopes: ["write"]) + token2 = insert(:oauth_token, scopes: ["read"]) + + for token <- [token1, token2] do + conn = + conn + |> put_req_header("authorization", "Bearer #{token.token}") + |> get("/api/pleroma/friend_requests") + + if token == token1 do + assert %{"error" => "Insufficient permissions: read."} == json_response(conn, 403) + else + assert json_response(conn, 200) + end + end + end end describe "POST /api/pleroma/friendships/approve" do diff --git a/test/web/twitter_api/util_controller_test.exs b/test/web/twitter_api/util_controller_test.exs index 007d7d8e6..fc762ab18 100644 --- a/test/web/twitter_api/util_controller_test.exs +++ b/test/web/twitter_api/util_controller_test.exs @@ -16,6 +16,25 @@ test "it returns HTTP 200", %{conn: conn} do assert response == "job started" end + + test "requires 'follow' permission", %{conn: conn} do + token1 = insert(:oauth_token, scopes: ["read", "write"]) + token2 = insert(:oauth_token, scopes: ["follow"]) + another_user = insert(:user) + + for token <- [token1, token2] 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) + else + assert json_response(conn, 200) + end + end + end end describe "POST /api/pleroma/blocks_import" do