ruby-recaptcha 1.0.0
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/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
|