[#1234] Permissions-related fixes / new functionality (Masto 2.4.3 scopes).
authorIvan Tashkinov <ivantashkinov@gmail.com>
Sun, 15 Sep 2019 15:22:08 +0000 (18:22 +0300)
committerIvan Tashkinov <ivantashkinov@gmail.com>
Sun, 15 Sep 2019 15:22:08 +0000 (18:22 +0300)
14 files changed:
lib/pleroma/plugs/oauth_scopes_plug.ex
lib/pleroma/web/activity_pub/activity_pub_controller.ex
lib/pleroma/web/admin_api/admin_api_controller.ex
lib/pleroma/web/mastodon_api/controllers/list_controller.ex
lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex
lib/pleroma/web/mastodon_api/controllers/search_controller.ex
lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex
lib/pleroma/web/pleroma_api/pleroma_api_controller.ex
lib/pleroma/web/router.ex
lib/pleroma/web/twitter_api/controllers/util_controller.ex
lib/pleroma/web/twitter_api/twitter_api_controller.ex
test/support/factory.ex
test/web/mastodon_api/controllers/mastodon_api_controller/update_credentials_test.exs
test/web/oauth/oauth_controller_test.exs

index 41403047efec8369fc8fbd011b1ac4b2b53c2922..e0d61c4ebebef059954a334b57c08f367bce2918 100644 (file)
@@ -6,6 +6,8 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do
   import Plug.Conn
   import Pleroma.Web.Gettext
 
+  alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
+
   @behaviour Plug
 
   def init(%{scopes: _} = options), do: options
@@ -17,7 +19,7 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do
 
     cond do
       is_nil(token) ->
-        conn
+        maybe_perform_instance_privacy_check(conn, options)
 
       op == :| && Enum.any?(matched_scopes) ->
         conn
@@ -29,6 +31,7 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do
         conn
         |> assign(:user, nil)
         |> assign(:token, nil)
+        |> maybe_perform_instance_privacy_check(options)
 
       true ->
         missing_scopes = scopes -- matched_scopes
@@ -56,4 +59,11 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do
       end
     )
   end
+
+  defp maybe_perform_instance_privacy_check(%Plug.Conn{} = conn, options) do
+    case options[:skip_instance_privacy_check] do
+      true -> conn
+      _ -> EnsurePublicOrAuthenticatedPlug.call(conn, [])
+    end
+  end
 end
index 08bf1c7521b0f6891faaa715a32b05d49f981142..7047b8254f1d84143537bef37ad915ce321a0752 100644 (file)
@@ -23,6 +23,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
 
   action_fallback(:errors)
 
+  plug(
+    Pleroma.Plugs.OAuthScopesPlug,
+    %{scopes: ["read:accounts"]} when action in [:followers, :following]
+  )
+
   plug(Pleroma.Web.FederatingPlug when action in [:inbox, :relay])
   plug(:set_requester_reachable when action in [:inbox])
   plug(:relay_active? when action in [:relay])
index 544b9d7d8b530018120c3f1a9f15af36a223e43a..0a508d40ea987011e56e92208a6ef7c31fdcbc6e 100644 (file)
@@ -6,6 +6,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
   use Pleroma.Web, :controller
   alias Pleroma.Activity
   alias Pleroma.ModerationLog
+  alias Pleroma.Plugs.OAuthScopesPlug
   alias Pleroma.User
   alias Pleroma.UserInviteToken
   alias Pleroma.Web.ActivityPub.ActivityPub
@@ -23,6 +24,56 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
 
   require Logger
 
+  plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action == :list_user_statuses)
+
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["write:statuses"]} when action in [:status_update, :status_delete]
+  )
+
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["read"]}
+    when action in [
+           :list_reports,
+           :report_show,
+           :right_get,
+           :get_invite_token,
+           :invites,
+           :get_password_reset,
+           :list_users,
+           :user_show,
+           :config_show,
+           :migrate_to_db,
+           :migrate_from_db,
+           :list_log
+         ]
+  )
+
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["write"]}
+    when action in [
+           :report_update_state,
+           :report_respond,
+           :user_follow,
+           :user_unfollow,
+           :user_delete,
+           :users_create,
+           :user_toggle_activation,
+           :tag_users,
+           :untag_users,
+           :right_add,
+           :right_delete,
+           :set_activation_status,
+           :relay_follow,
+           :relay_unfollow,
+           :revoke_invite,
+           :email_invite,
+           :config_update
+         ]
+  )
+
   @users_page_size 50
 
   action_fallback(:errors)
index 2873deda8ddc891fe6be836c5063bb90560480de..be70896305f165a81a84190db0b49f9f828198de 100644 (file)
@@ -5,11 +5,20 @@
 defmodule Pleroma.Web.MastodonAPI.ListController do
   use Pleroma.Web, :controller
 
+  alias Pleroma.Plugs.OAuthScopesPlug
   alias Pleroma.User
   alias Pleroma.Web.MastodonAPI.AccountView
 
   plug(:list_by_id_and_user when action not in [:index, :create])
 
+  plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action in [:index, :show, :list_accounts])
+
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["write:lists"]}
+    when action in [:create, :update, :delete, :add_to_list, :remove_from_list]
+  )
+
   action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
 
   # GET /api/v1/lists
index 118446c8534ce4b5266e3cf0929eb9360b9d53b7..704664f5fa65fe8727a3ed6dd5b04c64309cf626 100644 (file)
@@ -53,6 +53,123 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
   require Logger
   require Pleroma.Constants
 
+  plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action != :index)
+
+  @unauthenticated_access %{fallback: :proceed_unauthenticated, scopes: []}
+
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["read"], skip_instance_privacy_check: true} when action == :index
+  )
+
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["read"]} when action in [:suggestions, :verify_app_credentials]
+  )
+
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["write:accounts"]}
+    # Note: the following actions are not permission-secured in Mastodon:
+    when action in [
+           :put_settings,
+           :update_avatar,
+           :update_banner,
+           :update_background,
+           :set_mascot
+         ]
+  )
+
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["write:accounts"]}
+    when action in [:pin_status, :unpin_status, :update_credentials]
+  )
+
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["read:statuses"]}
+    when action in [
+           :conversations,
+           :scheduled_statuses,
+           :show_scheduled_status,
+           :home_timeline,
+           :dm_timeline
+         ]
+  )
+
+  plug(
+    OAuthScopesPlug,
+    %{@unauthenticated_access | scopes: ["read:statuses"]}
+    when action in [:user_statuses, :get_status, :get_context, :status_card, :get_poll]
+  )
+
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["write:statuses"]}
+    when action in [
+           :update_scheduled_status,
+           :delete_scheduled_status,
+           :post_status,
+           :delete_status,
+           :reblog_status,
+           :unreblog_status,
+           :poll_vote
+         ]
+  )
+
+  plug(OAuthScopesPlug, %{scopes: ["write:conversations"]} when action == :conversation_read)
+
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["read:accounts"]}
+    when action in [:endorsements, :verify_credentials, :followers, :following, :get_mascot]
+  )
+
+  plug(
+    OAuthScopesPlug,
+    %{@unauthenticated_access | scopes: ["read:accounts"]}
+    when action in [:user, :favourited_by, :reblogged_by]
+  )
+
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["read:favourites"]} when action in [:favourites, :user_favourites]
+  )
+
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["write:favourites"]} when action in [:fav_status, :unfav_status]
+  )
+
+  plug(OAuthScopesPlug, %{scopes: ["read:filters"]} when action in [:get_filters, :get_filter])
+
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["write:filters"]} when action in [:create_filter, :update_filter, :delete_filter]
+  )
+
+  plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action in [:account_lists, :list_timeline])
+
+  plug(OAuthScopesPlug, %{scopes: ["write:media"]} when action in [:upload, :update_media])
+
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["read:notifications"]} when action in [:notifications, :get_notification]
+  )
+
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["write:notifications"]}
+    when action in [:clear_notifications, :dismiss_notification, :destroy_multiple_notifications]
+  )
+
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["write:reports"]}
+    when action in [:create_report, :report_update_state, :report_respond]
+  )
+
   plug(
     OAuthScopesPlug,
     %{scopes: ["follow", "read:blocks"]} when action in [:blocks, :domain_blocks]
@@ -64,6 +181,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
     when action in [:block, :unblock, :block_domain, :unblock_domain]
   )
 
+  plug(OAuthScopesPlug, %{scopes: ["read:follows"]} when action == :relationships)
   plug(OAuthScopesPlug, %{scopes: ["follow", "read:follows"]} when action == :follow_requests)
 
   plug(
@@ -84,8 +202,15 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
 
   plug(
     OAuthScopesPlug,
-    %{scopes: ["write:mutes"]}
-    when action in [:mute_conversation, :unmute_conversation]
+    %{scopes: ["write:mutes"]} when action in [:mute_conversation, :unmute_conversation]
+  )
+
+  # Note: scopes not present in Mastodon: read:bookmarks, write:bookmarks
+  plug(OAuthScopesPlug, %{scopes: ["read:bookmarks"]} when action == :bookmarks)
+
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["write:bookmarks"]} when action in [:bookmark_status, :unbookmark_status]
   )
 
   @rate_limited_relations_actions ~w(follow unfollow)a
@@ -776,7 +901,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
     end
   end
 
-  def destroy_multiple(%{assigns: %{user: user}} = conn, %{"ids" => ids} = _params) do
+  def destroy_multiple_notifications(%{assigns: %{user: user}} = conn, %{"ids" => ids} = _params) do
     Notification.destroy_multiple(user, ids)
     json(conn, %{})
   end
@@ -1488,6 +1613,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
     json(conn, %{})
   end
 
+  def endorsements(conn, params), do: empty_array(conn, params)
+
   def get_filters(%{assigns: %{user: user}} = conn, _) do
     filters = Filter.get_filters(user)
     res = FilterView.render("filters.json", filters: filters)
@@ -1610,7 +1737,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
     end
   end
 
-  def reports(%{assigns: %{user: user}} = conn, params) do
+  def create_report(%{assigns: %{user: user}} = conn, params) do
     case CommonAPI.report(user, params) do
       {:ok, activity} ->
         conn
index 9072aa7a47c1a3702b8ae46b45cfd676be57d30a..f49ca89edfe603fed6f031178a1e05c7d833a983 100644 (file)
@@ -6,6 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
   use Pleroma.Web, :controller
 
   alias Pleroma.Activity
+  alias Pleroma.Plugs.OAuthScopesPlug
   alias Pleroma.Plugs.RateLimiter
   alias Pleroma.Repo
   alias Pleroma.User
@@ -15,6 +16,10 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
   alias Pleroma.Web.MastodonAPI.StatusView
 
   require Logger
+
+  # Note: Mastodon doesn't allow unauthenticated access (requires read:accounts / read:search)
+  plug(OAuthScopesPlug, %{scopes: ["read:search"], fallback: :proceed_unauthenticated})
+
   plug(RateLimiter, :search when action in [:search, :search2, :account_search])
 
   def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
index e2b17aab1ebd7e7bc894bc65f0d6a11f4203ad08..287eebf921e4b726b6164fb2a21f54c71bc35e7a 100644 (file)
@@ -12,6 +12,8 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionController do
 
   action_fallback(:errors)
 
+  plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["push"]})
+
   # Creates PushSubscription
   # POST /api/v1/push/subscription
   #
index f4df3b024e1004ddd77026b42539f505a46c7ef5..17c568a9d12224b8a621c64f2be405626ef45b1e 100644 (file)
@@ -9,11 +9,24 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
 
   alias Pleroma.Conversation.Participation
   alias Pleroma.Notification
+  alias Pleroma.Plugs.OAuthScopesPlug
   alias Pleroma.Web.ActivityPub.ActivityPub
   alias Pleroma.Web.MastodonAPI.ConversationView
   alias Pleroma.Web.MastodonAPI.NotificationView
   alias Pleroma.Web.MastodonAPI.StatusView
 
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["read:statuses"]} when action in [:conversation, :conversation_statuses]
+  )
+
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["write:conversations"]} when action in [:conversations, :conversation_read]
+  )
+
+  plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :read_notification)
+
   def conversation(%{assigns: %{user: user}} = conn, %{"id" => participation_id}) do
     with %Participation{} = participation <- Participation.get(participation_id),
          true <- user.id == participation.user_id do
index 8c93e535e0e67c402e2c01fc6a09dd9347aee1f4..593da01fd555666fa28d0c7aaa1925ba4cd57a47 100644 (file)
@@ -87,27 +87,6 @@ defmodule Pleroma.Web.Router do
     plug(Pleroma.Plugs.EnsureUserKeyPlug)
   end
 
-  pipeline :oauth_read_or_public do
-    plug(Pleroma.Plugs.OAuthScopesPlug, %{
-      scopes: ["read"],
-      fallback: :proceed_unauthenticated
-    })
-
-    plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
-  end
-
-  pipeline :oauth_read do
-    plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["read"]})
-  end
-
-  pipeline :oauth_write do
-    plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["write"]})
-  end
-
-  pipeline :oauth_push do
-    plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["push"]})
-  end
-
   pipeline :well_known do
     plug(:accepts, ["json", "jrd+json", "xml", "xrd+xml"])
   end
@@ -149,7 +128,12 @@ defmodule Pleroma.Web.Router do
   end
 
   scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do
-    pipe_through([:admin_api, :oauth_write])
+    pipe_through(:admin_api)
+
+    get("/reports", AdminAPIController, :list_reports)
+    get("/reports/:id", AdminAPIController, :report_show)
+    put("/reports/:id", AdminAPIController, :report_update_state)
+    post("/reports/:id/respond", AdminAPIController, :report_respond)
 
     post("/users/follow", AdminAPIController, :user_follow)
     post("/users/unfollow", AdminAPIController, :user_unfollow)
@@ -184,15 +168,6 @@ defmodule Pleroma.Web.Router do
 
     get("/users", AdminAPIController, :list_users)
     get("/users/:nickname", AdminAPIController, :user_show)
-    get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses)
-
-    get("/reports", AdminAPIController, :list_reports)
-    get("/reports/:id", AdminAPIController, :report_show)
-    put("/reports/:id", AdminAPIController, :report_update_state)
-    post("/reports/:id/respond", AdminAPIController, :report_respond)
-
-    put("/statuses/:id", AdminAPIController, :status_update)
-    delete("/statuses/:id", AdminAPIController, :status_delete)
 
     get("/config", AdminAPIController, :config_show)
     post("/config", AdminAPIController, :config_update)
@@ -200,6 +175,10 @@ defmodule Pleroma.Web.Router do
     get("/config/migrate_from_db", AdminAPIController, :migrate_from_db)
 
     get("/moderation_log", AdminAPIController, :list_log)
+
+    get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses)
+    put("/statuses/:id", AdminAPIController, :status_update)
+    delete("/statuses/:id", AdminAPIController, :status_delete)
   end
 
   scope "/", Pleroma.Web.TwitterAPI do
@@ -213,19 +192,13 @@ defmodule Pleroma.Web.Router do
   scope "/api/pleroma", Pleroma.Web.TwitterAPI do
     pipe_through(:authenticated_api)
 
-    scope [] do
-      pipe_through(:oauth_write)
+    post("/change_password", UtilController, :change_password)
+    post("/delete_account", UtilController, :delete_account)
+    put("/notification_settings", UtilController, :update_notificaton_settings)
+    post("/disable_account", UtilController, :disable_account)
 
-      post("/change_password", UtilController, :change_password)
-      post("/delete_account", UtilController, :delete_account)
-      put("/notification_settings", UtilController, :update_notificaton_settings)
-      post("/disable_account", UtilController, :disable_account)
-    end
-
-    scope [] do
-      post("/blocks_import", UtilController, :blocks_import)
-      post("/follow_import", UtilController, :follow_import)
-    end
+    post("/blocks_import", UtilController, :blocks_import)
+    post("/follow_import", UtilController, :follow_import)
   end
 
   scope "/oauth", Pleroma.Web.OAuth do
@@ -252,148 +225,134 @@ defmodule Pleroma.Web.Router do
   scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
     pipe_through(:authenticated_api)
 
-    scope [] do
-      pipe_through(:oauth_read)
-      get("/conversations/:id/statuses", PleromaAPIController, :conversation_statuses)
-      get("/conversations/:id", PleromaAPIController, :conversation)
-    end
-
-    scope [] do
-      pipe_through(:oauth_write)
-      patch("/conversations/:id", PleromaAPIController, :update_conversation)
-      post("/notifications/read", PleromaAPIController, :read_notification)
-    end
+    get("/conversations/:id/statuses", PleromaAPIController, :conversation_statuses)
+    get("/conversations/:id", PleromaAPIController, :conversation)
+    patch("/conversations/:id", PleromaAPIController, :update_conversation)
+    post("/notifications/read", PleromaAPIController, :read_notification)
   end
 
   scope "/api/v1", Pleroma.Web.MastodonAPI do
     pipe_through(:authenticated_api)
 
-    scope [] do
-      pipe_through(:oauth_read)
-
-      get("/accounts/verify_credentials", MastodonAPIController, :verify_credentials)
-
-      get("/accounts/relationships", MastodonAPIController, :relationships)
+    get("/blocks", MastodonAPIController, :blocks)
+    get("/mutes", MastodonAPIController, :mutes)
+    get("/domain_blocks", MastodonAPIController, :domain_blocks)
 
-      get("/accounts/:id/lists", MastodonAPIController, :account_lists)
-      get("/accounts/:id/identity_proofs", MastodonAPIController, :empty_array)
+    get("/accounts/:id/lists", MastodonAPIController, :account_lists)
+    get("/lists", ListController, :index)
+    get("/lists/:id", ListController, :show)
+    get("/lists/:id/accounts", ListController, :list_accounts)
 
-      get("/follow_requests", MastodonAPIController, :follow_requests)
-      get("/blocks", MastodonAPIController, :blocks)
-      get("/mutes", MastodonAPIController, :mutes)
+    post("/notifications/clear", MastodonAPIController, :clear_notifications)
+    post("/notifications/dismiss", MastodonAPIController, :dismiss_notification)
+    get("/notifications", MastodonAPIController, :notifications)
+    get("/notifications/:id", MastodonAPIController, :get_notification)
 
-      get("/timelines/home", MastodonAPIController, :home_timeline)
-      get("/timelines/direct", MastodonAPIController, :dm_timeline)
+    delete(
+      "/notifications/destroy_multiple",
+      MastodonAPIController,
+      :destroy_multiple_notifications
+    )
 
-      get("/favourites", MastodonAPIController, :favourites)
-      get("/bookmarks", MastodonAPIController, :bookmarks)
+    # Note: not present in Mastodon
+    get("/bookmarks", MastodonAPIController, :bookmarks)
 
-      post("/notifications/clear", MastodonAPIController, :clear_notifications)
-      post("/notifications/dismiss", MastodonAPIController, :dismiss_notification)
-      get("/notifications", MastodonAPIController, :notifications)
-      get("/notifications/:id", MastodonAPIController, :get_notification)
-      delete("/notifications/destroy_multiple", MastodonAPIController, :destroy_multiple)
+    get("/accounts/:id/identity_proofs", MastodonAPIController, :empty_array)
 
-      get("/scheduled_statuses", MastodonAPIController, :scheduled_statuses)
-      get("/scheduled_statuses/:id", MastodonAPIController, :show_scheduled_status)
+    get("/favourites", MastodonAPIController, :favourites)
 
-      get("/lists", ListController, :index)
-      get("/lists/:id", ListController, :show)
-      get("/lists/:id/accounts", ListController, :list_accounts)
+    get("/accounts/relationships", MastodonAPIController, :relationships)
 
-      get("/domain_blocks", MastodonAPIController, :domain_blocks)
+    get("/accounts/verify_credentials", MastodonAPIController, :verify_credentials)
 
-      get("/filters", MastodonAPIController, :get_filters)
+    get("/timelines/home", MastodonAPIController, :home_timeline)
+    get("/timelines/direct", MastodonAPIController, :dm_timeline)
 
-      get("/suggestions", MastodonAPIController, :suggestions)
+    get("/suggestions", MastodonAPIController, :suggestions)
+    get("/scheduled_statuses", MastodonAPIController, :scheduled_statuses)
+    get("/scheduled_statuses/:id", MastodonAPIController, :show_scheduled_status)
+    get("/follow_requests", MastodonAPIController, :follow_requests)
+    get("/filters", MastodonAPIController, :get_filters)
+    get("/endorsements", MastodonAPIController, :endorsements)
+    get("/conversations", MastodonAPIController, :conversations)
+    post("/conversations/:id/read", MastodonAPIController, :conversation_read)
 
-      get("/conversations", MastodonAPIController, :conversations)
-      post("/conversations/:id/read", MastodonAPIController, :conversation_read)
+    delete("/lists/:id", ListController, :delete)
+    post("/lists", ListController, :create)
+    put("/lists/:id", ListController, :update)
 
-      get("/endorsements", MastodonAPIController, :empty_array)
-    end
+    post("/lists/:id/accounts", ListController, :add_to_list)
+    delete("/lists/:id/accounts", ListController, :remove_from_list)
 
-    scope [] do
-      pipe_through(:oauth_write)
+    post("/reports", MastodonAPIController, :create_report)
 
-      patch("/accounts/update_credentials", MastodonAPIController, :update_credentials)
+    patch("/pleroma/accounts/update_avatar", MastodonAPIController, :update_avatar)
+    patch("/pleroma/accounts/update_banner", MastodonAPIController, :update_banner)
+    patch("/pleroma/accounts/update_background", MastodonAPIController, :update_background)
 
-      post("/statuses", MastodonAPIController, :post_status)
-      delete("/statuses/:id", MastodonAPIController, :delete_status)
+    get("/pleroma/mascot", MastodonAPIController, :get_mascot)
+    put("/pleroma/mascot", MastodonAPIController, :set_mascot)
 
-      post("/statuses/:id/reblog", MastodonAPIController, :reblog_status)
-      post("/statuses/:id/unreblog", MastodonAPIController, :unreblog_status)
-      post("/statuses/:id/favourite", MastodonAPIController, :fav_status)
-      post("/statuses/:id/unfavourite", MastodonAPIController, :unfav_status)
-      post("/statuses/:id/pin", MastodonAPIController, :pin_status)
-      post("/statuses/:id/unpin", MastodonAPIController, :unpin_status)
-      post("/statuses/:id/bookmark", MastodonAPIController, :bookmark_status)
-      post("/statuses/:id/unbookmark", MastodonAPIController, :unbookmark_status)
-      post("/statuses/:id/mute", MastodonAPIController, :mute_conversation)
-      post("/statuses/:id/unmute", MastodonAPIController, :unmute_conversation)
+    post("/media", MastodonAPIController, :upload)
+    put("/media/:id", MastodonAPIController, :update_media)
 
-      put("/scheduled_statuses/:id", MastodonAPIController, :update_scheduled_status)
-      delete("/scheduled_statuses/:id", MastodonAPIController, :delete_scheduled_status)
+    patch("/accounts/update_credentials", MastodonAPIController, :update_credentials)
 
-      post("/polls/:id/votes", MastodonAPIController, :poll_vote)
+    post("/polls/:id/votes", MastodonAPIController, :poll_vote)
 
-      post("/media", MastodonAPIController, :upload)
-      put("/media/:id", MastodonAPIController, :update_media)
+    post("/statuses/:id/reblog", MastodonAPIController, :reblog_status)
+    post("/statuses/:id/unreblog", MastodonAPIController, :unreblog_status)
 
-      delete("/lists/:id", ListController, :delete)
-      post("/lists", ListController, :create)
-      put("/lists/:id", ListController, :update)
+    post("/statuses/:id/pin", MastodonAPIController, :pin_status)
+    post("/statuses/:id/unpin", MastodonAPIController, :unpin_status)
 
-      post("/lists/:id/accounts", ListController, :add_to_list)
-      delete("/lists/:id/accounts", ListController, :remove_from_list)
+    post("/statuses/:id/mute", MastodonAPIController, :mute_conversation)
+    post("/statuses/:id/unmute", MastodonAPIController, :unmute_conversation)
 
-      post("/filters", MastodonAPIController, :create_filter)
-      get("/filters/:id", MastodonAPIController, :get_filter)
-      put("/filters/:id", MastodonAPIController, :update_filter)
-      delete("/filters/:id", MastodonAPIController, :delete_filter)
+    post("/statuses/:id/favourite", MastodonAPIController, :fav_status)
+    post("/statuses/:id/unfavourite", MastodonAPIController, :unfav_status)
 
-      patch("/pleroma/accounts/update_avatar", MastodonAPIController, :update_avatar)
-      patch("/pleroma/accounts/update_banner", MastodonAPIController, :update_banner)
-      patch("/pleroma/accounts/update_background", MastodonAPIController, :update_background)
+    post("/statuses", MastodonAPIController, :post_status)
+    delete("/statuses/:id", MastodonAPIController, :delete_status)
 
-      get("/pleroma/mascot", MastodonAPIController, :get_mascot)
-      put("/pleroma/mascot", MastodonAPIController, :set_mascot)
+    put("/scheduled_statuses/:id", MastodonAPIController, :update_scheduled_status)
+    delete("/scheduled_statuses/:id", MastodonAPIController, :delete_scheduled_status)
 
-      post("/reports", MastodonAPIController, :reports)
-    end
+    post("/filters", MastodonAPIController, :create_filter)
+    get("/filters/:id", MastodonAPIController, :get_filter)
+    put("/filters/:id", MastodonAPIController, :update_filter)
+    delete("/filters/:id", MastodonAPIController, :delete_filter)
 
-    scope [] do
-      post("/follows", MastodonAPIController, :follow)
-      post("/accounts/:id/follow", MastodonAPIController, :follow)
+    post("/follows", MastodonAPIController, :follow)
+    post("/accounts/:id/follow", MastodonAPIController, :follow)
 
-      post("/accounts/:id/unfollow", MastodonAPIController, :unfollow)
-      post("/accounts/:id/block", MastodonAPIController, :block)
-      post("/accounts/:id/unblock", MastodonAPIController, :unblock)
-      post("/accounts/:id/mute", MastodonAPIController, :mute)
-      post("/accounts/:id/unmute", MastodonAPIController, :unmute)
+    post("/accounts/:id/unfollow", MastodonAPIController, :unfollow)
+    post("/accounts/:id/block", MastodonAPIController, :block)
+    post("/accounts/:id/unblock", MastodonAPIController, :unblock)
+    post("/accounts/:id/mute", MastodonAPIController, :mute)
+    post("/accounts/:id/unmute", MastodonAPIController, :unmute)
 
-      post("/follow_requests/:id/authorize", MastodonAPIController, :authorize_follow_request)
-      post("/follow_requests/:id/reject", MastodonAPIController, :reject_follow_request)
+    post("/follow_requests/:id/authorize", MastodonAPIController, :authorize_follow_request)
+    post("/follow_requests/:id/reject", MastodonAPIController, :reject_follow_request)
 
-      post("/domain_blocks", MastodonAPIController, :block_domain)
-      delete("/domain_blocks", MastodonAPIController, :unblock_domain)
+    post("/domain_blocks", MastodonAPIController, :block_domain)
+    delete("/domain_blocks", MastodonAPIController, :unblock_domain)
 
-      post("/pleroma/accounts/:id/subscribe", MastodonAPIController, :subscribe)
-      post("/pleroma/accounts/:id/unsubscribe", MastodonAPIController, :unsubscribe)
-    end
+    post("/pleroma/accounts/:id/subscribe", MastodonAPIController, :subscribe)
+    post("/pleroma/accounts/:id/unsubscribe", MastodonAPIController, :unsubscribe)
 
-    scope [] do
-      pipe_through(:oauth_push)
+    post("/push/subscription", SubscriptionController, :create)
+    get("/push/subscription", SubscriptionController, :get)
+    put("/push/subscription", SubscriptionController, :update)
+    delete("/push/subscription", SubscriptionController, :delete)
 
-      post("/push/subscription", SubscriptionController, :create)
-      get("/push/subscription", SubscriptionController, :get)
-      put("/push/subscription", SubscriptionController, :update)
-      delete("/push/subscription", SubscriptionController, :delete)
-    end
+    # Note: not present in Mastodon: bookmark, unbookmark
+    post("/statuses/:id/bookmark", MastodonAPIController, :bookmark_status)
+    post("/statuses/:id/unbookmark", MastodonAPIController, :unbookmark_status)
   end
 
   scope "/api/web", Pleroma.Web.MastodonAPI do
-    pipe_through([:authenticated_api, :oauth_write])
+    pipe_through(:authenticated_api)
 
     put("/settings", MastodonAPIController, :put_settings)
   end
@@ -424,31 +383,29 @@ defmodule Pleroma.Web.Router do
       :account_confirmation_resend
     )
 
-    scope [] do
-      pipe_through(:oauth_read_or_public)
+    get("/timelines/public", MastodonAPIController, :public_timeline)
+    get("/timelines/tag/:tag", MastodonAPIController, :hashtag_timeline)
 
-      get("/timelines/public", MastodonAPIController, :public_timeline)
-      get("/timelines/tag/:tag", MastodonAPIController, :hashtag_timeline)
-      get("/timelines/list/:list_id", MastodonAPIController, :list_timeline)
+    get("/pleroma/accounts/:id/favourites", MastodonAPIController, :user_favourites)
 
-      get("/statuses/:id", MastodonAPIController, :get_status)
-      get("/statuses/:id/context", MastodonAPIController, :get_context)
+    get("/search", SearchController, :search)
 
-      get("/polls/:id", MastodonAPIController, :get_poll)
+    get("/polls/:id", MastodonAPIController, :get_poll)
 
-      get("/accounts/:id/statuses", MastodonAPIController, :user_statuses)
-      get("/accounts/:id/followers", MastodonAPIController, :followers)
-      get("/accounts/:id/following", MastodonAPIController, :following)
-      get("/accounts/:id", MastodonAPIController, :user)
+    get("/accounts/:id/followers", MastodonAPIController, :followers)
+    get("/accounts/:id/following", MastodonAPIController, :following)
 
-      get("/search", SearchController, :search)
+    get("/timelines/list/:list_id", MastodonAPIController, :list_timeline)
 
-      get("/pleroma/accounts/:id/favourites", MastodonAPIController, :user_favourites)
-    end
+    get("/accounts/:id", MastodonAPIController, :user)
+
+    get("/accounts/:id/statuses", MastodonAPIController, :user_statuses)
+    get("/statuses/:id", MastodonAPIController, :get_status)
+    get("/statuses/:id/context", MastodonAPIController, :get_context)
   end
 
   scope "/api/v2", Pleroma.Web.MastodonAPI do
-    pipe_through([:api, :oauth_read_or_public])
+    pipe_through(:api)
     get("/search", SearchController, :search2)
   end
 
@@ -478,12 +435,7 @@ defmodule Pleroma.Web.Router do
 
     get("/oauth_tokens", TwitterAPI.Controller, :oauth_tokens)
     delete("/oauth_tokens/:id", TwitterAPI.Controller, :revoke_token)
-
-    scope [] do
-      pipe_through(:oauth_read)
-
-      post("/qvitter/statuses/notifications/read", TwitterAPI.Controller, :notifications_read)
-    end
+    post("/qvitter/statuses/notifications/read", TwitterAPI.Controller, :notifications_read)
   end
 
   pipeline :ap_service_actor do
@@ -547,26 +499,16 @@ defmodule Pleroma.Web.Router do
   scope "/", Pleroma.Web.ActivityPub do
     pipe_through([:activitypub_client])
 
-    scope [] do
-      pipe_through(:oauth_read)
-      get("/api/ap/whoami", ActivityPubController, :whoami)
-      get("/users/:nickname/inbox", ActivityPubController, :read_inbox)
-    end
-
-    scope [] do
-      pipe_through(:oauth_write)
-      post("/users/:nickname/outbox", ActivityPubController, :update_outbox)
-    end
-
-    scope [] do
-      pipe_through(:oauth_read_or_public)
-      get("/users/:nickname/followers", ActivityPubController, :followers)
-      get("/users/:nickname/following", ActivityPubController, :following)
-    end
+    get("/api/ap/whoami", ActivityPubController, :whoami)
+    get("/users/:nickname/inbox", ActivityPubController, :read_inbox)
+    post("/users/:nickname/outbox", ActivityPubController, :update_outbox)
+    get("/users/:nickname/followers", ActivityPubController, :followers)
+    get("/users/:nickname/following", ActivityPubController, :following)
   end
 
   scope "/", Pleroma.Web.ActivityPub do
     pipe_through(:activitypub)
+
     post("/inbox", ActivityPubController, :inbox)
     post("/users/:nickname/inbox", ActivityPubController, :inbox)
   end
@@ -612,10 +554,7 @@ defmodule Pleroma.Web.Router do
 
     post("/auth/password", MastodonAPIController, :password_reset)
 
-    scope [] do
-      pipe_through(:oauth_read)
-      get("/web/*path", MastodonAPIController, :index)
-    end
+    get("/web/*path", MastodonAPIController, :index)
   end
 
   pipeline :remote_media do
index 1c6ad5057551a91dc087ac86a7c2cfece5f93615..82ed0c287c952c5b84e3a50b5d086c4d0b34f540 100644 (file)
@@ -27,6 +27,17 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
 
   plug(OAuthScopesPlug, %{scopes: ["follow", "write:blocks"]} when action == :blocks_import)
 
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["write:accounts"]}
+    when action in [
+           :change_password,
+           :delete_account,
+           :update_notificaton_settings,
+           :disable_account
+         ]
+  )
+
   plug(Pleroma.Plugs.SetFormatPlug when action in [:config, :version])
 
   def help_test(conn, _params) do
index 42234ae09e020019e5d06709d620a986348d4fe6..42bd74eb5c6d22d71de9644c90b78a9d6cbbb228 100644 (file)
@@ -7,12 +7,15 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
 
   alias Ecto.Changeset
   alias Pleroma.Notification
+  alias Pleroma.Plugs.OAuthScopesPlug
   alias Pleroma.User
   alias Pleroma.Web.OAuth.Token
   alias Pleroma.Web.TwitterAPI.TokenView
 
   require Logger
 
+  plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :notifications_read)
+
   action_fallback(:errors)
 
   def confirm_email(conn, %{"user_id" => uid, "token" => token}) do
index 7191150031c703e6565b2746310fd3fe0770522e..c14c8ddb34c756bfa636a8fe00747809da6616a6 100644 (file)
@@ -283,6 +283,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,
index 87ee82050cf47c65296354cbe4d903558a8d8466..1680ec12277a156a055a0951bfc47bc17d6f427e 100644 (file)
@@ -257,7 +257,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"])
 
@@ -268,7 +268,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
index b492c77942067d8d9db17811d10da2606ca0fd5f..e919ea112a8e007254b81c16215eaf5933101413 100644 (file)
@@ -556,7 +556,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"
           }
         })
@@ -569,7 +569,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
@@ -626,7 +626,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)