File: page_limiter.rb

package info (click to toggle)
gitlab 17.6.5-19
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 629,368 kB
  • sloc: ruby: 1,915,304; javascript: 557,307; sql: 60,639; xml: 6,509; sh: 4,567; makefile: 1,239; python: 406
file content (69 lines) | stat: -rw-r--r-- 1,976 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# 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

    return if params[:page].blank?
    return if params[:page].to_i <= max_page_number

    record_page_limit_interception
    raise PageOutOfBoundsError, max_page_number
  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 = Gitlab::SafeDeviceDetector.new(request.user_agent)

    Gitlab::Metrics.counter(:gitlab_page_out_of_bounds,
      controller: params[:controller],
      action: params[:action],
      bot: dd.bot?
    ).increment
  end
end