2020-07-16 14:51:24 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
class ApiKeyScope < ActiveRecord::Base
|
|
|
|
validates_presence_of :resource
|
|
|
|
validates_presence_of :action
|
|
|
|
|
|
|
|
class << self
|
|
|
|
def list_actions
|
2020-08-18 14:12:04 -04:00
|
|
|
actions = %w[list#category_feed]
|
2020-07-16 14:51:24 -04:00
|
|
|
|
|
|
|
TopTopic.periods.each do |p|
|
|
|
|
actions.concat(["list#category_top_#{p}", "list#top_#{p}", "list#top_#{p}_feed"])
|
|
|
|
end
|
|
|
|
|
|
|
|
%i[latest unread new top].each { |f| actions.concat(["list#category_#{f}", "list##{f}"]) }
|
|
|
|
|
|
|
|
actions
|
|
|
|
end
|
|
|
|
|
|
|
|
def default_mappings
|
2020-08-24 11:15:08 -04:00
|
|
|
return @default_mappings unless @default_mappings.nil?
|
2020-08-18 14:12:04 -04:00
|
|
|
|
2020-08-24 11:15:08 -04:00
|
|
|
mappings = {
|
2020-07-16 14:51:24 -04:00
|
|
|
topics: {
|
2020-08-24 11:15:08 -04:00
|
|
|
write: { actions: %w[posts#create], params: %i[topic_id] },
|
2020-08-18 14:12:04 -04:00
|
|
|
read: {
|
2020-08-24 11:15:08 -04:00
|
|
|
actions: %w[topics#show topics#feed topics#posts],
|
|
|
|
params: %i[topic_id], aliases: { topic_id: :id }
|
2020-08-18 14:12:04 -04:00
|
|
|
},
|
|
|
|
read_lists: {
|
|
|
|
actions: list_actions, params: %i[category_id],
|
2020-08-24 11:15:08 -04:00
|
|
|
aliases: { category_id: :category_slug_path_with_id }
|
|
|
|
},
|
|
|
|
wordpress: { actions: %w[topics#wordpress], params: %i[topic_id] }
|
|
|
|
},
|
|
|
|
users: {
|
|
|
|
bookmarks: { actions: %w[users#bookmarks], params: %i[username] },
|
|
|
|
sync_sso: { actions: %w[admin/users#sync_sso], params: %i[sso sig] },
|
|
|
|
show: { actions: %w[users#show], params: %i[username external_id] },
|
|
|
|
check_emails: { actions: %w[users#check_emails], params: %i[username] }
|
2020-07-16 14:51:24 -04:00
|
|
|
}
|
|
|
|
}
|
2020-08-24 11:15:08 -04:00
|
|
|
|
|
|
|
mappings.each_value do |resource_actions|
|
|
|
|
resource_actions.each_value do |action_data|
|
|
|
|
action_data[:urls] = find_urls(action_data[:actions])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
@default_mappings = mappings
|
2020-07-16 14:51:24 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def scope_mappings
|
|
|
|
plugin_mappings = DiscoursePluginRegistry.api_key_scope_mappings
|
|
|
|
|
|
|
|
default_mappings.tap do |mappings|
|
|
|
|
plugin_mappings.each do |mapping|
|
2020-08-18 14:12:04 -04:00
|
|
|
mapping[:urls] = find_urls(mapping[:actions])
|
|
|
|
|
2020-07-16 14:51:24 -04:00
|
|
|
mappings.deep_merge!(mapping)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2020-08-18 14:12:04 -04:00
|
|
|
|
|
|
|
def find_urls(actions)
|
|
|
|
Rails.application.routes.routes.reduce([]) do |memo, 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]
|
|
|
|
|
|
|
|
memo.tap do |m|
|
|
|
|
m << path if actions.include?(action) && api_supported_path && !excluded_paths.include?(path)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2020-07-16 14:51:24 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def permits?(route_param)
|
|
|
|
path_params = "#{route_param['controller']}##{route_param['action']}"
|
|
|
|
|
|
|
|
mapping[:actions].include?(path_params) && (allowed_parameters.blank? || params_allowed?(route_param))
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def params_allowed?(route_param)
|
|
|
|
mapping[:params].all? do |param|
|
|
|
|
param_alias = mapping.dig(:aliases, param)
|
|
|
|
allowed_values = [allowed_parameters[param.to_s]].flatten
|
|
|
|
value = route_param[param.to_s]
|
|
|
|
alias_value = route_param[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 mapping
|
|
|
|
@mapping ||= self.class.scope_mappings.dig(resource.to_sym, action.to_sym)
|
|
|
|
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
|
|
|
|
end
|
|
|
|
|
|
|
|
# == Schema Information
|
|
|
|
#
|
|
|
|
# Table name: api_key_scopes
|
|
|
|
#
|
|
|
|
# id :bigint not null, primary key
|
|
|
|
# api_key_id :integer not null
|
|
|
|
# resource :string not null
|
|
|
|
# action :string not null
|
|
|
|
# allowed_parameters :json
|
|
|
|
# created_at :datetime not null
|
|
|
|
# updated_at :datetime not null
|
|
|
|
#
|
|
|
|
# Indexes
|
|
|
|
#
|
|
|
|
# index_api_key_scopes_on_api_key_id (api_key_id)
|
|
|
|
#
|