discourse/plugins/discourse-templates/app/controllers/discourse_templates/templates_controller.rb
Alan Guo Xiang Tan c9391cdc75
FIX: Check topic visibility in templates use action (#37980)
What is the problem?

- The discourse-templates plugin lets users mark topics as
  templates and track usage counts via `TemplatesController#use`
- The `use` action checks that the caller is logged in and passes the
  global `can_use_templates?` gate, then verifies the topic is a valid
  template via `Topic#template?`
- However, `Topic#template?` for category templates only checks whether
  the topic's category is in the configured template categories list —
  it does not check whether the calling user can see that category
- If an admin configures both a public and a restricted category as
  template sources, a regular user with access to the public category
  passes the global gate and can then call the `use` endpoint with a
  topic ID from the restricted category and succeed

What is the solution?

- Add `guardian.ensure_can_see!(topic)` to `TemplatesController#use`
2026-02-23 16:18:17 +08:00

49 lines
1.6 KiB
Ruby
Vendored

# frozen_string_literal: true
module DiscourseTemplates
class TemplatesController < ::ApplicationController
requires_plugin PLUGIN_NAME
before_action :ensure_logged_in
before_action :ensure_discourse_templates_enabled
skip_before_action :check_xhr
def ensure_discourse_templates_enabled
raise Discourse::InvalidAccess.new unless guardian.can_use_templates?
end
def use
template_id = params.require(:id)
topic = Topic.find_by(id: template_id)
return render_json_error("Invalid template id", status: 422) if topic.blank?
guardian.ensure_can_see!(topic)
unless topic.template?(current_user)
return(render_json_error("Id does not belong to a template", status: 422))
end
record = topic.increment_template_item_usage_count!
render json: record
end
def index
list_options = {
# limit defined in a hidden setting with a sane default value (1000) that should be enough to fetch all
# templates at once in most cases, but it still small enough to prevent things to blow up if the user
# selected the wrong category in settings with thousands and thousands of posts
per_page: SiteSetting.discourse_templates_max_replies_fetched.to_i,
}
topic_query = TopicQuery.new(current_user, list_options)
category_templates = topic_query.list_category_templates&.topics || []
private_templates = topic_query.list_private_templates&.topics || []
templates = category_templates + private_templates
render json: templates, each_serializer: TemplatesSerializer
end
end
end