recaptcha 0.3.6 → 5.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +141 -0
- data/README.md +520 -0
- data/lib/recaptcha.rb +89 -15
- data/lib/recaptcha/adapters/controller_methods.rb +94 -0
- data/lib/recaptcha/adapters/view_methods.rb +26 -0
- data/lib/recaptcha/configuration.rb +34 -24
- data/lib/recaptcha/helpers.rb +322 -0
- data/lib/recaptcha/rails.rb +3 -4
- data/lib/recaptcha/railtie.rb +31 -11
- data/lib/recaptcha/version.rb +3 -1
- data/rails/locales/en.yml +5 -0
- metadata +98 -35
- data/.gitignore +0 -3
- data/CHANGELOG +0 -33
- data/Gemfile +0 -3
- data/README.rdoc +0 -146
- data/Rakefile +0 -9
- data/init.rb +0 -5
- data/lib/recaptcha/client_helper.rb +0 -69
- data/lib/recaptcha/merb.rb +0 -4
- data/lib/recaptcha/verify.rb +0 -75
- data/recaptcha.gemspec +0 -24
- data/test/recaptcha_test.rb +0 -62
- data/test/verify_recaptcha_test.rb +0 -151
data/Rakefile
DELETED
data/init.rb
DELETED
@@ -1,69 +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
|
-
# Default options
|
7
|
-
key = options[:public_key] ||= Recaptcha.configuration.public_key
|
8
|
-
raise RecaptchaError, "No public key specified." unless key
|
9
|
-
error = options[:error] ||= ((defined? flash) ? flash[:recaptcha_error] : "")
|
10
|
-
uri = Recaptcha.configuration.api_server_url(options[:ssl])
|
11
|
-
lang = options[:display] && options[:display][:lang] ? options[:display][:lang].to_sym : ""
|
12
|
-
html = ""
|
13
|
-
if options[:display]
|
14
|
-
html << %{<script type="text/javascript">\n}
|
15
|
-
html << %{ var RecaptchaOptions = #{hash_to_json(options[:display])};\n}
|
16
|
-
html << %{</script>\n}
|
17
|
-
end
|
18
|
-
if options[:ajax]
|
19
|
-
html << <<-EOS
|
20
|
-
<div id="dynamic_recaptcha"></div>
|
21
|
-
<script type="text/javascript">
|
22
|
-
var rc_script_tag = document.createElement('script'),
|
23
|
-
rc_init_func = function(){Recaptcha.create("#{key}", document.getElementById("dynamic_recaptcha")#{',RecaptchaOptions' if options[:display]});}
|
24
|
-
rc_script_tag.src = "#{uri}/js/recaptcha_ajax.js";
|
25
|
-
rc_script_tag.type = 'text/javascript';
|
26
|
-
rc_script_tag.onload = function(){rc_init_func.call();};
|
27
|
-
rc_script_tag.onreadystatechange = function(){
|
28
|
-
if (rc_script_tag.readyState == 'loaded' || rc_script_tag.readyState == 'complete') {rc_init_func.call();}
|
29
|
-
};
|
30
|
-
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(rc_script_tag);
|
31
|
-
</script>
|
32
|
-
EOS
|
33
|
-
else
|
34
|
-
html << %{<script type="text/javascript" src="#{uri}/challenge?k=#{key}}
|
35
|
-
html << %{#{error ? "&error=#{CGI::escape(error)}" : ""}}
|
36
|
-
html << %{#{lang ? "&lang=#{lang}" : ""}"></script>\n}
|
37
|
-
unless options[:noscript] == false
|
38
|
-
html << %{<noscript>\n }
|
39
|
-
html << %{<iframe src="#{uri}/noscript?k=#{key}" }
|
40
|
-
html << %{height="#{options[:iframe_height] ||= 300}" }
|
41
|
-
html << %{width="#{options[:iframe_width] ||= 500}" }
|
42
|
-
html << %{style="border:none;"></iframe><br/>\n }
|
43
|
-
html << %{<textarea name="recaptcha_challenge_field" }
|
44
|
-
html << %{rows="#{options[:textarea_rows] ||= 3}" }
|
45
|
-
html << %{cols="#{options[:textarea_cols] ||= 40}"></textarea>\n }
|
46
|
-
html << %{<input type="hidden" name="recaptcha_response_field" value="manual_challenge"/>}
|
47
|
-
html << %{</noscript>\n}
|
48
|
-
end
|
49
|
-
end
|
50
|
-
return (html.respond_to?(:html_safe) && html.html_safe) || html
|
51
|
-
end # recaptcha_tags
|
52
|
-
|
53
|
-
private
|
54
|
-
|
55
|
-
def hash_to_json(hash)
|
56
|
-
result = "{"
|
57
|
-
result << hash.map do |k, v|
|
58
|
-
if v.is_a?(Hash)
|
59
|
-
"\"#{k}\": #{hash_to_json(v)}"
|
60
|
-
elsif ! v.is_a?(String) || k.to_s == "callback"
|
61
|
-
"\"#{k}\": #{v}"
|
62
|
-
else
|
63
|
-
"\"#{k}\": \"#{v}\""
|
64
|
-
end
|
65
|
-
end.join(", ")
|
66
|
-
result << "}"
|
67
|
-
end
|
68
|
-
end # ClientHelper
|
69
|
-
end # Recaptcha
|
data/lib/recaptcha/merb.rb
DELETED
data/lib/recaptcha/verify.rb
DELETED
@@ -1,75 +0,0 @@
|
|
1
|
-
require "uri"
|
2
|
-
module Recaptcha
|
3
|
-
module Verify
|
4
|
-
# Your private API can be specified in the +options+ hash or preferably
|
5
|
-
# using the Configuration.
|
6
|
-
def verify_recaptcha(options = {})
|
7
|
-
if !options.is_a? Hash
|
8
|
-
options = {:model => options}
|
9
|
-
end
|
10
|
-
|
11
|
-
env = options[:env] || ENV['RAILS_ENV']
|
12
|
-
return true if Recaptcha.configuration.skip_verify_env.include? env
|
13
|
-
model = options[:model]
|
14
|
-
attribute = options[:attribute] || :base
|
15
|
-
private_key = options[:private_key] || Recaptcha.configuration.private_key
|
16
|
-
raise RecaptchaError, "No private key specified." unless private_key
|
17
|
-
|
18
|
-
begin
|
19
|
-
recaptcha = nil
|
20
|
-
if(Recaptcha.configuration.proxy)
|
21
|
-
proxy_server = URI.parse(Recaptcha.configuration.proxy)
|
22
|
-
http = Net::HTTP::Proxy(proxy_server.host, proxy_server.port, proxy_server.user, proxy_server.password)
|
23
|
-
else
|
24
|
-
http = Net::HTTP
|
25
|
-
end
|
26
|
-
|
27
|
-
Timeout::timeout(options[:timeout] || 3) do
|
28
|
-
recaptcha = http.post_form(URI.parse(Recaptcha.configuration.verify_url), {
|
29
|
-
"privatekey" => private_key,
|
30
|
-
"remoteip" => request.remote_ip,
|
31
|
-
"challenge" => params[:recaptcha_challenge_field],
|
32
|
-
"response" => params[:recaptcha_response_field]
|
33
|
-
})
|
34
|
-
end
|
35
|
-
answer, error = recaptcha.body.split.map { |s| s.chomp }
|
36
|
-
unless answer == 'true'
|
37
|
-
flash[:recaptcha_error] = if defined?(I18n)
|
38
|
-
I18n.translate("recaptcha.errors.#{error}", {:default => error})
|
39
|
-
else
|
40
|
-
error
|
41
|
-
end
|
42
|
-
|
43
|
-
if model
|
44
|
-
message = "Word verification response is incorrect, please try again."
|
45
|
-
message = I18n.translate('recaptcha.errors.verification_failed', {:default => message}) if defined?(I18n)
|
46
|
-
model.errors.add attribute, options[:message] || message
|
47
|
-
end
|
48
|
-
return false
|
49
|
-
else
|
50
|
-
flash.delete(:recaptcha_error)
|
51
|
-
return true
|
52
|
-
end
|
53
|
-
rescue Timeout::Error
|
54
|
-
if Recaptcha.configuration.handle_timeouts_gracefully
|
55
|
-
flash[:recaptcha_error] = if defined?(I18n)
|
56
|
-
I18n.translate('recaptcha.errors.recaptcha_unreachable', {:default => 'Recaptcha unreachable.'})
|
57
|
-
else
|
58
|
-
'Recaptcha unreachable.'
|
59
|
-
end
|
60
|
-
|
61
|
-
if model
|
62
|
-
message = "Oops, we failed to validate your word verification response. Please try again."
|
63
|
-
message = I18n.translate('recaptcha.errors.recaptcha_unreachable', :default => message) if defined?(I18n)
|
64
|
-
model.errors.add attribute, options[:message] || message
|
65
|
-
end
|
66
|
-
return false
|
67
|
-
else
|
68
|
-
raise RecaptchaError, "Recaptcha unreachable."
|
69
|
-
end
|
70
|
-
rescue Exception => e
|
71
|
-
raise RecaptchaError, e.message, e.backtrace
|
72
|
-
end
|
73
|
-
end # verify_recaptcha
|
74
|
-
end # Verify
|
75
|
-
end # Recaptcha
|
data/recaptcha.gemspec
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
$:.push File.expand_path("../lib", __FILE__)
|
3
|
-
require "recaptcha/version"
|
4
|
-
|
5
|
-
Gem::Specification.new do |s|
|
6
|
-
s.name = "recaptcha"
|
7
|
-
s.version = Recaptcha::VERSION
|
8
|
-
s.authors = ["Jason L Perry"]
|
9
|
-
s.email = ["jasper@ambethia.com"]
|
10
|
-
s.homepage = "http://github.com/ambethia/recaptcha"
|
11
|
-
s.summary = %q{Helpers for the reCAPTCHA API}
|
12
|
-
s.description = %q{This plugin adds helpers for the reCAPTCHA API}
|
13
|
-
|
14
|
-
s.rubyforge_project = "recaptcha"
|
15
|
-
|
16
|
-
s.files = `git ls-files`.split("\n")
|
17
|
-
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
-
s.require_paths = ["lib"]
|
19
|
-
|
20
|
-
s.add_development_dependency "mocha"
|
21
|
-
s.add_development_dependency "rake"
|
22
|
-
s.add_development_dependency "activesupport"
|
23
|
-
s.add_development_dependency "i18n"
|
24
|
-
end
|
data/test/recaptcha_test.rb
DELETED
@@ -1,62 +0,0 @@
|
|
1
|
-
require 'test/unit'
|
2
|
-
require 'cgi'
|
3
|
-
require File.dirname(File.expand_path(__FILE__)) + '/../lib/recaptcha'
|
4
|
-
|
5
|
-
class RecaptchaClientHelperTest < Test::Unit::TestCase
|
6
|
-
include Recaptcha
|
7
|
-
include Recaptcha::ClientHelper
|
8
|
-
include Recaptcha::Verify
|
9
|
-
|
10
|
-
attr_accessor :session
|
11
|
-
|
12
|
-
def setup
|
13
|
-
@session = {}
|
14
|
-
Recaptcha.configure do |config|
|
15
|
-
config.public_key = '0000000000000000000000000000000000000000'
|
16
|
-
config.private_key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def test_recaptcha_tags
|
21
|
-
# Might as well match something...
|
22
|
-
assert_match /"\/\/www.google.com\/recaptcha\/api\/challenge/, recaptcha_tags
|
23
|
-
end
|
24
|
-
|
25
|
-
def test_ssl_by_default
|
26
|
-
Recaptcha.configuration.use_ssl_by_default = true
|
27
|
-
assert_match /https:\/\/www.google.com\/recaptcha\/api\/challenge/, recaptcha_tags
|
28
|
-
end
|
29
|
-
|
30
|
-
def test_relative_protocol_by_default_without_ssl
|
31
|
-
Recaptcha.configuration.use_ssl_by_default = false
|
32
|
-
assert_match /\/\/www.google.com\/recaptcha\/api\/challenge/, recaptcha_tags(:ssl => false)
|
33
|
-
end
|
34
|
-
|
35
|
-
def test_recaptcha_tags_with_ssl
|
36
|
-
assert_match /https:\/\/www.google.com\/recaptcha\/api\/challenge/, recaptcha_tags(:ssl => true)
|
37
|
-
end
|
38
|
-
|
39
|
-
def test_recaptcha_tags_without_noscript
|
40
|
-
assert_no_match /noscript/, recaptcha_tags(:noscript => false)
|
41
|
-
end
|
42
|
-
|
43
|
-
def test_should_raise_exception_without_public_key
|
44
|
-
assert_raise RecaptchaError do
|
45
|
-
Recaptcha.configuration.public_key = nil
|
46
|
-
recaptcha_tags
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def test_different_configuration_within_with_configuration_block
|
51
|
-
key = Recaptcha.with_configuration(:public_key => '12345') do
|
52
|
-
Recaptcha.configuration.public_key
|
53
|
-
end
|
54
|
-
|
55
|
-
assert_equal('12345', key)
|
56
|
-
end
|
57
|
-
|
58
|
-
def test_reset_configuration_after_with_configuration_block
|
59
|
-
Recaptcha.with_configuration(:public_key => '12345')
|
60
|
-
assert_equal('0000000000000000000000000000000000000000', Recaptcha.configuration.public_key)
|
61
|
-
end
|
62
|
-
end
|
@@ -1,151 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
|
3
|
-
require 'test/unit'
|
4
|
-
require 'rubygems'
|
5
|
-
require 'active_support'
|
6
|
-
require 'active_support/core_ext/string'
|
7
|
-
require 'mocha/setup'
|
8
|
-
require 'i18n'
|
9
|
-
require 'net/http'
|
10
|
-
require File.dirname(File.expand_path(__FILE__)) + '/../lib/recaptcha'
|
11
|
-
|
12
|
-
class RecaptchaVerifyTest < Test::Unit::TestCase
|
13
|
-
def setup
|
14
|
-
Recaptcha.configuration.private_key = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
|
15
|
-
@controller = TestController.new
|
16
|
-
@controller.request = stub(:remote_ip => "1.1.1.1")
|
17
|
-
@controller.params = {:recaptcha_challenge_field => "challenge", :recaptcha_response_field => "response"}
|
18
|
-
|
19
|
-
@expected_post_data = {}
|
20
|
-
@expected_post_data["privatekey"] = Recaptcha.configuration.private_key
|
21
|
-
@expected_post_data["remoteip"] = @controller.request.remote_ip
|
22
|
-
@expected_post_data["challenge"] = "challenge"
|
23
|
-
@expected_post_data["response"] = "response"
|
24
|
-
|
25
|
-
@expected_uri = URI.parse(Recaptcha.configuration.verify_url)
|
26
|
-
end
|
27
|
-
|
28
|
-
def test_should_raise_exception_without_private_key
|
29
|
-
assert_raise Recaptcha::RecaptchaError do
|
30
|
-
Recaptcha.configuration.private_key = nil
|
31
|
-
@controller.verify_recaptcha
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def test_should_return_false_when_key_is_invalid
|
36
|
-
expect_http_post(response_with_body("false\ninvalid-site-private-key"))
|
37
|
-
|
38
|
-
assert !@controller.verify_recaptcha
|
39
|
-
assert_equal "invalid-site-private-key", @controller.flash[:recaptcha_error]
|
40
|
-
end
|
41
|
-
|
42
|
-
def test_returns_true_on_success
|
43
|
-
@controller.flash[:recaptcha_error] = "previous error that should be cleared"
|
44
|
-
expect_http_post(response_with_body("true\n"))
|
45
|
-
|
46
|
-
assert @controller.verify_recaptcha
|
47
|
-
assert_nil @controller.flash[:recaptcha_error]
|
48
|
-
end
|
49
|
-
|
50
|
-
def test_errors_should_be_added_to_model
|
51
|
-
expect_http_post(response_with_body("false\nbad-news"))
|
52
|
-
|
53
|
-
errors = mock
|
54
|
-
errors.expects(:add).with(:base, "Word verification response is incorrect, please try again.")
|
55
|
-
model = mock(:errors => errors)
|
56
|
-
|
57
|
-
assert !@controller.verify_recaptcha(:model => model)
|
58
|
-
assert_equal "bad-news", @controller.flash[:recaptcha_error]
|
59
|
-
end
|
60
|
-
|
61
|
-
def test_returns_true_on_success_with_optional_key
|
62
|
-
@controller.flash[:recaptcha_error] = "previous error that should be cleared"
|
63
|
-
# reset private key
|
64
|
-
@expected_post_data["privatekey"] = 'ADIFFERENTPRIVATEKEYXXXXXXXXXXXXXX'
|
65
|
-
expect_http_post(response_with_body("true\n"))
|
66
|
-
|
67
|
-
assert @controller.verify_recaptcha(:private_key => 'ADIFFERENTPRIVATEKEYXXXXXXXXXXXXXX')
|
68
|
-
assert_nil @controller.flash[:recaptcha_error]
|
69
|
-
end
|
70
|
-
|
71
|
-
def test_timeout
|
72
|
-
expect_http_post(Timeout::Error, :exception => true)
|
73
|
-
assert !@controller.verify_recaptcha()
|
74
|
-
assert_equal "Recaptcha unreachable.", @controller.flash[:recaptcha_error]
|
75
|
-
end
|
76
|
-
|
77
|
-
def test_timeout_when_handle_timeouts_gracefully_disabled
|
78
|
-
Recaptcha.with_configuration(:handle_timeouts_gracefully => false) do
|
79
|
-
expect_http_post(Timeout::Error, :exception => true)
|
80
|
-
assert_raise Recaptcha::RecaptchaError, "Recaptcha unreachable." do
|
81
|
-
assert @controller.verify_recaptcha()
|
82
|
-
end
|
83
|
-
assert_nil @controller.flash[:recaptcha_error]
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def test_message_should_use_i18n
|
88
|
-
I18n.locale = :de
|
89
|
-
verification_failed_translated = "Sicherheitscode konnte nicht verifiziert werden."
|
90
|
-
verification_failed_default = "Word verification response is incorrect, please try again."
|
91
|
-
recaptcha_unreachable_translated = "Netzwerkfehler, bitte versuchen Sie es später erneut."
|
92
|
-
recaptcha_unreachable_default = "Oops, we failed to validate your word verification response. Please try again."
|
93
|
-
|
94
|
-
I18n.expects(:translate).with('recaptcha.errors.bad-news', {:default => 'bad-news'})
|
95
|
-
I18n.expects(:translate).with('recaptcha.errors.recaptcha_unreachable', {:default => 'Recaptcha unreachable.'})
|
96
|
-
|
97
|
-
I18n.expects(:translate).with('recaptcha.errors.verification_failed', :default => verification_failed_default).returns(verification_failed_translated)
|
98
|
-
I18n.expects(:translate).with('recaptcha.errors.recaptcha_unreachable', :default => recaptcha_unreachable_default).returns(recaptcha_unreachable_translated)
|
99
|
-
|
100
|
-
errors = mock
|
101
|
-
errors.expects(:add).with(:base, verification_failed_translated)
|
102
|
-
errors.expects(:add).with(:base, recaptcha_unreachable_translated)
|
103
|
-
model = mock; model.stubs(:errors => errors)
|
104
|
-
|
105
|
-
expect_http_post(response_with_body("false\nbad-news"))
|
106
|
-
@controller.verify_recaptcha(:model => model)
|
107
|
-
|
108
|
-
expect_http_post(Timeout::Error, :exception => true)
|
109
|
-
@controller.verify_recaptcha(:model => model)
|
110
|
-
|
111
|
-
end
|
112
|
-
|
113
|
-
def test_it_translates_api_response_with_i18n
|
114
|
-
api_error_translated = "Bad news, body :("
|
115
|
-
expect_http_post(response_with_body("false\nbad-news"))
|
116
|
-
I18n.expects(:translate).with('recaptcha.errors.bad-news', :default => 'bad-news').returns(api_error_translated)
|
117
|
-
|
118
|
-
assert !@controller.verify_recaptcha
|
119
|
-
assert_equal api_error_translated, @controller.flash[:recaptcha_error]
|
120
|
-
end
|
121
|
-
|
122
|
-
def test_it_fallback_to_api_response_if_i18n_translation_is_missing
|
123
|
-
expect_http_post(response_with_body("false\nbad-news"))
|
124
|
-
|
125
|
-
assert !@controller.verify_recaptcha
|
126
|
-
assert_equal 'bad-news', @controller.flash[:recaptcha_error]
|
127
|
-
end
|
128
|
-
|
129
|
-
private
|
130
|
-
|
131
|
-
class TestController
|
132
|
-
include Recaptcha::Verify
|
133
|
-
attr_accessor :request, :params, :flash
|
134
|
-
|
135
|
-
def initialize
|
136
|
-
@flash = {}
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
def expect_http_post(response, options = {})
|
141
|
-
unless options[:exception]
|
142
|
-
Net::HTTP.expects(:post_form).with(@expected_uri, @expected_post_data).returns(response)
|
143
|
-
else
|
144
|
-
Net::HTTP.expects(:post_form).raises response
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
def response_with_body(body)
|
149
|
-
stub(:body => body)
|
150
|
-
end
|
151
|
-
end
|