castle-rb 4.2.1 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/README.md +160 -45
 - data/lib/castle.rb +49 -28
 - data/lib/castle/api.rb +21 -14
 - data/lib/castle/api/approve_device.rb +20 -0
 - data/lib/castle/api/authenticate.rb +37 -0
 - data/lib/castle/api/end_impersonation.rb +24 -0
 - data/lib/castle/api/filter.rb +37 -0
 - data/lib/castle/api/get_device.rb +20 -0
 - data/lib/castle/api/get_devices_for_user.rb +20 -0
 - data/lib/castle/api/log.rb +37 -0
 - data/lib/castle/api/report_device.rb +20 -0
 - data/lib/castle/api/risk.rb +37 -0
 - data/lib/castle/api/start_impersonation.rb +24 -0
 - data/lib/castle/api/track.rb +21 -0
 - data/lib/castle/client.rb +78 -51
 - data/lib/castle/{extractors/client_id.rb → client_id/extract.rb} +2 -2
 - data/lib/castle/commands/approve_device.rb +17 -0
 - data/lib/castle/commands/authenticate.rb +13 -13
 - data/lib/castle/commands/end_impersonation.rb +25 -0
 - data/lib/castle/commands/filter.rb +23 -0
 - data/lib/castle/commands/get_device.rb +17 -0
 - data/lib/castle/commands/get_devices_for_user.rb +17 -0
 - data/lib/castle/commands/log.rb +23 -0
 - data/lib/castle/commands/report_device.rb +17 -0
 - data/lib/castle/commands/risk.rb +23 -0
 - data/lib/castle/commands/start_impersonation.rb +25 -0
 - data/lib/castle/commands/track.rb +12 -13
 - data/lib/castle/configuration.rb +57 -32
 - data/lib/castle/context/{default.rb → get_default.rb} +5 -6
 - data/lib/castle/context/{merger.rb → merge.rb} +3 -3
 - data/lib/castle/context/prepare.rb +18 -0
 - data/lib/castle/context/{sanitizer.rb → sanitize.rb} +1 -1
 - data/lib/castle/core/get_connection.rb +27 -0
 - data/lib/castle/{api/response.rb → core/process_response.rb} +8 -3
 - data/lib/castle/core/process_webhook.rb +25 -0
 - data/lib/castle/core/send_request.rb +42 -0
 - data/lib/castle/errors.rb +38 -12
 - data/lib/castle/failover/prepare_response.rb +18 -0
 - data/lib/castle/failover/strategy.rb +23 -0
 - data/lib/castle/headers/extract.rb +47 -0
 - data/lib/castle/headers/filter.rb +40 -0
 - data/lib/castle/headers/format.rb +24 -0
 - data/lib/castle/{extractors/ip.rb → ips/extract.rb} +31 -9
 - data/lib/castle/logger.rb +19 -0
 - data/lib/castle/payload/prepare.rb +26 -0
 - data/lib/castle/secure_mode.rb +7 -2
 - data/lib/castle/session.rb +18 -0
 - data/lib/castle/singleton_configuration.rb +9 -0
 - data/lib/castle/support/hanami.rb +2 -6
 - data/lib/castle/support/rails.rb +1 -3
 - data/lib/castle/utils/clean_invalid_chars.rb +22 -0
 - data/lib/castle/utils/clone.rb +15 -0
 - data/lib/castle/utils/deep_symbolize_keys.rb +45 -0
 - data/lib/castle/utils/get_timestamp.rb +15 -0
 - data/lib/castle/utils/{merger.rb → merge.rb} +3 -3
 - data/lib/castle/utils/secure_compare.rb +22 -0
 - data/lib/castle/validators/not_supported.rb +1 -0
 - data/lib/castle/validators/present.rb +1 -0
 - data/lib/castle/verdict.rb +15 -0
 - data/lib/castle/version.rb +1 -1
 - data/lib/castle/webhooks/verify.rb +45 -0
 - data/spec/integration/rails/rails_spec.rb +42 -14
 - data/spec/integration/rails/support/application.rb +3 -1
 - data/spec/integration/rails/support/home_controller.rb +50 -6
 - data/spec/lib/castle/api/approve_device_spec.rb +21 -0
 - data/spec/lib/castle/api/authenticate_spec.rb +136 -0
 - data/spec/lib/castle/api/end_impersonation_spec.rb +65 -0
 - data/spec/lib/castle/api/filter_spec.rb +5 -0
 - data/spec/lib/castle/api/get_device_spec.rb +19 -0
 - data/spec/lib/castle/api/get_devices_for_user_spec.rb +19 -0
 - data/spec/lib/castle/api/log_spec.rb +5 -0
 - data/spec/lib/castle/api/report_device_spec.rb +21 -0
 - data/spec/lib/castle/api/risk_spec.rb +5 -0
 - data/spec/lib/castle/api/start_impersonation_spec.rb +65 -0
 - data/spec/lib/castle/api/track_spec.rb +72 -0
 - data/spec/lib/castle/api_spec.rb +14 -15
 - data/spec/lib/castle/{extractors/client_id_spec.rb → client_id/extract_spec.rb} +6 -15
 - data/spec/lib/castle/client_spec.rb +108 -93
 - data/spec/lib/castle/commands/approve_device_spec.rb +24 -0
 - data/spec/lib/castle/commands/authenticate_spec.rb +15 -31
 - data/spec/lib/castle/commands/end_impersonation_spec.rb +79 -0
 - data/spec/lib/castle/commands/filter_spec.rb +99 -0
 - data/spec/lib/castle/commands/get_device_spec.rb +24 -0
 - data/spec/lib/castle/commands/{review_spec.rb → get_devices_for_user_spec.rb} +7 -7
 - data/spec/lib/castle/commands/log_spec.rb +100 -0
 - data/spec/lib/castle/commands/report_device_spec.rb +24 -0
 - data/spec/lib/castle/commands/risk_spec.rb +100 -0
 - data/spec/lib/castle/commands/start_impersonation_spec.rb +79 -0
 - data/spec/lib/castle/commands/track_spec.rb +14 -34
 - data/spec/lib/castle/configuration_spec.rb +8 -141
 - data/spec/lib/castle/context/{default_spec.rb → get_default_spec.rb} +9 -10
 - data/spec/lib/castle/context/{merger_spec.rb → merge_spec.rb} +1 -1
 - data/spec/lib/castle/context/prepare_spec.rb +43 -0
 - data/spec/lib/castle/context/{sanitizer_spec.rb → sanitize_spec.rb} +1 -1
 - data/spec/lib/castle/core/get_connection_spec.rb +43 -0
 - data/spec/lib/castle/{api/response_spec.rb → core/process_response_spec.rb} +49 -1
 - data/spec/lib/castle/core/process_webhook_spec.rb +46 -0
 - data/spec/lib/castle/core/send_request_spec.rb +77 -0
 - data/spec/lib/castle/failover/strategy_spec.rb +12 -0
 - data/spec/lib/castle/{extractors/headers_spec.rb → headers/extract_spec.rb} +18 -20
 - data/spec/lib/castle/headers/filter_spec.rb +39 -0
 - data/spec/lib/castle/headers/format_spec.rb +25 -0
 - data/spec/lib/castle/{extractors/ip_spec.rb → ips/extract_spec.rb} +27 -8
 - data/spec/lib/castle/logger_spec.rb +38 -0
 - data/spec/lib/castle/payload/prepare_spec.rb +55 -0
 - data/spec/lib/castle/session_spec.rb +65 -0
 - data/spec/lib/castle/singleton_configuration_spec.rb +14 -0
 - data/spec/lib/castle/utils/clean_invalid_chars_spec.rb +69 -0
 - data/spec/lib/castle/utils/{cloner_spec.rb → clone_spec.rb} +3 -3
 - data/spec/lib/castle/utils/deep_symbolize_keys_spec.rb +50 -0
 - data/spec/lib/castle/utils/{timestamp_spec.rb → get_timestamp_spec.rb} +1 -1
 - data/spec/lib/castle/utils/merge_spec.rb +15 -0
 - data/spec/lib/castle/validators/present_spec.rb +5 -6
 - data/spec/lib/castle/verdict_spec.rb +9 -0
 - data/spec/lib/castle/webhooks/verify_spec.rb +53 -0
 - data/spec/lib/castle_spec.rb +4 -10
 - data/spec/spec_helper.rb +3 -3
 - data/spec/support/shared_examples/action_request.rb +152 -0
 - data/spec/support/shared_examples/configuration.rb +101 -0
 - metadata +146 -64
 - data/lib/castle/api/request.rb +0 -42
 - data/lib/castle/api/session.rb +0 -39
 - data/lib/castle/commands/identify.rb +0 -23
 - data/lib/castle/commands/impersonate.rb +0 -26
 - data/lib/castle/commands/review.rb +0 -14
 - data/lib/castle/events.rb +0 -49
 - data/lib/castle/extractors/headers.rb +0 -45
 - data/lib/castle/failover_auth_response.rb +0 -21
 - data/lib/castle/headers_filter.rb +0 -35
 - data/lib/castle/headers_formatter.rb +0 -22
 - data/lib/castle/review.rb +0 -11
 - data/lib/castle/utils.rb +0 -55
 - data/lib/castle/utils/cloner.rb +0 -11
 - data/lib/castle/utils/timestamp.rb +0 -12
 - data/spec/lib/castle/api/request_spec.rb +0 -72
 - data/spec/lib/castle/commands/identify_spec.rb +0 -88
 - data/spec/lib/castle/commands/impersonate_spec.rb +0 -107
 - data/spec/lib/castle/events_spec.rb +0 -5
 - data/spec/lib/castle/headers_filter_spec.rb +0 -37
 - data/spec/lib/castle/headers_formatter_spec.rb +0 -25
 - data/spec/lib/castle/review_spec.rb +0 -19
 - data/spec/lib/castle/utils/merger_spec.rb +0 -13
 - data/spec/lib/castle/utils_spec.rb +0 -156
 
| 
         @@ -2,11 +2,11 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            module Castle
         
     | 
| 
       4 
4 
     | 
    
         
             
              module Context
         
     | 
| 
       5 
     | 
    
         
            -
                class  
     | 
| 
      
 5 
     | 
    
         
            +
                class Merge
         
     | 
| 
       6 
6 
     | 
    
         
             
                  class << self
         
     | 
| 
       7 
7 
     | 
    
         
             
                    def call(initial_context, request_context)
         
     | 
| 
       8 
     | 
    
         
            -
                      main_context = Castle::Utils:: 
     | 
| 
       9 
     | 
    
         
            -
                      Castle::Utils:: 
     | 
| 
      
 8 
     | 
    
         
            +
                      main_context = Castle::Utils::Clone.call(initial_context)
         
     | 
| 
      
 9 
     | 
    
         
            +
                      Castle::Utils::Merge.call(main_context, request_context || {})
         
     | 
| 
       10 
10 
     | 
    
         
             
                    end
         
     | 
| 
       11 
11 
     | 
    
         
             
                  end
         
     | 
| 
       12 
12 
     | 
    
         
             
                end
         
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Castle
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Context
         
     | 
| 
      
 5 
     | 
    
         
            +
                # prepares the context from the request
         
     | 
| 
      
 6 
     | 
    
         
            +
                module Prepare
         
     | 
| 
      
 7 
     | 
    
         
            +
                  class << self
         
     | 
| 
      
 8 
     | 
    
         
            +
                    # @param request [Request]
         
     | 
| 
      
 9 
     | 
    
         
            +
                    # @param options [Hash]
         
     | 
| 
      
 10 
     | 
    
         
            +
                    # @return [Hash]
         
     | 
| 
      
 11 
     | 
    
         
            +
                    def call(request, options = {})
         
     | 
| 
      
 12 
     | 
    
         
            +
                      default_context = Castle::Context::GetDefault.new(request, options[:cookies]).call
         
     | 
| 
      
 13 
     | 
    
         
            +
                      Castle::Context::Merge.call(default_context, options[:context])
         
     | 
| 
      
 14 
     | 
    
         
            +
                    end
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,27 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Castle
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Core
         
     | 
| 
      
 5 
     | 
    
         
            +
                # this module returns a new configured Net::HTTP object
         
     | 
| 
      
 6 
     | 
    
         
            +
                module GetConnection
         
     | 
| 
      
 7 
     | 
    
         
            +
                  HTTPS_SCHEME = 'https'
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                  class << self
         
     | 
| 
      
 10 
     | 
    
         
            +
                    # @param config [Castle::Configuration, Castle::SingletonConfiguration]
         
     | 
| 
      
 11 
     | 
    
         
            +
                    # @return [Net::HTTP]
         
     | 
| 
      
 12 
     | 
    
         
            +
                    def call(config = nil)
         
     | 
| 
      
 13 
     | 
    
         
            +
                      config ||= Castle.config
         
     | 
| 
      
 14 
     | 
    
         
            +
                      http = Net::HTTP.new(config.base_url.host, config.base_url.port)
         
     | 
| 
      
 15 
     | 
    
         
            +
                      http.read_timeout = config.request_timeout / 1000.0
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                      if config.base_url.scheme == HTTPS_SCHEME
         
     | 
| 
      
 18 
     | 
    
         
            +
                        http.use_ssl = true
         
     | 
| 
      
 19 
     | 
    
         
            +
                        http.verify_mode = OpenSSL::SSL::VERIFY_PEER
         
     | 
| 
      
 20 
     | 
    
         
            +
                      end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                      http
         
     | 
| 
      
 23 
     | 
    
         
            +
                    end
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -1,9 +1,9 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            module Castle
         
     | 
| 
       4 
     | 
    
         
            -
              module  
     | 
| 
      
 4 
     | 
    
         
            +
              module Core
         
     | 
| 
       5 
5 
     | 
    
         
             
                # parses api response
         
     | 
| 
       6 
     | 
    
         
            -
                module  
     | 
| 
      
 6 
     | 
    
         
            +
                module ProcessResponse
         
     | 
| 
       7 
7 
     | 
    
         
             
                  RESPONSE_ERRORS = {
         
     | 
| 
       8 
8 
     | 
    
         
             
                    400 => Castle::BadRequestError,
         
     | 
| 
       9 
9 
     | 
    
         
             
                    401 => Castle::UnauthorizedError,
         
     | 
| 
         @@ -14,9 +14,14 @@ module Castle 
     | 
|
| 
       14 
14 
     | 
    
         
             
                  }.freeze
         
     | 
| 
       15 
15 
     | 
    
         | 
| 
       16 
16 
     | 
    
         
             
                  class << self
         
     | 
| 
       17 
     | 
    
         
            -
                     
     | 
| 
      
 17 
     | 
    
         
            +
                    # @param response [Response]
         
     | 
| 
      
 18 
     | 
    
         
            +
                    # @param config [Castle::Configuration, Castle::SingletonConfiguration, nil]
         
     | 
| 
      
 19 
     | 
    
         
            +
                    # @return [Hash]
         
     | 
| 
      
 20 
     | 
    
         
            +
                    def call(response, config = nil)
         
     | 
| 
       18 
21 
     | 
    
         
             
                      verify!(response)
         
     | 
| 
       19 
22 
     | 
    
         | 
| 
      
 23 
     | 
    
         
            +
                      Castle::Logger.call('response:', response.body.to_s, config)
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
       20 
25 
     | 
    
         
             
                      return {} if response.body.nil? || response.body.empty?
         
     | 
| 
       21 
26 
     | 
    
         | 
| 
       22 
27 
     | 
    
         
             
                      begin
         
     | 
| 
         @@ -0,0 +1,25 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Castle
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Core
         
     | 
| 
      
 5 
     | 
    
         
            +
                # Parses a webhook
         
     | 
| 
      
 6 
     | 
    
         
            +
                module ProcessWebhook
         
     | 
| 
      
 7 
     | 
    
         
            +
                  class << self
         
     | 
| 
      
 8 
     | 
    
         
            +
                    # Checks if webhook is valid
         
     | 
| 
      
 9 
     | 
    
         
            +
                    # @param webhook [Request]
         
     | 
| 
      
 10 
     | 
    
         
            +
                    # @param config [Castle::Configuration, Castle::SingletonConfiguration, nil]
         
     | 
| 
      
 11 
     | 
    
         
            +
                    # @return [String]
         
     | 
| 
      
 12 
     | 
    
         
            +
                    def call(webhook, config = nil)
         
     | 
| 
      
 13 
     | 
    
         
            +
                      webhook
         
     | 
| 
      
 14 
     | 
    
         
            +
                        .body
         
     | 
| 
      
 15 
     | 
    
         
            +
                        .read
         
     | 
| 
      
 16 
     | 
    
         
            +
                        .tap do |result|
         
     | 
| 
      
 17 
     | 
    
         
            +
                          raise Castle::ApiError, 'Invalid webhook from Castle API' if result.blank?
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                          Castle::Logger.call('webhook:', result.to_s, config)
         
     | 
| 
      
 20 
     | 
    
         
            +
                        end
         
     | 
| 
      
 21 
     | 
    
         
            +
                    end
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,42 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Castle
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Core
         
     | 
| 
      
 5 
     | 
    
         
            +
                # this class is responsible for making requests to api
         
     | 
| 
      
 6 
     | 
    
         
            +
                module SendRequest
         
     | 
| 
      
 7 
     | 
    
         
            +
                  # Default headers that we add to passed ones
         
     | 
| 
      
 8 
     | 
    
         
            +
                  DEFAULT_HEADERS = { 'Content-Type' => 'application/json' }.freeze
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  private_constant :DEFAULT_HEADERS
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  class << self
         
     | 
| 
      
 13 
     | 
    
         
            +
                    # @param command [String]
         
     | 
| 
      
 14 
     | 
    
         
            +
                    # @param headers [Hash]
         
     | 
| 
      
 15 
     | 
    
         
            +
                    # @param http [Net::HTTP]
         
     | 
| 
      
 16 
     | 
    
         
            +
                    # @param config [Castle::Configuration, Castle::SingletonConfiguration, nil]
         
     | 
| 
      
 17 
     | 
    
         
            +
                    def call(command, headers, http = nil, config = nil)
         
     | 
| 
      
 18 
     | 
    
         
            +
                      (http || Castle::Core::GetConnection.call).request(
         
     | 
| 
      
 19 
     | 
    
         
            +
                        build(command, headers.merge(DEFAULT_HEADERS), config)
         
     | 
| 
      
 20 
     | 
    
         
            +
                      )
         
     | 
| 
      
 21 
     | 
    
         
            +
                    end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                    # @param command [String]
         
     | 
| 
      
 24 
     | 
    
         
            +
                    # @param headers [Hash]
         
     | 
| 
      
 25 
     | 
    
         
            +
                    # @param config [Castle::Configuration, Castle::SingletonConfiguration, nil]
         
     | 
| 
      
 26 
     | 
    
         
            +
                    def build(command, headers, config)
         
     | 
| 
      
 27 
     | 
    
         
            +
                      url = "#{config.base_url.path}/#{command.path}"
         
     | 
| 
      
 28 
     | 
    
         
            +
                      request_obj = Net::HTTP.const_get(command.method.to_s.capitalize).new(url, headers)
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                      unless command.method == :get
         
     | 
| 
      
 31 
     | 
    
         
            +
                        request_obj.body = ::Castle::Utils::CleanInvalidChars.call(command.data).to_json
         
     | 
| 
      
 32 
     | 
    
         
            +
                      end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                      Castle::Logger.call("#{url}:", request_obj.body, config)
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                      request_obj.basic_auth('', config.api_secret)
         
     | 
| 
      
 37 
     | 
    
         
            +
                      request_obj
         
     | 
| 
      
 38 
     | 
    
         
            +
                    end
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
              end
         
     | 
| 
      
 42 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/castle/errors.rb
    CHANGED
    
    | 
         @@ -2,7 +2,9 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            module Castle
         
     | 
| 
       4 
4 
     | 
    
         
             
              # general error
         
     | 
| 
       5 
     | 
    
         
            -
              class Error < RuntimeError 
     | 
| 
      
 5 
     | 
    
         
            +
              class Error < RuntimeError
         
     | 
| 
      
 6 
     | 
    
         
            +
              end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
       6 
8 
     | 
    
         
             
              # Raised when anything is wrong with the request (any unhappy path)
         
     | 
| 
       7 
9 
     | 
    
         
             
              # This error indicates that either we would wait too long for a response or something
         
     | 
| 
       8 
10 
     | 
    
         
             
              # else happened somewhere in the middle and we weren't able to get the results
         
     | 
| 
         @@ -14,28 +16,52 @@ module Castle 
     | 
|
| 
       14 
16 
     | 
    
         
             
                  @reason = reason
         
     | 
| 
       15 
17 
     | 
    
         
             
                end
         
     | 
| 
       16 
18 
     | 
    
         
             
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
       17 
20 
     | 
    
         
             
              # security error
         
     | 
| 
       18 
     | 
    
         
            -
              class SecurityError < Castle::Error 
     | 
| 
      
 21 
     | 
    
         
            +
              class SecurityError < Castle::Error
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
       19 
24 
     | 
    
         
             
              # wrong configuration error
         
     | 
| 
       20 
     | 
    
         
            -
              class ConfigurationError < Castle::Error 
     | 
| 
      
 25 
     | 
    
         
            +
              class ConfigurationError < Castle::Error
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
       21 
28 
     | 
    
         
             
              # error returned by api
         
     | 
| 
       22 
     | 
    
         
            -
              class ApiError < Castle::Error 
     | 
| 
      
 29 
     | 
    
         
            +
              class ApiError < Castle::Error
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
              # webhook signature verification error
         
     | 
| 
      
 33 
     | 
    
         
            +
              class WebhookVerificationError < Castle::Error
         
     | 
| 
      
 34 
     | 
    
         
            +
              end
         
     | 
| 
       23 
35 
     | 
    
         | 
| 
       24 
36 
     | 
    
         
             
              # api error bad request 400
         
     | 
| 
       25 
     | 
    
         
            -
              class BadRequestError < Castle::ApiError 
     | 
| 
      
 37 
     | 
    
         
            +
              class BadRequestError < Castle::ApiError
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
       26 
40 
     | 
    
         
             
              # api error forbidden 403
         
     | 
| 
       27 
     | 
    
         
            -
              class ForbiddenError < Castle::ApiError 
     | 
| 
      
 41 
     | 
    
         
            +
              class ForbiddenError < Castle::ApiError
         
     | 
| 
      
 42 
     | 
    
         
            +
              end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
       28 
44 
     | 
    
         
             
              # api error not found 404
         
     | 
| 
       29 
     | 
    
         
            -
              class NotFoundError < Castle::ApiError 
     | 
| 
      
 45 
     | 
    
         
            +
              class NotFoundError < Castle::ApiError
         
     | 
| 
      
 46 
     | 
    
         
            +
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
       30 
48 
     | 
    
         
             
              # api error user unauthorized 419
         
     | 
| 
       31 
     | 
    
         
            -
              class UserUnauthorizedError < Castle::ApiError 
     | 
| 
      
 49 
     | 
    
         
            +
              class UserUnauthorizedError < Castle::ApiError
         
     | 
| 
      
 50 
     | 
    
         
            +
              end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
       32 
52 
     | 
    
         
             
              # api error invalid param 422
         
     | 
| 
       33 
     | 
    
         
            -
              class InvalidParametersError < Castle::ApiError 
     | 
| 
      
 53 
     | 
    
         
            +
              class InvalidParametersError < Castle::ApiError
         
     | 
| 
      
 54 
     | 
    
         
            +
              end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
       34 
56 
     | 
    
         
             
              # api error unauthorized 401
         
     | 
| 
       35 
     | 
    
         
            -
              class UnauthorizedError < Castle::ApiError 
     | 
| 
      
 57 
     | 
    
         
            +
              class UnauthorizedError < Castle::ApiError
         
     | 
| 
      
 58 
     | 
    
         
            +
              end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
       36 
60 
     | 
    
         
             
              # all internal server errors
         
     | 
| 
       37 
     | 
    
         
            -
              class InternalServerError < Castle::ApiError 
     | 
| 
      
 61 
     | 
    
         
            +
              class InternalServerError < Castle::ApiError
         
     | 
| 
      
 62 
     | 
    
         
            +
              end
         
     | 
| 
       38 
63 
     | 
    
         | 
| 
       39 
64 
     | 
    
         
             
              # impersonation command failed
         
     | 
| 
       40 
     | 
    
         
            -
              class ImpersonationFailed < Castle::ApiError 
     | 
| 
      
 65 
     | 
    
         
            +
              class ImpersonationFailed < Castle::ApiError
         
     | 
| 
      
 66 
     | 
    
         
            +
              end
         
     | 
| 
       41 
67 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Castle
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Failover
         
     | 
| 
      
 5 
     | 
    
         
            +
                # generate failover authentication response
         
     | 
| 
      
 6 
     | 
    
         
            +
                class PrepareResponse
         
     | 
| 
      
 7 
     | 
    
         
            +
                  def initialize(user_id, reason:, strategy:)
         
     | 
| 
      
 8 
     | 
    
         
            +
                    @strategy = strategy
         
     | 
| 
      
 9 
     | 
    
         
            +
                    @reason = reason
         
     | 
| 
      
 10 
     | 
    
         
            +
                    @user_id = user_id
         
     | 
| 
      
 11 
     | 
    
         
            +
                  end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  def call
         
     | 
| 
      
 14 
     | 
    
         
            +
                    { action: @strategy.to_s, user_id: @user_id, failover: true, failover_reason: @reason }
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Castle
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Failover
         
     | 
| 
      
 5 
     | 
    
         
            +
                # handles failover strategy consts
         
     | 
| 
      
 6 
     | 
    
         
            +
                module Strategy
         
     | 
| 
      
 7 
     | 
    
         
            +
                  # allow
         
     | 
| 
      
 8 
     | 
    
         
            +
                  ALLOW = :allow
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  # deny
         
     | 
| 
      
 11 
     | 
    
         
            +
                  DENY = :deny
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  # challenge
         
     | 
| 
      
 14 
     | 
    
         
            +
                  CHALLENGE = :challenge
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  # throw an error
         
     | 
| 
      
 17 
     | 
    
         
            +
                  THROW = :throw
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                # list of possible strategies
         
     | 
| 
      
 21 
     | 
    
         
            +
                STRATEGIES = %i[allow deny challenge throw].freeze
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,47 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Castle
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Headers
         
     | 
| 
      
 5 
     | 
    
         
            +
                # used for extraction of cookies and headers from the request
         
     | 
| 
      
 6 
     | 
    
         
            +
                class Extract
         
     | 
| 
      
 7 
     | 
    
         
            +
                  # Headers that we will never scrub, even if they land on the configuration denylist.
         
     | 
| 
      
 8 
     | 
    
         
            +
                  ALWAYS_ALLOWLISTED = %w[User-Agent].freeze
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  # Headers that will always be scrubbed, even if allowlisted.
         
     | 
| 
      
 11 
     | 
    
         
            +
                  ALWAYS_DENYLISTED = %w[Cookie Authorization].freeze
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  private_constant :ALWAYS_ALLOWLISTED, :ALWAYS_DENYLISTED
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  # @param headers [Hash]
         
     | 
| 
      
 16 
     | 
    
         
            +
                  # @param config [Castle::Configuration, Castle::SingletonConfiguration, nil]
         
     | 
| 
      
 17 
     | 
    
         
            +
                  def initialize(headers, config = nil)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    @headers = headers
         
     | 
| 
      
 19 
     | 
    
         
            +
                    @config = config || Castle.config
         
     | 
| 
      
 20 
     | 
    
         
            +
                    @no_allowlist = @config.allowlisted.empty?
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  # Serialize HTTP headers
         
     | 
| 
      
 24 
     | 
    
         
            +
                  # @return [Hash]
         
     | 
| 
      
 25 
     | 
    
         
            +
                  def call
         
     | 
| 
      
 26 
     | 
    
         
            +
                    @headers.each_with_object({}) do |(name, value), acc|
         
     | 
| 
      
 27 
     | 
    
         
            +
                      acc[name] = header_value(name, value)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    end
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  private
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  # scrub header value
         
     | 
| 
      
 34 
     | 
    
         
            +
                  # @param name [String]
         
     | 
| 
      
 35 
     | 
    
         
            +
                  # @param value [String]
         
     | 
| 
      
 36 
     | 
    
         
            +
                  # @return [TrueClass | FalseClass | String]
         
     | 
| 
      
 37 
     | 
    
         
            +
                  def header_value(name, value)
         
     | 
| 
      
 38 
     | 
    
         
            +
                    return true if ALWAYS_DENYLISTED.include?(name)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    return value if ALWAYS_ALLOWLISTED.include?(name)
         
     | 
| 
      
 40 
     | 
    
         
            +
                    return true if @config.denylisted.include?(name)
         
     | 
| 
      
 41 
     | 
    
         
            +
                    return value if @no_allowlist || @config.allowlisted.include?(name)
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                    true
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,40 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Castle
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Headers
         
     | 
| 
      
 5 
     | 
    
         
            +
                # used for preparing valuable headers list
         
     | 
| 
      
 6 
     | 
    
         
            +
                class Filter
         
     | 
| 
      
 7 
     | 
    
         
            +
                  # headers filter
         
     | 
| 
      
 8 
     | 
    
         
            +
                  # HTTP_ - this is how Rack prefixes incoming HTTP headers
         
     | 
| 
      
 9 
     | 
    
         
            +
                  # CONTENT_LENGTH - for responses without Content-Length or Transfer-Encoding header
         
     | 
| 
      
 10 
     | 
    
         
            +
                  # REMOTE_ADDR - ip address header returned by web server
         
     | 
| 
      
 11 
     | 
    
         
            +
                  VALUABLE_HEADERS = /^
         
     | 
| 
      
 12 
     | 
    
         
            +
                  HTTP(?:_|-).*|
         
     | 
| 
      
 13 
     | 
    
         
            +
                    CONTENT(?:_|-)LENGTH|
         
     | 
| 
      
 14 
     | 
    
         
            +
                  REMOTE(?:_|-)ADDR
         
     | 
| 
      
 15 
     | 
    
         
            +
                  $/xi
         
     | 
| 
      
 16 
     | 
    
         
            +
                    .freeze
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  private_constant :VALUABLE_HEADERS
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  # @param request [Rack::Request]
         
     | 
| 
      
 21 
     | 
    
         
            +
                  def initialize(request)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    @request_env = request.env
         
     | 
| 
      
 23 
     | 
    
         
            +
                    @header_format = Castle::Headers::Format
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  # Serialize HTTP headers
         
     | 
| 
      
 27 
     | 
    
         
            +
                  # @return [Hash]
         
     | 
| 
      
 28 
     | 
    
         
            +
                  def call
         
     | 
| 
      
 29 
     | 
    
         
            +
                    @request_env
         
     | 
| 
      
 30 
     | 
    
         
            +
                      .keys
         
     | 
| 
      
 31 
     | 
    
         
            +
                      .each_with_object({}) do |header_name, acc|
         
     | 
| 
      
 32 
     | 
    
         
            +
                        next unless header_name.match(VALUABLE_HEADERS)
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                        formatted_name = @header_format.call(header_name)
         
     | 
| 
      
 35 
     | 
    
         
            +
                        acc[formatted_name] = @request_env[header_name]
         
     | 
| 
      
 36 
     | 
    
         
            +
                      end
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
              end
         
     | 
| 
      
 40 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,24 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Castle
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Headers
         
     | 
| 
      
 5 
     | 
    
         
            +
                # formats header name
         
     | 
| 
      
 6 
     | 
    
         
            +
                class Format
         
     | 
| 
      
 7 
     | 
    
         
            +
                  class << self
         
     | 
| 
      
 8 
     | 
    
         
            +
                    # @param header [String]
         
     | 
| 
      
 9 
     | 
    
         
            +
                    # @return [String]
         
     | 
| 
      
 10 
     | 
    
         
            +
                    def call(header)
         
     | 
| 
      
 11 
     | 
    
         
            +
                      format(header.to_s.gsub(/^HTTP(?:_|-)/i, ''))
         
     | 
| 
      
 12 
     | 
    
         
            +
                    end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                    private
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                    # @param header [String]
         
     | 
| 
      
 17 
     | 
    
         
            +
                    # @return [String]
         
     | 
| 
      
 18 
     | 
    
         
            +
                    def format(header)
         
     | 
| 
      
 19 
     | 
    
         
            +
                      header.split(/_|-/).map(&:capitalize).join('-')
         
     | 
| 
      
 20 
     | 
    
         
            +
                    end
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -1,19 +1,27 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            module Castle
         
     | 
| 
       4 
     | 
    
         
            -
              module 
     | 
| 
      
 4 
     | 
    
         
            +
              # IPs-related module
         
     | 
| 
      
 5 
     | 
    
         
            +
              module IPs
         
     | 
| 
       5 
6 
     | 
    
         
             
                # used for extraction of ip from the request
         
     | 
| 
       6 
     | 
    
         
            -
                class  
     | 
| 
      
 7 
     | 
    
         
            +
                class Extract
         
     | 
| 
       7 
8 
     | 
    
         
             
                  # ordered list of ip headers for ip extraction
         
     | 
| 
       8 
9 
     | 
    
         
             
                  DEFAULT = %w[X-Forwarded-For Remote-Addr].freeze
         
     | 
| 
       9 
10 
     | 
    
         | 
| 
      
 11 
     | 
    
         
            +
                  # list of header which are used with proxy depth setting
         
     | 
| 
      
 12 
     | 
    
         
            +
                  DEPTH_RELATED = %w[X-Forwarded-For].freeze
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
       10 
14 
     | 
    
         
             
                  private_constant :DEFAULT
         
     | 
| 
       11 
15 
     | 
    
         | 
| 
       12 
16 
     | 
    
         
             
                  # @param headers [Hash]
         
     | 
| 
       13 
     | 
    
         
            -
                   
     | 
| 
      
 17 
     | 
    
         
            +
                  # @param config [Castle::Configuration, Castle::SingletonConfiguration, nil]
         
     | 
| 
      
 18 
     | 
    
         
            +
                  def initialize(headers, config = nil)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    config ||= Castle.config
         
     | 
| 
       14 
20 
     | 
    
         
             
                    @headers = headers
         
     | 
| 
       15 
     | 
    
         
            -
                    @ip_headers =  
     | 
| 
       16 
     | 
    
         
            -
                    @proxies =  
     | 
| 
      
 21 
     | 
    
         
            +
                    @ip_headers = config.ip_headers.empty? ? DEFAULT : config.ip_headers
         
     | 
| 
      
 22 
     | 
    
         
            +
                    @proxies = config.trusted_proxies + Castle::Configuration::TRUSTED_PROXIES
         
     | 
| 
      
 23 
     | 
    
         
            +
                    @trust_proxy_chain = config.trust_proxy_chain
         
     | 
| 
      
 24 
     | 
    
         
            +
                    @trusted_proxy_depth = config.trusted_proxy_depth
         
     | 
| 
       17 
25 
     | 
    
         
             
                  end
         
     | 
| 
       18 
26 
     | 
    
         | 
| 
       19 
27 
     | 
    
         
             
                  # Order of headers:
         
     | 
| 
         @@ -26,13 +34,14 @@ module Castle 
     | 
|
| 
       26 
34 
     | 
    
         | 
| 
       27 
35 
     | 
    
         
             
                    @ip_headers.each do |ip_header|
         
     | 
| 
       28 
36 
     | 
    
         
             
                      ips = ips_from(ip_header)
         
     | 
| 
       29 
     | 
    
         
            -
                      ip_value = remove_proxies(ips) 
     | 
| 
      
 37 
     | 
    
         
            +
                      ip_value = remove_proxies(ips)
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
       30 
39 
     | 
    
         
             
                      return ip_value if ip_value
         
     | 
| 
       31 
40 
     | 
    
         | 
| 
       32 
41 
     | 
    
         
             
                      all_ips.push(*ips)
         
     | 
| 
       33 
42 
     | 
    
         
             
                    end
         
     | 
| 
       34 
43 
     | 
    
         | 
| 
       35 
     | 
    
         
            -
                    # fallback to first  
     | 
| 
      
 44 
     | 
    
         
            +
                    # fallback to first listed ip
         
     | 
| 
       36 
45 
     | 
    
         
             
                    all_ips.first
         
     | 
| 
       37 
46 
     | 
    
         
             
                  end
         
     | 
| 
       38 
47 
     | 
    
         | 
| 
         @@ -41,7 +50,9 @@ module Castle 
     | 
|
| 
       41 
50 
     | 
    
         
             
                  # @param ips [Array<String>]
         
     | 
| 
       42 
51 
     | 
    
         
             
                  # @return [Array<String>]
         
     | 
| 
       43 
52 
     | 
    
         
             
                  def remove_proxies(ips)
         
     | 
| 
       44 
     | 
    
         
            -
                    ips. 
     | 
| 
      
 53 
     | 
    
         
            +
                    return ips.first if @trust_proxy_chain
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                    ips.reject { |ip| proxy?(ip) }.last
         
     | 
| 
       45 
56 
     | 
    
         
             
                  end
         
     | 
| 
       46 
57 
     | 
    
         | 
| 
       47 
58 
     | 
    
         
             
                  # @param ip [String]
         
     | 
| 
         @@ -57,7 +68,18 @@ module Castle 
     | 
|
| 
       57 
68 
     | 
    
         | 
| 
       58 
69 
     | 
    
         
             
                    return [] unless value
         
     | 
| 
       59 
70 
     | 
    
         | 
| 
       60 
     | 
    
         
            -
                    value.strip.split(/[,\s]+/)
         
     | 
| 
      
 71 
     | 
    
         
            +
                    ips = value.strip.split(/[,\s]+/)
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                    limit_proxy_depth(ips, header)
         
     | 
| 
      
 74 
     | 
    
         
            +
                  end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                  # @param ips [Array<String>]
         
     | 
| 
      
 77 
     | 
    
         
            +
                  # @param ip_header [String]
         
     | 
| 
      
 78 
     | 
    
         
            +
                  # @return [Array<String>]
         
     | 
| 
      
 79 
     | 
    
         
            +
                  def limit_proxy_depth(ips, ip_header)
         
     | 
| 
      
 80 
     | 
    
         
            +
                    ips.pop(@trusted_proxy_depth) if DEPTH_RELATED.include?(ip_header)
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                    ips
         
     | 
| 
       61 
83 
     | 
    
         
             
                  end
         
     | 
| 
       62 
84 
     | 
    
         
             
                end
         
     | 
| 
       63 
85 
     | 
    
         
             
              end
         
     |