FIX: Allow plugins to correctly extend API key scopes. (#12113)

Adding a scope from a plugin was broken. This commit fixes it and adds a test.

It also documents the instance method and renames the serialized "id" attribute to "scope_id" to avoid a conflict when the scope also has a parameter with the same name.
This commit is contained in:
Roman Rizzi 2021-02-17 14:42:44 -03:00 committed by GitHub
parent a174c8b8d4
commit 07cf0f9460
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 31 additions and 10 deletions

View File

@ -27,7 +27,7 @@ class Admin::ApiController < Admin::AdminController
memo.tap do |m| memo.tap do |m|
m[resource] = actions.map do |k, v| m[resource] = actions.map do |k, v|
{ {
id: "#{resource}:#{k}", scope_id: "#{resource}:#{k}",
key: k, key: k,
name: k.to_s.gsub('_', ' '), name: k.to_s.gsub('_', ' '),
params: v[:params], params: v[:params],
@ -99,7 +99,7 @@ class Admin::ApiController < Admin::AdminController
def build_scopes def build_scopes
params.require(:key)[:scopes].to_a.map do |scope_params| params.require(:key)[:scopes].to_a.map do |scope_params|
resource, action = scope_params[:id].split(':') resource, action = scope_params[:scope_id].split(':')
mapping = ApiKeyScope.scope_mappings.dig(resource.to_sym, action.to_sym) mapping = ApiKeyScope.scope_mappings.dig(resource.to_sym, action.to_sym)
raise Discourse::InvalidParameters if mapping.nil? # invalid mapping raise Discourse::InvalidParameters if mapping.nil? # invalid mapping

View File

@ -36,7 +36,7 @@ class ApiKeyScope < ActiveRecord::Base
users: { users: {
bookmarks: { actions: %w[users#bookmarks], params: %i[username] }, bookmarks: { actions: %w[users#bookmarks], params: %i[username] },
sync_sso: { actions: %w[admin/users#sync_sso], params: %i[sso sig] }, sync_sso: { actions: %w[admin/users#sync_sso], params: %i[sso sig] },
show: { actions: %w[users#show], params: %i[username external_id] }, show: { actions: %w[users#show], params: %i[username external_id external_provider] },
check_emails: { actions: %w[users#check_emails], params: %i[username] }, check_emails: { actions: %w[users#check_emails], params: %i[username] },
update: { actions: %w[users#update], params: %i[username] }, update: { actions: %w[users#update], params: %i[username] },
log_out: { actions: %w[admin/users#log_out] }, log_out: { actions: %w[admin/users#log_out] },
@ -61,10 +61,15 @@ class ApiKeyScope < ActiveRecord::Base
plugin_mappings = DiscoursePluginRegistry.api_key_scope_mappings plugin_mappings = DiscoursePluginRegistry.api_key_scope_mappings
default_mappings.tap do |mappings| default_mappings.tap do |mappings|
plugin_mappings.each do |mapping| plugin_mappings.each do |resource|
mapping[:urls] = find_urls(mapping[:actions])
mappings.deep_merge!(mapping) resource.each_value do |resource_actions|
resource_actions.each_value do |action_data|
action_data[:urls] = find_urls(action_data[:actions])
end
end
mappings.deep_merge!(resource)
end end
end end
end end

View File

@ -791,6 +791,13 @@ class Plugin::Instance
end end
end end
# Register a new API key scope.
#
# Example:
# add_api_key_scope(:groups, { delete: { actions: %w[groups#add_members], params: %i[id] } })
#
# This scope lets you add members to a group. Additionally, you can specify which group ids are allowed.
# The delete action is added to the groups resource.
def add_api_key_scope(resource, action) def add_api_key_scope(resource, action)
DiscoursePluginRegistry.register_api_key_scope_mapping({ resource => action }, self) DiscoursePluginRegistry.register_api_key_scope_mapping({ resource => action }, self)
end end

View File

@ -593,4 +593,13 @@ describe Plugin::Instance do
expect(ReviewableScore.types.values.max).to eq(highest_flag_id + 2) expect(ReviewableScore.types.values.max).to eq(highest_flag_id + 2)
end end
end end
describe '#add_api_key_scope' do
it 'adds a custom api key scope' do
actions = %w[admin/groups#create]
subject.add_api_key_scope(:groups, create: { actions: actions })
expect(ApiKeyScope.scope_mappings.dig(:groups, :create, :actions)).to contain_exactly(*actions)
end
end
end end

View File

@ -137,7 +137,7 @@ describe Admin::ApiController do
post "/admin/api/keys.json", params: { post "/admin/api/keys.json", params: {
key: { key: {
description: "master key description", description: "master key description",
scopes: [{ id: 'topics:write', topic_id: '55' }] scopes: [{ scope_id: 'topics:write', topic_id: '55' }]
} }
} }
expect(response.status).to eq(200) expect(response.status).to eq(200)
@ -154,7 +154,7 @@ describe Admin::ApiController do
post "/admin/api/keys.json", params: { post "/admin/api/keys.json", params: {
key: { key: {
description: "master key description", description: "master key description",
scopes: [{ id: 'topics:write', topic_id: '55,33' }] scopes: [{ scope_id: 'topics:write', topic_id: '55,33' }]
} }
} }
expect(response.status).to eq(200) expect(response.status).to eq(200)
@ -170,7 +170,7 @@ describe Admin::ApiController do
post "/admin/api/keys.json", params: { post "/admin/api/keys.json", params: {
key: { key: {
description: "master key description", description: "master key description",
scopes: [{ id: 'topics:write', fake_id: '55' }] scopes: [{ scope_id: 'topics:write', fake_id: '55' }]
} }
} }
@ -186,7 +186,7 @@ describe Admin::ApiController do
post "/admin/api/keys.json", params: { post "/admin/api/keys.json", params: {
key: { key: {
description: "master key description", description: "master key description",
scopes: [{ id: 'something:else' }] scopes: [{ scope_id: 'something:else' }]
} }
} }