class Incline::Recaptcha
A helper class for reCAPTCHA.
To use reCAPTCHA, you will need to define recaptcha_public
and recaptcha_private
in your 'config/secrets.yml'. If you need to use a proxy server, you will need to configure the proxy settings as well.
# config/secrets.yml default: &default recaptcha_public: SomeBase64StringFromGoogle recaptcha_private: AnotherBase64StringFromGoogle recaptcha_proxy: host: 10.10.10.10 port: 1000 user: username password: top_secret
Constants
- DISABLED
A string that will validated when reCAPTCHA is disabled.
- VALID_SIZES
Gets the valid sizes for the reCAPTCHA field.
- VALID_THEMES
Gets the valid themes for the reCAPTCHA field.
- VALID_TYPES
Gets the valid types for the reCAPTCHA field.
Public Class Methods
Generates the bare minimum code needed to include a reCAPTCHA challenge in a form.
# File lib/incline/recaptcha.rb, line 156 def self.add unless disabled? "<div class=\"g-recaptcha\" data-sitekey=\"#{CGI::escape_html(public_key)}\"></div>\n<script src=\"https://www.google.com/recaptcha/api.js\"></script><br>".html_safe end end
Determines if recaptcha is disabled either due to a test environment or because :recaptcha_public or :recaptcha_private is not defined in secrets.yml
.
# File lib/incline/recaptcha.rb, line 131 def self.disabled? temp_lock || public_key.blank? || private_key.blank? || (Rails.env.test? && !enabled_for_testing?) end
Contains a collection of onload callbacks for explicit reCAPTCHA fields.
Used by the Incline::Recaptcha::Tag
helper.
# File lib/incline/recaptcha.rb, line 275 def self.onload_callbacks # FIXME: Should probably move this to the session. @onload_callbacks ||= [] end
Pauses reCAPTCHA validation for the specified block of code.
# File lib/incline/recaptcha.rb, line 297 def self.pause_for(&block) # already paused, so just call the block. return block.call if paused? # otherwise pause and then call the block. self.temp_lock = true begin return block.call ensure self.temp_lock = false end end
Determines if reCAPTCHA validation is currently paused.
# File lib/incline/recaptcha.rb, line 312 def self.paused? temp_lock end
Gets the private key.
# File lib/incline/recaptcha.rb, line 143 def self.private_key @private_key ||= Rails.application.secrets[:recaptcha_private].to_s.strip end
Gets the proxy configuration (if any).
# File lib/incline/recaptcha.rb, line 149 def self.proxy @proxy ||= (Rails.application.secrets[:recaptcha_proxy] || {}).symbolize_keys end
Gets the public key.
# File lib/incline/recaptcha.rb, line 137 def self.public_key @public_key ||= Rails.application.secrets[:recaptcha_public].to_s.strip end
Generates a script block to load reCAPTCHA and activate any reCAPTCHA fields.
# File lib/incline/recaptcha.rb, line 282 def self.script_block if onload_callbacks.any? ret = "<script type=\"text/javascript\">\n// <![CDATA[\nfunction recaptcha_onload() { " onload_callbacks.each { |onload| ret += CGI::escape_html(onload) + '(); ' } ret += "}\n// ]]>\n</script>\n<script type=\"text/javascript\" src=\"https://www.google.com/recaptcha/api.js?onload=recaptcha_onload&render=explicit\" async defer></script>" # clear the cache. onload_callbacks.clear ret.html_safe end end
Verifies the response from a reCAPTCHA challenge.
Valid options:
- model
-
Sets the model that this challenge is verifying.
- attribute
-
If a model is provided, you can supply an attribute to retrieve the response data from. This attribute should return a hash with :response and :remote_ip keys. If this is provided, then the remaining options are ignored.
- response
-
If specified, defines the response from the reCAPTCHA challenge that we want to verify. If not specified, then the request parameters (if any) are searched for the “g-recaptcha-response” value.
- remote_ip
-
If specified, defines the remote IP of the user that was challenged. If not specified, then the remote IP from the request (if any) is used.
- request
-
Specifies the request to use for information. This must be provided unless :response and :remote_ip are both specified. This is the default option if an object other than a Hash is provided to verify.
Returns true on success, or false on failure.
# File lib/incline/recaptcha.rb, line 185 def self.verify(options = {}) return true if temp_lock options = { request: options } unless options.is_a?(::Hash) model = options[:model] response = if model && options[:attribute] && model.respond_to?(options[:attribute]) model.send(options[:attribute]) else nil end remote_ip = nil if response.is_a?(::Hash) remote_ip = response[:remote_ip] response = response[:response] end # model must respond to the 'errors' message and the result of that must respond to 'add' if !model || !model.respond_to?('errors') || !model.send('errors').respond_to?('add') model = nil end response ||= options[:response] remote_ip ||= options[:remote_ip] if response.blank? || remote_ip.blank? request = options[:request] raise ArgumentError, 'Either :request must be specified or both :response and :remote_ip must be specified.' unless request response = request.params['g-recaptcha-response'] remote_ip = request.respond_to?(:remote_ip) ? request.send(:remote_ip) : ENV['REMOTE_ADDR'] end if disabled? # In tests or environments where reCAPTCHA is disabled, # the response should be 'disabled' to verify successfully. return response == 'disabled' else begin if proxy.blank? http = Net::HTTP else http = Net::HTTP::Proxy(proxy.host, proxy.port, proxy.user, proxy.password) end verify_hash = { secret: private_key, remoteip: remote_ip, response: response } recaptcha = nil Timeout::timeout(5) do uri = URI.parse('https://www.google.com/recaptcha/api/siteverify') http_instance = http.new(uri.host, uri.port) if uri.port == 443 http_instance.use_ssl = true end request = Net::HTTP::Post.new(uri.request_uri) request.set_form_data(verify_hash) recaptcha = http_instance.request(request) end answer = JSON.parse(recaptcha.body) unless answer['success'].to_s.downcase == 'true' if model model.errors.add(options[:attribute] || :base, 'Recaptcha verification failed.') end return false end return true rescue Timeout::Error if model model.errors.add(:base, 'Recaptcha unreachable.') end end end false end
Private Class Methods
# File lib/incline/recaptcha.rb, line 318 def self.enable_for_testing(pub_key = nil, priv_key = nil) raise 'This method is only valid when testing.' unless Rails.env.test? @enabled_for_testing = true @public_key = pub_key @private_key = priv_key begin yield if block_given? ensure @private_key = nil @public_key = nil @enabled_for_testing = false end end
# File lib/incline/recaptcha.rb, line 333 def self.enabled_for_testing? @enabled_for_testing ||= false end
# File lib/incline/recaptcha.rb, line 337 def self.temp_lock @temp_lock ||= false end
# File lib/incline/recaptcha.rb, line 341 def self.temp_lock=(bool) @temp_lock = bool end