recaptcha 1.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,21 +1,26 @@
1
- require 'recaptcha/configuration'
2
- require 'recaptcha/client_helper'
3
- require 'recaptcha/verify'
4
- require 'recaptcha/token'
5
- require 'uri'
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
6
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
7
14
 
8
15
  module Recaptcha
9
- CONFIG = {
10
- 'server_url' => '//www.google.com/recaptcha/api.js',
11
- 'secure_server_url' => 'https://www.google.com/recaptcha/api.js',
12
- 'verify_url' => 'https://www.google.com/recaptcha/api/siteverify'
13
- }
14
-
15
- USE_SSL_BY_DEFAULT = false
16
- HANDLE_TIMEOUTS_GRACEFULLY = true
17
- SKIP_VERIFY_ENV = ['test', 'cucumber']
18
16
  DEFAULT_TIMEOUT = 3
17
+ RESPONSE_LIMIT = 4000
18
+
19
+ class RecaptchaError < StandardError
20
+ end
21
+
22
+ class VerifyError < RecaptchaError
23
+ end
19
24
 
20
25
  # Gives access to the current Configuration.
21
26
  def self.configuration
@@ -41,43 +46,128 @@ module Recaptcha
41
46
  configuration.send("#{key}=", value)
42
47
  end
43
48
 
44
- result = yield if block_given?
45
-
49
+ yield if block_given?
50
+ ensure
46
51
  original_config.each { |key, value| configuration.send("#{key}=", value) }
47
- result
48
52
  end
49
53
 
50
- def self.get(verify_hash, options)
51
- http = if Recaptcha.configuration.proxy
52
- proxy_server = URI.parse(Recaptcha.configuration.proxy)
53
- 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)
54
65
  else
55
- Net::HTTP
66
+ verify_via_api_call_free(response, options)
56
67
  end
57
- query = URI.encode_www_form(verify_hash)
58
- uri = URI.parse(Recaptcha.configuration.verify_url + '?' + query)
59
- http_instance = http.new(uri.host, uri.port)
60
- http_instance.read_timeout = http_instance.open_timeout = options[:timeout] || DEFAULT_TIMEOUT
61
- if uri.port == 443
62
- http_instance.use_ssl = true
63
- http_instance.verify_mode = OpenSSL::SSL::VERIFY_NONE
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: 1.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-04-08 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,16 +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/token.rb
154
- - lib/recaptcha/verify.rb
155
+ - lib/recaptcha/railtie.rb
155
156
  - lib/recaptcha/version.rb
157
+ - rails/locales/en.yml
158
+ - rails/locales/fr.yml
156
159
  homepage: http://github.com/ambethia/recaptcha
157
160
  licenses:
158
161
  - MIT
159
- metadata: {}
162
+ metadata:
163
+ source_code_uri: https://github.com/ambethia/recaptcha
160
164
  post_install_message:
161
165
  rdoc_options: []
162
166
  require_paths:
@@ -165,15 +169,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
165
169
  requirements:
166
170
  - - ">="
167
171
  - !ruby/object:Gem::Version
168
- version: 2.0.0
172
+ version: 2.4.0
169
173
  required_rubygems_version: !ruby/object:Gem::Requirement
170
174
  requirements:
171
175
  - - ">="
172
176
  - !ruby/object:Gem::Version
173
177
  version: '0'
174
178
  requirements: []
175
- rubyforge_project:
176
- rubygems_version: 2.4.5.1
179
+ rubygems_version: 3.2.16
177
180
  signing_key:
178
181
  specification_version: 4
179
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
- public_key = options[:public_key] || Recaptcha.configuration.public_key!
7
-
8
- script_url = Recaptcha.configuration.api_server_url(ssl: options[:ssl])
9
- script_url += "?hl=#{options[:hl]}" unless options[:hl].to_s == ""
10
- fallback_uri = "#{script_url.chomp('.js')}/fallback?k=#{public_key}"
11
-
12
- data_attributes = [:theme, :type, :callback, :expired_callback, :size]
13
- data_attributes = options.each_with_object({}) do |(k, v), a|
14
- a[k] = v if data_attributes.include?(k)
15
- end
16
- data_attributes[:sitekey] = public_key
17
- data_attributes[:stoken] = Recaptcha::Token.secure_token if options[:stoken] != false
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,24 +0,0 @@
1
- require 'json'
2
- require 'recaptcha'
3
- require 'base64'
4
- require 'securerandom'
5
- require 'openssl'
6
-
7
- module Recaptcha
8
- module Token
9
-
10
- def self.secure_token
11
- private_key = Recaptcha.configuration.private_key
12
- raise RecaptchaError, "No private key specified." unless private_key
13
-
14
- stoken_json = {'session_id' => SecureRandom.uuid, 'ts_ms' => (Time.now.to_f * 1000).to_i}.to_json
15
- cipher = OpenSSL::Cipher::AES128.new(:ECB)
16
- private_key_digest = Digest::SHA1.digest(private_key)[0...16]
17
-
18
- cipher.encrypt
19
- cipher.key = private_key_digest
20
- encrypted_stoken = cipher.update(stoken_json) << cipher.final
21
- Base64.urlsafe_encode64(encrypted_stoken).gsub(/\=+\Z/, '')
22
- end
23
- end
24
- end
@@ -1,93 +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
- model = options[:model]
10
- attribute = options[:attribute] || :base
11
-
12
- return true if Recaptcha::Verify.skip?(options[:env])
13
-
14
- private_key = options[:private_key] || Recaptcha.configuration.private_key!
15
- recaptcha_response = options[:response] || params['g-recaptcha-response'].to_s
16
-
17
- begin
18
- # env['REMOTE_ADDR'] to retrieve IP for Grape API
19
- remote_ip = (request.respond_to?(:remote_ip) && request.remote_ip) || (env && env['REMOTE_ADDR'])
20
- verify_hash = {
21
- "secret" => private_key,
22
- "remoteip" => remote_ip.to_s,
23
- "response" => recaptcha_response
24
- }
25
-
26
- raw_reply = Recaptcha.get(verify_hash, options)
27
- reply = JSON.parse(raw_reply)
28
- answer = reply['success']
29
-
30
- if hostname_valid?(reply['hostname'], options[:hostname]) && answer.to_s == 'true'
31
- flash.delete(:recaptcha_error) if recaptcha_flash_supported? && !model
32
- true
33
- else
34
- recaptcha_error(
35
- model,
36
- attribute,
37
- options[:message],
38
- "recaptcha.errors.verification_failed",
39
- "reCAPTCHA verification failed, please try again."
40
- )
41
- false
42
- end
43
- rescue Timeout::Error
44
- if Recaptcha.configuration.handle_timeouts_gracefully
45
- recaptcha_error(
46
- model,
47
- attribute,
48
- options[:message],
49
- "recaptcha.errors.recaptcha_unreachable",
50
- "Oops, we failed to validate your reCAPTCHA response. Please try again."
51
- )
52
- false
53
- else
54
- raise RecaptchaError, "Recaptcha unreachable."
55
- end
56
- rescue StandardError => e
57
- raise RecaptchaError, e.message, e.backtrace
58
- end
59
- end
60
-
61
- def verify_recaptcha!(options = {})
62
- verify_recaptcha(options) or raise VerifyError
63
- end
64
-
65
- private
66
-
67
- def hostname_valid?(hostname, validation)
68
- case validation
69
- when nil, FalseClass then true
70
- when String then validation == hostname
71
- else validation.call(hostname)
72
- end
73
- end
74
-
75
- def recaptcha_error(model, attribute, message, key, default)
76
- message = message || Recaptcha.i18n(key, default)
77
- if model
78
- model.errors.add attribute, message
79
- else
80
- flash[:recaptcha_error] = message if recaptcha_flash_supported?
81
- end
82
- end
83
-
84
- def recaptcha_flash_supported?
85
- request.respond_to?(:format) && request.format == :html && respond_to?(:flash)
86
- end
87
-
88
- def self.skip?(env)
89
- env ||= ENV['RACK_ENV'] || ENV['RAILS_ENV'] || (Rails.env if defined? Rails.env)
90
- Recaptcha.configuration.skip_verify_env.include? env
91
- end
92
- end
93
- end