FEATURE: Allow plugins to register parameter-based API routes (#10505)

Example usage:

```
add_api_parameter_route(
  method: :get,
  route: "users#bookmarks",
  format: :ics
)
```
This commit is contained in:
David Taylor 2020-08-24 10:24:52 +01:00 committed by GitHub
parent 23b823dd3a
commit 629ee5494d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 34 additions and 1 deletions

View File

@ -359,6 +359,10 @@ class Auth::DefaultCurrentUserProvider
private private
def parameter_api_patterns
PARAMETER_API_PATTERNS + DiscoursePluginRegistry.api_parameter_routes
end
# By default we only allow headers for sending API credentials # By default we only allow headers for sending API credentials
# However, in some scenarios it is essential to send them via url parameters # However, in some scenarios it is essential to send them via url parameters
# so we need to add some exceptions # so we need to add some exceptions
@ -369,7 +373,7 @@ class Auth::DefaultCurrentUserProvider
path_params = @env['action_dispatch.request.path_parameters'] path_params = @env['action_dispatch.request.path_parameters']
request_route = "#{path_params[:controller]}##{path_params[:action]}" if path_params request_route = "#{path_params[:controller]}##{path_params[:action]}" if path_params
PARAMETER_API_PATTERNS.any? do |p| parameter_api_patterns.any? do |p|
(p[:method] == "*" || Array(p[:method]).include?(request_method)) && (p[:method] == "*" || Array(p[:method]).include?(request_method)) &&
(p[:format] == "*" || Array(p[:format]).include?(request_format)) && (p[:format] == "*" || Array(p[:format]).include?(request_format)) &&
(p[:route] == "*" || Array(p[:route]).include?(request_route)) (p[:route] == "*" || Array(p[:route]).include?(request_route))

View File

@ -79,6 +79,7 @@ class DiscoursePluginRegistry
define_filtered_register :topic_thumbnail_sizes define_filtered_register :topic_thumbnail_sizes
define_filtered_register :api_parameter_routes
define_filtered_register :api_key_scope_mappings define_filtered_register :api_key_scope_mappings
def self.register_auth_provider(auth_provider) def self.register_auth_provider(auth_provider)

View File

@ -782,6 +782,21 @@ class Plugin::Instance
DiscoursePluginRegistry.register_api_key_scope_mapping({ resource => action }, self) DiscoursePluginRegistry.register_api_key_scope_mapping({ resource => action }, self)
end end
# Register a route which can be authenticated using an api key or user api key
# in a query parameter rather than a header. For example:
#
# add_api_parameter_route(
# method: :get,
# route: "users#bookmarks",
# format: :ics
# )
#
# See Auth::DefaultCurrentUserProvider::PARAMETER_API_PATTERNS for more examples
# and Auth::DefaultCurrentUserProvider#api_parameter_allowed? for implementation
def add_api_parameter_route(method:, route:, format:)
DiscoursePluginRegistry.register_api_parameter_route({ method: method, route: route, format: format }, self)
end
protected protected
def self.js_path def self.js_path

View File

@ -48,6 +48,19 @@ describe 'api keys' do
expect(response.status).to eq(302) expect(response.status).to eq(302)
end end
context "with a plugin registered filter" do
before do
plugin = Plugin::Instance.new
plugin.add_api_parameter_route method: :get, route: "session#current", format: "*"
end
it 'allows parameter access to the registered route' do
get '/session/current.json', params: {
api_key: api_key.key
}
expect(response.status).to eq(200)
end
end
end end
describe 'user api keys' do describe 'user api keys' do