From 8d96761a4f67810daf3019ebd16dc3d952996b0c Mon Sep 17 00:00:00 2001 From: Osama Sayegh Date: Thu, 10 Mar 2022 22:01:22 +0300 Subject: [PATCH] FIX: Include engine mount path for API scopes added by plugins (#16154) In the API keys page where admins can create API keys with restricted scopes, each scope shows a list of URLs that it allows. But currently, this list of allowed URLs shows incomplete URLs for scopes that are added by plugins. For example, the allowed URL for the "run queries" scope of the data-explorer plugin is shown as `/queries/:id/run` when the correct URL for this scope is `/admin/plugins/explorer/queries/:id/run`. The first 3 segments of the path are the mount path of the plugin's engine and it's missing because the routes set of the engine doesn't include the mount path. To fix this, this commit gets the mount path and prepends it to the URL so the complete URL is shown to the user. It's not possible to write tests for this change because plugins are not loaded in the test environment by default when core's tests suite is running. --- app/models/api_key_scope.rb | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/app/models/api_key_scope.rb b/app/models/api_key_scope.rb index 9d26803ec40..a0b86bf590b 100644 --- a/app/models/api_key_scope.rb +++ b/app/models/api_key_scope.rb @@ -106,22 +106,26 @@ class ApiKeyScope < ActiveRecord::Base urls = [] if actions.present? - routes = Rails.application.routes.routes.to_a + route_sets = [Rails.application.routes] Rails::Engine.descendants.each do |engine| next if engine == Rails::Application # abstract engine, can't call routes on it next if engine == Discourse::Application # equiv. to Rails.application - routes.concat(engine.routes.routes.to_a) + route_sets << engine.routes end - routes.each do |route| - defaults = route.defaults - action = "#{defaults[:controller].to_s}##{defaults[:action]}" - path = route.path.spec.to_s.gsub(/\(\.:format\)/, '') - api_supported_path = path.end_with?('.rss') || route.path.requirements[:format]&.match?('json') - excluded_paths = %w[/new-topic /new-message /exception] + route_sets.each do |set| + engine_mount_path = set.find_script_name({}).presence + engine_mount_path = nil if engine_mount_path == "/" + set.routes.each do |route| + defaults = route.defaults + action = "#{defaults[:controller].to_s}##{defaults[:action]}" + path = route.path.spec.to_s.gsub(/\(\.:format\)/, '') + api_supported_path = path.end_with?('.rss') || route.path.requirements[:format]&.match?('json') + excluded_paths = %w[/new-topic /new-message /exception] - if actions.include?(action) && api_supported_path && !excluded_paths.include?(path) - urls << "#{path} (#{route.verb})" + if actions.include?(action) && api_supported_path && !excluded_paths.include?(path) + urls << "#{engine_mount_path}#{path} (#{route.verb})" + end end end end