Skip to content

Commit

Permalink
Merge branch 'develop' into fix/CV2-5326-validate-video-size-after-co…
Browse files Browse the repository at this point in the history
…nverting-2
  • Loading branch information
caiosba committed Sep 24, 2024
2 parents e729b4e + 27270fc commit 73bb7af
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 43 deletions.
12 changes: 11 additions & 1 deletion app/models/api_key.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ class ApiKey < ApplicationRecord

validate :validate_team_api_keys_limit, on: :create

has_one :bot_user, dependent: :destroy
has_one :bot_user

after_destroy :delete_bot_user

# Reimplement this method in your application
def self.applications
Expand Down Expand Up @@ -52,6 +54,14 @@ def create_bot_user
end
end

def delete_bot_user
if bot_user.present? && bot_user.owns_media?
bot_user.update(api_key_id: nil)
else
bot_user.destroy
end
end

def set_user_and_team
self.user = User.current unless User.current.nil?
self.team = Team.current unless Team.current.nil?
Expand Down
3 changes: 1 addition & 2 deletions app/models/concerns/user_private.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,7 @@ def set_blank_email_for_unconfirmed_user
end

def can_destroy_user
count = ProjectMedia.where(user_id: self.id).count
throw :abort if count > 0
throw :abort if owns_media?
end

def set_user_notification_settings(type, enabled)
Expand Down
5 changes: 5 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,11 @@ def self.reset_change_password(inputs)
user
end

def owns_media?
ProjectMedia.where(user_id: self.id).count > 0
end


# private
#
# Please add private methods to app/models/concerns/user_private.rb
Expand Down
2 changes: 1 addition & 1 deletion config/config.yml.example
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ development: &default
nlu_user_rate_limit: 30
devise_maximum_attempts: 5
devise_unlock_accounts_after: 1
login_rate_limit: 10
login_block_limit: 100
api_rate_limit: 100
export_csv_maximum_number_of_results: 10000
export_csv_expire: 604800 # Seconds: Default is 7 days
Expand Down
39 changes: 36 additions & 3 deletions config/initializers/devise.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,43 @@ def http_auth_body
config.mailer = 'DeviseMailer'
config.invite_for = 1.month

extract_client_ip = Proc.new do |request|
if request.env['HTTP_CF_CONNECTING_IP']
request.env['HTTP_CF_CONNECTING_IP'].split(',').first.strip
else
request.ip
end
end

Warden::Manager.after_authentication do |user, auth, opts|
@redis = Redis.new(REDIS_CONFIG)
ip = auth.request.ip
@redis.decr("track:#{ip}")
redis = Redis.new(REDIS_CONFIG)
request = auth.request
ip = extract_client_ip.call(request)
redis.del("track:#{ip}")
end

Warden::Manager.before_failure do |env, opts|
if opts[:attempted_path] == '/api/users/sign_in'
redis = Redis.new(REDIS_CONFIG)
request = Rack::Request.new(env)
ip = extract_client_ip.call(request)

begin
count = redis.incr("track:#{ip}")
redis.expire("track:#{ip}", 3600)
redis.set("track:#{ip}", 0) if count.to_i < 0

login_block_limit = CheckConfig.get('login_block_limit', 100, :integer)

# Add IP to blocklist if count exceeds the threshold
if count.to_i >= login_block_limit
redis.set("block:#{ip}", true) # No expiration, IP is blocked
Rails.logger.info("IP #{ip} has been blocked after #{count} failed login attempts.")
end
rescue => e
Rails.logger.error("Warden before_failure Error: #{e.message}")
end
end
end
end

Expand Down
23 changes: 0 additions & 23 deletions config/initializers/rack_attack.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,29 +27,6 @@ def self.authenticated?(req)
redis.get("block:#{real_ip(req)}") == "true"
end

# Track excessive login attempts for permanent blocking
track('track excessive logins/ip') do |req|
if req.path == '/api/users/sign_in' && req.post?
ip = real_ip(req)
begin
# Increment the counter for the IP and check if it should be blocked
count = redis.incr("track:#{ip}")
redis.expire("track:#{ip}", 3600) # Set the expiration time to 1 hour

redis.set("track:#{ip}", 0) if count < 0

# Add IP to blocklist if count exceeds the threshold
if count.to_i >= CheckConfig.get('login_block_limit', 100, :integer)
redis.set("block:#{ip}", true) # No expiration
end
rescue => e
Rails.logger.error("Rack::Attack Error: #{e.message}")
end

ip
end
end

# Response to blocked requests
self.blocklisted_responder = lambda do |req|
[403, {}, ['Blocked - Your IP has been permanently blocked due to suspicious activity.']]
Expand Down
26 changes: 13 additions & 13 deletions test/lib/check_rack_attack_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,41 +87,41 @@ def real_ip(request)
end

test "should not increment counter on successful login" do
stub_configs({ 'login_block_limit' => 3 }) do
stub_configs({ 'login_block_limit' => 2 }) do
password = random_complex_password
user = create_user password: password
user_params = { api_user: { email: user.email, password: password } }

# Successful logins
2.times do
3.times do
post api_user_session_path, params: user_params, as: :json
assert_response :success
end

ip = real_ip(@request)
counter_value = @redis.get("track:#{ip}")
assert_equal "0", counter_value, "Counter should not be incremented for successful logins"
assert_nil counter_value, "Counter should not exist after successful logins"
end
end

delete destroy_api_user_session_path, as: :json
assert_response :success
test "should block IP after excessive invalid login attempts" do
stub_configs({ 'login_block_limit' => 2 }) do
password = random_complex_password
user = create_user password: password

# Unsuccessful login attempts
2.times do
post api_user_session_path, params: { api_user: { email: user.email, password: 'wrong_password' } }, as: :json
assert_response :unauthorized
end

# Check counter value after unsuccessful logins
ip = real_ip(@request)
counter_value = @redis.get("track:#{ip}")
assert_equal "2", counter_value, "Counter should be incremented for unsuccessful logins"

# Ensure that the IP is not blocked after successful logins
post api_user_session_path, params: user_params, as: :json
assert_response :success

# Subsequent unsuccessful login attempts should result in a block
# Subsequent unsuccessful login attempts should result in a blocked IP
post api_user_session_path, params: { api_user: { email: user.email, password: 'wrong_password' } }, as: :json
assert_response :forbidden

assert_equal "true", @redis.get("block:#{ip}")
end
end
end
18 changes: 18 additions & 0 deletions test/models/api_key_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,22 @@ class ApiKeyTest < ActiveSupport::TestCase
end
end
end

test "should delete API key even if key's user has been used to create media" do
a = create_api_key
b = create_bot_user
b.api_key = a
b.save!
pm = create_project_media user: b

assert_equal b, pm.user

assert_difference 'ApiKey.count', -1 do
a.destroy
end

assert_raises ActiveRecord::RecordNotFound do
ApiKey.find(a.id)
end
end
end

0 comments on commit 73bb7af

Please sign in to comment.