recaptcha 2.3.0 → 5.8.1

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.
data/lib/recaptcha.rb CHANGED
@@ -1,20 +1,26 @@
1
- require 'recaptcha/configuration'
2
- require 'recaptcha/client_helper'
3
- require 'recaptcha/verify'
4
- require 'uri'
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
5
4
  require 'net/http'
5
+ require 'uri'
6
+
7
+ require 'recaptcha/configuration'
8
+ require 'recaptcha/helpers'
9
+ require 'recaptcha/adapters/controller_methods'
10
+ require 'recaptcha/adapters/view_methods'
11
+ if defined?(Rails)
12
+ require 'recaptcha/railtie'
13
+ end
6
14
 
7
15
  module Recaptcha
8
- CONFIG = {
9
- 'server_url' => '//www.google.com/recaptcha/api.js',
10
- 'secure_server_url' => 'https://www.google.com/recaptcha/api.js',
11
- 'verify_url' => 'https://www.google.com/recaptcha/api/siteverify'
12
- }
13
-
14
- USE_SSL_BY_DEFAULT = false
15
- HANDLE_TIMEOUTS_GRACEFULLY = true
16
- SKIP_VERIFY_ENV = ['test', 'cucumber']
17
16
  DEFAULT_TIMEOUT = 3
17
+ RESPONSE_LIMIT = 4000
18
+
19
+ class RecaptchaError < StandardError
20
+ end
21
+
22
+ class VerifyError < RecaptchaError
23
+ end
18
24
 
19
25
  # Gives access to the current Configuration.
20
26
  def self.configuration
@@ -45,39 +51,123 @@ module Recaptcha
45
51
  original_config.each { |key, value| configuration.send("#{key}=", value) }
46
52
  end
47
53
 
48
- def self.get(verify_hash, options)
49
- http = if Recaptcha.configuration.proxy
50
- proxy_server = URI.parse(Recaptcha.configuration.proxy)
51
- Net::HTTP::Proxy(proxy_server.host, proxy_server.port, proxy_server.user, proxy_server.password)
54
+ def self.skip_env?(env)
55
+ configuration.skip_verify_env.include?(env || configuration.default_env)
56
+ end
57
+
58
+ def self.invalid_response?(resp)
59
+ resp.empty? || resp.length > RESPONSE_LIMIT
60
+ end
61
+
62
+ def self.verify_via_api_call(response, options)
63
+ if Recaptcha.configuration.enterprise
64
+ verify_via_api_call_enterprise(response, options)
52
65
  else
53
- Net::HTTP
66
+ verify_via_api_call_free(response, options)
54
67
  end
55
- query = URI.encode_www_form(verify_hash)
56
- uri = URI.parse(Recaptcha.configuration.verify_url + '?' + query)
57
- http_instance = http.new(uri.host, uri.port)
58
- http_instance.read_timeout = http_instance.open_timeout = options[:timeout] || DEFAULT_TIMEOUT
59
- if uri.port == 443
60
- http_instance.use_ssl = true
61
- if Recaptcha.configuration.disable_ssl_verification
62
- http_instance.verify_mode = OpenSSL::SSL::VERIFY_NONE
63
- 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
64
92
  end
65
- request = Net::HTTP::Get.new(uri.request_uri)
66
- http_instance.request(request).body
67
93
  end
68
94
 
69
- def self.i18n(key, default)
70
- if defined?(I18n)
71
- I18n.translate(key, default: default)
95
+ def self.verify_via_api_call_free(response, options)
96
+ secret_key = options.fetch(:secret_key) { configuration.secret_key! }
97
+ verify_hash = { 'secret' => secret_key, 'response' => response }
98
+ verify_hash['remoteip'] = options[:remote_ip] if options.key?(:remote_ip)
99
+
100
+ reply = api_verification_free(verify_hash, timeout: options[:timeout])
101
+ success = reply['success'].to_s == 'true' &&
102
+ hostname_valid?(reply['hostname'], options[:hostname]) &&
103
+ action_valid?(reply['action'], options[:action]) &&
104
+ score_above_threshold?(reply['score'], options[:minimum_score])
105
+
106
+ if options[:with_reply] == true
107
+ return success, reply
72
108
  else
73
- default
109
+ return success
74
110
  end
75
111
  end
76
112
 
113
+ def self.hostname_valid?(hostname, validation)
114
+ validation ||= configuration.hostname
77
115
 
78
- class RecaptchaError < StandardError
116
+ case validation
117
+ when nil, FalseClass then true
118
+ when String then validation == hostname
119
+ else validation.call(hostname)
120
+ end
79
121
  end
80
122
 
81
- class VerifyError < RecaptchaError
123
+ def self.action_valid?(action, expected_action)
124
+ case expected_action
125
+ when nil, FalseClass then true
126
+ else action == expected_action
127
+ end
128
+ end
129
+
130
+ # Returns true iff score is greater or equal to (>=) minimum_score, or if no minimum_score was specified
131
+ def self.score_above_threshold?(score, minimum_score)
132
+ return true if minimum_score.nil?
133
+ return false if score.nil?
134
+
135
+ case minimum_score
136
+ when nil, FalseClass then true
137
+ else score >= minimum_score
138
+ end
139
+ end
140
+
141
+ def self.http_client_for(uri:, timeout: nil)
142
+ timeout ||= DEFAULT_TIMEOUT
143
+ http = if configuration.proxy
144
+ proxy_server = URI.parse(configuration.proxy)
145
+ Net::HTTP::Proxy(proxy_server.host, proxy_server.port, proxy_server.user, proxy_server.password)
146
+ else
147
+ Net::HTTP
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)
157
+ query = URI.encode_www_form(verify_hash)
158
+ uri = URI.parse(configuration.verify_url + '?' + query)
159
+ http_instance = http_client_for(uri: uri, timeout: timeout)
160
+ request = Net::HTTP::Get.new(uri.request_uri)
161
+ JSON.parse(http_instance.request(request).body)
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)
82
172
  end
83
173
  end
@@ -0,0 +1,5 @@
1
+ en:
2
+ recaptcha:
3
+ errors:
4
+ verification_failed: reCAPTCHA verification failed, please try again.
5
+ recaptcha_unreachable: Oops, we failed to validate your reCAPTCHA response. Please try again.
@@ -0,0 +1,5 @@
1
+ fr:
2
+ recaptcha:
3
+ errors:
4
+ verification_failed: La vérification reCAPTCHA a échoué, 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: 2.3.0
4
+ version: 5.8.1
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: 2016-05-25 00:00:00.000000000 Z
11
+ date: 2021-07-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -53,7 +53,7 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: activesupport
56
+ name: i18n
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
@@ -67,7 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: i18n
70
+ name: maxitest
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
@@ -81,7 +81,7 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: maxitest
84
+ name: pry-byebug
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - ">="
@@ -95,7 +95,7 @@ dependencies:
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
- name: pry-byebug
98
+ name: bump
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - ">="
@@ -109,7 +109,7 @@ dependencies:
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
111
  - !ruby/object:Gem::Dependency
112
- name: bump
112
+ name: webmock
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - ">="
@@ -123,7 +123,7 @@ dependencies:
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
125
  - !ruby/object:Gem::Dependency
126
- name: webmock
126
+ name: rubocop
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
129
  - - ">="
@@ -147,15 +147,20 @@ files:
147
147
  - LICENSE
148
148
  - README.md
149
149
  - lib/recaptcha.rb
150
- - lib/recaptcha/client_helper.rb
150
+ - lib/recaptcha/adapters/controller_methods.rb
151
+ - lib/recaptcha/adapters/view_methods.rb
151
152
  - lib/recaptcha/configuration.rb
153
+ - lib/recaptcha/helpers.rb
152
154
  - lib/recaptcha/rails.rb
153
- - lib/recaptcha/verify.rb
155
+ - lib/recaptcha/railtie.rb
154
156
  - lib/recaptcha/version.rb
157
+ - rails/locales/en.yml
158
+ - rails/locales/fr.yml
155
159
  homepage: http://github.com/ambethia/recaptcha
156
160
  licenses:
157
161
  - MIT
158
- metadata: {}
162
+ metadata:
163
+ source_code_uri: https://github.com/ambethia/recaptcha
159
164
  post_install_message:
160
165
  rdoc_options: []
161
166
  require_paths:
@@ -164,15 +169,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
164
169
  requirements:
165
170
  - - ">="
166
171
  - !ruby/object:Gem::Version
167
- version: 2.0.0
172
+ version: 2.4.0
168
173
  required_rubygems_version: !ruby/object:Gem::Requirement
169
174
  requirements:
170
175
  - - ">="
171
176
  - !ruby/object:Gem::Version
172
177
  version: '0'
173
178
  requirements: []
174
- rubyforge_project:
175
- rubygems_version: 2.4.5.1
179
+ rubygems_version: 3.2.16
176
180
  signing_key:
177
181
  specification_version: 4
178
182
  summary: Helpers for the reCAPTCHA API
@@ -1,52 +0,0 @@
1
- module Recaptcha
2
- module ClientHelper
3
- # Your public API can be specified in the +options+ hash or preferably
4
- # using the Configuration.
5
- def recaptcha_tags(options = {})
6
- raise(RecaptchaError, "Secure Token is deprecated.") if options[:stoken]
7
- public_key = options[:public_key] || Recaptcha.configuration.public_key!
8
-
9
- script_url = Recaptcha.configuration.api_server_url(ssl: options[:ssl])
10
- script_url += "?hl=#{options[:hl]}" unless options[:hl].to_s == ""
11
- fallback_uri = "#{script_url.chomp('.js')}/fallback?k=#{public_key}"
12
-
13
- data_attributes = [:theme, :type, :callback, :expired_callback, :size]
14
- data_attributes = options.each_with_object({}) do |(k, v), a|
15
- a[k] = v if data_attributes.include?(k)
16
- end
17
- data_attributes[:sitekey] = public_key
18
- data_attributes = data_attributes.map { |k,v| %{data-#{k.to_s.tr('_','-')}="#{v}"} }.join(" ")
19
-
20
- html = %{<script src="#{script_url}" async defer></script>\n}
21
- html << %{<div class="g-recaptcha" #{data_attributes}></div>\n}
22
-
23
- if options[:noscript] != false
24
- html << <<-HTML
25
- <noscript>
26
- <div style="width: 302px; height: 352px;">
27
- <div style="width: 302px; height: 352px; position: relative;">
28
- <div style="width: 302px; height: 352px; position: absolute;">
29
- <iframe
30
- src="#{fallback_uri}"
31
- frameborder="0" scrolling="no"
32
- style="width: 302px; height:352px; border-style: none;">
33
- </iframe>
34
- </div>
35
- <div style="width: 250px; height: 80px; position: absolute; border-style: none;
36
- bottom: 21px; left: 25px; margin: 0px; padding: 0px; right: 25px;">
37
- <textarea id="g-recaptcha-response" name="g-recaptcha-response"
38
- class="g-recaptcha-response"
39
- style="width: 250px; height: 80px; border: 1px solid #c1c1c1;
40
- margin: 0px; padding: 0px; resize: none;" value="">
41
- </textarea>
42
- </div>
43
- </div>
44
- </div>
45
- </noscript>
46
- HTML
47
- end
48
-
49
- html.respond_to?(:html_safe) ? html.html_safe : html
50
- end
51
- end
52
- end
@@ -1,102 +0,0 @@
1
- require 'json'
2
-
3
- module Recaptcha
4
- module Verify
5
- # Your private API can be specified in the +options+ hash or preferably
6
- # using the Configuration.
7
- def verify_recaptcha(options = {})
8
- options = {:model => options} unless options.is_a? Hash
9
- return true if Recaptcha::Verify.skip?(options[:env])
10
-
11
- model = options[:model]
12
- attribute = options[:attribute] || :base
13
- recaptcha_response = options[:response] || params['g-recaptcha-response'].to_s
14
-
15
- begin
16
- verified = if recaptcha_response.empty?
17
- false
18
- else
19
- recaptcha_verify_via_api_call(request, recaptcha_response, options)
20
- end
21
-
22
- if verified
23
- flash.delete(:recaptcha_error) if recaptcha_flash_supported? && !model
24
- true
25
- else
26
- recaptcha_error(
27
- model,
28
- attribute,
29
- options[:message],
30
- "recaptcha.errors.verification_failed",
31
- "reCAPTCHA verification failed, please try again."
32
- )
33
- false
34
- end
35
- rescue Timeout::Error
36
- if Recaptcha.configuration.handle_timeouts_gracefully
37
- recaptcha_error(
38
- model,
39
- attribute,
40
- options[:message],
41
- "recaptcha.errors.recaptcha_unreachable",
42
- "Oops, we failed to validate your reCAPTCHA response. Please try again."
43
- )
44
- false
45
- else
46
- raise RecaptchaError, "Recaptcha unreachable."
47
- end
48
- rescue StandardError => e
49
- raise RecaptchaError, e.message, e.backtrace
50
- end
51
- end
52
-
53
- def verify_recaptcha!(options = {})
54
- verify_recaptcha(options) or raise VerifyError
55
- end
56
-
57
- private
58
-
59
- def recaptcha_verify_via_api_call(request, recaptcha_response, options)
60
- private_key = options[:private_key] || Recaptcha.configuration.private_key!
61
- remote_ip = (request.respond_to?(:remote_ip) && request.remote_ip) || (env && env['REMOTE_ADDR'])
62
-
63
- verify_hash = {
64
- "secret" => private_key,
65
- "remoteip" => remote_ip.to_s,
66
- "response" => recaptcha_response
67
- }
68
-
69
- reply = JSON.parse(Recaptcha.get(verify_hash, options))
70
- reply['success'].to_s == "true" &&
71
- recaptcha_hostname_valid?(reply['hostname'], options[:hostname])
72
- end
73
-
74
- def recaptcha_hostname_valid?(hostname, validation)
75
- validation ||= Recaptcha.configuration.hostname
76
-
77
- case validation
78
- when nil, FalseClass then true
79
- when String then validation == hostname
80
- else validation.call(hostname)
81
- end
82
- end
83
-
84
- def recaptcha_error(model, attribute, message, key, default)
85
- message = message || Recaptcha.i18n(key, default)
86
- if model
87
- model.errors.add attribute, message
88
- else
89
- flash[:recaptcha_error] = message if recaptcha_flash_supported?
90
- end
91
- end
92
-
93
- def recaptcha_flash_supported?
94
- request.respond_to?(:format) && request.format == :html && respond_to?(:flash)
95
- end
96
-
97
- def self.skip?(env)
98
- env ||= ENV['RACK_ENV'] || ENV['RAILS_ENV'] || (Rails.env if defined? Rails.env)
99
- Recaptcha.configuration.skip_verify_env.include? env
100
- end
101
- end
102
- end