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 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
@@ -0,0 +1,7 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'recaptcha'
5
+ module RubyRecaptcha
6
+ VERSION = '1.0.0'
7
+ end
@@ -0,0 +1,3 @@
1
+ require 'stringio'
2
+ require 'test/unit'
3
+ require File.dirname(__FILE__) + '/../lib/ruby-recaptcha'
@@ -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