]> cat aescling's git repositories - mastodon.git/commitdiff
Add more granular OAuth scopes (#7929)
authorEugen Rochko <eugen@zeonfederated.com>
Thu, 5 Jul 2018 16:31:35 +0000 (18:31 +0200)
committerGitHub <noreply@github.com>
Thu, 5 Jul 2018 16:31:35 +0000 (18:31 +0200)
* Add more granular OAuth scopes

* Add human-readable descriptions of the new scopes

* Ensure new scopes look good on the app UI

* Add tests

* Group scopes in screen and color-code dangerous ones

* Fix wrong extra scope

69 files changed:
app/controllers/api/base_controller.rb
app/controllers/api/v1/accounts/credentials_controller.rb
app/controllers/api/v1/accounts/follower_accounts_controller.rb
app/controllers/api/v1/accounts/following_accounts_controller.rb
app/controllers/api/v1/accounts/lists_controller.rb
app/controllers/api/v1/accounts/relationships_controller.rb
app/controllers/api/v1/accounts/search_controller.rb
app/controllers/api/v1/accounts/statuses_controller.rb
app/controllers/api/v1/accounts_controller.rb
app/controllers/api/v1/blocks_controller.rb
app/controllers/api/v1/domain_blocks_controller.rb
app/controllers/api/v1/favourites_controller.rb
app/controllers/api/v1/filters_controller.rb
app/controllers/api/v1/follow_requests_controller.rb
app/controllers/api/v1/follows_controller.rb
app/controllers/api/v1/lists/accounts_controller.rb
app/controllers/api/v1/lists_controller.rb
app/controllers/api/v1/media_controller.rb
app/controllers/api/v1/mutes_controller.rb
app/controllers/api/v1/notifications_controller.rb
app/controllers/api/v1/reports_controller.rb
app/controllers/api/v1/search_controller.rb
app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb
app/controllers/api/v1/statuses/favourites_controller.rb
app/controllers/api/v1/statuses/mutes_controller.rb
app/controllers/api/v1/statuses/pins_controller.rb
app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb
app/controllers/api/v1/statuses/reblogs_controller.rb
app/controllers/api/v1/statuses_controller.rb
app/controllers/api/v1/timelines/direct_controller.rb
app/controllers/api/v1/timelines/home_controller.rb
app/controllers/api/v1/timelines/list_controller.rb
app/helpers/application_helper.rb
app/javascript/styles/mastodon/forms.scss
app/views/settings/applications/_fields.html.haml
config/initializers/doorkeeper.rb
config/locales/doorkeeper.en.yml
config/locales/simple_form.en.yml
spec/controllers/api/v1/accounts/credentials_controller_spec.rb
spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb
spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb
spec/controllers/api/v1/accounts/lists_controller_spec.rb
spec/controllers/api/v1/accounts/relationships_controller_spec.rb
spec/controllers/api/v1/accounts/search_controller_spec.rb
spec/controllers/api/v1/accounts/statuses_controller_spec.rb
spec/controllers/api/v1/accounts_controller_spec.rb
spec/controllers/api/v1/blocks_controller_spec.rb
spec/controllers/api/v1/domain_blocks_controller_spec.rb
spec/controllers/api/v1/favourites_controller_spec.rb
spec/controllers/api/v1/filter_controller_spec.rb
spec/controllers/api/v1/follow_requests_controller_spec.rb
spec/controllers/api/v1/follows_controller_spec.rb
spec/controllers/api/v1/lists/accounts_controller_spec.rb
spec/controllers/api/v1/lists_controller_spec.rb
spec/controllers/api/v1/media_controller_spec.rb
spec/controllers/api/v1/mutes_controller_spec.rb
spec/controllers/api/v1/notifications_controller_spec.rb
spec/controllers/api/v1/reports_controller_spec.rb
spec/controllers/api/v1/search_controller_spec.rb
spec/controllers/api/v1/statuses/favourited_by_accounts_controller_spec.rb
spec/controllers/api/v1/statuses/favourites_controller_spec.rb
spec/controllers/api/v1/statuses/mutes_controller_spec.rb
spec/controllers/api/v1/statuses/pins_controller_spec.rb
spec/controllers/api/v1/statuses/reblogged_by_accounts_controller_spec.rb
spec/controllers/api/v1/statuses/reblogs_controller_spec.rb
spec/controllers/api/v1/statuses_controller_spec.rb
spec/controllers/api/v1/timelines/home_controller_spec.rb
spec/controllers/api/v1/timelines/list_controller_spec.rb
spec/controllers/api/v2/search_controller_spec.rb [new file with mode: 0644]

index b5c084e14516907ba79d99a38089a6e474641119..770a69921a8a4da6f1bc78de85a8afc0ec005b3f 100644 (file)
@@ -78,4 +78,8 @@ class Api::BaseController < ApplicationController
   def render_empty
     render json: {}, status: 200
   end
+
+  def authorize_if_got_token!(*scopes)
+    doorkeeper_authorize!(*scopes) if doorkeeper_token
+  end
 end
index 2d0737ee4637feda80d6c6dd250ce04e0ecd4da5..dcd41b35c1b3722a30483650e69bcb783219cad3 100644 (file)
@@ -1,8 +1,8 @@
 # frozen_string_literal: true
 
 class Api::V1::Accounts::CredentialsController < Api::BaseController
-  before_action -> { doorkeeper_authorize! :read }, except: [:update]
-  before_action -> { doorkeeper_authorize! :write }, only: [:update]
+  before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, except: [:update]
+  before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:update]
   before_action :require_user!
 
   def show
index 4578cf6ca679b1ac9cc031b06db7385b2f68ed05..daa35769ebf12a6c078d5694924cecdd9b06f6cd 100644 (file)
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
-  before_action -> { doorkeeper_authorize! :read }
+  before_action -> { doorkeeper_authorize! :read, :'read:accounts' }
   before_action :set_account
   after_action :insert_pagination_headers
 
index ce2bbda855990c939a9290a171f21d6850aa55ca..6be97b87ece4c131275b2ed611b5367728ee5988 100644 (file)
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
-  before_action -> { doorkeeper_authorize! :read }
+  before_action -> { doorkeeper_authorize! :read, :'read:accounts' }
   before_action :set_account
   after_action :insert_pagination_headers
 
index a7ba89ce29a807d94756deab93ba27bc83dcfefb..72392453c4e35b3f496b05c98a01eb5352220fc5 100644 (file)
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 class Api::V1::Accounts::ListsController < Api::BaseController
-  before_action -> { doorkeeper_authorize! :read }
+  before_action -> { doorkeeper_authorize! :read, :'read:lists' }
   before_action :require_user!
   before_action :set_account
 
index 70236d1a8bfc1cf6a9c2c17106b062200e851adc..ab8a0461f5e0d34987931350f32b132c40252368 100644 (file)
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 class Api::V1::Accounts::RelationshipsController < Api::BaseController
-  before_action -> { doorkeeper_authorize! :read }
+  before_action -> { doorkeeper_authorize! :read, :'read:follows' }
   before_action :require_user!
 
   respond_to :json
index 7649da4339e7a05f955596895da1a61ce5cc880a..91c9f154728e48affef0f316f2f0749f888fd80b 100644 (file)
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 class Api::V1::Accounts::SearchController < Api::BaseController
-  before_action -> { doorkeeper_authorize! :read }
+  before_action -> { doorkeeper_authorize! :read, :'read:accounts' }
   before_action :require_user!
 
   respond_to :json
index c40155cb56cbf5c98d74d0475e3d4934929b31a5..06fa6c762399f70603e8e6203011489558b517e4 100644 (file)
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 class Api::V1::Accounts::StatusesController < Api::BaseController
-  before_action -> { doorkeeper_authorize! :read }
+  before_action -> { doorkeeper_authorize! :read, :'read:statuses' }
   before_action :set_account
   after_action :insert_pagination_headers
 
index b7133ca8e56fc6a667336569325211af25c781ed..1d5372a8cdd4f2d7d0793119d99545efd1deba00 100644 (file)
@@ -1,8 +1,11 @@
 # frozen_string_literal: true
 
 class Api::V1::AccountsController < Api::BaseController
-  before_action -> { doorkeeper_authorize! :read }, except: [:follow, :unfollow, :block, :unblock, :mute, :unmute]
-  before_action -> { doorkeeper_authorize! :follow }, only: [:follow, :unfollow, :block, :unblock, :mute, :unmute]
+  before_action -> { authorize_if_got_token! :read, :'read:accounts' }, except: [:follow, :unfollow, :block, :unblock, :mute, :unmute]
+  before_action -> { doorkeeper_authorize! :follow, :'write:follows' }, only: [:follow, :unfollow]
+  before_action -> { doorkeeper_authorize! :follow, :'write:mutes' }, only: [:mute, :unmute]
+  before_action -> { doorkeeper_authorize! :follow, :'write:blocks' }, only: [:block, :unblock]
+
   before_action :require_user!, except: [:show]
   before_action :set_account
   before_action :check_account_suspension, only: [:show]
index a3970134079562babe658eeb66e7e3210549a6ca..99c53d59af8bf9b70b067049bce8e89e6856dfeb 100644 (file)
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 class Api::V1::BlocksController < Api::BaseController
-  before_action -> { doorkeeper_authorize! :follow }
+  before_action -> { doorkeeper_authorize! :follow, :'read:blocks' }
   before_action :require_user!
   after_action :insert_pagination_headers
 
index e55d622c3a7a76418aeb10f641e240c334d20a93..af9e7a20f75bf9346b01d4727091029c5ba43811 100644 (file)
@@ -3,7 +3,8 @@
 class Api::V1::DomainBlocksController < Api::BaseController
   BLOCK_LIMIT = 100
 
-  before_action -> { doorkeeper_authorize! :follow }
+  before_action -> { doorkeeper_authorize! :follow, :'read:blocks' }, only: :show
+  before_action -> { doorkeeper_authorize! :follow, :'write:blocks' }, except: :show
   before_action :require_user!
   after_action :insert_pagination_headers, only: :show
 
index b4265ed343fca113f7e6a5a4689adf806c52be31..ab5204355cd620a4b6f5765c6ed4d51e07b7eb35 100644 (file)
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 class Api::V1::FavouritesController < Api::BaseController
-  before_action -> { doorkeeper_authorize! :read }
+  before_action -> { doorkeeper_authorize! :read, :'read:favourites' }
   before_action :require_user!
   after_action :insert_pagination_headers
 
index c89722b853499afd58afda779c2855ced65520ac..02efd323baf1d2fcd8137b7c0b6b438d68543791 100644 (file)
@@ -1,8 +1,8 @@
 # frozen_string_literal: true
 
 class Api::V1::FiltersController < Api::BaseController
-  before_action -> { doorkeeper_authorize! :read }, only: [:index, :show]
-  before_action -> { doorkeeper_authorize! :write }, except: [:index, :show]
+  before_action -> { doorkeeper_authorize! :read, :'read:filters' }, only: [:index, :show]
+  before_action -> { doorkeeper_authorize! :write, :'write:filters' }, except: [:index, :show]
   before_action :require_user!
   before_action :set_filters, only: :index
   before_action :set_filter, only: [:show, :update, :destroy]
index d5c7c565a08f3f588f03805680a7bd3c52453b3b..313fe2f81ce82eda317a31b8dc3ce88070b3f178 100644 (file)
@@ -1,7 +1,8 @@
 # frozen_string_literal: true
 
 class Api::V1::FollowRequestsController < Api::BaseController
-  before_action -> { doorkeeper_authorize! :follow }
+  before_action -> { doorkeeper_authorize! :follow, :'read:follows' }, only: :index
+  before_action -> { doorkeeper_authorize! :follow, :'write:follows' }, except: :index
   before_action :require_user!
   after_action :insert_pagination_headers, only: :index
 
index 5a2b2f32f3845fc94a3bd358909e8325135f7f0b..5420c053367ec68d2998f8af98effb671b03dcc3 100644 (file)
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 class Api::V1::FollowsController < Api::BaseController
-  before_action -> { doorkeeper_authorize! :follow }
+  before_action -> { doorkeeper_authorize! :follow, :'write:follows' }
   before_action :require_user!
 
   respond_to :json
index f2bded851c52f33c4ffa2cac050da7dd7375fab1..19de567321641d1503ef790e05be47cc743f06e9 100644 (file)
@@ -1,8 +1,8 @@
 # frozen_string_literal: true
 
 class Api::V1::Lists::AccountsController < Api::BaseController
-  before_action -> { doorkeeper_authorize! :read },    only: [:show]
-  before_action -> { doorkeeper_authorize! :write }, except: [:show]
+  before_action -> { doorkeeper_authorize! :read, :'read:lists' },    only:  [:show]
+  before_action -> { doorkeeper_authorize! :write, :'write:lists' }, except: [:show]
 
   before_action :require_user!
   before_action :set_list
index 180a91d81b22be7bad96c93ef9568352f2164e09..b42b8b9710d2a9f42f5d32d4ede3c8875624203e 100644 (file)
@@ -1,8 +1,8 @@
 # frozen_string_literal: true
 
 class Api::V1::ListsController < Api::BaseController
-  before_action -> { doorkeeper_authorize! :read },    only: [:index, :show]
-  before_action -> { doorkeeper_authorize! :write }, except: [:index, :show]
+  before_action -> { doorkeeper_authorize! :read, :'read:lists' },    only:  [:index, :show]
+  before_action -> { doorkeeper_authorize! :write, :'write:lists' }, except: [:index, :show]
 
   before_action :require_user!
   before_action :set_list, except: [:index, :create]
index d4e6337e76436ba22c8be5487c181d804db82679..aaa93b61583cf5b073b576c1332d10ed33ee6054 100644 (file)
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 class Api::V1::MediaController < Api::BaseController
-  before_action -> { doorkeeper_authorize! :write }
+  before_action -> { doorkeeper_authorize! :write, :'write:media' }
   before_action :require_user!
 
   include ObfuscateFilename
index c457408ba7ca0ff00d2004ce2961f0e848cc1717..faa7d16cd9f59ef2ae3263bde6770063eebc5e00 100644 (file)
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 class Api::V1::MutesController < Api::BaseController
-  before_action -> { doorkeeper_authorize! :follow }
+  before_action -> { doorkeeper_authorize! :follow, :'read:mutes' }
   before_action :require_user!
   after_action :insert_pagination_headers
 
index ebbe0b292e6c2d403b04f0e687e4cab445b81911..593c8f9a9c8a201e4218479bd642a5f0a3a67fca 100644 (file)
@@ -1,7 +1,8 @@
 # frozen_string_literal: true
 
 class Api::V1::NotificationsController < Api::BaseController
-  before_action -> { doorkeeper_authorize! :read }
+  before_action -> { doorkeeper_authorize! :read, :'read:notifications' }, except: [:clear, :dismiss]
+  before_action -> { doorkeeper_authorize! :write, :'write:notifications' }, only: [:clear, :dismiss]
   before_action :require_user!
   after_action :insert_pagination_headers, only: :index
 
index f5095e07304538031c0b8b1d12e8a817589aac40..a954101cb8fee46da9ed91f1cd807dc96e5d22ac 100644 (file)
@@ -1,8 +1,8 @@
 # frozen_string_literal: true
 
 class Api::V1::ReportsController < Api::BaseController
-  before_action -> { doorkeeper_authorize! :read }, except: [:create]
-  before_action -> { doorkeeper_authorize! :write }, only:  [:create]
+  before_action -> { doorkeeper_authorize! :read, :'read:reports' }, except: [:create]
+  before_action -> { doorkeeper_authorize! :write, :'write:reports' }, only: [:create]
   before_action :require_user!
 
   respond_to :json
index 05754d0f2438b304fc13a139bee0d6be3f21a8af..dc1a37599e6256ef04eef074bc09c53def1451cb 100644 (file)
@@ -5,7 +5,7 @@ class Api::V1::SearchController < Api::BaseController
 
   RESULTS_LIMIT = 5
 
-  before_action -> { doorkeeper_authorize! :read }
+  before_action -> { doorkeeper_authorize! :read, :'read:search' }
   before_action :require_user!
 
   respond_to :json
index 3fe304153e4995629c38bfa160a37a5dc0b06247..8f4070bc781adedd70f6b90f079d455eefcafbab 100644 (file)
@@ -3,7 +3,7 @@
 class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController
   include Authorization
 
-  before_action :authorize_if_got_token
+  before_action -> { authorize_if_got_token! :read, :'read:accounts' }
   before_action :set_status
   after_action :insert_pagination_headers
 
@@ -71,11 +71,6 @@ class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController
     raise ActiveRecord::RecordNotFound
   end
 
-  def authorize_if_got_token
-    request_token = Doorkeeper::OAuth::Token.from_request(request, *Doorkeeper.configuration.access_token_methods)
-    doorkeeper_authorize! :read if request_token
-  end
-
   def pagination_params(core_params)
     params.slice(:limit).permit(:limit).merge(core_params)
   end
index 35f8a48cd6c2ff09efc5fe3d1adbfd6095fbbb46..cceee90609745b2b3e07cff2b490f34611f336e9 100644 (file)
@@ -3,7 +3,7 @@
 class Api::V1::Statuses::FavouritesController < Api::BaseController
   include Authorization
 
-  before_action -> { doorkeeper_authorize! :write }
+  before_action -> { doorkeeper_authorize! :write, :'write:favourites' }
   before_action :require_user!
 
   respond_to :json
index a4bf0acdd26b2a478eb74cfc103f015e3b9b29ac..b02469b4f4b674d525b1230ca61d3d28c1f76ed9 100644 (file)
@@ -3,7 +3,7 @@
 class Api::V1::Statuses::MutesController < Api::BaseController
   include Authorization
 
-  before_action -> { doorkeeper_authorize! :write }
+  before_action -> { doorkeeper_authorize! :write, :'write:mutes' }
   before_action :require_user!
   before_action :set_status
   before_action :set_conversation
index 54f8be667d5d7aa1005799eff16f7a791920df18..4118a8ce4e51be709ef12d51c98155d43c423c3e 100644 (file)
@@ -3,7 +3,7 @@
 class Api::V1::Statuses::PinsController < Api::BaseController
   include Authorization
 
-  before_action -> { doorkeeper_authorize! :write }
+  before_action -> { doorkeeper_authorize! :write, :'write:accounts' }
   before_action :require_user!
   before_action :set_status
 
index b065db2c755e2d5c4758d104bbef751d5ca03897..93b83ce485179690dc23c83d78af0e419d43a138 100644 (file)
@@ -3,7 +3,7 @@
 class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController
   include Authorization
 
-  before_action :authorize_if_got_token
+  before_action -> { authorize_if_got_token! :read, :'read:accounts' }
   before_action :set_status
   after_action :insert_pagination_headers
 
@@ -68,11 +68,6 @@ class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController
     raise ActiveRecord::RecordNotFound
   end
 
-  def authorize_if_got_token
-    request_token = Doorkeeper::OAuth::Token.from_request(request, *Doorkeeper.configuration.access_token_methods)
-    doorkeeper_authorize! :read if request_token
-  end
-
   def pagination_params(core_params)
     params.slice(:limit).permit(:limit).merge(core_params)
   end
index 634af474f9ba73d65122bdf96db2b07fd24c09f7..04847a6b76f137efa7ef94e28aa32b7ec9e526cc 100644 (file)
@@ -3,7 +3,7 @@
 class Api::V1::Statuses::ReblogsController < Api::BaseController
   include Authorization
 
-  before_action -> { doorkeeper_authorize! :write }
+  before_action -> { doorkeeper_authorize! :write, :'write:statuses' }
   before_action :require_user!
 
   respond_to :json
index 289d910454d1fbd780ec98f328979a70e8746ab7..c6925d46292de0a837fb6fae8793bfc23e4d344f 100644 (file)
@@ -3,8 +3,8 @@
 class Api::V1::StatusesController < Api::BaseController
   include Authorization
 
-  before_action :authorize_if_got_token, except:            [:create, :destroy]
-  before_action -> { doorkeeper_authorize! :write }, only:  [:create, :destroy]
+  before_action -> { authorize_if_got_token! :read, :'read:statuses' }, except: [:create, :destroy]
+  before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, only:   [:create, :destroy]
   before_action :require_user!, except:  [:show, :context, :card]
   before_action :set_status, only:       [:show, :context, :card]
 
@@ -84,9 +84,4 @@ class Api::V1::StatusesController < Api::BaseController
   def pagination_params(core_params)
     params.slice(:limit).permit(:limit).merge(core_params)
   end
-
-  def authorize_if_got_token
-    request_token = Doorkeeper::OAuth::Token.from_request(request, *Doorkeeper.configuration.access_token_methods)
-    doorkeeper_authorize! :read if request_token
-  end
 end
index ef64078be871d87ec1ebfc1223f5fecfa143c5d5..d8a76d153a6c5167492af980a3e2562924d22131 100644 (file)
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 class Api::V1::Timelines::DirectController < Api::BaseController
-  before_action -> { doorkeeper_authorize! :read }, only: [:show]
+  before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: [:show]
   before_action :require_user!, only: [:show]
   after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
 
index cde4e8420c0e685328f627f3194ddaaeff5a8661..4412aaaa391c7f0dca66d03e7741e476c5aee86e 100644 (file)
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 class Api::V1::Timelines::HomeController < Api::BaseController
-  before_action -> { doorkeeper_authorize! :read }, only: [:show]
+  before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: [:show]
   before_action :require_user!, only: [:show]
   after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
 
index 06d596c08a1082d12ef2165cdabac0ede41ac000..cfc5f3b5e4d68326ffe03161f96ec5dd65863c93 100644 (file)
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 class Api::V1::Timelines::ListController < Api::BaseController
-  before_action -> { doorkeeper_authorize! :read }
+  before_action -> { doorkeeper_authorize! :read, :'read:lists' }
   before_action :require_user!
   before_action :set_list
   before_action :set_statuses
index 95863ab1f03e37cf055518eba09bc1874a66c915..327901e4e2d0e782080d6a511448cc45bc5c9fb0 100644 (file)
@@ -1,6 +1,12 @@
 # frozen_string_literal: true
 
 module ApplicationHelper
+  DANGEROUS_SCOPES = %w(
+    read
+    write
+    follow
+  ).freeze
+
   def active_nav_class(path)
     current_page?(path) ? 'active' : ''
   end
@@ -43,6 +49,10 @@ module ApplicationHelper
     Rails.env.production? ? site_title : "#{site_title} (Dev)"
   end
 
+  def class_for_scope(scope)
+    'scope-danger' if DANGEROUS_SCOPES.include?(scope.to_s)
+  end
+
   def can?(action, record)
     return false if record.nil?
     policy(record).public_send("#{action}?")
index e4fd6c1f1aeaa06ac1d422a0ec0602a1fd6cc242..458eb86e98f748ad4e7288c36d51e5cee7ed4701 100644 (file)
@@ -612,3 +612,7 @@ code {
     display: block;
   }
 }
+
+.scope-danger {
+  color: $warning-red;
+}
index b21f3cca62b2a717869776b84ca0f05e458c1ddc..db90df3491996f725488904979e5345c49f3197f 100644 (file)
@@ -8,14 +8,9 @@
   %p.hint= t('doorkeeper.applications.help.native_redirect_uri', native_redirect_uri: Doorkeeper.configuration.native_redirect_uri)
 
 .field-group
-  = f.input :scopes,
-    label: t('activerecord.attributes.doorkeeper/application.scopes'),
-    collection: Doorkeeper.configuration.scopes,
-    wrapper: :with_label,
-    include_blank: false,
-    label_method: lambda { |scope| safe_join([scope, content_tag(:span, t("doorkeeper.scopes.#{scope}"), class: 'hint')]) },
-    selected: f.object.scopes.all,
-    required: false,
-    as: :check_boxes,
-    collection_wrapper_tag: 'ul',
-    item_wrapper_tag: 'li'
+  .input.with_block_label
+    %label= t('activerecord.attributes.doorkeeper/application.scopes')
+    %span.hint= t('simple_form.hints.defaults.scopes')
+
+  - Doorkeeper.configuration.scopes.group_by { |s| s.split(':').first }.each do |k, v|
+    = f.input :scopes, label: false, hint: false, collection: v.sort, wrapper: :with_block_label, include_blank: false, label_method: lambda { |scope| safe_join([content_tag(:samp, scope, class: class_for_scope(scope)), content_tag(:span, t("doorkeeper.scopes.#{scope}"), class: 'hint')]) }, selected: f.object.scopes.all, required: false, as: :check_boxes, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li'
index 4695538030fb54b1b7a36e64d2b578168639f7d6..fe2490b326ababd31221d4b4a1662d8c3a274572 100644 (file)
@@ -55,7 +55,32 @@ Doorkeeper.configure do
   # For more information go to
   # https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes
   default_scopes  :read
-  optional_scopes :write, :follow, :push
+  optional_scopes :write,
+                  :'write:accounts',
+                  :'write:blocks',
+                  :'write:favourites',
+                  :'write:filters',
+                  :'write:follows',
+                  :'write:lists',
+                  :'write:media',
+                  :'write:mutes',
+                  :'write:notifications',
+                  :'write:reports',
+                  :'write:statuses',
+                  :read,
+                  :'read:accounts',
+                  :'read:blocks',
+                  :'read:favourites',
+                  :'read:filters',
+                  :'read:follows',
+                  :'read:lists',
+                  :'read:mutes',
+                  :'read:notifications',
+                  :'read:reports',
+                  :'read:search',
+                  :'read:statuses',
+                  :follow,
+                  :push
 
   # Change the way client credentials are retrieved from the request object.
   # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
index eca1fc675f101a0796a69aa212a217c8b45a7f28..f1fe03716a1605141f66cf1a973a20751f5fbddc 100644 (file)
@@ -114,7 +114,29 @@ en:
       application:
         title: OAuth authorization required
     scopes:
-      follow: follow, block, unblock and unfollow accounts
-      push: receive push notifications for your account
-      read: read your account's data
-      write: post on your behalf
+      follow: modify account relationships
+      push: receive your push notifications
+      read: read all your account's data
+      read:accounts: see accounts information
+      read:blocks: see your blocks
+      read:favourites: see your favourites
+      read:filters: see your filters
+      read:follows: see your follows
+      read:lists: see your lists
+      read:mutes: see your mutes
+      read:notifications: see your notifications
+      read:reports: see your reports
+      read:search: search on your behalf
+      read:statuses: see all statuses
+      write: modify all your account's data
+      write:accounts: modify your profile
+      write:blocks: block accounts and domains
+      write:favourites: favourite statuses
+      write:filters: create filters
+      write:follows: follow people
+      write:lists: create lists
+      write:media: upload media files
+      write:mutes: mute people and conversations
+      write:notifications: clear your notifications
+      write:reports: report other people
+      write:statuses: publish statuses
index 59133ea733c5bb2db4992e905e9a4be0314c1102..49d94bcde9e1d0cafd416021169043ac8d3e6fbf 100644 (file)
@@ -20,6 +20,7 @@ en:
           one: <span class="note-counter">1</span> character left
           other: <span class="note-counter">%{count}</span> characters left
         phrase: Will be matched regardless of casing in text or content warning of a toot
+        scopes: Which APIs the application will be allowed to access. If you select a top-level scope, you don't need to select individual ones.
         setting_default_language: The language of your toots can be detected automatically, but it's not always accurate
         setting_hide_network: Who you follow and who follows you will not be shown on your profile
         setting_noindex: Affects your public profile and status pages
index 9a52fd14c51ee01a2cb1459a7d0617ce4f9dc9dd..72766988635a23db3545b8926352b190bad7bb87 100644 (file)
@@ -4,7 +4,7 @@ describe Api::V1::Accounts::CredentialsController do
   render_views
 
   let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read write') }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
 
   context 'with an oauth token' do
     before do
@@ -12,6 +12,8 @@ describe Api::V1::Accounts::CredentialsController do
     end
 
     describe 'GET #show' do
+      let(:scopes) { 'read:accounts' }
+
       it 'returns http success' do
         get :show
         expect(response).to have_http_status(200)
@@ -19,6 +21,8 @@ describe Api::V1::Accounts::CredentialsController do
     end
 
     describe 'PATCH #update' do
+      let(:scopes) { 'write:accounts' }
+
       describe 'with valid data' do
         before do
           allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_async)
index b47af49631e39455e040c621d342bd48a552ec35..75e0570e92794368af77109ebc121aaa7724fb96 100644 (file)
@@ -4,7 +4,7 @@ describe Api::V1::Accounts::FollowerAccountsController do
   render_views
 
   let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
 
   before do
     Fabricate(:follow, target_account: user.account)
index 29fd7cd5b786d929ff89d44ce5d2146b08db018e..7f7105ad3aa3b68d0516d9344dfb6354b5fa4225 100644 (file)
@@ -4,7 +4,7 @@ describe Api::V1::Accounts::FollowingAccountsController do
   render_views
 
   let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
 
   before do
     Fabricate(:follow, account: user.account)
index df9fe0e34cc5b015621bf158de987511a7fb7218..baafea8e6bee876cdc8b921d1805244e73427e65 100644 (file)
@@ -4,7 +4,7 @@ describe Api::V1::Accounts::ListsController do
   render_views
 
   let(:user)    { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
-  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:lists') }
   let(:account) { Fabricate(:account) }
   let(:list)    { Fabricate(:list, account: user.account) }
 
index 7e350da7e5182943602edd58a7e0772c8198971d..fe715ff62597327c206178d8a5ee2d82448dcc1c 100644 (file)
@@ -4,7 +4,7 @@ describe Api::V1::Accounts::RelationshipsController do
   render_views
 
   let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:follows') }
 
   before do
     allow(controller).to receive(:doorkeeper_token) { token }
index dbc4b9f3e6e5db5ac00a34b4cb0044aab5cd717f..8ff2b17deed98bd1d9b4e0d010347b7d41c10f2d 100644 (file)
@@ -4,7 +4,7 @@ RSpec.describe Api::V1::Accounts::SearchController, type: :controller do
   render_views
 
   let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
 
   before do
     allow(controller).to receive(:doorkeeper_token) { token }
index 09bb469373eccd2795590c00ebac2459a0d11a65..693cd1ac66b3fc458a9721f259d1ec01a173142e 100644 (file)
@@ -4,7 +4,7 @@ describe Api::V1::Accounts::StatusesController do
   render_views
 
   let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:statuses') }
 
   before do
     allow(controller).to receive(:doorkeeper_token) { token }
index 7a9e0f8e41a59e8fa97a63fcdf0227d387e9a78f..3e54e88a5b79e8cf82347af596c57508d1e32eb1 100644 (file)
@@ -3,21 +3,38 @@ require 'rails_helper'
 RSpec.describe Api::V1::AccountsController, type: :controller do
   render_views
 
-  let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'follow read') }
+  let(:user)   { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
+  let(:scopes) { '' }
+  let(:token)  { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
 
   before do
     allow(controller).to receive(:doorkeeper_token) { token }
   end
 
+  shared_examples 'forbidden for wrong scope' do |wrong_scope|
+    let(:scopes) { wrong_scope }
+
+    it 'returns http forbidden' do
+      expect(response).to have_http_status(403)
+    end
+  end
+
   describe 'GET #show' do
-    it 'returns http success' do
+    let(:scopes) { 'read:accounts' }
+
+    before do
       get :show, params: { id: user.account.id }
+    end
+
+    it 'returns http success' do
       expect(response).to have_http_status(200)
     end
+
+    it_behaves_like 'forbidden for wrong scope', 'write:statuses'
   end
 
   describe 'POST #follow' do
+    let(:scopes) { 'write:follows' }
     let(:other_account) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob', locked: locked)).account }
 
     before do
@@ -41,6 +58,8 @@ RSpec.describe Api::V1::AccountsController, type: :controller do
       it 'creates a following relation between user and target user' do
         expect(user.account.following?(other_account)).to be true
       end
+
+      it_behaves_like 'forbidden for wrong scope', 'read:accounts'
     end
 
     context 'with locked account' do
@@ -60,10 +79,13 @@ RSpec.describe Api::V1::AccountsController, type: :controller do
       it 'creates a follow request relation between user and target user' do
         expect(user.account.requested?(other_account)).to be true
       end
+
+      it_behaves_like 'forbidden for wrong scope', 'read:accounts'
     end
   end
 
   describe 'POST #unfollow' do
+    let(:scopes) { 'write:follows' }
     let(:other_account) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account }
 
     before do
@@ -78,9 +100,12 @@ RSpec.describe Api::V1::AccountsController, type: :controller do
     it 'removes the following relation between user and target user' do
       expect(user.account.following?(other_account)).to be false
     end
+
+    it_behaves_like 'forbidden for wrong scope', 'read:accounts'
   end
 
   describe 'POST #block' do
+    let(:scopes) { 'write:blocks' }
     let(:other_account) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account }
 
     before do
@@ -99,9 +124,12 @@ RSpec.describe Api::V1::AccountsController, type: :controller do
     it 'creates a blocking relation' do
       expect(user.account.blocking?(other_account)).to be true
     end
+
+    it_behaves_like 'forbidden for wrong scope', 'read:accounts'
   end
 
   describe 'POST #unblock' do
+    let(:scopes) { 'write:blocks' }
     let(:other_account) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account }
 
     before do
@@ -116,9 +144,12 @@ RSpec.describe Api::V1::AccountsController, type: :controller do
     it 'removes the blocking relation between user and target user' do
       expect(user.account.blocking?(other_account)).to be false
     end
+
+    it_behaves_like 'forbidden for wrong scope', 'read:accounts'
   end
 
   describe 'POST #mute' do
+    let(:scopes) { 'write:mutes' }
     let(:other_account) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account }
 
     before do
@@ -141,9 +172,12 @@ RSpec.describe Api::V1::AccountsController, type: :controller do
     it 'mutes notifications' do
       expect(user.account.muting_notifications?(other_account)).to be true
     end
+
+    it_behaves_like 'forbidden for wrong scope', 'read:accounts'
   end
 
   describe 'POST #mute with notifications set to false' do
+    let(:scopes) { 'write:mutes' }
     let(:other_account) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account }
 
     before do
@@ -166,9 +200,12 @@ RSpec.describe Api::V1::AccountsController, type: :controller do
     it 'does not mute notifications' do
       expect(user.account.muting_notifications?(other_account)).to be false
     end
+
+    it_behaves_like 'forbidden for wrong scope', 'read:accounts'
   end
 
   describe 'POST #unmute' do
+    let(:scopes) { 'write:mutes' }
     let(:other_account) { Fabricate(:user, email: 'bob@example.com', account: Fabricate(:account, username: 'bob')).account }
 
     before do
@@ -183,5 +220,7 @@ RSpec.describe Api::V1::AccountsController, type: :controller do
     it 'removes the muting relation between user and target user' do
       expect(user.account.muting?(other_account)).to be false
     end
+
+    it_behaves_like 'forbidden for wrong scope', 'read:accounts'
   end
 end
index eff5fb9daa653f1313d43fec27a55daf0f93f851..818f76c92352d77151c39ef21901eb3a94f19ee4 100644 (file)
@@ -3,8 +3,9 @@ require 'rails_helper'
 RSpec.describe Api::V1::BlocksController, type: :controller do
   render_views
 
-  let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'follow') }
+  let(:user)   { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
+  let(:scopes) { 'read:blocks' }
+  let(:token)  { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
 
   before { allow(controller).to receive(:doorkeeper_token) { token } }
 
@@ -49,5 +50,14 @@ RSpec.describe Api::V1::BlocksController, type: :controller do
       get :index
       expect(response).to have_http_status(200)
     end
+
+    context 'with wrong scopes' do
+      let(:scopes) { 'write:blocks' }
+
+      it 'returns http forbidden' do
+        get :index
+        expect(response).to have_http_status(403)
+      end
+    end
   end
 end
index bae4612a280e75ea715b675eed7c2cc15b64132a..6a7a35c7a5158d23ffbe3b30df564987d47263bd 100644 (file)
@@ -4,14 +4,24 @@ RSpec.describe Api::V1::DomainBlocksController, type: :controller do
   render_views
 
   let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'follow') }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
 
   before do
     user.account.block_domain!('example.com')
     allow(controller).to receive(:doorkeeper_token) { token }
   end
 
+  shared_examples 'forbidden for wrong scope' do |wrong_scope|
+    let(:scopes) { wrong_scope }
+
+    it 'returns http forbidden' do
+      expect(response).to have_http_status(403)
+    end
+  end
+
   describe 'GET #show' do
+    let(:scopes) { 'read:blocks' }
+
     before do
       get :show, params: { limit: 1 }
     end
@@ -23,9 +33,13 @@ RSpec.describe Api::V1::DomainBlocksController, type: :controller do
     it 'returns blocked domains' do
       expect(body_as_json.first).to eq 'example.com'
     end
+
+    it_behaves_like 'forbidden for wrong scope', 'write:statuses'
   end
 
   describe 'POST #create' do
+    let(:scopes) { 'write:blocks' }
+
     before do
       post :create, params: { domain: 'example.org' }
     end
@@ -37,9 +51,13 @@ RSpec.describe Api::V1::DomainBlocksController, type: :controller do
     it 'creates a domain block' do
       expect(user.account.domain_blocking?('example.org')).to be true
     end
+
+    it_behaves_like 'forbidden for wrong scope', 'write:statuses'
   end
 
   describe 'DELETE #destroy' do
+    let(:scopes) { 'write:blocks' }
+
     before do
       delete :destroy, params: { domain: 'example.com' }
     end
@@ -51,5 +69,7 @@ RSpec.describe Api::V1::DomainBlocksController, type: :controller do
     it 'deletes a domain block' do
       expect(user.account.domain_blocking?('example.com')).to be false
     end
+
+    it_behaves_like 'forbidden for wrong scope', 'write:statuses'
   end
 end
index 46cf70f4d273557acb63df512e486e5f6c8ed2eb..2bdf927f21f7db2b75451ae87da9339896fefae3 100644 (file)
@@ -45,7 +45,7 @@ RSpec.describe Api::V1::FavouritesController, type: :controller do
       context 'with read scope and valid resource owner' do
         before do
           allow(controller).to receive(:doorkeeper_token) do
-            Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read')
+            Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:favourites')
           end
         end
 
index 3ffd8f784a9e18927650a05510516087ec9d4358..5948809e3f14306a39706bf693acb7e312f8bb68 100644 (file)
@@ -4,13 +4,14 @@ RSpec.describe Api::V1::FiltersController, type: :controller do
   render_views
 
   let(:user)  { Fabricate(:user) }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read write') }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
 
   before do
     allow(controller).to receive(:doorkeeper_token) { token }
   end
 
   describe 'GET #index' do
+    let(:scopes) { 'read:filters' }
     let!(:filter) { Fabricate(:custom_filter, account: user.account) }
 
     it 'returns http success' do
@@ -20,6 +21,8 @@ RSpec.describe Api::V1::FiltersController, type: :controller do
   end
 
   describe 'POST #create' do
+    let(:scopes) { 'write:filters' }
+
     before do
       post :create, params: { phrase: 'magic', context: %w(home), irreversible: true }
     end
@@ -39,6 +42,7 @@ RSpec.describe Api::V1::FiltersController, type: :controller do
   end
 
   describe 'GET #show' do
+    let(:scopes) { 'read:filters' }
     let(:filter) { Fabricate(:custom_filter, account: user.account) }
 
     it 'returns http success' do
@@ -48,6 +52,7 @@ RSpec.describe Api::V1::FiltersController, type: :controller do
   end
 
   describe 'PUT #update' do
+    let(:scopes) { 'write:filters' }
     let(:filter) { Fabricate(:custom_filter, account: user.account) }
 
     before do
@@ -64,6 +69,7 @@ RSpec.describe Api::V1::FiltersController, type: :controller do
   end
 
   describe 'DELETE #destroy' do
+    let(:scopes) { 'write:filters' }
     let(:filter) { Fabricate(:custom_filter, account: user.account) }
 
     before do
index 3c0b84af8b59296fbd51a3fa38068eeadbb2e995..87292d9ce1ee452e12203753d5b845da0fc405f1 100644 (file)
@@ -4,7 +4,7 @@ RSpec.describe Api::V1::FollowRequestsController, type: :controller do
   render_views
 
   let(:user)     { Fabricate(:user, account: Fabricate(:account, username: 'alice', locked: true)) }
-  let(:token)    { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'follow') }
+  let(:token)    { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
   let(:follower) { Fabricate(:account, username: 'bob') }
 
   before do
@@ -13,6 +13,8 @@ RSpec.describe Api::V1::FollowRequestsController, type: :controller do
   end
 
   describe 'GET #index' do
+    let(:scopes) { 'read:follows' }
+
     before do
       get :index, params: { limit: 1 }
     end
@@ -23,6 +25,8 @@ RSpec.describe Api::V1::FollowRequestsController, type: :controller do
   end
 
   describe 'POST #authorize' do
+    let(:scopes) { 'write:follows' }
+
     before do
       post :authorize, params: { id: follower.id }
     end
@@ -37,6 +41,8 @@ RSpec.describe Api::V1::FollowRequestsController, type: :controller do
   end
 
   describe 'POST #reject' do
+    let(:scopes) { 'write:follows' }
+
     before do
       post :reject, params: { id: follower.id }
     end
index 38badb80aeed634d9d1473d6add9dc35ee347730..089e0fe5ecadcb5462143bda6f924d16f2324a3c 100644 (file)
@@ -4,7 +4,7 @@ RSpec.describe Api::V1::FollowsController, type: :controller do
   render_views
 
   let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'follow') }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'write:follows') }
 
   before do
     allow(controller).to receive(:doorkeeper_token) { token }
index c37a481d6dc2db6eaf13722a16f275d3fefb545a..08c22de5687e4d93641ea5c6becca46bee976534 100644 (file)
@@ -4,7 +4,7 @@ describe Api::V1::Lists::AccountsController do
   render_views
 
   let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read write') }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
   let(:list)  { Fabricate(:list, account: user.account) }
 
   before do
@@ -14,6 +14,8 @@ describe Api::V1::Lists::AccountsController do
   end
 
   describe 'GET #index' do
+    let(:scopes) { 'read:lists' }
+
     it 'returns http success' do
       get :show, params: { list_id: list.id }
 
@@ -22,6 +24,7 @@ describe Api::V1::Lists::AccountsController do
   end
 
   describe 'POST #create' do
+    let(:scopes) { 'write:lists' }
     let(:bob) { Fabricate(:account, username: 'bob') }
 
     before do
@@ -39,6 +42,8 @@ describe Api::V1::Lists::AccountsController do
   end
 
   describe 'DELETE #destroy' do
+    let(:scopes) { 'write:lists' }
+
     before do
       delete :destroy, params: { list_id: list.id, account_ids: [list.accounts.first.id] }
     end
index 2134295815cb0b6d460cd84f4e935e89528c8d89..e92213789a306c95acee70026c7ab948437df709 100644 (file)
@@ -4,12 +4,14 @@ RSpec.describe Api::V1::ListsController, type: :controller do
   render_views
 
   let!(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
-  let!(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read write') }
+  let!(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
   let!(:list)  { Fabricate(:list, account: user.account) }
 
   before { allow(controller).to receive(:doorkeeper_token) { token } }
 
   describe 'GET #index' do
+    let(:scopes) { 'read:lists' }
+
     it 'returns http success' do
       get :index
       expect(response).to have_http_status(200)
@@ -17,6 +19,8 @@ RSpec.describe Api::V1::ListsController, type: :controller do
   end
 
   describe 'GET #show' do
+    let(:scopes) { 'read:lists' }
+
     it 'returns http success' do
       get :show, params: { id: list.id }
       expect(response).to have_http_status(200)
@@ -24,6 +28,8 @@ RSpec.describe Api::V1::ListsController, type: :controller do
   end
 
   describe 'POST #create' do
+    let(:scopes) { 'write:lists' }
+
     before do
       post :create, params: { title: 'Foo bar' }
     end
@@ -39,6 +45,8 @@ RSpec.describe Api::V1::ListsController, type: :controller do
   end
 
   describe 'PUT #update' do
+    let(:scopes) { 'write:lists' }
+
     before do
       put :update, params: { id: list.id, title: 'Updated title' }
     end
@@ -53,6 +61,8 @@ RSpec.describe Api::V1::ListsController, type: :controller do
   end
 
   describe 'DELETE #destroy' do
+    let(:scopes) { 'write:lists' }
+
     before do
       delete :destroy, params: { id: list.id }
     end
index ce260eb90a51e411c1dd827d88337274ec282b54..f01fcd9424c74e2efca6d386f959f860a4fd9b68 100644 (file)
@@ -4,7 +4,7 @@ RSpec.describe Api::V1::MediaController, type: :controller do
   render_views
 
   let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'write') }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'write:media') }
 
   before do
     allow(controller).to receive(:doorkeeper_token) { token }
index dc4a9753a0ad472ed8d31e792d7d918bcf89d32f..f9603b7ff1e645af72e9421c70f38ade1d32c454 100644 (file)
@@ -4,7 +4,7 @@ RSpec.describe Api::V1::MutesController, type: :controller do
   render_views
 
   let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'follow') }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:mutes') }
 
   before do
     Fabricate(:mute, account: user.account, hide_notifications: false)
index 2e6163fcd6f9275d81f33c1aab5df3c49ec20dda..9f679cb8a7594e8a247b74efe6c04dbfedbd1bca 100644 (file)
@@ -4,7 +4,7 @@ RSpec.describe Api::V1::NotificationsController, type: :controller do
   render_views
 
   let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
   let(:other) { Fabricate(:user, account: Fabricate(:account, username: 'bob')) }
 
   before do
@@ -12,6 +12,8 @@ RSpec.describe Api::V1::NotificationsController, type: :controller do
   end
 
   describe 'GET #show' do
+    let(:scopes) { 'read:notifications' }
+
     it 'returns http success' do
       notification = Fabricate(:notification, account: user.account)
       get :show, params: { id: notification.id }
@@ -21,6 +23,8 @@ RSpec.describe Api::V1::NotificationsController, type: :controller do
   end
 
   describe 'POST #dismiss' do
+    let(:scopes) { 'write:notifications' }
+
     it 'destroys the notification' do
       notification = Fabricate(:notification, account: user.account)
       post :dismiss, params: { id: notification.id }
@@ -31,6 +35,8 @@ RSpec.describe Api::V1::NotificationsController, type: :controller do
   end
 
   describe 'POST #clear' do
+    let(:scopes) { 'write:notifications' }
+
     it 'clears notifications for the account' do
       notification = Fabricate(:notification, account: user.account)
       post :clear
@@ -41,6 +47,8 @@ RSpec.describe Api::V1::NotificationsController, type: :controller do
   end
 
   describe 'GET #index' do
+    let(:scopes) { 'read:notifications' }
+
     before do
       first_status = PostStatusService.new.call(user.account, 'Test')
       @reblog_of_first_status = ReblogService.new.call(other.account, first_status)
index 1e1ef9308ba7eaa7af4907b966c5f7efc97ab145..ac93998c6901d0b2be8ad4b7df1593416ba57d65 100644 (file)
@@ -6,13 +6,15 @@ RSpec.describe Api::V1::ReportsController, type: :controller do
   render_views
 
   let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read write') }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
 
   before do
     allow(controller).to receive(:doorkeeper_token) { token }
   end
 
   describe 'GET #index' do
+    let(:scopes) { 'read:reports' }
+
     it 'returns http success' do
       get :index
 
@@ -21,6 +23,7 @@ RSpec.describe Api::V1::ReportsController, type: :controller do
   end
 
   describe 'POST #create' do
+    let(:scopes)  { 'write:reports' }
     let!(:status) { Fabricate(:status) }
     let!(:admin)  { Fabricate(:user, admin: true) }
 
index 0247038675739d1af29791d248c5c2a381c7b2c4..c9e544cc790ac4fe4066b0da229526287ccbc7ab 100644 (file)
@@ -6,7 +6,7 @@ RSpec.describe Api::V1::SearchController, type: :controller do
   render_views
 
   let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:search') }
 
   before do
     allow(controller).to receive(:doorkeeper_token) { token }
index c873e05dd86b97c346b8fe0e60b2069d5100a73f..23b5d7de92e39f7a63d89e3c2527b1edd1d3ebfe 100644 (file)
@@ -5,7 +5,7 @@ RSpec.describe Api::V1::Statuses::FavouritedByAccountsController, type: :control
 
   let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
   let(:app)   { Fabricate(:application, name: 'Test app', website: 'http://testapp.com') }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, application: app) }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, application: app, scopes: 'read:accounts') }
 
   context 'with an oauth token' do
     before do
index 53f602616d8ff5cd907a853036ed7421d10c9150..24a760e20e644a79e1b00ee4dae63c9c65d87f26 100644 (file)
@@ -7,7 +7,7 @@ describe Api::V1::Statuses::FavouritesController do
 
   let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
   let(:app)   { Fabricate(:application, name: 'Test app', website: 'http://testapp.com') }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'write', application: app) }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'write:favourites', application: app) }
 
   context 'with an oauth token' do
     before do
index 13b4625d19e8bd451fc17846fd48a5365af086d4..966398580d68a194da31b0122f79e946ffba9aea 100644 (file)
@@ -7,7 +7,7 @@ describe Api::V1::Statuses::MutesController do
 
   let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
   let(:app)   { Fabricate(:application, name: 'Test app', website: 'http://testapp.com') }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'write', application: app) }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'write:mutes', application: app) }
 
   context 'with an oauth token' do
     before do
index 8f5b0800b0b11345fc194c69e8d0e556523c5c9d..13405d285d29ea1a326900b566a517ccfe2e1aac 100644 (file)
@@ -7,7 +7,7 @@ describe Api::V1::Statuses::PinsController do
 
   let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
   let(:app)   { Fabricate(:application, name: 'Test app', website: 'http://testapp.com') }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'write', application: app) }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'write:accounts', application: app) }
 
   context 'with an oauth token' do
     before do
index 9c0c2b60cb2be3c759e7543dad9048d97b73fef3..d758786dc99c07003f8130f809452a04ea73a1fa 100644 (file)
@@ -5,7 +5,7 @@ RSpec.describe Api::V1::Statuses::RebloggedByAccountsController, type: :controll
 
   let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
   let(:app)   { Fabricate(:application, name: 'Test app', website: 'http://testapp.com') }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, application: app) }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, application: app, scopes: 'read:accounts') }
 
   context 'with an oauth token' do
     before do
index e60f8da2a4f4a6debea9e97cd96505bdc11abbfd..d14ca3e8b6ad5ac224c7ef1d50f78dd683a733be 100644 (file)
@@ -7,7 +7,7 @@ describe Api::V1::Statuses::ReblogsController do
 
   let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
   let(:app)   { Fabricate(:application, name: 'Test app', website: 'http://testapp.com') }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'write', application: app) }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'write:statuses', application: app) }
 
   context 'with an oauth token' do
     before do
index 27e4f4eb2468fa1b58eeb21c78e4ded930b5352a..8bc3b0c6718bb85cb5a40928906b8e552c2877bf 100644 (file)
@@ -5,7 +5,7 @@ RSpec.describe Api::V1::StatusesController, type: :controller do
 
   let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
   let(:app)   { Fabricate(:application, name: 'Test app', website: 'http://testapp.com') }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, application: app, scopes: 'write') }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, application: app, scopes: scopes) }
 
   context 'with an oauth token' do
     before do
@@ -13,6 +13,7 @@ RSpec.describe Api::V1::StatusesController, type: :controller do
     end
 
     describe 'GET #show' do
+      let(:scopes) { 'read:statuses' }
       let(:status) { Fabricate(:status, account: user.account) }
 
       it 'returns http success' do
@@ -22,6 +23,7 @@ RSpec.describe Api::V1::StatusesController, type: :controller do
     end
 
     describe 'GET #context' do
+      let(:scopes) { 'read:statuses' }
       let(:status) { Fabricate(:status, account: user.account) }
 
       before do
@@ -35,6 +37,8 @@ RSpec.describe Api::V1::StatusesController, type: :controller do
     end
 
     describe 'POST #create' do
+      let(:scopes) { 'write:statuses' }
+
       before do
         post :create, params: { status: 'Hello world' }
       end
@@ -45,6 +49,7 @@ RSpec.describe Api::V1::StatusesController, type: :controller do
     end
 
     describe 'DELETE #destroy' do
+      let(:scopes) { 'write:statuses' }
       let(:status) { Fabricate(:status, account: user.account) }
 
       before do
index 85b0316418150ac82c98eff22ebbeea8228dffae..a667c33fadb8126bf8212bf606cb43f1501c4793 100644 (file)
@@ -12,7 +12,7 @@ describe Api::V1::Timelines::HomeController do
   end
 
   context 'with a user context' do
-    let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') }
+    let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:statuses') }
 
     describe 'GET #show' do
       before do
index 1729217c91a14ee246e0b9f3cc3e42eb89cfe549..93a2be6e6cd747aa38b4392c034efecc9d848fdb 100644 (file)
@@ -13,7 +13,7 @@ describe Api::V1::Timelines::ListController do
   end
 
   context 'with a user context' do
-    let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') }
+    let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:lists') }
 
     describe 'GET #show' do
       before do
diff --git a/spec/controllers/api/v2/search_controller_spec.rb b/spec/controllers/api/v2/search_controller_spec.rb
new file mode 100644 (file)
index 0000000..8ee8753
--- /dev/null
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe Api::V2::SearchController, type: :controller do
+  render_views
+
+  let(:user)  { Fabricate(:user, account: Fabricate(:account, username: 'alice')) }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:search') }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index, params: { q: 'test' }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end