diff --git a/app/controllers/sidebar_sections_controller.rb b/app/controllers/sidebar_sections_controller.rb index 0c9a919d18f..5e84793d841 100644 --- a/app/controllers/sidebar_sections_controller.rb +++ b/app/controllers/sidebar_sections_controller.rb @@ -29,6 +29,8 @@ class SidebarSectionsController < ApplicationController render_serialized(sidebar_section, SidebarSectionSerializer) rescue ActiveRecord::RecordInvalid => e render_json_error(e.record.errors.full_messages.first) + rescue ActiveRecord::NestedAttributes::TooManyRecords => e + render_json_error(e.message) end def update @@ -62,6 +64,8 @@ class SidebarSectionsController < ApplicationController render_serialized(sidebar_section.reload, SidebarSectionSerializer) rescue ActiveRecord::RecordInvalid => e render_json_error(e.record.errors.full_messages.first) + rescue ActiveRecord::NestedAttributes::TooManyRecords => e + render_json_error(e.message) rescue Discourse::InvalidAccess render json: failed_json, status: 403 end diff --git a/app/models/sidebar_section.rb b/app/models/sidebar_section.rb index 0162ca5fe4b..3b564c3faf0 100644 --- a/app/models/sidebar_section.rb +++ b/app/models/sidebar_section.rb @@ -11,7 +11,9 @@ class SidebarSection < ActiveRecord::Base source: :linkable, source_type: "SidebarUrl" - accepts_nested_attributes_for :sidebar_urls, allow_destroy: true + accepts_nested_attributes_for :sidebar_urls, + allow_destroy: true, + limit: -> { SiteSetting.max_sidebar_section_links } before_save :set_system_user_for_public_section diff --git a/config/site_settings.yml b/config/site_settings.yml index 52f857e45ad..e1d65ce5cea 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -2137,6 +2137,9 @@ developer: default: "" allow_any: false refresh: true + max_sidebar_section_links: + default: 50 + hidden: true navigation: navigation_menu: diff --git a/spec/requests/sidebar_sections_controller_spec.rb b/spec/requests/sidebar_sections_controller_spec.rb index 61124d3e7e4..a8a0bb3764a 100644 --- a/spec/requests/sidebar_sections_controller_spec.rb +++ b/spec/requests/sidebar_sections_controller_spec.rb @@ -87,6 +87,24 @@ RSpec.describe SidebarSectionsController do expect(sidebar_section.sidebar_urls.fourth.external).to be false end + it "validates max number of links" do + SiteSetting.max_sidebar_section_links = 5 + + sign_in(user) + + links = + 6.times.map do + { icon: "external-link-alt", name: "My preferences", value: "/my/preferences" } + end + + post "/sidebar_sections.json", params: { title: "custom section", links: links } + + expect(response.status).to eq(422) + expect(response.parsed_body["errors"]).to eq( + ["Maximum 5 records are allowed. Got 6 records instead."], + ) + end + it "does not allow regular user to create public section" do sign_in(user) @@ -236,6 +254,28 @@ RSpec.describe SidebarSectionsController do ) end + it "validates limit of links" do + SiteSetting.max_sidebar_section_links = 5 + + sign_in(user) + + links = + 6.times.map do + { icon: "external-link-alt", name: "My preferences", value: "/my/preferences" } + end + + put "/sidebar_sections/#{sidebar_section.id}.json", + params: { + title: "custom section", + links: links, + } + + expect(response.status).to eq(422) + expect(response.parsed_body["errors"]).to eq( + ["Maximum 5 records are allowed. Got 6 records instead."], + ) + end + it "doesn't allow to edit other's sections" do sidebar_section_2 = Fabricate(:sidebar_section) sidebar_url_3 = Fabricate(:sidebar_url, name: "other_tags", value: "/tags")