Skip to content

Commit

Permalink
Merge branch 'develop' into CV2-4072-review-fields-of-types-team-type…
Browse files Browse the repository at this point in the history
…-and-user-type
  • Loading branch information
melsawy committed Jan 25, 2024
2 parents 05e7871 + ea0eb3f commit 3d68515
Show file tree
Hide file tree
Showing 14 changed files with 144 additions and 9 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ gem 'graphiql-rails', git: 'https://github.com/meedan/graphiql-rails.git', ref:
gem 'graphql-formatter'
gem 'nokogiri', '1.14.3'
gem 'puma'
gem 'rack-attack'
gem 'rack-cors', '1.0.6', require: 'rack/cors'
gem 'sidekiq', '5.2.10'
gem 'sidekiq-cloudwatchmetrics'
Expand Down
3 changes: 3 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,8 @@ GEM
raabro (1.4.0)
racc (1.7.1)
rack (2.2.6.4)
rack-attack (6.7.0)
rack (>= 1.0, < 4)
rack-cors (1.0.6)
rack (>= 1.6.0)
rack-protection (2.2.0)
Expand Down Expand Up @@ -983,6 +985,7 @@ DEPENDENCIES
puma
pusher
rack (= 2.2.6.4)
rack-attack
rack-cors (= 1.0.6)
rack-protection (= 2.2.0)
railroady
Expand Down
11 changes: 8 additions & 3 deletions app/models/concerns/alegre_v2.rb
Original file line number Diff line number Diff line change
Expand Up @@ -252,9 +252,14 @@ def get_confirmed_items(project_media, field)
end

def get_similar_items_v2(project_media, field)
suggested_or_confirmed = get_suggested_items(project_media, field)
confirmed = get_confirmed_items(project_media, field)
Bot::Alegre.merge_suggested_and_confirmed(suggested_or_confirmed, confirmed, project_media)
type = get_type(project_media)
if !Bot::Alegre.should_get_similar_items_of_type?('master', project_media.team_id) || !Bot::Alegre.should_get_similar_items_of_type?(type, project_media.team_id)
{}
else
suggested_or_confirmed = get_suggested_items(project_media, field)
confirmed = get_confirmed_items(project_media, field)
Bot::Alegre.merge_suggested_and_confirmed(suggested_or_confirmed, confirmed, project_media)
end
end

def relate_project_media(project_media, field=nil)
Expand Down
2 changes: 1 addition & 1 deletion app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class ToSOrPrivacyPolicyReadError < StandardError; end

devise :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable,
:omniauthable, omniauth_providers: [:twitter, :facebook, :slack, :google_oauth2]
:omniauthable, :lockable, omniauth_providers: [:twitter, :facebook, :slack, :google_oauth2]

before_create :skip_confirmation_for_non_email_provider
after_create :create_source_and_account, :set_source_image, :send_welcome_email
Expand Down
3 changes: 3 additions & 0 deletions config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,5 +99,8 @@ class Application < Rails::Application
})

config.active_record.yaml_column_permitted_classes = [Time, Symbol]

# Rack Attack Configuration
config.middleware.use Rack::Attack
end
end
4 changes: 4 additions & 0 deletions config/config.yml.example
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,10 @@ development: &default
#
tipline_user_max_messages_per_day: 1500

devise_maximum_attempts: 5
devise_unlock_accounts_after: 1
login_rate_limit: 10
api_rate_limit: 100
test:
<<: *default
checkdesk_base_url_private: http://api:3000
Expand Down
3 changes: 2 additions & 1 deletion config/environments/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@

# Show full error reports and disable caching.
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
config.action_controller.perform_caching = true
config.cache_store = :memory_store

# Raise exceptions instead of rendering exception templates.
config.action_dispatch.show_exceptions = false
Expand Down
7 changes: 7 additions & 0 deletions config/initializers/devise.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ def http_auth_body
end
config.mailer = 'DeviseMailer'
config.invite_for = 1.month

# Account lockout
config.lock_strategy = :failed_attempts
config.unlock_strategy = :both
config.unlock_keys = [ :time ]
config.maximum_attempts = CheckConfig.get('devise_maximum_attempts', 5)
config.unlock_in = CheckConfig.get('devise_unlock_accounts_after', 1).hour
end

AuthTrail.geocode = false
Expand Down
21 changes: 21 additions & 0 deletions config/initializers/rack_attack.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class Rack::Attack
# Throttle login attempts by IP address
throttle('logins/ip', limit: proc { CheckConfig.get('login_rate_limit', 10, :integer) }, period: 60.seconds) do |req|
if req.path == '/api/users/sign_in' && req.post?
req.ip
end
end

# Throttle login attempts by email address
throttle('logins/email', limit: proc { CheckConfig.get('login_rate_limit', 10, :integer) }, period: 60.seconds) do |req|
if req.path == '/api/users/sign_in' && req.post?
# Return the email if present, nil otherwise
req.params['user']['email'].presence if req.params['user']
end
end

# Throttle all graphql requests by IP address
throttle('api/graphql', limit: proc { CheckConfig.get('api_rate_limit', 100, :integer) }, period: 60.seconds) do |req|
req.ip if req.path == '/api/graphql'
end
end
7 changes: 7 additions & 0 deletions db/migrate/20240115101312_add_lockable_to_devise.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class AddLockableToDevise < ActiveRecord::Migration[6.1]
def change
add_column :users, :failed_attempts, :integer, default: 0, null: false
add_column :users, :unlock_token, :string
add_column :users, :locked_at, :datetime
end
end
9 changes: 6 additions & 3 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2024_01_14_024701) do
ActiveRecord::Schema.define(version: 2024_01_15_101312) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Expand Down Expand Up @@ -669,7 +669,6 @@
t.datetime "updated_at", null: false
t.string "state"
t.index ["external_id", "state"], name: "index_tipline_messages_on_external_id_and_state", unique: true
t.index ["external_id"], name: "index_tipline_messages_on_external_id"
t.index ["team_id"], name: "index_tipline_messages_on_team_id"
t.index ["uid"], name: "index_tipline_messages_on_uid"
end
Expand Down Expand Up @@ -799,6 +798,9 @@
t.integer "consumed_timestep"
t.boolean "otp_required_for_login"
t.string "otp_backup_codes", array: true
t.integer "failed_attempts", default: 0, null: false
t.string "unlock_token"
t.datetime "locked_at"
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
t.index ["email"], name: "index_users_on_email", unique: true, where: "((email IS NOT NULL) AND ((email)::text <> ''::text))"
t.index ["invitation_token"], name: "index_users_on_invitation_token", unique: true
Expand All @@ -810,7 +812,8 @@
end

create_table "versions", id: :serial, force: :cascade do |t|
t.string "item_type", null: false
t.string "item_type"
t.string "{:null=>false}"
t.string "item_id", null: false
t.string "event", null: false
t.string "whodunnit"
Expand Down
30 changes: 30 additions & 0 deletions test/controllers/sessions_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,35 @@ def setup
assert_not_nil @controller.current_api_user
end

test "should lock user after excessive login requests" do
u = create_user login: 'test', password: '12345678', password_confirmation: '12345678', email: '[email protected]'
u.confirm
Devise.maximum_attempts = 2

2.times do
post :create, params: { api_user: { email: '[email protected]', password: '12345679' } }
end

u.reload
assert u.access_locked?
assert_not_nil u.locked_at
end

test "should unlock locked user accounts after specified time" do
u = create_user login: 'test', password: '12345678', password_confirmation: '12345678', email: '[email protected]'
u.confirm
maximum_attempts = Devise.maximum_attempts

maximum_attempts.times do
post :create, params: { api_user: { email: '[email protected]', password: '12345679' } }
end

travel_to CheckConfig.get('devise_unlock_accounts_after', 5, :integer) + 1.hours.from_now do
u.reload
assert !u.access_locked?
assert_nil u.locked_at
end
end


end
32 changes: 32 additions & 0 deletions test/lib/check_rack_attack_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
require 'test_helper'

class ThrottlingTest < ActionDispatch::IntegrationTest
setup do
Rails.cache.clear
end

test "should throttle excessive requests to /api/graphql" do
stub_configs({ 'api_rate_limit' => 2 }) do
2.times do
post api_graphql_path
assert_response :unauthorized
end

post api_graphql_path
assert_response :too_many_requests
end
end

test "should throttle excessive requests to /api/users/sign_in" do
stub_configs({ 'login_rate_limit' => 2 }) do
user_params = { api_user: { email: '[email protected]', password: 'password' } }

2.times do
post api_user_session_path, params: user_params, as: :json
end

post api_user_session_path, params: user_params, as: :json
assert_response :too_many_requests
end
end
end
20 changes: 19 additions & 1 deletion test/models/bot/alegre_v2_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -650,4 +650,22 @@ def teardown
assert_equal relationship.relationship_type, Relationship.suggested_type
Bot::Alegre.unstub(:get_similar_items_v2)
end
end

test "should not relate project media for audio if disabled on workspace" do
tbi = TeamBotInstallation.where(team: @team, user: @bot).last
tbi.set_audio_similarity_enabled = false
tbi.save!
Bot::Alegre.stubs(:merge_suggested_and_confirmed).never
pm = create_project_media team: @team, media: create_uploaded_audio
assert_equal({}, Bot::Alegre.get_similar_items_v2(pm, nil))
end

test "should not relate project media for image if disabled on workspace" do
tbi = TeamBotInstallation.where(team: @team, user: @bot).last
tbi.set_image_similarity_enabled = false
tbi.save!
Bot::Alegre.stubs(:merge_suggested_and_confirmed).never
pm = create_project_media team: @team, media: create_uploaded_image
assert_equal({}, Bot::Alegre.get_similar_items_v2(pm, nil))
end
end

0 comments on commit 3d68515

Please sign in to comment.