Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/controllers/concerns/page_limiter.rb')
-rw-r--r--app/controllers/concerns/page_limiter.rb68
1 files changed, 68 insertions, 0 deletions
diff --git a/app/controllers/concerns/page_limiter.rb b/app/controllers/concerns/page_limiter.rb
new file mode 100644
index 00000000000..5b078d80fca
--- /dev/null
+++ b/app/controllers/concerns/page_limiter.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+# Include this in your controller and call `limit_pages` in order
+# to configure the limiter.
+#
+# Examples:
+# class MyController < ApplicationController
+# include PageLimiter
+#
+# before_action only: [:index] do
+# limit_pages(500)
+# end
+#
+# # You can override the default response
+# rescue_from PageOutOfBoundsError, with: :page_out_of_bounds
+#
+# def page_out_of_bounds(error)
+# # Page limit number is available as error.message
+# head :ok
+# end
+#
+
+module PageLimiter
+ extend ActiveSupport::Concern
+
+ PageLimiterError = Class.new(StandardError)
+ PageLimitNotANumberError = Class.new(PageLimiterError)
+ PageLimitNotSensibleError = Class.new(PageLimiterError)
+ PageOutOfBoundsError = Class.new(PageLimiterError)
+
+ included do
+ rescue_from PageOutOfBoundsError, with: :default_page_out_of_bounds_response
+ end
+
+ def limit_pages(max_page_number)
+ check_page_number!(max_page_number)
+ end
+
+ private
+
+ # If the page exceeds the defined maximum, raise a PageOutOfBoundsError
+ # If the page doesn't exceed the limit, it does nothing.
+ def check_page_number!(max_page_number)
+ raise PageLimitNotANumberError unless max_page_number.is_a?(Integer)
+ raise PageLimitNotSensibleError unless max_page_number > 0
+
+ if params[:page].present? && params[:page].to_i > max_page_number
+ record_page_limit_interception
+ raise PageOutOfBoundsError.new(max_page_number)
+ end
+ end
+
+ # By default just return a HTTP status code and an empty response
+ def default_page_out_of_bounds_response
+ head :bad_request
+ end
+
+ # Record the page limit being hit in Prometheus
+ def record_page_limit_interception
+ dd = DeviceDetector.new(request.user_agent)
+
+ Gitlab::Metrics.counter(:gitlab_page_out_of_bounds,
+ controller: params[:controller],
+ action: params[:action],
+ bot: dd.bot?
+ )
+ end
+end