recaptcha 0.3.6 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +8 -0
- data/README.md +295 -0
- data/lib/recaptcha.rb +19 -7
- data/lib/recaptcha/client_helper.rb +68 -6
- data/lib/recaptcha/configuration.rb +23 -7
- data/lib/recaptcha/rails.rb +10 -2
- data/lib/recaptcha/token.rb +24 -0
- data/lib/recaptcha/verify.rb +72 -31
- data/lib/recaptcha/version.rb +1 -1
- metadata +77 -30
- data/.gitignore +0 -3
- data/Gemfile +0 -3
- data/README.rdoc +0 -146
- data/Rakefile +0 -9
- data/init.rb +0 -5
- data/lib/recaptcha/merb.rb +0 -4
- data/lib/recaptcha/railtie.rb +0 -15
- data/recaptcha.gemspec +0 -24
- data/test/recaptcha_test.rb +0 -62
- data/test/verify_recaptcha_test.rb +0 -151
data/Gemfile
DELETED
data/README.rdoc
DELETED
@@ -1,146 +0,0 @@
|
|
1
|
-
= reCAPTCHA
|
2
|
-
|
3
|
-
Author:: Jason L Perry (http://ambethia.com)
|
4
|
-
Copyright:: Copyright (c) 2007-2013 Jason L Perry
|
5
|
-
License:: {MIT}[http://creativecommons.org/licenses/MIT/]
|
6
|
-
Info:: http://github.com/ambethia/recaptcha
|
7
|
-
Bugs:: http://github.com/ambethia/recaptcha/issues
|
8
|
-
|
9
|
-
This plugin adds helpers for the {reCAPTCHA API}[http://recaptcha.net]. In your
|
10
|
-
views you can use the +recaptcha_tags+ method to embed the needed javascript,
|
11
|
-
and you can validate in your controllers with +verify_recaptcha+.
|
12
|
-
|
13
|
-
Beforehand you need to configure Recaptcha with your custom private and public
|
14
|
-
key. You may find detailed examples below. Exceptions will be raised if you
|
15
|
-
call these methods and the keys can't be found.
|
16
|
-
|
17
|
-
== Rails Installation
|
18
|
-
|
19
|
-
reCAPTCHA for Rails > 3.0, add this to your Gemfile:
|
20
|
-
|
21
|
-
gem "recaptcha", :require => "recaptcha/rails"
|
22
|
-
|
23
|
-
Rails apps below 3.0 are no longer supported, but you can install an older
|
24
|
-
release and view it's README.
|
25
|
-
|
26
|
-
== Setting up your API Keys
|
27
|
-
|
28
|
-
There are multiple ways to setup your reCAPTCHA API key once you
|
29
|
-
{obtain}[http://recaptcha.net/whyrecaptcha.html] a pair.
|
30
|
-
|
31
|
-
=== Recaptcha.configure
|
32
|
-
|
33
|
-
You may use the block style configuration. The following code could be placed
|
34
|
-
into a +config/initializers/recaptcha.rb+ when used in a Rails project.
|
35
|
-
|
36
|
-
Recaptcha.configure do |config|
|
37
|
-
config.public_key = '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
|
38
|
-
config.private_key = '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
|
39
|
-
config.proxy = 'http://myproxy.com.au:8080'
|
40
|
-
end
|
41
|
-
|
42
|
-
This way, you may also set additional options to fit recaptcha into your
|
43
|
-
deployment environment.
|
44
|
-
|
45
|
-
== Recaptcha#with_configuration
|
46
|
-
|
47
|
-
If you want to temporarily overwrite the configuration you set with `Recaptcha.configure` (when testing, for example), you can use a `Recaptcha#with_configuration` block:
|
48
|
-
|
49
|
-
Recaptcha.with_configuration(:public_key => '12345') do
|
50
|
-
# Do stuff with the overwritten public_key.
|
51
|
-
end
|
52
|
-
|
53
|
-
=== Heroku & Shell environment
|
54
|
-
|
55
|
-
Or, you can keep your keys out of your code base by exporting the following
|
56
|
-
environment variables. You might do this in the .profile/rc, or equivalent for
|
57
|
-
the user running your application. This would also be the preffered method
|
58
|
-
in an Heroku deployment.
|
59
|
-
|
60
|
-
export RECAPTCHA_PUBLIC_KEY = '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
|
61
|
-
export RECAPTCHA_PRIVATE_KEY = '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
|
62
|
-
|
63
|
-
=== Per call
|
64
|
-
|
65
|
-
You can also pass in your keys as options at runtime, for example:
|
66
|
-
|
67
|
-
recaptcha_tags :public_key => '6Lc6BAAAAAAAAChqRbQZcn_yyyyyyyyyyyyyyyyy'
|
68
|
-
|
69
|
-
and later,
|
70
|
-
|
71
|
-
verify_recaptcha :private_key => '6Lc6BAAAAAAAAKN3DRm6VA_xxxxxxxxxxxxxxxxx'
|
72
|
-
|
73
|
-
This option might be useful, if the same code base is used for multiple
|
74
|
-
reCAPTCHA setups.
|
75
|
-
|
76
|
-
== To use 'recaptcha'
|
77
|
-
|
78
|
-
Add +recaptcha_tags+ to each form you want to protect. Place it where you want the recaptcha widget to appear.
|
79
|
-
|
80
|
-
Example:
|
81
|
-
|
82
|
-
<%= form_for @foo do |f| %>
|
83
|
-
# ... additional lines truncated for brevity ...
|
84
|
-
<%= recaptcha_tags %>
|
85
|
-
# ... additional lines truncated for brevity ...
|
86
|
-
<% end %>
|
87
|
-
|
88
|
-
And, add +verify_recaptcha+ logic to each form action that you've protected.
|
89
|
-
|
90
|
-
=== +recaptcha_tags+
|
91
|
-
|
92
|
-
Some of the options available:
|
93
|
-
|
94
|
-
<tt>:ssl</tt>:: Uses secure http for captcha widget (default +false+, but can be changed by setting +config.use_ssl_by_default+)
|
95
|
-
<tt>:noscript</tt>:: Include <noscript> content (default +true+)
|
96
|
-
<tt>:display</tt>:: Takes a hash containing the +theme+ and +tabindex+ options per the API. (default +nil+), options: 'red', 'white', 'blackglass', 'clean', 'custom'
|
97
|
-
<tt>:ajax</tt>:: Render the dynamic AJAX captcha per the API. (default +false+)
|
98
|
-
<tt>:public_key</tt>:: Your public API key, takes precedence over the ENV variable (default +nil+)
|
99
|
-
<tt>:error</tt>:: Override the error code returned from the reCAPTCHA API (default +nil+)
|
100
|
-
|
101
|
-
You can also override the html attributes for the sizes of the generated +textarea+ and +iframe+
|
102
|
-
elements, if CSS isn't your thing. Inspect the source of +recaptcha_tags+ to see these options.
|
103
|
-
|
104
|
-
=== +verify_recaptcha+
|
105
|
-
|
106
|
-
This method returns +true+ or +false+ after processing the parameters from the reCAPTCHA widget. Why
|
107
|
-
isn't this a model validation? Because that violates MVC. You can use it like this, or how ever you
|
108
|
-
like. Passing in the ActiveRecord object is optional, if you do--and the captcha fails to verify--an
|
109
|
-
error will be added to the object for you to use.
|
110
|
-
|
111
|
-
Some of the options available:
|
112
|
-
|
113
|
-
<tt>:model</tt>:: Model to set errors
|
114
|
-
<tt>:attribute</tt>:: Model attribute to receive errors (default :base)
|
115
|
-
<tt>:message</tt>:: Custom error message
|
116
|
-
<tt>:private_key</tt>:: Your private API key, takes precedence over the ENV variable (default +nil+).
|
117
|
-
<tt>:timeout</tt>:: The number of seconds to wait for reCAPTCHA servers before give up. (default +3+)
|
118
|
-
|
119
|
-
respond_to do |format|
|
120
|
-
if verify_recaptcha(:model => @post, :message => "Oh! It's error with reCAPTCHA!") && @post.save
|
121
|
-
# ...
|
122
|
-
else
|
123
|
-
# ...
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
== I18n support
|
128
|
-
reCAPTCHA passes two types of error explanation to a linked model. It will use the I18n gem
|
129
|
-
to translate the default error message if I18n is available. To customize the messages to your locale,
|
130
|
-
add these keys to your I18n backend:
|
131
|
-
|
132
|
-
<tt>recaptcha.errors.verification_failed</tt>:: error message displayed if the captcha words didn't match
|
133
|
-
<tt>recaptcha.errors.recaptcha_unreachable</tt>:: displayed if a timeout error occured while attempting to verify the captcha
|
134
|
-
|
135
|
-
Also you can translate API response errors to human friendly by adding translations to the locale (+config/locales/en.yml+):
|
136
|
-
|
137
|
-
en:
|
138
|
-
recaptcha:
|
139
|
-
errors:
|
140
|
-
incorrect-captcha-sol: 'Fail'
|
141
|
-
|
142
|
-
== TODO
|
143
|
-
* Remove Rails/ActionController dependencies
|
144
|
-
* Framework agnostic
|
145
|
-
* Add some helpers to use in before_filter and what not
|
146
|
-
* Better documentation
|
data/Rakefile
DELETED
data/init.rb
DELETED
data/lib/recaptcha/merb.rb
DELETED
data/lib/recaptcha/railtie.rb
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
require 'net/http'
|
2
|
-
require 'recaptcha'
|
3
|
-
module Rails
|
4
|
-
module Recaptcha
|
5
|
-
class Railtie < Rails::Railtie
|
6
|
-
initializer "setup config" do
|
7
|
-
begin
|
8
|
-
ActionView::Base.send(:include, ::Recaptcha::ClientHelper)
|
9
|
-
ActionController::Base.send(:include, ::Recaptcha::Verify)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
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
|