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 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