Create settings model for rate limits (#13381)

Co-authored-by: rhymes <rhymes@hey.com>
This commit is contained in:
Michael Kohl 2021-04-20 11:29:22 +07:00 committed by GitHub
parent 2f1dad4e37
commit 28dd50e718
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 415 additions and 145 deletions

View file

@ -0,0 +1,22 @@
module Admin
module Settings
class RateLimitsController < Admin::ApplicationController
def create
result = ::RateLimits::SettingsUpsert.call(settings_params)
if result.success?
Audit::Logger.log(:internal, current_user, params.dup)
redirect_to admin_config_path, notice: "Site configuration was successfully updated."
else
redirect_to admin_config_path, alert: "😭 #{result.errors.to_sentence}"
end
end
def settings_params
params
.require(:settings_rate_limit)
.permit(*::Settings::RateLimit.keys)
end
end
end
end

View file

@ -108,7 +108,7 @@ class UserDecorator < ApplicationDecorator
end
def considered_new?
min_days = SiteConfig.user_considered_new_days
min_days = Settings::RateLimit.user_considered_new_days
return false unless min_days.positive?
created_at.after?(min_days.days.ago)

View file

@ -8,91 +8,91 @@ module RateLimitCheckerHelper
end
CONFIGURABLE_RATES = {
rate_limit_published_article_creation: {
published_article_creation: {
min: 0,
placeholder: 9,
title: "Limit number of posts created",
description: "How many posts can someone create within any 30 second period?"
},
rate_limit_published_article_antispam_creation: {
published_article_antispam_creation: {
min: 0,
placeholder: 1,
title: "Limit number of posts created by a new member",
description: new_user_message("posts")
},
rate_limit_article_update: {
article_update: {
min: 1,
placeholder: 30,
title: "Limit number of updates to a post",
description: "How many updates can someone make within any 30 second period? Update API docs when changed."
},
rate_limit_image_upload: {
image_upload: {
min: 0,
placeholder: 9,
title: "Limit number of images uploaded",
description: "How many images can someone upload within any 30 second period?"
},
rate_limit_user_update: {
user_update: {
min: 1,
placeholder: 5,
title: "Limit number of changes someone can make to their account",
description: "How many changes can someone make to their user account within any 30 second period?"
},
rate_limit_follow_count_daily: {
follow_count_daily: {
min: 0,
placeholder: 500,
title: "Limit number of followers someone can follow daily",
description: "How many people can someone follow in a day?"
},
rate_limit_reaction_creation: {
reaction_creation: {
min: 1,
placeholder: 10,
title: "Limit number of reactions to a post or comment",
description: "How many times can someone react to a post or comment within any 30 second period?"
},
rate_limit_feedback_message_creation: {
feedback_message_creation: {
min: 1,
placeholder: 5,
title: "Limit number of times someone can report abuse",
description: "How many times can someone report abuse within any 5 minute period?"
},
rate_limit_comment_creation: {
comment_creation: {
min: 0,
placeholder: 9,
title: "Limit number of comments created",
description: "How many comments can someone create within any 30 second period?"
},
rate_limit_comment_antispam_creation: {
comment_antispam_creation: {
min: 0,
placeholder: 1,
title: "Limit number of comments created by a new member",
description: new_user_message("comments")
},
rate_limit_listing_creation: {
listing_creation: {
min: 1,
placeholder: 1,
title: "Limit number of listings created",
description: "How many listings can someone create within any 1 minute period?"
},
rate_limit_organization_creation: {
organization_creation: {
min: 1,
placeholder: 1,
title: "Limit number of organizations created",
description: "How many organizations can someone create within any 5 minute period?"
},
rate_limit_user_subscription_creation: {
user_subscription_creation: {
min: 0,
placeholder: 3,
title: "Limit number of times someone can subscribe to mailing list liquid tag",
description: "How many times can someone subscribe to a mailing list within any 30 second period?"
},
rate_limit_email_recipient: {
email_recipient: {
min: 0,
placeholder: 5,
title: "Limit number of general emails we send",
description: "How many emails can we send to someone within any 2 minute period?"
},
rate_limit_send_email_confirmation: {
send_email_confirmation: {
min: 1,
placeholder: 2,
title: "Limit number of confirmation emails we send",

View file

@ -0,0 +1,17 @@
module Constants
module Settings
module RateLimit
DETAILS = {
spam_trigger_terms: {
description: "Individual (case insensitive) phrases that trigger spam alerts, comma separated.",
placeholder: "used cars near you, pokemon go hack"
},
user_considered_new_days: {
description: "The number of days a user is considered new. The default is 3 days, but you "\
"can disable this entirely by inputting 0.",
placeholder: ::SiteConfig.user_considered_new_days
}
}.freeze
end
end
end

View file

@ -172,10 +172,6 @@ module Constants
description: "A place for an alternate logo, if you have one. Used throughout member onboarding and in some sign in forms.",
placeholder: IMAGE_PLACEHOLDER
},
spam_trigger_terms: {
description: "Individual (case insensitive) phrases that trigger spam alerts, comma separated.",
placeholder: "used cars near you, pokemon go hack"
},
shop_url: {
description: "Used as the shop url of the community",
placeholder: "https://shop.url"
@ -229,10 +225,6 @@ module Constants
description: "Used as the twitter hashtag of the community",
placeholder: "#DEVCommunity"
},
user_considered_new_days: {
description: "The number of days a user is considered new. The default is 3 days, but you can disable this entirely by inputting 0.",
placeholder: ::SiteConfig.user_considered_new_days
},
video_encoder_key: {
description: "Secret key used to allow AWS video encoding through the VideoStatesController",
placeholder: ""

View file

@ -0,0 +1,31 @@
module Settings
class RateLimit < RailsSettings::Base
self.table_name = :settings_rate_limits
# The configuration is cached, change this if you want to force update
# the cache, or call Settings::RateLimit.clear_cache
cache_prefix { "v1" }
field :article_update, type: :integer, default: 30
field :comment_antispam_creation, type: :integer, default: 1
field :comment_creation, type: :integer, default: 9
field :email_recipient, type: :integer, default: 5
field :feedback_message_creation, type: :integer, default: 5
field :follow_count_daily, type: :integer, default: 500
field :image_upload, type: :integer, default: 9
field :listing_creation, type: :integer, default: 1
field :organization_creation, type: :integer, default: 1
field :published_article_antispam_creation, type: :integer, default: 1
field :published_article_creation, type: :integer, default: 9
field :reaction_creation, type: :integer, default: 10
field :send_email_confirmation, type: :integer, default: 2
field :spam_trigger_terms, type: :array, default: []
field :user_considered_new_days, type: :integer, default: 3
field :user_subscription_creation, type: :integer, default: 3
field :user_update, type: :integer, default: 15
def self.get_default(field)
get_field(field)[:default]
end
end
end

View file

@ -142,6 +142,8 @@ class SiteConfig < RailsSettings::Base
field :prefer_manual_suggested_users, type: :boolean, default: false
# Rate limits and spam prevention
# NOTE: @citizen428 These will be removed once we migrated to the new settings
# model across the fleet.
field :rate_limit_follow_count_daily, type: :integer, default: 500
field :rate_limit_comment_creation, type: :integer, default: 9
field :rate_limit_comment_antispam_creation, type: :integer, default: 1

View file

@ -59,7 +59,7 @@ class RateLimitChecker
def limit_by_email_recipient_address(address)
# This is related to the recipient, not the "user" initiator, like in action.
EmailMessage.where(to: address).where("sent_at > ?", 2.minutes.ago).size >
SiteConfig.rate_limit_email_recipient
Settings::RateLimit.email_recipient
end
private
@ -78,39 +78,39 @@ class RateLimitChecker
end
def action_rate_limit(action)
SiteConfig.public_send("rate_limit_#{action}")
Settings::RateLimit.public_send(action)
end
def check_comment_creation_limit
user.comments.where("created_at > ?", 30.seconds.ago).size >
SiteConfig.rate_limit_comment_creation
Settings::RateLimit.comment_creation
end
def check_published_article_creation_limit
# TODO: Vaidehi Joshi - We should make this time frame configurable.
user.articles.published.where("created_at > ?", 30.seconds.ago).size >
SiteConfig.rate_limit_published_article_creation
Settings::RateLimit.published_article_creation
end
def check_published_article_antispam_creation_limit
# TODO: Vaidehi Joshi - We should make this time frame configurable.
user.articles.published.where("created_at > ?", 5.minutes.ago).size >
SiteConfig.rate_limit_published_article_antispam_creation
Settings::RateLimit.published_article_antispam_creation
end
def check_comment_antispam_creation_limit
# TODO: We should make this time frame configurable.
user.comments.where(created_at: 5.minutes.ago...).size >
SiteConfig.rate_limit_comment_antispam_creation
Settings::RateLimit.comment_antispam_creation
end
def check_follow_account_limit
user_today_follow_count > SiteConfig.rate_limit_follow_count_daily
user_today_follow_count > Settings::RateLimit.follow_count_daily
end
def user_today_follow_count
following_users_count = user.following_users_count
return following_users_count if following_users_count < SiteConfig.rate_limit_follow_count_daily
return following_users_count if following_users_count < Settings::RateLimit.follow_count_daily
now = Time.zone.now
user.follows.where(created_at: (now.beginning_of_day..now)).size

View file

@ -0,0 +1,41 @@
module RateLimits
class SettingsUpsert
attr_reader :errors
def self.call(configs)
new(configs).call
end
def initialize(configs)
@configs = configs
@errors = []
end
def call
upsert_configs
self
end
def success?
@errors.none?
end
private
# NOTE: @citizen428 - This was adapted from Settings::Upsert. I'll see if
# a pattern for refactoring emerges in the future, but for now I'll leave
# this as-is.
def upsert_configs
@configs.each do |key, value|
if value.is_a?(Array) && value.any?
Settings::RateLimit.public_send("#{key}=", value.reject(&:blank?))
elsif value.present?
Settings::RateLimit.public_send("#{key}=", value.strip)
end
rescue ActiveRecord::RecordInvalid => e
@errors << e.message
next
end
end
end
end

View file

@ -1071,7 +1071,7 @@
</div>
<% end %>
<%= form_for(SiteConfig.new, url: admin_config_path) do |f| %>
<%= form_for(Settings::RateLimit.new, url: admin_settings_rate_limits_path) do |f| %>
<div class="card mt-3">
<%= render partial: "admin/shared/card_header",
locals: {
@ -1083,35 +1083,35 @@
<div id="rateLimitsBodyContainer" class="card-body collapse hide" aria-labelledby="rateLimitsBodyContainer">
<fieldset class="grid gap-4">
<div class="crayons-field">
<%= admin_config_label :user_considered_new_days %>
<%= admin_config_description Constants::SiteConfig::DETAILS[:user_considered_new_days][:description] %>
<%= admin_config_label :user_considered_new_days, model: Settings::RateLimit %>
<%= admin_config_description Constants::Settings::RateLimit::DETAILS[:user_considered_new_days][:description] %>
<%= f.number_field :user_considered_new_days,
class: "crayons-textfield",
value: SiteConfig.user_considered_new_days,
placeholder: Constants::SiteConfig::DETAILS[:user_considered_new_days][:placeholder] %>
value: Settings::RateLimit.user_considered_new_days,
placeholder: Constants::Settings::RateLimit::DETAILS[:user_considered_new_days][:placeholder] %>
</div>
<% configurable_rate_limits.each do |key, field_hash| %>
<div class="crayons-field">
<%= admin_config_label field_hash[:title] %>
<%= admin_config_label field_hash[:title], model: Settings::RateLimit %>
<p class="crayons-field__description">
<%= field_hash[:description] %>
</p>
<%= f.number_field key,
class: "crayons-textfield",
value: SiteConfig.public_send(key),
value: Settings::RateLimit.public_send(key),
min: field_hash[:min],
placeholder: field_hash[:placeholder] %>
</div>
<% end %>
<div class="crayons-field">
<%= admin_config_label :spam_trigger_terms %>
<%= admin_config_description Constants::SiteConfig::DETAILS[:spam_trigger_terms][:description] %>
<%= admin_config_label :spam_trigger_terms, model: Settings::RateLimit %>
<%= admin_config_description Constants::Settings::RateLimit::DETAILS[:spam_trigger_terms][:description] %>
<%= f.text_area :spam_trigger_terms,
class: "crayons-textfield",
value: SiteConfig.spam_trigger_terms.join(","),
placeholder: Constants::SiteConfig::DETAILS[:spam_trigger_terms][:placeholder] %>
value: Settings::RateLimit.spam_trigger_terms.join(","),
placeholder: Constants::Settings::RateLimit::DETAILS[:spam_trigger_terms][:placeholder] %>
</div>
<%= render "form_submission", f: f %>
</fieldset>

View file

@ -18,7 +18,7 @@
<% if @followed_tags.any? %>
<%= javascript_packs_with_chunks_tag "dashboardTagsDisableUnchangedButtons", defer: true %>
<div class="crayons-notice p-4 px-6 mb-4 mx-2 m:mx-0" aria-live="polite">
<div class="crayons-notice p-4 px-6 mb-4 mx-2 m:mx-0" aria-live="polite">
Adjust tag weight to modify your home feed. Higher values mean more appearances for that tag.
<span class="crayons-indicator crayons-indicator--outlined crayons-indicator--accent ml-3">Default 1.0</span>
</div>

View file

@ -1,6 +1,6 @@
<div id="sign-in-password-form" class="mt-8 mb-6 crayons-card p-7 align-left mx-auto" style="max-width:580px;">
<% if flash[:notice] %>
<div class="crayons-notice crayons-notice--danger mb-6" role="alert" >
<div class="crayons-notice crayons-notice--danger mb-6" role="alert">
<%= flash[:notice] %>
</div>
<% end %>

View file

@ -57,6 +57,7 @@ Rails.application.routes.draw do
namespace :settings do
resources :authentications, only: [:create]
resources :campaigns, only: [:create]
resources :rate_limits, only: [:create]
end
namespace :users do
resources :gdpr_delete_requests, only: %i[index destroy]

View file

@ -0,0 +1,49 @@
describe('Campaign Section', () => {
beforeEach(() => {
cy.testSetup();
cy.fixture('users/adminUser.json').as('user');
cy.get('@user').then((user) => {
cy.loginUser(user);
});
});
describe('rate limit settings', () => {
it('can change for how many days a user is considered new', () => {
cy.get('@user').then(({ username }) => {
cy.visit('/admin/config');
cy.get('#new_settings_rate_limit').as('rateLimitSectionForm');
cy.get('@rateLimitSectionForm')
.findByText('Rate limits and anti-spam')
.click();
cy.get('@rateLimitSectionForm')
.get('#settings_rate_limit_user_considered_new_days')
.clear()
.type('42');
cy.get('@rateLimitSectionForm')
.findByPlaceholderText('Confirmation text')
.type(
`My username is @${username} and this action is 100% safe and appropriate.`,
);
cy.get('@rateLimitSectionForm')
.findByText('Update Site Configuration')
.click();
cy.url().should('contains', '/admin/config');
cy.findByText('Site configuration was successfully updated.').should(
'be.visible',
);
cy.get('#settings_rate_limit_user_considered_new_days').should(
'have.value',
'42',
);
});
});
});
});

View file

@ -24,8 +24,12 @@ describe('Show log in modal', () => {
cy.findAllByText('Test article').last().click();
cy.findByRole('button', { name: 'Like' }).as('heartReaction');
cy.findByRole('button', { name: 'React with unicorn' }).as('unicornReaction');
cy.findByRole('button', { name: 'Add to reading list' }).as('bookmarkReaction');
cy.findByRole('button', { name: 'React with unicorn' }).as(
'unicornReaction',
);
cy.findByRole('button', { name: 'Add to reading list' }).as(
'bookmarkReaction',
);
['@heartReaction', '@unicornReaction', '@bookmarkReaction'].forEach(
(reaction) => {
@ -46,7 +50,9 @@ describe('Show log in modal', () => {
it('should show login modal for comment subscription', () => {
cy.findAllByText('Test article').last().click();
cy.findByRole('button', { name: /Subscribe/ }).as('subscribe').click();
cy.findByRole('button', { name: /Subscribe/ })
.as('subscribe')
.click();
cy.findByTestId('modal-container').as('modal');
cy.get('@modal').findByText('Log in to continue').should('exist');

View file

@ -0,0 +1,16 @@
class CreateSettingsRateLimits < ActiveRecord::Migration[6.1]
def self.up
create_table :settings_rate_limits do |t|
t.string :var, null: false
t.text :value, null: true
t.timestamps
end
add_index :settings_rate_limits, :var, unique: true
end
def self.down
drop_table :settings_rate_limits
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2021_04_07_172628) do
ActiveRecord::Schema.define(version: 2021_04_14_033457) do
# These are extensions that must be enabled in order to support this database
enable_extension "citext"
@ -1066,6 +1066,14 @@ ActiveRecord::Schema.define(version: 2021_04_07_172628) do
t.index ["var"], name: "index_settings_campaigns_on_var", unique: true
end
create_table "settings_rate_limits", force: :cascade do |t|
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.text "value"
t.string "var", null: false
t.index ["var"], name: "index_settings_rate_limits_on_var", unique: true
end
create_table "site_configs", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false

View file

@ -30,14 +30,14 @@ Settings managed via your ENV can be found in
We use this gem for managing settings used within the app's business logic.
Examples:
- `main_social_image`
- `rate_limit_follow_count_daily`
- `suggested_tags`
- `SiteConfig.main_social_image`
- `Settings::RateLimit.follow_count_daily`
- `Settings::Authentication.twitter_secret`
These settings can be accessed via the
[`SiteConfig`](https://github.com/forem/forem/blob/main/app/models/site_config.rb)
object and viewed / modified via `/admin/config` (see
[the Admin guide](/admin)).
[`SiteConfig`](https://github.com/forem/forem/blob/master/app/models/site_config.rb)
object and various models in the `Settings::` namespace and viewed / modified
via `/admin/config` (see [the Admin guide](/admin)).
![Screenshot of site configuration admin interface](https://user-images.githubusercontent.com/47985/73627238-6276d500-467e-11ea-8724-afb703f056bc.png)

View file

@ -1,9 +1,9 @@
module DataUpdateScripts
class UpdateUserUpdateRateLimitDefault
def run
return if SiteConfig.rate_limit_user_update > 5
return if Settings::RateLimit.user_update > 5
SiteConfig.rate_limit_user_update = 15
Settings::RateLimit.user_update = 15
end
end
end

View file

@ -0,0 +1,36 @@
module DataUpdateScripts
class MoveRateLimitSettings
RENAMED_RATE_LIMIT_SETTINGS = %w[
article_update
comment_antispam_creation
comment_creation
email_recipient
feedback_message_creation
follow_count_daily
image_upload
listing_creation
organization_creation
published_article_antispam_creation
published_article_creation
reaction_creation
send_email_confirmation
user_subscription_creation
user_update
].freeze
def run
return if Settings::RateLimit.any?
RENAMED_RATE_LIMIT_SETTINGS.each do |setting|
Settings::RateLimit.public_send(
"#{setting}=",
SiteConfig.public_send("rate_limit_#{setting}"),
)
end
Settings::RateLimit.spam_trigger_terms = SiteConfig.spam_trigger_terms
Settings::RateLimit.user_considered_new_days =
SiteConfig.user_considered_new_days
end
end
end

View file

@ -3,8 +3,9 @@ require "rails_helper"
describe RateLimitCheckerHelper, type: :helper do
describe "#configurable_rate_limits" do
it "returns a hash with the right structure" do
settings_keys = Settings::RateLimit.keys.map(&:to_sym)
helper.configurable_rate_limits.each do |key, value_hash|
expect(key).to match(/\Arate_limit/)
expect(settings_keys).to include(key)
expect(value_hash.keys).to match_array(%i[title min placeholder description])
end
end

View file

@ -0,0 +1,17 @@
require "rails_helper"
require Rails.root.join(
"lib/data_update_scripts/20210414060839_move_rate_limit_settings.rb",
)
describe DataUpdateScripts::MoveRateLimitSettings do
it "migrates settings from SiteConfig to Settings::RateLimit" do
allow(SiteConfig).to receive(:rate_limit_follow_count_daily).and_return(23)
allow(SiteConfig).to receive(:user_considered_new_days).and_return(42)
expect do
described_class.new.run
end
.to change(Settings::RateLimit, :follow_count_daily)
.and change(Settings::RateLimit, :user_considered_new_days)
end
end

View file

@ -6,12 +6,12 @@ require Rails.root.join(
describe DataUpdateScripts::UpdateUserUpdateRateLimitDefault do
it "updates rate limit if 5 or less" do
described_class.new.run
expect(SiteConfig.rate_limit_user_update).to eq(15)
expect(Settings::RateLimit.user_update).to eq(15)
end
it "does NOT update the rate limit if greater than 5" do
allow(SiteConfig).to receive(:rate_limit_user_update).and_return(10)
allow(Settings::RateLimit).to receive(:user_update).and_return(10)
described_class.new.run
expect(SiteConfig.rate_limit_user_update).to eq(10)
expect(Settings::RateLimit.user_update).to eq(10)
end
end

View file

@ -634,122 +634,152 @@ RSpec.describe "/admin/config", type: :request do
end
describe "Rate Limits and spam" do
it "updates rate_limit_follow_count_daily" do
default_value = SiteConfig.get_default(:rate_limit_follow_count_daily)
it "updates follow_count_daily" do
default_value = Settings::RateLimit.get_default(:follow_count_daily)
expect do
post "/admin/config", params: { site_config: { rate_limit_follow_count_daily: 3 },
confirmation: confirmation_message }
end.to change(SiteConfig, :rate_limit_follow_count_daily).from(default_value).to(3)
post admin_settings_rate_limits_path, params: {
settings_rate_limit: { follow_count_daily: 3 },
confirmation: confirmation_message
}
end.to change(Settings::RateLimit, :follow_count_daily).from(default_value).to(3)
end
it "updates rate_limit_comment_creation" do
default_value = SiteConfig.get_default(:rate_limit_comment_creation)
it "updates comment_creation" do
default_value = Settings::RateLimit.get_default(:comment_creation)
expect do
post "/admin/config", params: { site_config: { rate_limit_comment_creation: 3 },
confirmation: confirmation_message }
end.to change(SiteConfig, :rate_limit_comment_creation).from(default_value).to(3)
post admin_settings_rate_limits_path, params: {
settings_rate_limit: { comment_creation: 3 },
confirmation: confirmation_message
}
end.to change(Settings::RateLimit, :comment_creation).from(default_value).to(3)
end
it "updates rate_limit_published_article_creation" do
default_value = SiteConfig.get_default(:rate_limit_published_article_creation)
it "updates published_article_creation" do
default_value = Settings::RateLimit.get_default(:published_article_creation)
expect do
post "/admin/config", params: { site_config: { rate_limit_published_article_creation: 3 },
confirmation: confirmation_message }
end.to change(SiteConfig, :rate_limit_published_article_creation).from(default_value).to(3)
post admin_settings_rate_limits_path, params: {
settings_rate_limit: { published_article_creation: 3 },
confirmation: confirmation_message
}
end.to change(Settings::RateLimit, :published_article_creation).from(default_value).to(3)
end
it "updates rate_limit_published_article_antispam_creation" do
default_value = SiteConfig.get_default(:rate_limit_published_article_antispam_creation)
it "updates published_article_antispam_creation" do
default_value = Settings::RateLimit.get_default(:published_article_antispam_creation)
expect do
post "/admin/config", params: { site_config: { rate_limit_published_article_antispam_creation: 3 },
confirmation: confirmation_message }
end.to change(SiteConfig, :rate_limit_published_article_antispam_creation).from(default_value).to(3)
post admin_settings_rate_limits_path, params: {
settings_rate_limit: { published_article_antispam_creation: 3 },
confirmation: confirmation_message
}
end.to change(Settings::RateLimit, :published_article_antispam_creation).from(default_value).to(3)
end
it "updates rate_limit_organization_creation" do
default_value = SiteConfig.get_default(:rate_limit_organization_creation)
it "updates organization_creation" do
default_value = Settings::RateLimit.get_default(:organization_creation)
expect do
post "/admin/config", params: { site_config: { rate_limit_organization_creation: 3 },
confirmation: confirmation_message }
end.to change(SiteConfig, :rate_limit_organization_creation).from(default_value).to(3)
post admin_settings_rate_limits_path, params: {
settings_rate_limit: { organization_creation: 3 },
confirmation: confirmation_message
}
end.to change(Settings::RateLimit, :organization_creation).from(default_value).to(3)
end
it "updates rate_limit_image_upload" do
default_value = SiteConfig.get_default(:rate_limit_image_upload)
it "updates image_upload" do
default_value = Settings::RateLimit.get_default(:image_upload)
expect do
post "/admin/config", params: { site_config: { rate_limit_image_upload: 3 },
confirmation: confirmation_message }
end.to change(SiteConfig, :rate_limit_image_upload).from(default_value).to(3)
post admin_settings_rate_limits_path, params: {
settings_rate_limit: { image_upload: 3 },
confirmation: confirmation_message
}
end.to change(Settings::RateLimit, :image_upload).from(default_value).to(3)
end
it "updates rate_limit_email_recipient" do
default_value = SiteConfig.get_default(:rate_limit_email_recipient)
it "updates email_recipient" do
default_value = Settings::RateLimit.get_default(:email_recipient)
expect do
post "/admin/config", params: { site_config: { rate_limit_email_recipient: 3 },
confirmation: confirmation_message }
end.to change(SiteConfig, :rate_limit_email_recipient).from(default_value).to(3)
post admin_settings_rate_limits_path, params: {
settings_rate_limit: { email_recipient: 3 },
confirmation: confirmation_message
}
end.to change(Settings::RateLimit, :email_recipient).from(default_value).to(3)
end
it "updates rate_limit_user_subscription_creation" do
default_value = SiteConfig.get_default(:rate_limit_user_subscription_creation)
it "updates user_subscription_creation" do
default_value = Settings::RateLimit.get_default(:user_subscription_creation)
expect do
post "/admin/config", params: { site_config: { rate_limit_user_subscription_creation: 1 },
confirmation: confirmation_message }
end.to change(SiteConfig, :rate_limit_user_subscription_creation).from(default_value).to(1)
post admin_settings_rate_limits_path, params: {
settings_rate_limit: { user_subscription_creation: 1 },
confirmation: confirmation_message
}
end.to change(Settings::RateLimit, :user_subscription_creation).from(default_value).to(1)
end
it "updates rate_limit_article_update" do
default_value = SiteConfig.get_default(:rate_limit_article_update)
it "updates article_update" do
default_value = Settings::RateLimit.get_default(:article_update)
expect do
post "/admin/config", params: { site_config: { rate_limit_article_update: 3 },
confirmation: confirmation_message }
end.to change(SiteConfig, :rate_limit_article_update).from(default_value).to(3)
post admin_settings_rate_limits_path, params: {
settings_rate_limit: { article_update: 3 },
confirmation: confirmation_message
}
end.to change(Settings::RateLimit, :article_update).from(default_value).to(3)
end
it "updates rate_limit_user_update" do
it "updates user_update" do
expect do
post "/admin/config", params: { site_config: { rate_limit_user_update: 3 },
confirmation: confirmation_message }
end.to change(SiteConfig, :rate_limit_user_update).to(3)
post admin_settings_rate_limits_path, params: {
settings_rate_limit: { user_update: 3 },
confirmation: confirmation_message
}
end.to change(Settings::RateLimit, :user_update).to(3)
end
it "updates rate_limit_feedback_message_creation" do
default_value = SiteConfig.get_default(:rate_limit_feedback_message_creation)
it "updates feedback_message_creation" do
default_value = Settings::RateLimit.get_default(:feedback_message_creation)
expect do
post "/admin/config", params: { site_config: { rate_limit_feedback_message_creation: 3 },
confirmation: confirmation_message }
end.to change(SiteConfig, :rate_limit_feedback_message_creation).from(default_value).to(3)
post admin_settings_rate_limits_path, params: {
settings_rate_limit: { feedback_message_creation: 3 },
confirmation: confirmation_message
}
end.to change(Settings::RateLimit, :feedback_message_creation).from(default_value).to(3)
end
it "updates rate_limit_listing_creation" do
default_value = SiteConfig.get_default(:rate_limit_listing_creation)
it "updates listing_creation" do
default_value = Settings::RateLimit.get_default(:listing_creation)
expect do
post "/admin/config", params: { site_config: { rate_limit_listing_creation: 3 },
confirmation: confirmation_message }
end.to change(SiteConfig, :rate_limit_listing_creation).from(default_value).to(3)
post admin_settings_rate_limits_path, params: {
settings_rate_limit: { listing_creation: 3 },
confirmation: confirmation_message
}
end.to change(Settings::RateLimit, :listing_creation).from(default_value).to(3)
end
it "updates rate_limit_reaction_creation" do
default_value = SiteConfig.get_default(:rate_limit_reaction_creation)
it "updates reaction_creation" do
default_value = Settings::RateLimit.get_default(:reaction_creation)
expect do
post "/admin/config", params: { site_config: { rate_limit_reaction_creation: 3 },
confirmation: confirmation_message }
end.to change(SiteConfig, :rate_limit_reaction_creation).from(default_value).to(3)
post admin_settings_rate_limits_path, params: {
settings_rate_limit: { reaction_creation: 3 },
confirmation: confirmation_message
}
end.to change(Settings::RateLimit, :reaction_creation).from(default_value).to(3)
end
it "updates rate_limit_send_email_confirmation" do
default_value = SiteConfig.get_default(:rate_limit_send_email_confirmation)
it "updates send_email_confirmation" do
default_value = Settings::RateLimit.get_default(:send_email_confirmation)
expect do
post "/admin/config", params: { site_config: { rate_limit_send_email_confirmation: 3 },
confirmation: confirmation_message }
end.to change(SiteConfig, :rate_limit_send_email_confirmation).from(default_value).to(3)
post admin_settings_rate_limits_path, params: {
settings_rate_limit: { send_email_confirmation: 3 },
confirmation: confirmation_message
}
end.to change(Settings::RateLimit, :send_email_confirmation).from(default_value).to(3)
end
it "updates spam_trigger_terms" do
spam_trigger_terms = "hey, pokemon go hack"
post "/admin/config", params: { site_config: { spam_trigger_terms: spam_trigger_terms },
confirmation: confirmation_message }
expect(SiteConfig.spam_trigger_terms).to eq(["hey", "pokemon go hack"])
post admin_settings_rate_limits_path, params: {
settings_rate_limit: { spam_trigger_terms: spam_trigger_terms },
confirmation: confirmation_message
}
expect(Settings::RateLimit.spam_trigger_terms).to eq(["hey", "pokemon go hack"])
end
it "updates recaptcha_site_key and recaptcha_secret_key" do

View file

@ -22,7 +22,7 @@ RSpec.describe "Follows #create", type: :request do
allow(rate_limit_checker)
.to receive(:user_today_follow_count)
.and_return(SiteConfig.rate_limit_follow_count_daily + 1)
.and_return(Settings::RateLimit.follow_count_daily + 1)
allow(RateLimitChecker)
.to receive(:new)

View file

@ -23,7 +23,8 @@ RSpec.describe RateLimitChecker, type: :service do
it "raises an error if no unique component is present for a cache key" do
action = described_class::ACTION_LIMITERS.keys.first
limiter = described_class.new(build(:user))
expect { limiter.limit_by_action(action) }.to raise_error("Invalid Cache Key: no unique component present")
expect { limiter.limit_by_action(action) }
.to raise_error("Invalid Cache Key: no unique component present")
end
# We check the excepted limits against the database, rather than our cache.
@ -34,7 +35,7 @@ RSpec.describe RateLimitChecker, type: :service do
it "returns true if #{action} limit has been reached" do
allow(Rails.cache).to receive(:read).with(
cache_key(action), raw: true
).and_return(SiteConfig.public_send("rate_limit_#{action}") + 1)
).and_return(Settings::RateLimit.public_send(action) + 1)
expect(rate_limit_checker.limit_by_action(action)).to be(true)
end
@ -42,7 +43,7 @@ RSpec.describe RateLimitChecker, type: :service do
it "returns false if #{action} limit has NOT been reached" do
allow(Rails.cache).to receive(:read).with(
cache_key(action), raw: true
).and_return(SiteConfig.public_send("rate_limit_#{action}"))
).and_return(Settings::RateLimit.public_send(action))
expect(rate_limit_checker.limit_by_action(action)).to be(false)
end
@ -50,7 +51,7 @@ RSpec.describe RateLimitChecker, type: :service do
context "when creating comments" do
before do
allow(SiteConfig).to receive(:rate_limit_comment_creation).and_return(1)
allow(Settings::RateLimit).to receive(:comment_creation).and_return(1)
end
it "returns true if too many comments at once" do
@ -64,13 +65,13 @@ RSpec.describe RateLimitChecker, type: :service do
end
it "returns true if too many published articles at once and potentially spammy" do
allow(SiteConfig).to receive(:rate_limit_published_article_antispam_creation).and_return(1)
allow(Settings::RateLimit).to receive(:published_article_antispam_creation).and_return(1)
create_list(:article, 2, user_id: user.id, published: true)
expect(rate_limit_checker.limit_by_action("published_article_antispam_creation")).to be(true)
end
it "returns true if too many published articles at once" do
allow(SiteConfig).to receive(:rate_limit_published_article_creation).and_return(1)
allow(Settings::RateLimit).to receive(:published_article_creation).and_return(1)
create_list(:article, 2, user_id: user.id, published: true)
expect(rate_limit_checker.limit_by_action("published_article_creation")).to be(true)
end
@ -78,7 +79,7 @@ RSpec.describe RateLimitChecker, type: :service do
it "returns true if a user has followed more than <daily_limit> accounts today" do
allow(rate_limit_checker)
.to receive(:user_today_follow_count)
.and_return(SiteConfig.rate_limit_follow_count_daily + 1)
.and_return(Settings::RateLimit.follow_count_daily + 1)
expect(rate_limit_checker.limit_by_action("follow_account")).to be(true)
end
@ -86,7 +87,7 @@ RSpec.describe RateLimitChecker, type: :service do
it "returns false if a user's following_users_count is less than <daily_limit>" do
allow(user)
.to receive(:following_users_count)
.and_return(SiteConfig.rate_limit_follow_count_daily - 1)
.and_return(Settings::RateLimit.follow_count_daily - 1)
expect(rate_limit_checker.limit_by_action("follow_account")).to be(false)
end
@ -94,7 +95,7 @@ RSpec.describe RateLimitChecker, type: :service do
it "returns false if a user has followed less than <daily_limit> accounts today" do
allow(rate_limit_checker)
.to receive(:user_today_follow_count)
.and_return(SiteConfig.rate_limit_follow_count_daily)
.and_return(Settings::RateLimit.follow_count_daily)
expect(rate_limit_checker.limit_by_action("follow_account")).to be(false)
end
@ -110,7 +111,7 @@ RSpec.describe RateLimitChecker, type: :service do
it "logs a rate limit hit to datadog" do
allow(Rails.cache)
.to receive(:read).with("#{user.id}_organization_creation", raw: true)
.and_return(SiteConfig.rate_limit_organization_creation + 1)
.and_return(Settings::RateLimit.organization_creation + 1)
allow(ForemStatsClient).to receive(:increment)
described_class.new(user).limit_by_action("organization_creation")
@ -147,7 +148,7 @@ RSpec.describe RateLimitChecker, type: :service do
describe "#limit_by_email_recipient_address" do
before do
allow(SiteConfig).to receive(:rate_limit_email_recipient).and_return(1)
allow(Settings::RateLimit).to receive(:email_recipient).and_return(1)
end
it "returns true if too many emails are sent to the same recipient" do