mirror of
https://github.com/discourse/discourse.git
synced 2026-08-04 10:39:43 +08:00
This ensures that the parameters stored include the method, that way there is no caching collision between different methods.
111 lines
3.5 KiB
Ruby
Vendored
111 lines
3.5 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
class RouteMatcher
|
|
PATH_PARAMETERS = "_DISCOURSE_REQUEST_PATH_PARAMETERS"
|
|
|
|
attr_reader :actions, :params, :methods, :aliases, :formats, :allowed_param_values
|
|
|
|
def initialize(
|
|
actions: nil,
|
|
params: nil,
|
|
methods: nil,
|
|
formats: nil,
|
|
aliases: nil,
|
|
allowed_param_values: nil
|
|
)
|
|
@actions = Array(actions) if actions
|
|
@params = Array(params) if params
|
|
@methods = Array(methods) if methods
|
|
@formats = Array(formats) if formats
|
|
@aliases = aliases
|
|
@allowed_param_values = allowed_param_values
|
|
end
|
|
|
|
# Return an identical route matcher, with the allowed_param_values replaced
|
|
def with_allowed_param_values(new_allowed_param_values)
|
|
RouteMatcher.new(
|
|
actions: actions,
|
|
params: params,
|
|
methods: methods,
|
|
formats: formats,
|
|
aliases: aliases,
|
|
allowed_param_values: new_allowed_param_values,
|
|
)
|
|
end
|
|
|
|
def match?(env:)
|
|
request = ActionDispatch::Request.new(env)
|
|
|
|
action_allowed?(request) && params_allowed?(request) && method_allowed?(request) &&
|
|
format_allowed?(request)
|
|
end
|
|
|
|
private
|
|
|
|
def action_allowed?(request)
|
|
return true if actions.nil? # actions are unrestricted
|
|
|
|
# message_bus is not a rails route, special handling
|
|
return true if actions.include?("message_bus") && request.fullpath =~ %r{\A/message-bus/.*/poll}
|
|
|
|
# logster is not a rails route, special handling
|
|
return true if actions.include?(Logster::Web) && request.fullpath =~ %r{\A/logs/.*\.json\z}
|
|
|
|
path_params = path_params_from_request(request)
|
|
actions.include? "#{path_params[:controller]}##{path_params[:action]}"
|
|
end
|
|
|
|
def params_allowed?(request)
|
|
return true if params.nil? || allowed_param_values.blank? # params are unrestricted
|
|
|
|
requested_params = request.parameters
|
|
|
|
params.all? do |param|
|
|
param_alias = aliases&.[](param)
|
|
allowed_values = [allowed_param_values.fetch(param.to_s, [])].flatten
|
|
|
|
value = requested_params[param.to_s]
|
|
alias_value = requested_params[param_alias.to_s]
|
|
|
|
return false if value.present? && alias_value.present?
|
|
|
|
value = value || alias_value
|
|
value = extract_category_id(value) if param_alias == :category_slug_path_with_id
|
|
|
|
allowed_values.blank? || allowed_values.include?(value)
|
|
end
|
|
end
|
|
|
|
def extract_category_id(category_slug_with_id)
|
|
parts = category_slug_with_id.split("/")
|
|
!parts.empty? && parts.last =~ /\A\d+\Z/ ? parts.pop : nil
|
|
end
|
|
|
|
def method_allowed?(request)
|
|
return true if methods.nil?
|
|
request_method = request.request_method&.downcase&.to_sym
|
|
methods.include?(request_method)
|
|
end
|
|
|
|
def format_allowed?(request)
|
|
return true if formats.nil?
|
|
request_format = request.formats&.first&.symbol
|
|
formats.include?(request_format)
|
|
end
|
|
|
|
def path_params_from_request(request)
|
|
if request.env[ActionDispatch::Http::Parameters::PARAMETERS_KEY].nil?
|
|
# We need to manually recognize the path when Rails hasn't done that yet. That can happen when
|
|
# the matcher gets called in a Middleware before the controller did its work.
|
|
# We store the result of `recognize_path` in a custom env key, so that we don't change
|
|
# some Rails behavior by accident.
|
|
request.env[PATH_PARAMETERS] ||= begin
|
|
Rails.application.routes.recognize_path(request.path_info, method: request.request_method)
|
|
rescue ActionController::RoutingError
|
|
{}
|
|
end
|
|
end
|
|
|
|
request.path_parameters.presence || request.env[PATH_PARAMETERS] || {}
|
|
end
|
|
end
|