discourse/lib/site_settings/validations.rb
Alan Guo Xiang Tan 8cf4ed5f88
DEV: Introduce hidden s3_inventory_bucket site setting (#27304)
This commit introduces a hidden `s3_inventory_bucket` site setting which
replaces the `enable_s3_inventory` and `s3_configure_inventory_policy`
site setting.

The reason `enable_s3_inventory` and `s3_configure_inventory_policy`
site settings are removed is because this feature has technically been
broken since it was introduced. When the `enable_s3_inventory` feature
is turned on, the app will because configure a daily inventory policy for the
`s3_upload_bucket` bucket and store the inventories under a prefix in
the bucket. The problem here is that once the inventories are created,
there is nothing cleaning up all these inventories so whoever that has
enabled this feature would have been paying the cost of storing a whole
bunch of inventory files which are never used. Given that we have not
received any complains about inventory files inflating S3 storage costs,
we think that it is very likely that this feature is no longer being
used and we are looking to drop support for this feature in the not too
distance future.

For now, we will still support a hidden `s3_inventory_bucket` site
setting which site administrators can configure via the
`DISCOURSE_S3_INVENTORY_BUCKET` env.
2024-06-10 13:16:00 +08:00

301 lines
9.6 KiB
Ruby

# frozen_string_literal: true
module SiteSettings
end
module SiteSettings::Validations
PROHIBITED_USER_AGENT_STRINGS = %w[
apple
windows
linux
ubuntu
gecko
firefox
chrome
safari
applewebkit
webkit
mozilla
macintosh
khtml
intel
osx
os\ x
iphone
ipad
mac
]
def validate_error(key, opts = {})
raise Discourse::InvalidParameters.new(I18n.t("errors.site_settings.#{key}", opts))
end
def validate_category_ids(category_ids)
category_ids = category_ids.split("|").map(&:to_i).to_set
if Category.where(id: category_ids).count != category_ids.size
validate_error :invalid_category_id
end
category_ids
end
def validate_default_categories(category_ids, default_categories_selected)
if (category_ids & default_categories_selected).size > 0
validate_error :default_categories_already_selected
end
end
def validate_default_categories_watching(new_val)
category_ids = validate_category_ids(new_val)
default_categories_selected = [
SiteSetting.default_categories_tracking.split("|"),
SiteSetting.default_categories_muted.split("|"),
SiteSetting.default_categories_watching_first_post.split("|"),
SiteSetting.default_categories_normal.split("|"),
].flatten.map(&:to_i).to_set
validate_default_categories(category_ids, default_categories_selected)
end
def validate_default_categories_tracking(new_val)
category_ids = validate_category_ids(new_val)
default_categories_selected = [
SiteSetting.default_categories_watching.split("|"),
SiteSetting.default_categories_muted.split("|"),
SiteSetting.default_categories_watching_first_post.split("|"),
SiteSetting.default_categories_normal.split("|"),
].flatten.map(&:to_i).to_set
validate_default_categories(category_ids, default_categories_selected)
end
def validate_default_categories_muted(new_val)
category_ids = validate_category_ids(new_val)
default_categories_selected = [
SiteSetting.default_categories_watching.split("|"),
SiteSetting.default_categories_tracking.split("|"),
SiteSetting.default_categories_watching_first_post.split("|"),
SiteSetting.default_categories_normal.split("|"),
].flatten.map(&:to_i).to_set
validate_default_categories(category_ids, default_categories_selected)
end
def validate_default_categories_watching_first_post(new_val)
category_ids = validate_category_ids(new_val)
default_categories_selected = [
SiteSetting.default_categories_watching.split("|"),
SiteSetting.default_categories_tracking.split("|"),
SiteSetting.default_categories_muted.split("|"),
SiteSetting.default_categories_normal.split("|"),
].flatten.map(&:to_i).to_set
validate_default_categories(category_ids, default_categories_selected)
end
def validate_default_categories_normal(new_val)
category_ids = validate_category_ids(new_val)
default_categories_selected = [
SiteSetting.default_categories_watching.split("|"),
SiteSetting.default_categories_tracking.split("|"),
SiteSetting.default_categories_muted.split("|"),
SiteSetting.default_categories_watching_first_post.split("|"),
].flatten.map(&:to_i).to_set
validate_default_categories(category_ids, default_categories_selected)
end
def validate_default_tags(tag_names, default_tags_selected)
validate_error :default_tags_already_selected if (tag_names & default_tags_selected).size > 0
end
def validate_default_tags_watching(new_val)
tag_names = new_val.split("|").to_set
default_tags_selected = [
SiteSetting.default_tags_tracking.split("|"),
SiteSetting.default_tags_muted.split("|"),
SiteSetting.default_tags_watching_first_post.split("|"),
].flatten.to_set
validate_default_tags(tag_names, default_tags_selected)
end
def validate_default_tags_tracking(new_val)
tag_names = new_val.split("|").to_set
default_tags_selected = [
SiteSetting.default_tags_watching.split("|"),
SiteSetting.default_tags_muted.split("|"),
SiteSetting.default_tags_watching_first_post.split("|"),
].flatten.to_set
validate_default_tags(tag_names, default_tags_selected)
end
def validate_default_tags_muted(new_val)
tag_names = new_val.split("|").to_set
default_tags_selected = [
SiteSetting.default_tags_watching.split("|"),
SiteSetting.default_tags_tracking.split("|"),
SiteSetting.default_tags_watching_first_post.split("|"),
].flatten.to_set
validate_default_tags(tag_names, default_tags_selected)
end
def validate_default_tags_watching_first_post(new_val)
tag_names = new_val.split("|").to_set
default_tags_selected = [
SiteSetting.default_tags_watching.split("|"),
SiteSetting.default_tags_tracking.split("|"),
SiteSetting.default_tags_muted.split("|"),
].flatten.to_set
validate_default_tags(tag_names, default_tags_selected)
end
def validate_enable_s3_uploads(new_val)
return if new_val == "f"
validate_error :cannot_enable_s3_uploads_when_s3_enabled_globally if GlobalSetting.use_s3?
validate_error :s3_upload_bucket_is_required if SiteSetting.s3_upload_bucket.blank?
end
def validate_secure_uploads(new_val)
if new_val == "t" && (!SiteSetting.Upload.enable_s3_uploads || !SiteSetting.s3_use_acls)
validate_error :secure_uploads_requirements
end
end
def validate_s3_use_acls(new_val)
validate_error :s3_use_acls_requirements if new_val == "f" && SiteSetting.secure_uploads
end
def validate_enable_page_publishing(new_val)
validate_error :page_publishing_requirements if new_val == "t" && SiteSetting.secure_uploads?
end
def validate_share_quote_buttons(new_val)
if new_val.include?("facebook") && SiteSetting.facebook_app_id.blank?
validate_error :share_quote_facebook_requirements
end
end
def validate_backup_location(new_val)
return unless new_val == BackupLocationSiteSetting::S3
if SiteSetting.s3_backup_bucket.blank?
validate_error(:s3_backup_requires_s3_settings, setting_name: "s3_backup_bucket")
end
unless SiteSetting.s3_use_iam_profile
if SiteSetting.s3_access_key_id.blank?
validate_error(:s3_backup_requires_s3_settings, setting_name: "s3_access_key_id")
end
if SiteSetting.s3_secret_access_key.blank?
validate_error(:s3_backup_requires_s3_settings, setting_name: "s3_secret_access_key")
end
end
end
def validate_s3_upload_bucket(new_val)
validate_bucket_setting("s3_upload_bucket", new_val, SiteSetting.s3_backup_bucket)
if new_val.blank? && SiteSetting.enable_s3_uploads?
validate_error(:s3_upload_bucket_is_required, setting_name: "s3_upload_bucket")
end
end
def validate_s3_backup_bucket(new_val)
validate_bucket_setting("s3_backup_bucket", SiteSetting.s3_upload_bucket, new_val)
end
def validate_enforce_second_factor(new_val)
if new_val != "no" && SiteSetting.enable_discourse_connect?
return validate_error :second_factor_cannot_be_enforced_with_discourse_connect_enabled
end
if new_val == "all" && Discourse.enabled_auth_providers.count > 0
auth_provider_names = Discourse.enabled_auth_providers.map(&:name).join(", ")
return(
validate_error(
:second_factor_cannot_enforce_with_socials,
auth_provider_names: auth_provider_names,
)
)
end
return if SiteSetting.enable_local_logins
return if new_val == "no"
validate_error :second_factor_cannot_be_enforced_with_disabled_local_login
end
def validate_enable_local_logins(new_val)
return if new_val == "t"
return if SiteSetting.enforce_second_factor == "no"
validate_error :local_login_cannot_be_disabled_if_second_factor_enforced
end
def validate_cors_origins(new_val)
return if new_val.blank?
return if new_val.split("|").none?(%r{/\z})
validate_error :cors_origins_should_not_have_trailing_slash
end
def validate_slow_down_crawler_user_agents(new_val)
return if new_val.blank?
new_val
.downcase
.split("|")
.each do |crawler|
if crawler.size < 3
validate_error(:slow_down_crawler_user_agent_must_be_at_least_3_characters)
end
if PROHIBITED_USER_AGENT_STRINGS.any? { |c| c.include?(crawler) }
validate_error(
:slow_down_crawler_user_agent_cannot_be_popular_browsers,
values: PROHIBITED_USER_AGENT_STRINGS.join(I18n.t("word_connector.comma")),
)
end
end
end
def validate_strip_image_metadata(new_val)
return if new_val == "t"
return if SiteSetting.composer_media_optimization_image_enabled == false
validate_error :strip_image_metadata_cannot_be_disabled_if_composer_media_optimization_image_enabled
end
def validate_twitter_summary_large_image(new_val)
return if new_val.blank?
return if !Upload.exists?(id: new_val, extension: "svg")
validate_error :twitter_summary_large_image_no_svg
end
private
def validate_bucket_setting(setting_name, upload_bucket, backup_bucket)
return if upload_bucket.blank? || backup_bucket.blank?
backup_bucket_name, backup_prefix = split_s3_bucket(backup_bucket)
upload_bucket_name, upload_prefix = split_s3_bucket(upload_bucket)
return if backup_bucket_name != upload_bucket_name
if backup_prefix == upload_prefix || backup_prefix.blank? ||
upload_prefix&.start_with?(backup_prefix)
validate_error(:s3_bucket_reused, setting_name: setting_name)
end
end
def split_s3_bucket(s3_bucket)
bucket_name, prefix = s3_bucket.downcase.split("/", 2)
prefix&.chomp!("/")
[bucket_name, prefix]
end
end