recaptcha 1.3.0 → 5.7.0

Sign up to get free protection for your applications and to get access to all the features.
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,79 @@ 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
+ secret_key = options.fetch(:secret_key) { configuration.secret_key! }
64
+ verify_hash = { 'secret' => secret_key, 'response' => response }
65
+ verify_hash['remoteip'] = options[:remote_ip] if options.key?(:remote_ip)
66
+
67
+ reply = api_verification(verify_hash, timeout: options[:timeout])
68
+ success = reply['success'].to_s == 'true' &&
69
+ hostname_valid?(reply['hostname'], options[:hostname]) &&
70
+ action_valid?(reply['action'], options[:action]) &&
71
+ score_above_threshold?(reply['score'], options[:minimum_score])
72
+
73
+ if options[:with_reply] == true
74
+ return success, reply
54
75
  else
55
- Net::HTTP
76
+ return success
56
77
  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
78
+ end
79
+
80
+ def self.hostname_valid?(hostname, validation)
81
+ validation ||= configuration.hostname
82
+
83
+ case validation
84
+ when nil, FalseClass then true
85
+ when String then validation == hostname
86
+ else validation.call(hostname)
64
87
  end
65
- request = Net::HTTP::Get.new(uri.request_uri)
66
- http_instance.request(request).body
67
88
  end
68
89
 
69
- def self.i18n(key, default)
70
- if defined?(I18n)
71
- I18n.translate(key, default: default)
72
- else
73
- default
90
+ def self.action_valid?(action, expected_action)
91
+ case expected_action
92
+ when nil, FalseClass then true
93
+ else action == expected_action
74
94
  end
75
95
  end
76
96
 
97
+ # Returns true iff score is greater or equal to (>=) minimum_score, or if no minimum_score was specified
98
+ def self.score_above_threshold?(score, minimum_score)
99
+ return true if minimum_score.nil?
100
+ return false if score.nil?
77
101
 
78
- class RecaptchaError < StandardError
102
+ case minimum_score
103
+ when nil, FalseClass then true
104
+ else score >= minimum_score
105
+ end
79
106
  end
80
107
 
81
- class VerifyError < RecaptchaError
108
+ def self.api_verification(verify_hash, timeout: nil)
109
+ timeout ||= DEFAULT_TIMEOUT
110
+ http = if configuration.proxy
111
+ proxy_server = URI.parse(configuration.proxy)
112
+ Net::HTTP::Proxy(proxy_server.host, proxy_server.port, proxy_server.user, proxy_server.password)
113
+ else
114
+ Net::HTTP
115
+ end
116
+ query = URI.encode_www_form(verify_hash)
117
+ 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
121
+ request = Net::HTTP::Get.new(uri.request_uri)
122
+ JSON.parse(http_instance.request(request).body)
82
123
  end
83
124
  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 vu 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.7.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: 2016-04-08 00:00:00.000000000 Z
11
+ date: 2021-02-05 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.1.3
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