Commit 54f13b1e authored by Stan Hu's avatar Stan Hu

Add rate limiting to guard against excessive scheduling of pipelines

parent f8c3a58a
......@@ -42,6 +42,13 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController
end
def play
limiter = ::Gitlab::ActionRateLimiter.new(action: 'play_pipeline_schedule')
if limiter.throttled?(throttle_key, 1)
flash[:notice] = 'You cannot play this scheduled pipeline at the moment. Please wait a minute.'
return redirect_to pipeline_schedules_path(@project)
end
job_id = RunPipelineScheduleWorker.perform_async(schedule.id, current_user.id)
flash[:notice] =
......@@ -74,6 +81,10 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController
private
def throttle_key
"user:#{current_user.id}:schedule:#{schedule.id}"
end
def schedule
@schedule ||= project.pipeline_schedules.find(params[:id])
end
......
module Gitlab
# This class implements a simple rate limiter that can be used to throttle
# certain actions. Unlike Rack Attack and Rack::Throttle, which operate at
# the middleware level, this can be used at the controller level.
class ActionRateLimiter
TIME_TO_EXPIRE = 60 # 1 min
attr_accessor :action, :expiry_time
def initialize(action:, expiry_time: TIME_TO_EXPIRE)
@action = action
@expiry_time = expiry_time
end
def increment(key)
value = 0
Gitlab::Redis::Cache.with do |redis|
cache_key = "action_rate_limiter:#{action}:#{key}"
value = redis.incr(cache_key)
redis.expire(cache_key, expiry_time) if value == 1
end
value.to_i
end
def throttled?(key, threshold_value)
self.increment(key) > threshold_value
end
end
end
......@@ -366,7 +366,7 @@ describe Projects::PipelineSchedulesController do
end
end
describe 'POST #play' do
describe 'POST #play', :clean_gitlab_redis_cache do
set(:user) { create(:user) }
let(:ref) { 'master' }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment