ruby-recaptcha 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +117 -0
- data/README.txt +113 -0
- data/lib/recaptcha.rb +143 -0
- data/lib/ruby-recaptcha.rb +7 -0
- data/test/test_helper.rb +3 -0
- data/test/test_recaptcha.rb +336 -0
- metadata +79 -0
data/History.txt
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
changeset: 49:201f032ce586
|
2
|
+
user: m@loonsoft.com
|
3
|
+
date: Tue Sep 02 10:59:53 2008 -0500
|
4
|
+
summary: removed test code which seems to cause trouble with the masses
|
5
|
+
|
6
|
+
changeset: 48:297ed7ad0790
|
7
|
+
user: m@loonsoft.com
|
8
|
+
date: Fri May 30 09:04:24 2008 -0500
|
9
|
+
summary: fix for loopback addr requests
|
10
|
+
|
11
|
+
changeset: 47:143671e3abb0
|
12
|
+
user: m@loonsoft.com
|
13
|
+
date: Mon Apr 07 23:24:24 2008 -0500
|
14
|
+
summary: fix for kim griggs
|
15
|
+
|
16
|
+
changeset: 46:52e0ff173f56
|
17
|
+
user: m@loonsoft.com
|
18
|
+
date: Wed Mar 05 20:27:58 2008 -0600
|
19
|
+
summary: fixed bug when nil challenge string
|
20
|
+
|
21
|
+
changeset: 45:f63d820d97f9
|
22
|
+
user: m@loonsoft.com
|
23
|
+
date: Sat Mar 01 08:40:33 2008 -0600
|
24
|
+
summary: integrated more patches from pv
|
25
|
+
|
26
|
+
changeset: 44:4964962ea7be
|
27
|
+
user: m@loonsoft.com
|
28
|
+
date: Sun Feb 24 13:59:27 2008 -0600
|
29
|
+
summary: reapplied missing patch
|
30
|
+
|
31
|
+
changeset: 43:299402495d48
|
32
|
+
user: m@loonsoft.com
|
33
|
+
date: Sun Feb 24 08:25:04 2008 -0600
|
34
|
+
summary: patch from peter vandenberk
|
35
|
+
|
36
|
+
changeset: 40:e55d02f563e4
|
37
|
+
user: m@loonsoft.com
|
38
|
+
date: Sat Jan 12 11:33:35 2008 -0600
|
39
|
+
summary: proxy support
|
40
|
+
|
41
|
+
changeset: 34:24e4ed5017bc
|
42
|
+
user: m@loonsoft.com
|
43
|
+
date: Thu Aug 23 15:01:14 2007 -0500
|
44
|
+
summary: fix from joey geiger
|
45
|
+
|
46
|
+
changeset: 33:310e75fbd650
|
47
|
+
user: m@loonsoft.com
|
48
|
+
date: Sat Jun 09 08:10:38 2007 -0500
|
49
|
+
summary: error parameter no longer included when the error is blank or nil
|
50
|
+
|
51
|
+
changeset: 31:ed21f08b6331
|
52
|
+
parent: 27:5a6746ce03f7
|
53
|
+
user: m@loonsoft.com
|
54
|
+
date: Wed Jun 06 22:36:58 2007 -0500
|
55
|
+
summary: Backed out changeset 5a6746ce03f73d0dd02db155816500870088bf6e
|
56
|
+
|
57
|
+
changeset: 27:5a6746ce03f7
|
58
|
+
parent: 25:d614a6a28630
|
59
|
+
user: m@loonsoft.com
|
60
|
+
date: Wed Jun 06 22:02:37 2007 -0500
|
61
|
+
summary: Backed out changeset d614a6a28630eb4dba913739518dbdc6d3aaa410
|
62
|
+
|
63
|
+
changeset: 25:d614a6a28630
|
64
|
+
user: m@loonsoft.com
|
65
|
+
date: Wed May 30 14:28:25 2007 -0500
|
66
|
+
summary: patch from victor cosby
|
67
|
+
|
68
|
+
changeset: 24:d1dc394b249b
|
69
|
+
user: m@loonsoft.com
|
70
|
+
date: Tue May 29 09:57:04 2007 -0500
|
71
|
+
summary: Backed out changeset 1086e6e6217f85433cc539e6d687880adc421c2f
|
72
|
+
|
73
|
+
changeset: 23:1086e6e6217f
|
74
|
+
user: m@loonsoft.com
|
75
|
+
date: Tue May 29 09:47:38 2007 -0500
|
76
|
+
summary: reworked error handling
|
77
|
+
|
78
|
+
changeset: 22:5497521e6a01
|
79
|
+
user: m@loonsoft.com
|
80
|
+
date: Sun May 27 17:21:16 2007 -0500
|
81
|
+
summary: removed bogon method stub
|
82
|
+
|
83
|
+
changeset: 19:943383442ab0
|
84
|
+
user: m@loonsoft.com
|
85
|
+
date: Sun May 27 15:21:27 2007 -0500
|
86
|
+
summary: mailhide supported
|
87
|
+
|
88
|
+
changeset: 16:a9f834ef0ffb
|
89
|
+
user: m@loonsoft.com
|
90
|
+
date: Fri May 25 19:22:49 2007 -0500
|
91
|
+
summary: added cgi module, some tests
|
92
|
+
|
93
|
+
changeset: 11:d7dcf24bb808
|
94
|
+
user: m@loonsoft.com
|
95
|
+
date: Fri May 25 11:58:27 2007 -0500
|
96
|
+
summary: changed license
|
97
|
+
|
98
|
+
changeset: 9:8047f6e7eceb
|
99
|
+
user: m@loonsoft.com
|
100
|
+
date: Fri May 25 11:46:12 2007 -0500
|
101
|
+
summary: turned off ssl
|
102
|
+
|
103
|
+
changeset: 4:d28f38ac7cde
|
104
|
+
user: m@loonsoft.com
|
105
|
+
date: Fri May 25 11:18:07 2007 -0500
|
106
|
+
summary: updated
|
107
|
+
|
108
|
+
changeset: 1:146d97cc2e51
|
109
|
+
user: m@loonsoft.com
|
110
|
+
date: Fri May 25 10:41:52 2007 -0500
|
111
|
+
summary: fixed license
|
112
|
+
|
113
|
+
changeset: 0:1f1ba6ed3ef1
|
114
|
+
user: m@loonsoft.com
|
115
|
+
date: Fri May 25 10:41:35 2007 -0500
|
116
|
+
summary: initial
|
117
|
+
|
data/README.txt
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
= ruby-recaptcha
|
2
|
+
|
3
|
+
h2. What
|
4
|
+
|
5
|
+
h2. Installing
|
6
|
+
|
7
|
+
<pre>
|
8
|
+
gem install recaptcha
|
9
|
+
</pre>
|
10
|
+
|
11
|
+
h2. The basics
|
12
|
+
|
13
|
+
The ReCaptchaClient abstracts the ReCaptcha API for use in Rails Applications
|
14
|
+
|
15
|
+
|
16
|
+
h2. Demonstration of usage
|
17
|
+
|
18
|
+
h3. reCAPTCHA
|
19
|
+
|
20
|
+
First, create an account at "ReCaptcha.net":http://www.recaptcha.net.
|
21
|
+
|
22
|
+
Get your keys, and make them available as constants in your application. You can do this however you want, but RCC_PUB, RCC_PRIV (for regular reCaptcha) and MH_PUB MH_PRIV (for MailHide) must be set to their respective values (the keys you receive from reCaptcha).
|
23
|
+
|
24
|
+
The two common methods of doing this (for Rails applications) are to set these variables in your environment.rb file, or via an environment variable, or in Rails 2.2+, in an initializer.
|
25
|
+
|
26
|
+
The ReCaptcha::Client constructor can also take an options hash containing keys thusly:
|
27
|
+
<pre>
|
28
|
+
Recaptcha::Client.new(:rcc_pub=>'some key', :rcc_priv=>'some other key')
|
29
|
+
</pre>
|
30
|
+
In recent versions of Rails, you can specify the gem in environment.rb:
|
31
|
+
|
32
|
+
<pre>
|
33
|
+
config.gem 'ruby-recaptcha'
|
34
|
+
</pre>
|
35
|
+
|
36
|
+
After your keys are configured, and the gem is loaded, include the ReCaptcha::AppHelper module in your ApplicationController:
|
37
|
+
<pre>
|
38
|
+
class ApplicationController < ActionController::Base
|
39
|
+
include ReCaptcha::AppHelper
|
40
|
+
</pre>
|
41
|
+
This will mix-in validate_recap method.
|
42
|
+
|
43
|
+
|
44
|
+
Then, in the controller where you want to do the validation, chain validate_recap() into your regular validation:
|
45
|
+
<pre>
|
46
|
+
def create
|
47
|
+
@user = User.new(params[:user])
|
48
|
+
if validate_recap(params, @user.errors) && @user.save
|
49
|
+
...do stuff...
|
50
|
+
</pre>
|
51
|
+
|
52
|
+
Require and include the view helper in your application_helper: NOTE: require is used here, not gem, not sure why.
|
53
|
+
<pre>
|
54
|
+
include ReCaptcha::ViewHelper
|
55
|
+
</pre>
|
56
|
+
|
57
|
+
This will mix get_captcha() into your view helper.
|
58
|
+
|
59
|
+
Now you can just call <pre>get_captcha()</pre> in your view to insert the requisite widget from ReCaptcha.
|
60
|
+
|
61
|
+
To customize theme and tabindex of the widget, you can include an options hash:
|
62
|
+
|
63
|
+
<pre>get_captcha(:options => {:theme => 'white', :tabindex => 10})</pre>
|
64
|
+
|
65
|
+
See the "reCAPTCHA API Documentation":http://recaptcha.net/apidocs/captcha/ under "Look and Feel Customization" for more information.
|
66
|
+
|
67
|
+
h3. Proxy support
|
68
|
+
|
69
|
+
If your rails application requires the use of a proxy, set proxy_host into your environment:
|
70
|
+
<pre>
|
71
|
+
ENV['proxy_host']='foo.example.com:8080'
|
72
|
+
</pre>
|
73
|
+
|
74
|
+
h3. Mail Hide
|
75
|
+
|
76
|
+
When you mix in ViewHelper as above, you also get <pre> mail_hide(address, contents)</pre>, which you can call in your view thusly:
|
77
|
+
|
78
|
+
<pre>
|
79
|
+
...
|
80
|
+
<%= mail_hide(user.email) %>
|
81
|
+
</pre>
|
82
|
+
|
83
|
+
Contents defaults to the first few characters of the email address.
|
84
|
+
|
85
|
+
h2. Bugs
|
86
|
+
|
87
|
+
http://www.bitbucket.org/mml/ruby-recaptcha/issues
|
88
|
+
|
89
|
+
h2. Code
|
90
|
+
|
91
|
+
Get it "here":http://www.bitbucket.org/mml/ruby-recaptcha
|
92
|
+
|
93
|
+
Note the wiki & forum & such there...
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
h2. License
|
98
|
+
|
99
|
+
This code is free to use under the terms of the MIT License.
|
100
|
+
|
101
|
+
h2. Contact
|
102
|
+
|
103
|
+
Comments are welcome. Send an email to "McClain Looney":mailto:mlooney@gmail.com.
|
104
|
+
|
105
|
+
h2. Contributors:
|
106
|
+
|
107
|
+
Victor Cosby (test cleanup, additional code to style widget)
|
108
|
+
<br>
|
109
|
+
Ronald Schroeter (proxy support suggestion & proto-patch)
|
110
|
+
<br>
|
111
|
+
Peter Vandenberk (multi-key support, ssl support, various unit tests, test refactoring)
|
112
|
+
<br>
|
113
|
+
Kim Griggs (found long address-newline bug)
|
data/lib/recaptcha.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
#Copyright (c) 2007, 2008, 2009 McClain Looney
|
2
|
+
#
|
3
|
+
#Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
#of this software and associated documentation files (the "Software"), to deal
|
5
|
+
#in the Software without restriction, including without limitation the rights
|
6
|
+
#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
#copies of the Software, and to permit persons to whom the Software is
|
8
|
+
#furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
#The above copyright notice and this permission notice shall be included in
|
11
|
+
#all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
#THE SOFTWARE.
|
20
|
+
require 'openssl'
|
21
|
+
require 'base64'
|
22
|
+
require 'cgi'
|
23
|
+
require 'net/http'
|
24
|
+
require 'net/https'
|
25
|
+
module ReCaptcha
|
26
|
+
module ViewHelper
|
27
|
+
def get_captcha(options={})
|
28
|
+
k = ReCaptcha::Client.new(options[:rcc_pub] || RCC_PUB, options[:rcc_priv] || RCC_PRIV, options[:ssl] || false)
|
29
|
+
r = k.get_challenge(session[:rcc_err] || '', options)
|
30
|
+
session[:rcc_err]=''
|
31
|
+
r
|
32
|
+
end
|
33
|
+
def mail_hide(address, contents=nil)
|
34
|
+
contents = truncate(address,10) if contents.nil?
|
35
|
+
k = ReCaptcha::MHClient.new(MH_PUB, MH_PRIV)
|
36
|
+
enciphered = k.encrypt(address)
|
37
|
+
uri = "http://mailhide.recaptcha.net/d?k=#{MH_PUB}&c=#{enciphered}"
|
38
|
+
t =<<-EOF
|
39
|
+
<a href="#{uri}"
|
40
|
+
onclick="window.open('#{uri}', '', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=500,height=300'); return false;" title="Reveal this e-mail address">#{contents}</a>
|
41
|
+
EOF
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
module AppHelper
|
46
|
+
private
|
47
|
+
def validate_recap(p, errors, options = {})
|
48
|
+
rcc=ReCaptcha::Client.new(options[:rcc_pub] || RCC_PUB, options[:rcc_priv] || RCC_PRIV)
|
49
|
+
res = rcc.validate(request.remote_ip, p[:recaptcha_challenge_field], p[:recaptcha_response_field], errors)
|
50
|
+
session[:rcc_err]=rcc.last_error
|
51
|
+
|
52
|
+
res
|
53
|
+
end
|
54
|
+
end
|
55
|
+
class MHClient
|
56
|
+
def initialize(pubkey, privkey)
|
57
|
+
@pubkey=pubkey
|
58
|
+
@privkey=privkey
|
59
|
+
@host='mailhide.recaptcha.net'
|
60
|
+
end
|
61
|
+
def encrypt(string)
|
62
|
+
padded = pad(string)
|
63
|
+
iv="\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00"
|
64
|
+
cipher=OpenSSL::Cipher::Cipher.new("AES-128-CBC")
|
65
|
+
binkey = @privkey.unpack('a2'*16).map{|x| x.hex}.pack('c'*16)
|
66
|
+
cipher.encrypt
|
67
|
+
cipher.key=binkey
|
68
|
+
cipher.iv=iv
|
69
|
+
ciphertext = []
|
70
|
+
cipher.padding=0
|
71
|
+
ciphertext = cipher.update(padded)
|
72
|
+
ciphertext << cipher.final() rescue nil
|
73
|
+
Base64.encode64(ciphertext).strip.gsub(/\+/, '-').gsub(/\//, '_').gsub(/\n/,'')
|
74
|
+
end
|
75
|
+
def pad(str)
|
76
|
+
l= 16-(str.length%16)
|
77
|
+
l.times do
|
78
|
+
str<< l
|
79
|
+
end
|
80
|
+
str
|
81
|
+
end
|
82
|
+
end
|
83
|
+
class Client
|
84
|
+
|
85
|
+
def initialize(pubkey, privkey, ssl=false)
|
86
|
+
@pubkey = pubkey
|
87
|
+
@privkey=privkey
|
88
|
+
@host = ssl ? 'api-secure.recaptcha.net':'api.recaptcha.net'
|
89
|
+
@vhost = 'api-verify.recaptcha.net'
|
90
|
+
@proto = ssl ? 'https' : 'http'
|
91
|
+
@ssl = ssl
|
92
|
+
@last_error=nil
|
93
|
+
end
|
94
|
+
|
95
|
+
def get_challenge(error='', options={})
|
96
|
+
s=''
|
97
|
+
if options[:options]
|
98
|
+
s << "<script type=\"text/javascript\">\nvar RecaptchaOptions = { "
|
99
|
+
options[:options].each do |k,v|
|
100
|
+
val = (v.class == Fixnum) ? "#{v}" : "\"#{v}\""
|
101
|
+
s << "#{k} : #{val}, "
|
102
|
+
end
|
103
|
+
s.sub!(/, $/, '};')
|
104
|
+
s << "\n</script>\n"
|
105
|
+
end
|
106
|
+
errslug = (error.empty?||error==nil||error=="success") ? '' : "&error=#{CGI.escape(error)}"
|
107
|
+
s <<<<-EOF
|
108
|
+
<script type="text/javascript" src="#{@proto}://#{@host}/challenge?k=#{CGI.escape(@pubkey)}#{errslug}"> </script>
|
109
|
+
<noscript>
|
110
|
+
<iframe src="#{@proto}://#{@host}/noscript?k=#{CGI.escape(@pubkey)}#{errslug}"
|
111
|
+
height="300" width="500" frameborder="0"></iframe><br>
|
112
|
+
<textarea name="recaptcha_challenge_field" rows="3" cols="40">
|
113
|
+
</textarea>
|
114
|
+
<input type="hidden" name="recaptcha_response_field"
|
115
|
+
value="manual_challenge">
|
116
|
+
</noscript>
|
117
|
+
EOF
|
118
|
+
end
|
119
|
+
|
120
|
+
def last_error
|
121
|
+
@last_error
|
122
|
+
end
|
123
|
+
def validate(remoteip, challenge, response, errors)
|
124
|
+
msg = "Captcha failed."
|
125
|
+
unless response and challenge
|
126
|
+
errors.add_to_base(msg)
|
127
|
+
return false
|
128
|
+
end
|
129
|
+
proxy_host, proxy_port = nil, nil
|
130
|
+
proxy_host, proxy_port = ENV['proxy_host'].split(':') if ENV.has_key?('proxy_host')
|
131
|
+
http = Net::HTTP::Proxy(proxy_host, proxy_port).start(@vhost)
|
132
|
+
path='/verify'
|
133
|
+
data = "privatekey=#{CGI.escape(@privkey)}&remoteip=#{CGI.escape(remoteip)}&challenge=#{CGI.escape(challenge)}&response=#{CGI.escape(response)}"
|
134
|
+
resp, data = http.post(path, data, {'Content-Type'=>'application/x-www-form-urlencoded'})
|
135
|
+
response = data.split
|
136
|
+
result = response[0].chomp
|
137
|
+
@last_error=response[1].chomp
|
138
|
+
errors.add_to_base(msg) if result != 'true'
|
139
|
+
result == 'true'
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,336 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
require 'rubygems'
|
3
|
+
gem 'mocha'
|
4
|
+
require 'mocha'
|
5
|
+
gem 'rails'
|
6
|
+
|
7
|
+
module ReCaptcha
|
8
|
+
module ViewHelper
|
9
|
+
def self.define_public_key
|
10
|
+
class_eval "RCC_PUB = 'foo'"
|
11
|
+
end
|
12
|
+
def self.define_private_key
|
13
|
+
class_eval "RCC_PRIV = 'bar'"
|
14
|
+
end
|
15
|
+
def self.undefine_public_key
|
16
|
+
remove_const :RCC_PUB
|
17
|
+
end
|
18
|
+
def self.undefine_private_key
|
19
|
+
remove_const :RCC_PRIV
|
20
|
+
end
|
21
|
+
end
|
22
|
+
module AppHelper
|
23
|
+
def self.define_public_key
|
24
|
+
class_eval "RCC_PUB = 'foo'"
|
25
|
+
end
|
26
|
+
def self.define_private_key
|
27
|
+
class_eval "RCC_PRIV = 'bar'"
|
28
|
+
end
|
29
|
+
def self.undefine_public_key
|
30
|
+
remove_const :RCC_PUB
|
31
|
+
end
|
32
|
+
def self.undefine_private_key
|
33
|
+
remove_const :RCC_PRIV
|
34
|
+
end
|
35
|
+
public :validate_recap
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class TestRecaptcha < Test::Unit::TestCase
|
40
|
+
class ViewFixture
|
41
|
+
PRIVKEY='6LdnAQAAAAAAAPYLVPwVvR7Cy9YLhRQcM3NWsK_C'
|
42
|
+
PUBKEY='6LdnAQAAAAAAAKEk59yjuP3csGEeDmdj__7cyMtY'
|
43
|
+
include ReCaptcha::ViewHelper
|
44
|
+
# views in Rails "inherit" the controller's session...
|
45
|
+
def session
|
46
|
+
@session ||= {}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
class ControllerFixture
|
50
|
+
include ReCaptcha::AppHelper
|
51
|
+
attr_reader :request
|
52
|
+
# all Rails controllers have a session...
|
53
|
+
def session
|
54
|
+
@session ||= {}
|
55
|
+
end
|
56
|
+
def initialize()
|
57
|
+
@request = mock()
|
58
|
+
@request.stubs(:remote_ip).returns('0.0.0.0') # this will skip the actual captcha validation...
|
59
|
+
@request
|
60
|
+
end
|
61
|
+
include Mocha::Standalone
|
62
|
+
end
|
63
|
+
|
64
|
+
def setup
|
65
|
+
@vf = ViewFixture.new
|
66
|
+
@cf = ControllerFixture.new
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_encrypt
|
70
|
+
mhc = ReCaptcha::MHClient.new('01S1TOX9aibKxfC9oJlp8IeA==', 'deadbeefdeadbeefdeadbeefdeadbeef')
|
71
|
+
z =mhc.encrypt('x@example.com')
|
72
|
+
assert_equal 'wBG7nOgntKqWeDpF9ucVNQ==', z
|
73
|
+
z =mhc.encrypt('johndoe@example.com')
|
74
|
+
assert_equal 'whWIqk0r4urZ-3S7y7uSceC9_ECd3hpAGy71E2o0HpI=', z
|
75
|
+
end
|
76
|
+
def test_encrypt_long
|
77
|
+
mhc = ReCaptcha::MHClient.new('01S1TOX9aibKxfC9oJlp8IeA==', 'deadbeefdeadbeefdeadbeefdeadbeef')
|
78
|
+
z =mhc.encrypt('averylongemailaddressofmorethan32cdharactersx@example.com')
|
79
|
+
assert_equal "q-0LLVT2bIxWbFpfLfpNhJAGadkfWXVk4hAxSlVaLrdnXrsB1NKNubavS5N-7PBued3K531vifN6NB3iz3W7qQ==",z
|
80
|
+
z =mhc.encrypt('johndoe@example.com')
|
81
|
+
assert_equal 'whWIqk0r4urZ-3S7y7uSceC9_ECd3hpAGy71E2o0HpI=', z
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_nil_challenge
|
85
|
+
client = new_client
|
86
|
+
estub = stub_everything('errors')
|
87
|
+
client.validate('abc', nil, 'foo', estub)
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_constructor
|
92
|
+
client = new_client('abc', 'def', true)
|
93
|
+
expected= <<-EOF
|
94
|
+
<script type=\"text/javascript\" src=\"https://api-secure.recaptcha.net/challenge?k=abc\"> </script>\n <noscript>\n <iframe src=\"https://api-secure.recaptcha.net/noscript?k=abc\"\n height=\"300\" width=\"500\" frameborder=\"0\"></iframe><br>\n <textarea name=\"recaptcha_challenge_field\" rows=\"3\" cols=\"40\">\n </textarea>\n <input type=\"hidden\" name=\"recaptcha_response_field\" \n value=\"manual_challenge\">\n </noscript>
|
95
|
+
EOF
|
96
|
+
assert_equal expected.strip, client.get_challenge.strip
|
97
|
+
client = new_client
|
98
|
+
expected= <<-EOF
|
99
|
+
<script type=\"text/javascript\" src=\"http://api.recaptcha.net/challenge?k=abc\"> </script>\n <noscript>\n <iframe src=\"http://api.recaptcha.net/noscript?k=abc\"\n height=\"300\" width=\"500\" frameborder=\"0\"></iframe><br>\n <textarea name=\"recaptcha_challenge_field\" rows=\"3\" cols=\"40\">\n </textarea>\n <input type=\"hidden\" name=\"recaptcha_response_field\" \n value=\"manual_challenge\">\n </noscript>
|
100
|
+
EOF
|
101
|
+
assert_equal expected.strip, client.get_challenge.strip
|
102
|
+
client = new_client
|
103
|
+
expected= <<-EOF
|
104
|
+
<script type=\"text/javascript\" src=\"http://api.recaptcha.net/challenge?k=abc\"> </script>\n <noscript>\n <iframe src=\"http://api.recaptcha.net/noscript?k=abc\"\n height=\"300\" width=\"500\" frameborder=\"0\"></iframe><br>\n <textarea name=\"recaptcha_challenge_field\" rows=\"3\" cols=\"40\">\n </textarea>\n <input type=\"hidden\" name=\"recaptcha_response_field\" \n value=\"manual_challenge\">\n </noscript>
|
105
|
+
EOF
|
106
|
+
assert_equal expected.strip, client.get_challenge.strip
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_constructor_with_recaptcha_options
|
110
|
+
# "Look and Feel Customization" per http://recaptcha.net/apidocs/captcha/
|
111
|
+
client = new_client
|
112
|
+
expected= <<-EOF
|
113
|
+
<script type=\"text/javascript\">\nvar RecaptchaOptions = { theme : \"white\", tabindex : 10};\n</script>\n <script type=\"text/javascript\" src=\"http://api.recaptcha.net/challenge?k=abc&error=somerror\"> </script>\n <noscript>\n <iframe src=\"http://api.recaptcha.net/noscript?k=abc&error=somerror\"\n height=\"300\" width=\"500\" frameborder=\"0\"></iframe><br>\n <textarea name=\"recaptcha_challenge_field\" rows=\"3\" cols=\"40\">\n </textarea>\n <input type=\"hidden\" name=\"recaptcha_response_field\" \n value=\"manual_challenge\">\n </noscript>
|
114
|
+
EOF
|
115
|
+
assert_equal expected.strip, client.get_challenge('somerror', :options => {:theme => 'white', :tabindex => 10}).strip
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_validate_fails
|
119
|
+
badwords_resp="false\r\n360 incorrect-captcha-sol"
|
120
|
+
err_stub=mock()
|
121
|
+
err_stub.expects(:add_to_base).with("Captcha failed.")
|
122
|
+
stub_proxy=mock('proxy')
|
123
|
+
stub_http = mock('http mock')
|
124
|
+
stub_proxy.expects(:start).with('api-verify.recaptcha.net').returns(stub_http)
|
125
|
+
stub_http.expects(:post).with('/verify', 'privatekey=def&remoteip=localhost&challenge=abc&response=def', {'Content-Type' => 'application/x-www-form-urlencoded'}).returns(['foo', badwords_resp])
|
126
|
+
Net::HTTP.expects(:Proxy).returns(stub_proxy)
|
127
|
+
client = new_client
|
128
|
+
assert !client.validate('localhost', 'abc', 'def', err_stub)
|
129
|
+
end
|
130
|
+
def test_validate_good
|
131
|
+
goodwords_resp="true\r\nsuccess"
|
132
|
+
err_stub=mock()
|
133
|
+
stub_proxy=mock('proxy')
|
134
|
+
stub_http = mock('http mock')
|
135
|
+
stub_proxy.expects(:start).with('api-verify.recaptcha.net').returns(stub_http)
|
136
|
+
stub_http.expects(:post).with('/verify', 'privatekey=def&remoteip=localhost&challenge=abc&response=def', {'Content-Type' => 'application/x-www-form-urlencoded'}).returns(['foo', goodwords_resp])
|
137
|
+
Net::HTTP.expects(:Proxy).with(nil, nil).returns(stub_proxy)
|
138
|
+
client = new_client
|
139
|
+
assert client.validate('localhost', 'abc', 'def', err_stub)
|
140
|
+
end
|
141
|
+
def test_validate_good_proxy
|
142
|
+
ENV['proxy_host']='fubar:8080'
|
143
|
+
goodwords_resp="true\r\nsuccess"
|
144
|
+
err_stub=mock()
|
145
|
+
stub_proxy=mock('proxy')
|
146
|
+
stub_http = mock('http mock')
|
147
|
+
stub_proxy.expects(:start).with('api-verify.recaptcha.net').returns(stub_http)
|
148
|
+
stub_http.expects(:post).with('/verify', 'privatekey=def&remoteip=localhost&challenge=abc&response=def', {'Content-Type' => 'application/x-www-form-urlencoded'}).returns(['foo', goodwords_resp])
|
149
|
+
Net::HTTP.expects(:Proxy).with('fubar', '8080').returns(stub_proxy)
|
150
|
+
client = new_client
|
151
|
+
assert client.validate('localhost', 'abc', 'def', err_stub)
|
152
|
+
ENV['proxy_host']='fubar'
|
153
|
+
err_stub=mock()
|
154
|
+
stub_proxy=mock('proxy')
|
155
|
+
stub_http = mock('http mock')
|
156
|
+
stub_proxy.expects(:start).with('api-verify.recaptcha.net').returns(stub_http)
|
157
|
+
stub_http.expects(:post).with('/verify', 'privatekey=def&remoteip=localhost&challenge=abc&response=def', {'Content-Type' => 'application/x-www-form-urlencoded'}).returns(['foo', goodwords_resp])
|
158
|
+
Net::HTTP.expects(:Proxy).with('fubar', nil).returns(stub_proxy)
|
159
|
+
client = new_client
|
160
|
+
assert client.validate('localhost', 'abc', 'def', err_stub)
|
161
|
+
end
|
162
|
+
|
163
|
+
#
|
164
|
+
# unit tests for the get_captcha() ViewHelper method
|
165
|
+
#
|
166
|
+
|
167
|
+
def test_get_captcha_fails_without_key_constants
|
168
|
+
assert !ReCaptcha::ViewHelper.const_defined?(:RCC_PUB)
|
169
|
+
assert !ReCaptcha::ViewHelper.const_defined?(:RCC_PRIV)
|
170
|
+
assert_raise NameError do
|
171
|
+
@vf.get_captcha
|
172
|
+
end
|
173
|
+
end
|
174
|
+
def test_get_captcha_fails_without_public_key_constant
|
175
|
+
assert !ReCaptcha::ViewHelper.const_defined?(:RCC_PUB)
|
176
|
+
assert !ReCaptcha::ViewHelper.const_defined?(:RCC_PRIV)
|
177
|
+
ReCaptcha::ViewHelper.define_private_key
|
178
|
+
assert ReCaptcha::ViewHelper.const_defined?(:RCC_PRIV)
|
179
|
+
assert_raise NameError do
|
180
|
+
@vf.get_captcha
|
181
|
+
end
|
182
|
+
ReCaptcha::ViewHelper.undefine_private_key
|
183
|
+
end
|
184
|
+
def test_get_captcha_fails_without_private_key_constant
|
185
|
+
assert !ReCaptcha::ViewHelper.const_defined?(:RCC_PUB)
|
186
|
+
assert !ReCaptcha::ViewHelper.const_defined?(:RCC_PRIV)
|
187
|
+
ReCaptcha::ViewHelper.define_public_key
|
188
|
+
assert ReCaptcha::ViewHelper.const_defined?(:RCC_PUB)
|
189
|
+
assert_raise NameError do
|
190
|
+
@vf.get_captcha
|
191
|
+
end
|
192
|
+
ReCaptcha::ViewHelper.undefine_public_key
|
193
|
+
end
|
194
|
+
def test_get_captcha_succeeds_with_key_constants
|
195
|
+
mock_client = mock('client')
|
196
|
+
ReCaptcha::Client.expects(:new).returns(mock_client)
|
197
|
+
mock_client.expects(:get_challenge).with('', {})
|
198
|
+
assert !ReCaptcha::ViewHelper.const_defined?(:RCC_PUB)
|
199
|
+
assert !ReCaptcha::ViewHelper.const_defined?(:RCC_PRIV)
|
200
|
+
ReCaptcha::ViewHelper.define_public_key
|
201
|
+
ReCaptcha::ViewHelper.define_private_key
|
202
|
+
assert ReCaptcha::ViewHelper.const_defined?(:RCC_PUB)
|
203
|
+
assert ReCaptcha::ViewHelper.const_defined?(:RCC_PRIV)
|
204
|
+
assert_nothing_raised do
|
205
|
+
@vf.get_captcha
|
206
|
+
end
|
207
|
+
ReCaptcha::ViewHelper.undefine_public_key
|
208
|
+
ReCaptcha::ViewHelper.undefine_private_key
|
209
|
+
end
|
210
|
+
def test_get_captcha_succeeds_without_key_constants_but_with_options
|
211
|
+
assert !ReCaptcha::ViewHelper.const_defined?(:RCC_PUB)
|
212
|
+
assert !ReCaptcha::ViewHelper.const_defined?(:RCC_PRIV)
|
213
|
+
assert_nothing_raised do
|
214
|
+
@vf.get_captcha(:rcc_pub => 'foo', :rcc_priv => 'bar')
|
215
|
+
end
|
216
|
+
end
|
217
|
+
def test_get_captcha_is_correct_with_constants_and_with_options
|
218
|
+
expected= <<-EOF
|
219
|
+
<script type=\"text/javascript\" src=\"http://api.recaptcha.net/challenge?k=%s\"> </script>\n <noscript>\n <iframe src=\"http://api.recaptcha.net/noscript?k=%s\"\n height=\"300\" width=\"500\" frameborder=\"0\"></iframe><br>\n <textarea name=\"recaptcha_challenge_field\" rows=\"3\" cols=\"40\">\n </textarea>\n <input type=\"hidden\" name=\"recaptcha_response_field\" \n value=\"manual_challenge\">\n </noscript>
|
220
|
+
EOF
|
221
|
+
# first, with constants
|
222
|
+
ReCaptcha::ViewHelper.define_public_key # 'foo'
|
223
|
+
ReCaptcha::ViewHelper.define_private_key # 'bar'
|
224
|
+
actual = @vf.get_captcha
|
225
|
+
assert_equal(((expected % ['foo', 'foo']).strip), actual.strip)
|
226
|
+
ReCaptcha::ViewHelper.undefine_public_key
|
227
|
+
ReCaptcha::ViewHelper.undefine_private_key
|
228
|
+
# next, with options
|
229
|
+
actual = @vf.get_captcha(:rcc_pub => 'foobar', :rcc_priv => 'blegga')
|
230
|
+
assert_equal(((expected % ['foobar', 'foobar']).strip), actual.strip)
|
231
|
+
end
|
232
|
+
|
233
|
+
#
|
234
|
+
# unit tests for the validate_recap() AppHelper method
|
235
|
+
#
|
236
|
+
|
237
|
+
def test_validate_recap_fails_without_key_constants
|
238
|
+
assert !ReCaptcha::AppHelper.const_defined?(:RCC_PUB)
|
239
|
+
assert !ReCaptcha::AppHelper.const_defined?(:RCC_PRIV)
|
240
|
+
assert_raise NameError do
|
241
|
+
@cf.validate_recap({}, {})
|
242
|
+
end
|
243
|
+
end
|
244
|
+
def test_validate_recap_fails_without_public_key_constant
|
245
|
+
assert !ReCaptcha::AppHelper.const_defined?(:RCC_PUB)
|
246
|
+
assert !ReCaptcha::AppHelper.const_defined?(:RCC_PRIV)
|
247
|
+
ReCaptcha::AppHelper.define_private_key
|
248
|
+
assert ReCaptcha::AppHelper.const_defined?(:RCC_PRIV)
|
249
|
+
assert_raise NameError do
|
250
|
+
@cf.validate_recap({}, {})
|
251
|
+
end
|
252
|
+
ReCaptcha::AppHelper.undefine_private_key
|
253
|
+
end
|
254
|
+
def test_validate_recap_fails_without_private_key_constant
|
255
|
+
assert !ReCaptcha::AppHelper.const_defined?(:RCC_PUB)
|
256
|
+
assert !ReCaptcha::AppHelper.const_defined?(:RCC_PRIV)
|
257
|
+
ReCaptcha::AppHelper.define_public_key
|
258
|
+
assert ReCaptcha::AppHelper.const_defined?(:RCC_PUB)
|
259
|
+
assert_raise NameError do
|
260
|
+
@cf.validate_recap({}, {})
|
261
|
+
end
|
262
|
+
ReCaptcha::AppHelper.undefine_public_key
|
263
|
+
end
|
264
|
+
def test_validate_recap_succeeds_with_key_constants
|
265
|
+
e = mock('errors')
|
266
|
+
e.expects(:add_to_base).with('Captcha failed.')
|
267
|
+
assert !ReCaptcha::AppHelper.const_defined?(:RCC_PUB)
|
268
|
+
assert !ReCaptcha::AppHelper.const_defined?(:RCC_PRIV)
|
269
|
+
ReCaptcha::AppHelper.define_public_key
|
270
|
+
ReCaptcha::AppHelper.define_private_key
|
271
|
+
assert ReCaptcha::AppHelper.const_defined?(:RCC_PUB)
|
272
|
+
assert ReCaptcha::AppHelper.const_defined?(:RCC_PRIV)
|
273
|
+
assert_nothing_raised do
|
274
|
+
@cf.validate_recap({}, e)
|
275
|
+
end
|
276
|
+
ReCaptcha::AppHelper.undefine_public_key
|
277
|
+
ReCaptcha::AppHelper.undefine_private_key
|
278
|
+
end
|
279
|
+
def test_validate_recap_succeeds_without_key_constants_but_with_options
|
280
|
+
mock = mock('client')
|
281
|
+
ReCaptcha::Client.expects(:new).returns(mock)
|
282
|
+
mock.expects(:validate).with('0.0.0.0', nil, nil, {}).returns(true)
|
283
|
+
mock.expects(:last_error)
|
284
|
+
assert !ReCaptcha::AppHelper.const_defined?(:RCC_PUB)
|
285
|
+
assert !ReCaptcha::AppHelper.const_defined?(:RCC_PRIV)
|
286
|
+
@cf.validate_recap({}, {}, :rcc_pub => 'foo', :rcc_priv => 'bar')
|
287
|
+
end
|
288
|
+
def test_validate_recap_is_correct_with_constants_and_with_options
|
289
|
+
# first, with constants
|
290
|
+
e = mock('errors')
|
291
|
+
mock = mock('client')
|
292
|
+
ReCaptcha::Client.expects(:new).returns(mock).times(2)
|
293
|
+
mock.expects(:validate).with('0.0.0.0', nil, nil, {}).returns(true)
|
294
|
+
mock.expects(:last_error).times(2)
|
295
|
+
ReCaptcha::AppHelper.define_public_key # 'foo'
|
296
|
+
ReCaptcha::AppHelper.define_private_key # 'bar'
|
297
|
+
assert @cf.validate_recap({}, {})
|
298
|
+
ReCaptcha::AppHelper.undefine_public_key
|
299
|
+
ReCaptcha::AppHelper.undefine_private_key
|
300
|
+
# next, with options
|
301
|
+
mock.expects(:validate).with('0.0.0.0', nil, nil, e).returns(true)
|
302
|
+
assert @cf.validate_recap({}, e, {:rcc_pub => 'foobar', :rcc_priv => 'blegga'})
|
303
|
+
end
|
304
|
+
|
305
|
+
#
|
306
|
+
# unit tests for HTTP/HTTPS-variants of get_captcha() method
|
307
|
+
#
|
308
|
+
|
309
|
+
def test_get_captcha_uses_http_without_options
|
310
|
+
expected= <<-EOF
|
311
|
+
<script type=\"text/javascript\" src=\"http://api.recaptcha.net/challenge?k=%s\"> </script>\n <noscript>\n <iframe src=\"http://api.recaptcha.net/noscript?k=%s\"\n height=\"300\" width=\"500\" frameborder=\"0\"></iframe><br>\n <textarea name=\"recaptcha_challenge_field\" rows=\"3\" cols=\"40\">\n </textarea>\n <input type=\"hidden\" name=\"recaptcha_response_field\" \n value=\"manual_challenge\">\n </noscript>
|
312
|
+
EOF
|
313
|
+
actual = @vf.get_captcha(:rcc_pub => 'foobar', :rcc_priv => 'blegga')
|
314
|
+
assert_equal(((expected % ['foobar', 'foobar']).strip), actual.strip)
|
315
|
+
end
|
316
|
+
def test_get_captcha_uses_https_with_options_true
|
317
|
+
expected= <<-EOF
|
318
|
+
<script type=\"text/javascript\" src=\"https://api-secure.recaptcha.net/challenge?k=%s\"> </script>\n <noscript>\n <iframe src=\"https://api-secure.recaptcha.net/noscript?k=%s\"\n height=\"300\" width=\"500\" frameborder=\"0\"></iframe><br>\n <textarea name=\"recaptcha_challenge_field\" rows=\"3\" cols=\"40\">\n </textarea>\n <input type=\"hidden\" name=\"recaptcha_response_field\" \n value=\"manual_challenge\">\n </noscript>
|
319
|
+
EOF
|
320
|
+
actual = @vf.get_captcha(:rcc_pub => 'foobar', :rcc_priv => 'blegga', :ssl => true)
|
321
|
+
assert_equal(((expected % ['foobar', 'foobar']).strip), actual.strip)
|
322
|
+
end
|
323
|
+
def test_get_captcha_uses_http_with_options_false
|
324
|
+
expected= <<-EOF
|
325
|
+
<script type=\"text/javascript\" src=\"http://api.recaptcha.net/challenge?k=%s\"> </script>\n <noscript>\n <iframe src=\"http://api.recaptcha.net/noscript?k=%s\"\n height=\"300\" width=\"500\" frameborder=\"0\"></iframe><br>\n <textarea name=\"recaptcha_challenge_field\" rows=\"3\" cols=\"40\">\n </textarea>\n <input type=\"hidden\" name=\"recaptcha_response_field\" \n value=\"manual_challenge\">\n </noscript>
|
326
|
+
EOF
|
327
|
+
actual = @vf.get_captcha(:rcc_pub => 'foobar', :rcc_priv => 'blegga', :ssl => false)
|
328
|
+
assert_equal(((expected % ['foobar', 'foobar']).strip), actual.strip)
|
329
|
+
end
|
330
|
+
|
331
|
+
private
|
332
|
+
|
333
|
+
def new_client(pubkey='abc', privkey='def', ssl=false)
|
334
|
+
ReCaptcha::Client.new(pubkey, privkey, ssl)
|
335
|
+
end
|
336
|
+
end
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-recaptcha
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- McClain Looney
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-02-13 00:00:00 -06:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: newgem
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.2.3
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: hoe
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.8.0
|
34
|
+
version:
|
35
|
+
description: ""
|
36
|
+
email:
|
37
|
+
- m@loonsoft.com
|
38
|
+
executables: []
|
39
|
+
|
40
|
+
extensions: []
|
41
|
+
|
42
|
+
extra_rdoc_files:
|
43
|
+
- History.txt
|
44
|
+
- README.txt
|
45
|
+
files:
|
46
|
+
- History.txt
|
47
|
+
- README.txt
|
48
|
+
- lib/recaptcha.rb
|
49
|
+
- lib/ruby-recaptcha.rb
|
50
|
+
has_rdoc: true
|
51
|
+
homepage: h2. What
|
52
|
+
post_install_message:
|
53
|
+
rdoc_options:
|
54
|
+
- --main
|
55
|
+
- README.txt
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: "0"
|
63
|
+
version:
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "0"
|
69
|
+
version:
|
70
|
+
requirements: []
|
71
|
+
|
72
|
+
rubyforge_project: ruby-recaptcha
|
73
|
+
rubygems_version: 1.3.1
|
74
|
+
signing_key:
|
75
|
+
specification_version: 2
|
76
|
+
summary: ""
|
77
|
+
test_files:
|
78
|
+
- test/test_helper.rb
|
79
|
+
- test/test_recaptcha.rb
|