recaptcha 5.7.0 → 5.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ec76ff47d12ef3388c2a45d56850272fff3675c9f59619a09b55c918f70b5494
4
- data.tar.gz: 697f0a4d41ffab63c6eb5b7d7b81c1f5ac2b1305f27bb84b5fccc421bcd38e2c
3
+ metadata.gz: 555ad69df9bcc4154972887c8df613e687e2370caf4c4aa9e8e4843e02effd30
4
+ data.tar.gz: 40675afb3efbe1bdc78e48250715a8dd00bd1f4b27419f8bc72fbc1fd3911904
5
5
  SHA512:
6
- metadata.gz: 51b3627583241aef9a99e2a3ed9c78091c87a92e87210749d38e9ccf0d418dbec455fd524d83210b8331be7064efac2be2308366b8288c574c53b84f6013333e
7
- data.tar.gz: 8018c4668d7eef292e5908d609a7ebdd5cfbfb84b0c86a361dac36d3982cc075dc7e694c51639dd09eaf148cfd9536cecb12f6fa188ec72f87328e85976a4e22
6
+ metadata.gz: c8cc3cf0b1ccc9076a23a3f0588fcde888c131118089cb11fca59b79eccca511a61622286d087757b2a764744ac1370d3ab39e07a059dd5ab939e3c7d592660f
7
+ data.tar.gz: 8e716c170d96a0e39521d3c5a01a609c95dcdbe4daf5735936132e37bfc6ea8a7f73b9583bea653d145a950edaf3fdcbd1b6abdf63f886e04a6e12bbb052b7f1
data/CHANGELOG.md CHANGED
@@ -1,5 +1,8 @@
1
1
  ## Next
2
2
 
3
+ ## 5.8.0
4
+ * Add support for the enterprise API
5
+
3
6
  ## 5.7.0
4
7
  * french locale
5
8
  * drop ruby 2.3
data/README.md CHANGED
@@ -68,6 +68,14 @@ export RECAPTCHA_SITE_KEY = '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
68
68
  export RECAPTCHA_SECRET_KEY = '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
69
69
  ```
70
70
 
71
+ If you have an Enterprise API key:
72
+
73
+ ```shell
74
+ export RECAPTCHA_ENTERPRISE = 'true'
75
+ export RECAPTCHA_ENTERPRISE_API_KEY = 'AIzvFyE3TU-g4K_Kozr9F1smEzZSGBVOfLKyupA'
76
+ export RECAPTCHA_ENTERPRISE_PROJECT_ID = 'my-project'
77
+ ```
78
+
71
79
  Add `recaptcha_tags` to the forms you want to protect:
72
80
 
73
81
  ```erb
@@ -159,16 +167,18 @@ you like.
159
167
 
160
168
  Some of the options available:
161
169
 
162
- | Option | Description |
163
- |----------------|-------------|
164
- | `:model` | Model to set errors.
165
- | `:attribute` | Model attribute to receive errors. (default: `:base`)
166
- | `:message` | Custom error message.
167
- | `:secret_key` | Override the secret API key from the configuration.
168
- | `:timeout` | The number of seconds to wait for reCAPTCHA servers before give up. (default: `3`)
169
- | `:response` | Custom response parameter. (default: `params['g-recaptcha-response-data']`)
170
- | `:hostname` | Expected hostname or a callable that validates the hostname, see [domain validation](https://developers.google.com/recaptcha/docs/domain_validation) and [hostname](https://developers.google.com/recaptcha/docs/verify#api-response) docs. (default: `nil`, but can be changed by setting `config.hostname`)
171
- | `:env` | Current environment. The request to verify will be skipped if the environment is specified in configuration under `skip_verify_env`
170
+ | Option | Description |
171
+ |---------------------------|-------------|
172
+ | `:model` | Model to set errors.
173
+ | `:attribute` | Model attribute to receive errors. (default: `:base`)
174
+ | `:message` | Custom error message.
175
+ | `:secret_key` | Override the secret API key from the configuration.
176
+ | `:enterprise_api_key` | Override the Enterprise API key from the configuration.
177
+ | `:enterprise_project_id ` | Override the Enterprise project ID from the configuration.
178
+ | `:timeout` | The number of seconds to wait for reCAPTCHA servers before give up. (default: `3`)
179
+ | `:response` | Custom response parameter. (default: `params['g-recaptcha-response-data']`)
180
+ | `:hostname` | Expected hostname or a callable that validates the hostname, see [domain validation](https://developers.google.com/recaptcha/docs/domain_validation) and [hostname](https://developers.google.com/recaptcha/docs/verify#api-response) docs. (default: `nil`, but can be changed by setting `config.hostname`)
181
+ | `:env` | Current environment. The request to verify will be skipped if the environment is specified in configuration under `skip_verify_env`
172
182
 
173
183
 
174
184
  ### `invisible_recaptcha_tags`
@@ -376,17 +386,19 @@ then you can either:
376
386
  2. write and specify a custom `callback` function. You may also want to pass `element: false` if you
377
387
  don't have a use for the hidden input element.
378
388
 
379
- Note that you cannot submit/verify the same response token more than once or you will get a
380
- `timeout-or-duplicate` error code. If you need reset the captcha and generate a new response token,
381
- then you need to call `grecaptcha.execute(…)` again. This helper provides a JavaScript method (for
382
- each action) named `executeRecaptchaFor{action}` to make this easier. That is the same method that
383
- is invoked immediately. It simply calls `grecaptcha.execute` again and then calls the `callback`
384
- function with the response token.
389
+ Note that you cannot submit/verify the same response token more than once or you
390
+ will get a `timeout-or-duplicate` error code. If you need reset the captcha and
391
+ generate a new response token, then you need to call `grecaptcha.execute(…)` or
392
+ `grecaptcha.enterprise.execute(…)` again. This helper provides a JavaScript
393
+ method (for each action) named `executeRecaptchaFor{action}` to make this
394
+ easier. That is the same method that is invoked immediately. It simply calls
395
+ `grecaptcha.execute` or `grecaptcha.enterprise.execute` again and then calls the
396
+ `callback` function with the response token.
385
397
 
386
398
  You will also get a `timeout-or-duplicate` error if too much time has passed between getting the
387
399
  response token and verifying it. This can easily happen with large forms that take the user a couple
388
400
  minutes to complete. Unlike v2, where you can use the `expired-callback` to be notified when the
389
- response expries, v3 appears to provide no such callback. See also
401
+ response expires, v3 appears to provide no such callback. See also
390
402
  [1](https://github.com/google/recaptcha/issues/281) and
391
403
  [2](https://stackoverflow.com/questions/54437745/recaptcha-v3-how-to-deal-with-expired-token-after-idle).
392
404
 
@@ -446,7 +458,7 @@ According to https://developers.google.com/recaptcha/docs/v3#placement,
446
458
 
447
459
  > Note: You can execute reCAPTCHA as many times as you'd like with different actions on the same page.
448
460
 
449
- You will need to verify each action individually with separate call to `verify_recaptcha`.
461
+ You will need to verify each action individually with a separate call to `verify_recaptcha`.
450
462
 
451
463
  ```ruby
452
464
  result_a = verify_recaptcha(action: 'a')
@@ -506,14 +518,20 @@ Recaptcha.configuration.skip_verify_env.delete("test")
506
518
  Recaptcha.configure do |config|
507
519
  config.site_key = '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
508
520
  config.secret_key = '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
521
+
509
522
  # Uncomment the following line if you are using a proxy server:
510
523
  # config.proxy = 'http://myproxy.com.au:8080'
524
+
525
+ # Uncomment the following lines if you are using the Enterprise API:
526
+ # config.enterprise = true
527
+ # config.enterprise_api_key = 'AIzvFyE3TU-g4K_Kozr9F1smEzZSGBVOfLKyupA'
528
+ # config.enterprise_project_id = 'my-project'
511
529
  end
512
530
  ```
513
531
 
514
532
  ### Recaptcha.with_configuration
515
533
 
516
- For temporary overwrites (not thread safe).
534
+ For temporary overwrites (not thread-safe).
517
535
 
518
536
  ```ruby
519
537
  Recaptcha.with_configuration(site_key: '12345') do
data/lib/recaptcha.rb CHANGED
@@ -60,11 +60,44 @@ module Recaptcha
60
60
  end
61
61
 
62
62
  def self.verify_via_api_call(response, options)
63
+ if Recaptcha.configuration.enterprise
64
+ verify_via_api_call_enterprise(response, options)
65
+ else
66
+ verify_via_api_call_free(response, options)
67
+ end
68
+ end
69
+
70
+ def self.verify_via_api_call_enterprise(response, options)
71
+ site_key = options.fetch(:site_key) { configuration.site_key! }
72
+ api_key = options.fetch(:enterprise_api_key) { configuration.enterprise_api_key! }
73
+ project_id = options.fetch(:enterprise_project_id) { configuration.enterprise_project_id! }
74
+
75
+ query_params = { 'key' => api_key }
76
+ body = { 'event' => { 'token' => response, 'siteKey' => site_key } }
77
+ body['event']['expectedAction'] = options[:action] if options.key?(:action)
78
+ body['event']['userIpAddress'] = options[:remote_ip] if options.key?(:remote_ip)
79
+
80
+ reply = api_verification_enterprise(query_params, body, project_id, timeout: options[:timeout])
81
+ token_properties = reply['tokenProperties']
82
+ success = !token_properties.nil? &&
83
+ token_properties['valid'].to_s == 'true' &&
84
+ hostname_valid?(token_properties['hostname'], options[:hostname]) &&
85
+ action_valid?(token_properties['action'], options[:action]) &&
86
+ score_above_threshold?(reply['score'], options[:minimum_score])
87
+
88
+ if options[:with_reply] == true
89
+ return success, reply
90
+ else
91
+ return success
92
+ end
93
+ end
94
+
95
+ def self.verify_via_api_call_free(response, options)
63
96
  secret_key = options.fetch(:secret_key) { configuration.secret_key! }
64
97
  verify_hash = { 'secret' => secret_key, 'response' => response }
65
98
  verify_hash['remoteip'] = options[:remote_ip] if options.key?(:remote_ip)
66
99
 
67
- reply = api_verification(verify_hash, timeout: options[:timeout])
100
+ reply = api_verification_free(verify_hash, timeout: options[:timeout])
68
101
  success = reply['success'].to_s == 'true' &&
69
102
  hostname_valid?(reply['hostname'], options[:hostname]) &&
70
103
  action_valid?(reply['action'], options[:action]) &&
@@ -105,7 +138,7 @@ module Recaptcha
105
138
  end
106
139
  end
107
140
 
108
- def self.api_verification(verify_hash, timeout: nil)
141
+ def self.http_client_for(uri:, timeout: nil)
109
142
  timeout ||= DEFAULT_TIMEOUT
110
143
  http = if configuration.proxy
111
144
  proxy_server = URI.parse(configuration.proxy)
@@ -113,12 +146,28 @@ module Recaptcha
113
146
  else
114
147
  Net::HTTP
115
148
  end
149
+ instance = http.new(uri.host, uri.port)
150
+ instance.read_timeout = instance.open_timeout = timeout
151
+ instance.use_ssl = true if uri.port == 443
152
+
153
+ instance
154
+ end
155
+
156
+ def self.api_verification_free(verify_hash, timeout: nil)
116
157
  query = URI.encode_www_form(verify_hash)
117
158
  uri = URI.parse(configuration.verify_url + '?' + query)
118
- http_instance = http.new(uri.host, uri.port)
119
- http_instance.read_timeout = http_instance.open_timeout = timeout
120
- http_instance.use_ssl = true if uri.port == 443
159
+ http_instance = http_client_for(uri: uri, timeout: timeout)
121
160
  request = Net::HTTP::Get.new(uri.request_uri)
122
161
  JSON.parse(http_instance.request(request).body)
123
162
  end
163
+
164
+ def self.api_verification_enterprise(query_params, body, project_id, timeout: nil)
165
+ query = URI.encode_www_form(query_params)
166
+ uri = URI.parse(configuration.verify_url + "/#{project_id}/assessments" + '?' + query)
167
+ http_instance = http_client_for(uri: uri, timeout: timeout)
168
+ request = Net::HTTP::Post.new(uri.request_uri)
169
+ request['Content-Type'] = 'application/json; charset=utf-8'
170
+ request.body = JSON.generate(body)
171
+ JSON.parse(http_instance.request(request).body)
172
+ end
124
173
  end
@@ -31,11 +31,14 @@ module Recaptcha
31
31
  #
32
32
  class Configuration
33
33
  DEFAULTS = {
34
- 'server_url' => 'https://www.recaptcha.net/recaptcha/api.js',
35
- 'verify_url' => 'https://www.recaptcha.net/recaptcha/api/siteverify'
34
+ 'free_server_url' => 'https://www.recaptcha.net/recaptcha/api.js',
35
+ 'enterprise_server_url' => 'https://www.recaptcha.net/recaptcha/enterprise.js',
36
+ 'free_verify_url' => 'https://www.recaptcha.net/recaptcha/api/siteverify',
37
+ 'enterprise_verify_url' => 'https://recaptchaenterprise.googleapis.com/v1beta1/projects'
36
38
  }.freeze
37
39
 
38
- attr_accessor :default_env, :skip_verify_env, :secret_key, :site_key, :proxy, :handle_timeouts_gracefully, :hostname
40
+ attr_accessor :default_env, :skip_verify_env, :proxy, :secret_key, :site_key, :handle_timeouts_gracefully, :hostname
41
+ attr_accessor :enterprise, :enterprise_api_key, :enterprise_project_id
39
42
  attr_writer :api_server_url, :verify_url
40
43
 
41
44
  def initialize #:nodoc:
@@ -45,6 +48,11 @@ module Recaptcha
45
48
 
46
49
  @secret_key = ENV['RECAPTCHA_SECRET_KEY']
47
50
  @site_key = ENV['RECAPTCHA_SITE_KEY']
51
+
52
+ @enterprise = ENV['RECAPTCHA_ENTERPRISE'] == 'true'
53
+ @enterprise_api_key = ENV['RECAPTCHA_ENTERPRISE_API_KEY']
54
+ @enterprise_project_id = ENV['RECAPTCHA_ENTERPRISE_PROJECT_ID']
55
+
48
56
  @verify_url = nil
49
57
  @api_server_url = nil
50
58
  end
@@ -57,12 +65,20 @@ module Recaptcha
57
65
  site_key || raise(RecaptchaError, "No site key specified.")
58
66
  end
59
67
 
68
+ def enterprise_api_key!
69
+ enterprise_api_key || raise(RecaptchaError, "No Enterprise API key specified.")
70
+ end
71
+
72
+ def enterprise_project_id!
73
+ enterprise_project_id || raise(RecaptchaError, "No Enterprise project ID specified.")
74
+ end
75
+
60
76
  def api_server_url
61
- @api_server_url || DEFAULTS.fetch('server_url')
77
+ @api_server_url || (enterprise ? DEFAULTS.fetch('enterprise_server_url') : DEFAULTS.fetch('free_server_url'))
62
78
  end
63
79
 
64
80
  def verify_url
65
- @verify_url || DEFAULTS.fetch('verify_url')
81
+ @verify_url || (enterprise ? DEFAULTS.fetch('enterprise_verify_url') : DEFAULTS.fetch('free_verify_url'))
66
82
  end
67
83
  end
68
84
  end
@@ -174,7 +174,8 @@ module Recaptcha
174
174
 
175
175
  # v3
176
176
 
177
- # Renders a script that calls `grecaptcha.execute` for the given `site_key` and `action` and
177
+ # Renders a script that calls `grecaptcha.execute` or
178
+ # `grecaptcha.enterprise.execute` for the given `site_key` and `action` and
178
179
  # calls the `callback` with the resulting response token.
179
180
  private_class_method def self.recaptcha_v3_inline_script(site_key, action, callback, id, options = {})
180
181
  nonce = options[:nonce]
@@ -185,8 +186,8 @@ module Recaptcha
185
186
  // Define function so that we can call it again later if we need to reset it
186
187
  // This executes reCAPTCHA and then calls our callback.
187
188
  function #{recaptcha_v3_execute_function_name(action)}() {
188
- grecaptcha.ready(function() {
189
- grecaptcha.execute('#{site_key}', {action: '#{action}'}).then(function(token) {
189
+ #{recaptcha_ready_method_name}(function() {
190
+ #{recaptcha_execute_method_name}('#{site_key}', {action: '#{action}'}).then(function(token) {
190
191
  #{callback}('#{id}', token)
191
192
  });
192
193
  });
@@ -199,8 +200,8 @@ module Recaptcha
199
200
  // Returns a Promise that resolves with the response token.
200
201
  async function #{recaptcha_v3_async_execute_function_name(action)}() {
201
202
  return new Promise((resolve, reject) => {
202
- grecaptcha.ready(async function() {
203
- resolve(await grecaptcha.execute('#{site_key}', {action: '#{action}'}))
203
+ #{recaptcha_ready_method_name}(async function() {
204
+ resolve(await #{recaptcha_execute_method_name}('#{site_key}', {action: '#{action}'}))
204
205
  });
205
206
  })
206
207
  };
@@ -217,8 +218,8 @@ module Recaptcha
217
218
  <<-HTML
218
219
  <script#{nonce_attr}>
219
220
  function #{recaptcha_v3_execute_function_name(action)}() {
220
- grecaptcha.ready(function() {
221
- grecaptcha.execute('#{site_key}', {action: '#{action}'}).then(function(token) {
221
+ #{recaptcha_ready_method_name}(function() {
222
+ #{recaptcha_execute_method_name}('#{site_key}', {action: '#{action}'}).then(function(token) {
222
223
  #{callback}('#{id}', token)
223
224
  });
224
225
  });
@@ -251,8 +252,9 @@ module Recaptcha
251
252
  recaptcha_v3_inline_script?(options)
252
253
  end
253
254
 
254
- # Returns the name of the JavaScript function that actually executes the reCAPTCHA code (calls
255
- # grecaptcha.execute). You can call it again later to reset it.
255
+ # Returns the name of the JavaScript function that actually executes the
256
+ # reCAPTCHA code (calls `grecaptcha.execute` or
257
+ # `grecaptcha.enterprise.execute`). You can call it again later to reset it.
256
258
  def self.recaptcha_v3_execute_function_name(action)
257
259
  "executeRecaptchaFor#{sanitize_action_for_js(action)}"
258
260
  end
@@ -296,6 +298,14 @@ module Recaptcha
296
298
  HTML
297
299
  end
298
300
 
301
+ def self.recaptcha_execute_method_name
302
+ Recaptcha.configuration.enterprise ? "grecaptcha.enterprise.execute" : "grecaptcha.execute"
303
+ end
304
+
305
+ def self.recaptcha_ready_method_name
306
+ Recaptcha.configuration.enterprise ? "grecaptcha.enterprise.ready" : "grecaptcha.ready"
307
+ end
308
+
299
309
  private_class_method def self.default_callback_required?(options)
300
310
  options[:callback] == 'invisibleRecaptchaSubmit' &&
301
311
  !Recaptcha.skip_env?(options[:env]) &&
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Recaptcha
4
- VERSION = '5.7.0'
4
+ VERSION = '5.8.0'
5
5
  end
data/rails/locales/fr.yml CHANGED
@@ -2,4 +2,4 @@ fr:
2
2
  recaptcha:
3
3
  errors:
4
4
  verification_failed: La vérification reCAPTCHA a échoué, veuillez essayer à nouveau.
5
- recaptcha_unreachable: Oops, nous n'avons pas vu valider votre réponse reCAPTCHA. Veuillez essayer à nouveau.
5
+ recaptcha_unreachable: Oops, nous n'avons pas pu valider votre réponse reCAPTCHA. Veuillez essayer à nouveau.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: recaptcha
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.7.0
4
+ version: 5.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason L Perry
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-05 00:00:00.000000000 Z
11
+ date: 2021-05-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -176,7 +176,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
176
176
  - !ruby/object:Gem::Version
177
177
  version: '0'
178
178
  requirements: []
179
- rubygems_version: 3.1.3
179
+ rubygems_version: 3.2.16
180
180
  signing_key:
181
181
  specification_version: 4
182
182
  summary: Helpers for the reCAPTCHA API