diff --git a/app/controllers/admin/themes_controller.rb b/app/controllers/admin/themes_controller.rb index 045cc6de0b6..5547b13de20 100644 --- a/app/controllers/admin/themes_controller.rb +++ b/app/controllers/admin/themes_controller.rb @@ -83,7 +83,7 @@ class Admin::ThemesController < Admin::AdminController if @theme.save log_theme_change(nil, @theme) - render json: @theme, status: :created + render json: serialize_data(@theme, ThemeSerializer), status: :created else render json: @theme.errors, status: :unprocessable_entity end @@ -113,7 +113,7 @@ class Admin::ThemesController < Admin::AdminController @theme = RemoteTheme.import_theme(remote, theme_user, private_key: private_key, branch: branch) - render json: @theme, status: :created + render json: serialize_data(@theme, ThemeSerializer), status: :created rescue RemoteTheme::ImportError => e if params[:force] theme_name = params[:remote].gsub(/.git\z/, "").split("/").last @@ -128,7 +128,7 @@ class Admin::ThemesController < Admin::AdminController @theme.remote_theme = remote_theme @theme.save! - render json: @theme, status: :created + render json: serialize_data(@theme, ThemeSerializer), status: :created else render_json_error e.message end @@ -156,7 +156,7 @@ class Admin::ThemesController < Admin::AdminController ) log_theme_change(nil, @theme) - render json: @theme, status: :created + render json: serialize_data(@theme, ThemeSerializer), status: :created rescue RemoteTheme::ImportError => e render_json_error e.message end @@ -200,7 +200,7 @@ class Admin::ThemesController < Admin::AdminController if @theme.save update_default_theme log_theme_change(nil, @theme) - format.json { render json: @theme, status: :created } + format.json { render json: serialize_data(@theme, ThemeSerializer), status: :created } else format.json { render json: @theme.errors, status: :unprocessable_entity } end @@ -250,7 +250,7 @@ class Admin::ThemesController < Admin::AdminController log_theme_component_disabled if disables_component log_theme_component_enabled if enables_component - format.json { render json: @theme, status: :ok } + format.json { render json: serialize_data(@theme, ThemeSerializer), status: :ok } else format.json do error = @theme.errors.full_messages.join(", ").presence diff --git a/app/controllers/admin/web_hooks_controller.rb b/app/controllers/admin/web_hooks_controller.rb index 8c99e6a7702..1195cba88d9 100644 --- a/app/controllers/admin/web_hooks_controller.rb +++ b/app/controllers/admin/web_hooks_controller.rb @@ -17,12 +17,18 @@ class Admin::WebHooksController < Admin::AdminController data = serialize_data(web_hooks, AdminWebHookSerializer, root: "web_hooks") + serialized_grouped_event_types = + WebHookEventType.active_grouped.transform_values do |array| + serialize_data(array, WebHookEventTypeSerializer) + end + json = { web_hooks: data.delete("web_hooks"), extras: data.merge( - grouped_event_types: WebHookEventType.active_grouped, - default_event_types: WebHook.default_event_types, + grouped_event_types: serialized_grouped_event_types, + default_event_types: + serialize_data(WebHook.default_event_types, WebHookEventTypeSerializer), content_types: WebHook.content_types.map { |name, id| { id: id, name: name } }, delivery_statuses: WebHook.last_delivery_statuses.map { |name, id| { id: id, name: name.to_s } }, diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb index 0123a065184..4785cfbb7a6 100644 --- a/app/controllers/topics_controller.rb +++ b/app/controllers/topics_controller.rb @@ -140,7 +140,7 @@ class TopicsController < ApplicationController custom_message_params: { group: group.name, }, - group: group, + group: serialize_data(group, BasicGroupSerializer, root: false), ) end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 3d30899474b..d9f47a431d0 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1544,12 +1544,14 @@ class UsersController < ApplicationController .select(:id, :name, :last_used, :created_at, :method) .where(enabled: true) .order(:created_at) + .as_json(only: %i[id name method last_used]) security_keys = current_user .security_keys .where(factor_type: UserSecurityKey.factor_types[:second_factor]) .order(:created_at) + .as_json(only: %i[id user_id credential_id public_key factor_type enabled name last_used]) render json: success_json.merge(totps: totp_second_factors, security_keys: security_keys) else diff --git a/app/jobs/regular/export_user_archive.rb b/app/jobs/regular/export_user_archive.rb index 873b78bac3f..edaf02fd515 100644 --- a/app/jobs/regular/export_user_archive.rb +++ b/app/jobs/regular/export_user_archive.rb @@ -533,7 +533,10 @@ module Jobs def get_user_archive_fields(user_archive) user_archive_array = [] topic_data = user_archive.topic - user_archive = user_archive.as_json + user_archive = + user_archive.as_json( + only: %i[topic_id post_number raw cooked like_count reply_count created_at id], + ) topic_data = Topic .with_deleted diff --git a/app/serializers/admin_web_hook_serializer.rb b/app/serializers/admin_web_hook_serializer.rb index fd7441ed964..e853028a6f2 100644 --- a/app/serializers/admin_web_hook_serializer.rb +++ b/app/serializers/admin_web_hook_serializer.rb @@ -8,8 +8,7 @@ class AdminWebHookSerializer < ApplicationSerializer :secret, :wildcard_web_hook, :verify_certificate, - :active, - :web_hook_event_types + :active has_many :categories, serializer: BasicCategorySerializer, embed: :ids, include: true has_many :tags, @@ -19,10 +18,10 @@ class AdminWebHookSerializer < ApplicationSerializer embed_key: :name, include: false has_many :groups, serializer: BasicGroupSerializer, embed: :ids, include: false - - def web_hook_event_types - ActiveModel::ArraySerializer.new(object.web_hook_event_types).as_json - end + has_many :web_hook_event_types, + serializer: WebHookEventTypeSerializer, + root: false, + embed: :objects def last_delivery_status object.active ? object.last_delivery_status : WebHook.last_delivery_statuses[:disabled] diff --git a/app/serializers/site_serializer.rb b/app/serializers/site_serializer.rb index d420a116e65..20c04c5a696 100644 --- a/app/serializers/site_serializer.rb +++ b/app/serializers/site_serializer.rb @@ -84,7 +84,10 @@ class SiteSerializer < ApplicationSerializer end def default_dark_color_scheme - ColorScheme.find_by_id(SiteSetting.default_dark_mode_color_scheme_id).as_json + ColorSchemeSerializer.new( + ColorScheme.find_by_id(SiteSetting.default_dark_mode_color_scheme_id), + root: false, + ).as_json end def groups diff --git a/app/serializers/theme_serializer.rb b/app/serializers/theme_serializer.rb index 95672fa09be..4407783f0eb 100644 --- a/app/serializers/theme_serializer.rb +++ b/app/serializers/theme_serializer.rb @@ -3,8 +3,7 @@ require "base64" class ThemeSerializer < BasicThemeSerializer - attributes :color_scheme, - :color_scheme_id, + attributes :color_scheme_id, :user_selectable, :auto_update, :remote_theme_id, @@ -16,6 +15,7 @@ class ThemeSerializer < BasicThemeSerializer :disabled_at, :theme_fields + has_one :color_scheme, serializer: ColorSchemeSerializer, embed: :object has_one :user, serializer: UserNameSerializer, embed: :object has_one :disabled_by, serializer: UserNameSerializer, embed: :object diff --git a/app/serializers/topic_view_bookmark_serializer.rb b/app/serializers/topic_view_bookmark_serializer.rb new file mode 100644 index 00000000000..60faf680941 --- /dev/null +++ b/app/serializers/topic_view_bookmark_serializer.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class TopicViewBookmarkSerializer < ApplicationSerializer + attributes :id, :bookmarkable_id, :bookmarkable_type, :reminder_at, :name, :auto_delete_preference +end diff --git a/app/serializers/topic_view_serializer.rb b/app/serializers/topic_view_serializer.rb index 3e9fa319de7..e23201281f4 100644 --- a/app/serializers/topic_view_serializer.rb +++ b/app/serializers/topic_view_serializer.rb @@ -64,7 +64,6 @@ class TopicViewSerializer < ApplicationSerializer :is_warning, :chunk_size, :bookmarked, - :bookmarks, :message_archived, :topic_timer, :unicode_title, @@ -85,6 +84,7 @@ class TopicViewSerializer < ApplicationSerializer has_one :details, serializer: TopicViewDetailsSerializer, root: false, embed: :objects has_many :pending_posts, serializer: TopicPendingPostSerializer, root: false, embed: :objects has_many :categories, serializer: CategoryBadgeSerializer, embed: :objects + has_many :bookmarks, serializer: TopicViewBookmarkSerializer, root: false, embed: :objects has_one :published_page, embed: :objects @@ -201,10 +201,6 @@ class TopicViewSerializer < ApplicationSerializer object.has_bookmarks? end - def bookmarks - object.bookmarks - end - def topic_timer topic_timer = object.topic.public_topic_timer diff --git a/app/serializers/web_hook_event_type_serializer.rb b/app/serializers/web_hook_event_type_serializer.rb new file mode 100644 index 00000000000..2b7ae73eead --- /dev/null +++ b/app/serializers/web_hook_event_type_serializer.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class WebHookEventTypeSerializer < ApplicationSerializer + attributes :id, :name, :group +end diff --git a/app/views/exceptions/not_found.html.erb b/app/views/exceptions/not_found.html.erb index ff8918d9f85..57bd2504b03 100644 --- a/app/views/exceptions/not_found.html.erb +++ b/app/views/exceptions/not_found.html.erb @@ -15,10 +15,10 @@ " class='btn btn-primary'><%= SvgSprite.raw_svg('fa-user') %><%= I18n.t('log_in') %> <%- end %> - <%- if @group&.allow_membership_requests %> - <%= SvgSprite.raw_svg('user-plus') %> <%= I18n.t('not_in_group.request_membership') %> - <%- elsif @group&.public_admission %> - <%= SvgSprite.raw_svg('user-plus') %> <%= I18n.t('not_in_group.join_group') %> + <%- if @group&.dig(:allow_membership_requests) %> + <%= SvgSprite.raw_svg('user-plus') %> <%= I18n.t('not_in_group.request_membership') %> + <%- elsif @group&.dig(:public_admission) %> + <%= SvgSprite.raw_svg('user-plus') %> <%= I18n.t('not_in_group.join_group') %> <%- end %> diff --git a/lib/freedom_patches/active_record_disable_serialization.rb b/lib/freedom_patches/active_record_disable_serialization.rb new file mode 100644 index 00000000000..47d50042d82 --- /dev/null +++ b/lib/freedom_patches/active_record_disable_serialization.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module ActiveRecordSerializationSafety + class BlockedSerializationError < StandardError + end + + def serializable_hash(options = nil) + if options.nil? || options[:only].nil? + message = + "Serializing ActiveRecord models (#{self.class.name}) without specifying fields is not allowed. Use a Serializer, or pass the :only option to #serializable_hash. More info: https://meta.discourse.org/t/-/314495" + + if Rails.env == "production" + Rails.logger.info(message) + else + raise BlockedSerializationError.new(message) + end + end + super + end +end + +ActiveRecord::Base.prepend(ActiveRecordSerializationSafety) diff --git a/plugins/chat/spec/plugin_helper.rb b/plugins/chat/spec/plugin_helper.rb index 2b4fec15ba2..3011db23f8b 100644 --- a/plugins/chat/spec/plugin_helper.rb +++ b/plugins/chat/spec/plugin_helper.rb @@ -113,6 +113,12 @@ module ChatSpecHelpers end def create_draft(channel, thread: nil, user: Discourse.system_user, data: { message: "draft" }) + if data[:uploads] + data[:uploads] = data[:uploads].map do |upload| + UploadSerializer.new(upload, root: false).as_json + end + end + result = ::Chat::UpsertDraft.call( guardian: user.guardian, diff --git a/spec/lib/topic_query_spec.rb b/spec/lib/topic_query_spec.rb index dcdcb0c5371..5325f8ffd1f 100644 --- a/spec/lib/topic_query_spec.rb +++ b/spec/lib/topic_query_spec.rb @@ -1410,7 +1410,7 @@ RSpec.describe TopicQuery do end def read(user, topic, post_number) - TopicUser.update_last_read(user, topic, post_number, post_number, 10_000) + TopicUser.update_last_read(user, topic.id, post_number, post_number, 10_000) end before do diff --git a/spec/requests/admin/themes_controller_spec.rb b/spec/requests/admin/themes_controller_spec.rb index 10834a649c6..cd9c3833447 100644 --- a/spec/requests/admin/themes_controller_spec.rb +++ b/spec/requests/admin/themes_controller_spec.rb @@ -222,7 +222,7 @@ RSpec.describe Admin::ThemesController do end it "can import a theme from Git" do - RemoteTheme.stubs(:import_theme) + RemoteTheme.stubs(:import_theme).returns(Fabricate(:theme)) post "/admin/themes/import.json", params: { remote: " https://github.com/discourse/discourse-brand-header.git ", @@ -1076,12 +1076,11 @@ RSpec.describe Admin::ThemesController do end it "should return the right error when value used to update a theme setting of `objects` typed is invalid" do - field = - theme.set_field( - target: :settings, - name: "yaml", - value: File.read("#{Rails.root}/spec/fixtures/theme_settings/objects_settings.yaml"), - ) + theme.set_field( + target: :settings, + name: "yaml", + value: File.read("#{Rails.root}/spec/fixtures/theme_settings/objects_settings.yaml"), + ) theme.save! @@ -1101,12 +1100,11 @@ RSpec.describe Admin::ThemesController do end it "should be able to update a theme setting of `objects` typed" do - field = - theme.set_field( - target: :settings, - name: "yaml", - value: File.read("#{Rails.root}/spec/fixtures/theme_settings/objects_settings.yaml"), - ) + theme.set_field( + target: :settings, + name: "yaml", + value: File.read("#{Rails.root}/spec/fixtures/theme_settings/objects_settings.yaml"), + ) theme.save! @@ -1354,7 +1352,7 @@ RSpec.describe Admin::ThemesController do let(:theme_setting) do yaml = File.read("#{Rails.root}/spec/fixtures/theme_settings/objects_settings.yaml") - field = theme.set_field(target: :settings, name: "yaml", value: yaml) + theme.set_field(target: :settings, name: "yaml", value: yaml) theme.save! theme.settings end diff --git a/spec/requests/api/notifications_spec.rb b/spec/requests/api/notifications_spec.rb index 239308a8d0d..d1c68f324fa 100644 --- a/spec/requests/api/notifications_spec.rb +++ b/spec/requests/api/notifications_spec.rb @@ -108,6 +108,11 @@ RSpec.describe "notifications" do response "200", "notifications marked read" do schema type: :object, properties: { success: { type: :string } } + let(:notification) do + notification = Fabricate(:notification) + NotificationSerializer.new(notification).as_json + end + run_test! end end diff --git a/spec/requests/api/posts_spec.rb b/spec/requests/api/posts_spec.rb index 51e4a672a69..f8f14029659 100644 --- a/spec/requests/api/posts_spec.rb +++ b/spec/requests/api/posts_spec.rb @@ -221,7 +221,10 @@ RSpec.describe "posts" do expected_response_schema = load_spec_schema("topic_create_response") schema expected_response_schema - let(:params) { Fabricate(:post) } + let(:params) do + post = Fabricate(:post) + post.serializable_hash(only: %i[topic_id raw created_at]).as_json + end it_behaves_like "a JSON endpoint", 200 do let(:expected_response_schema) { expected_response_schema } diff --git a/spec/serializers/site_serializer_spec.rb b/spec/serializers/site_serializer_spec.rb index d6440fb1d0a..c8860e4846d 100644 --- a/spec/serializers/site_serializer_spec.rb +++ b/spec/serializers/site_serializer_spec.rb @@ -113,7 +113,7 @@ RSpec.describe SiteSerializer do scheme = ColorScheme.last SiteSetting.default_dark_mode_color_scheme_id = scheme.id serialized = described_class.new(Site.new(guardian), scope: guardian, root: false).as_json - default_dark_scheme = expect(serialized[:default_dark_color_scheme]["name"]).to eq(scheme.name) + default_dark_scheme = expect(serialized[:default_dark_color_scheme][:name]).to eq(scheme.name) SiteSetting.default_dark_mode_color_scheme_id = -1 serialized = described_class.new(Site.new(guardian), scope: guardian, root: false).as_json