validates_captcha 0.9.3 → 0.9.4
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/CHANGELOG.rdoc +5 -0
- data/README.rdoc +35 -9
- data/lib/validates_captcha/provider/image.rb +0 -4
- data/lib/validates_captcha/provider/static_image.rb +224 -0
- data/lib/validates_captcha/version.rb +1 -1
- data/lib/validates_captcha.rb +1 -0
- data/tasks/static_image_tasks.rake +33 -0
- data/test/cases/controller_validation_test.rb +45 -5
- data/test/cases/model_validation_test.rb +61 -6
- data/test/cases/provider/static_image_test.rb +148 -0
- metadata +7 -3
data/CHANGELOG.rdoc
CHANGED
data/README.rdoc
CHANGED
@@ -5,10 +5,12 @@ ActiveRecord's validation mechanism and providing helpers for ActionController
|
|
5
5
|
and ActionView.
|
6
6
|
|
7
7
|
RDoc documentation (including this README as start page) can be found at
|
8
|
-
http://m4n.github.com/validates_captcha
|
8
|
+
http://m4n.github.com/validates_captcha. Validates Captcha was first
|
9
|
+
announced {here}[http://m4n.github.com/2009/09/28/introducing-validates-captcha.html].
|
9
10
|
|
10
|
-
|
11
|
-
|
11
|
+
By default, question/answer captcha challenges are used, but you can also switch
|
12
|
+
to the built-in image captcha providers. If you stick to ValidatesCaptcha’s API,
|
13
|
+
you can even implement your own captcha challenge provider.
|
12
14
|
|
13
15
|
|
14
16
|
|
@@ -18,8 +20,8 @@ Validates Captcha extends ActiveRecord, ActionController and ActionView with
|
|
18
20
|
helper methods that make it a snap to integrate captcha verification in your
|
19
21
|
Rails application.
|
20
22
|
|
21
|
-
<b>Step #1:</b> Extend
|
22
|
-
|
23
|
+
<b>Step #1:</b> Extend your view’s form with the necessary captcha display and
|
24
|
+
input logic.
|
23
25
|
|
24
26
|
# app/views/comments/new.html.erb
|
25
27
|
<% form_for @comment do |f| %>
|
@@ -152,7 +154,7 @@ Outside +with_captcha_validation+, no captcha validation is performed.
|
|
152
154
|
|
153
155
|
|
154
156
|
|
155
|
-
== Question/answer
|
157
|
+
== Question/answer challenge captchas (default provider)
|
156
158
|
|
157
159
|
You can set the captcha provider to use question/answer challenges with
|
158
160
|
the code below. It is best to put this in a Rails initializer.
|
@@ -170,10 +172,11 @@ to do it.
|
|
170
172
|
|
171
173
|
|
172
174
|
|
173
|
-
==
|
175
|
+
== Dynamic image challenge captchas
|
174
176
|
|
175
|
-
You can set the captcha provider to use image challenges
|
176
|
-
the code below.
|
177
|
+
You can set the captcha provider to use dynamically created image challenges
|
178
|
+
with the code below. Dynamic means that the captcha image is created on invocation.
|
179
|
+
If you want to utilize this provider, it is best to put this in a Rails initializer.
|
177
180
|
|
178
181
|
ValidatesCaptcha.provider = ValidatesCaptcha::Provider::Image.new
|
179
182
|
|
@@ -190,6 +193,29 @@ values to your needs.
|
|
190
193
|
|
191
194
|
|
192
195
|
|
196
|
+
== Static image challenge captchas
|
197
|
+
|
198
|
+
You can set the captcha provider to use static image challenges with
|
199
|
+
the code below. Static means that there exist some pre-created images
|
200
|
+
in a folder accessible by the web application. If you want to utilize
|
201
|
+
this provider, it is best to put this in a Rails initializer.
|
202
|
+
|
203
|
+
ValidatesCaptcha.provider = ValidatesCaptcha::Provider::StaticImage.new
|
204
|
+
|
205
|
+
There is a Rake tast for creating the static captcha images:
|
206
|
+
|
207
|
+
rake validates_captcha:create_static_images
|
208
|
+
|
209
|
+
This will create 3 images for you. To create a different number of images,
|
210
|
+
provide a COUNT argument:
|
211
|
+
|
212
|
+
rake validates_captcha:create_static_images COUNT=50
|
213
|
+
|
214
|
+
If you want to customize the path the images get saved to (or other stuff),
|
215
|
+
please see the documentation for ValidatesCaptcha::Provider::StaticImage.
|
216
|
+
|
217
|
+
|
218
|
+
|
193
219
|
== Extensibility
|
194
220
|
|
195
221
|
Don't like the built-in challenges? It's easy to extend them or to implement
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'openssl'
|
2
|
-
require 'active_support/secure_random'
|
3
1
|
require 'action_view/helpers'
|
4
2
|
|
5
3
|
module ValidatesCaptcha
|
@@ -80,8 +78,6 @@ module ValidatesCaptcha
|
|
80
78
|
class Image
|
81
79
|
include ActionView::Helpers
|
82
80
|
|
83
|
-
KEY = ::ActiveSupport::SecureRandom.hex(32).freeze
|
84
|
-
|
85
81
|
@@string_generator = nil
|
86
82
|
@@reversible_encrypter = nil
|
87
83
|
@@image_generator = nil
|
@@ -0,0 +1,224 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
require 'action_view/helpers'
|
3
|
+
|
4
|
+
module ValidatesCaptcha
|
5
|
+
module Provider
|
6
|
+
# An image captcha provider that relies on pre-created captcha images.
|
7
|
+
#
|
8
|
+
# There is a Rake tast for creating the captcha images:
|
9
|
+
#
|
10
|
+
# rake validates_captcha:create_static_images
|
11
|
+
#
|
12
|
+
# This will create 3 images in #filesystem_dir. To create a
|
13
|
+
# different number of images, provide a COUNT argument:
|
14
|
+
#
|
15
|
+
# rake validates_captcha:create_static_images COUNT=50
|
16
|
+
#
|
17
|
+
# This class contains the getters and setters for the backend classes:
|
18
|
+
# image generator and string generator. This allows you to replace them
|
19
|
+
# with your custom implementations. For more information on how to bring
|
20
|
+
# the image provider to use your own implementation instead of the default
|
21
|
+
# one, consult the documentation for the specific default class.
|
22
|
+
#
|
23
|
+
# The default captcha image generator uses ImageMagick's +convert+ command to
|
24
|
+
# create the captcha. So a recent and properly configured version of ImageMagick
|
25
|
+
# must be installed on the system. The version used while developing was 6.4.5.
|
26
|
+
# But you are not bound to ImageMagick. If you want to provide a custom image
|
27
|
+
# generator, take a look at the documentation for
|
28
|
+
# ValidatesCaptcha::ImageGenerator::Simple on how to create your own.
|
29
|
+
class StaticImage
|
30
|
+
include ActionView::Helpers
|
31
|
+
|
32
|
+
SALT = "3f(61&831_fa0712d4a?b58-eb4b8$a2%.36378f".freeze
|
33
|
+
|
34
|
+
@@string_generator = nil
|
35
|
+
@@image_generator = nil
|
36
|
+
@@filesystem_dir = nil
|
37
|
+
@@web_dir = nil
|
38
|
+
@@salt = nil
|
39
|
+
|
40
|
+
class << self
|
41
|
+
# Returns the current captcha string generator. Defaults to an
|
42
|
+
# instance of the ValidatesCaptcha::StringGenerator::Simple class.
|
43
|
+
def string_generator
|
44
|
+
@@string_generator ||= ValidatesCaptcha::StringGenerator::Simple.new
|
45
|
+
end
|
46
|
+
|
47
|
+
# Sets the current captcha string generator. Used to set a
|
48
|
+
# custom string generator.
|
49
|
+
def string_generator=(generator)
|
50
|
+
@@string_generator = generator
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns the current captcha image generator. Defaults to an
|
54
|
+
# instance of the ValidatesCaptcha::ImageGenerator::Simple class.
|
55
|
+
def image_generator
|
56
|
+
@@image_generator ||= ValidatesCaptcha::ImageGenerator::Simple.new
|
57
|
+
end
|
58
|
+
|
59
|
+
# Sets the current captcha image generator. Used to set a custom
|
60
|
+
# image generator.
|
61
|
+
def image_generator=(generator)
|
62
|
+
@@image_generator = generator
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns the current captcha image file system directory. Defaults to
|
66
|
+
# +RAILS_ROOT/public/images/captchas+.
|
67
|
+
def filesystem_dir
|
68
|
+
@@filesystem_dir ||= ::File.join(::Rails.public_path, 'images', 'captchas')
|
69
|
+
end
|
70
|
+
|
71
|
+
# Sets the current captcha image file system directory. Used to set a custom
|
72
|
+
# image directory.
|
73
|
+
def filesystem_dir=(dir)
|
74
|
+
@@filesystem_dir = dir
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns the current captcha image web directory. Defaults to
|
78
|
+
# +/images/captchas+.
|
79
|
+
def web_dir
|
80
|
+
@@web_dir ||= '/images/captchas'
|
81
|
+
end
|
82
|
+
|
83
|
+
# Sets the current captcha image web directory. Used to set a custom
|
84
|
+
# image directory.
|
85
|
+
def web_dir=(dir)
|
86
|
+
@@web_dir = dir
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns the current salt used for encryption.
|
90
|
+
def salt
|
91
|
+
@@salt ||= SALT
|
92
|
+
end
|
93
|
+
|
94
|
+
# Sets the current salt used for encryption. Used to set a custom
|
95
|
+
# salt.
|
96
|
+
def salt=(salt)
|
97
|
+
@@salt = salt
|
98
|
+
end
|
99
|
+
|
100
|
+
# Return the encryption of the +code+ using #salt.
|
101
|
+
def encrypt(code)
|
102
|
+
::Digest::SHA1.hexdigest "#{salt}--#{code}"
|
103
|
+
end
|
104
|
+
|
105
|
+
# Creates a captcha image in the #filesystem_dir and returns
|
106
|
+
# the path to it and the code displayed on the image.
|
107
|
+
def create_image
|
108
|
+
code = string_generator.generate
|
109
|
+
encrypted_code = encrypt(code)
|
110
|
+
|
111
|
+
image_filename = "#{encrypted_code}#{image_generator.file_extension}"
|
112
|
+
image_path = File.join(filesystem_dir, image_filename)
|
113
|
+
image_bytes = image_generator.generate(code)
|
114
|
+
|
115
|
+
File.open image_path, 'w' do |os|
|
116
|
+
os.write image_bytes
|
117
|
+
end
|
118
|
+
|
119
|
+
return image_path, code
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# This method is the one called by Rack.
|
124
|
+
#
|
125
|
+
# It returns HTTP status 404 if the path is not recognized. If the path is
|
126
|
+
# recognized, it returns HTTP status 200 and delivers a new challenge in
|
127
|
+
# JSON format.
|
128
|
+
#
|
129
|
+
# Please take a look at the source code if you want to learn more.
|
130
|
+
def call(env)
|
131
|
+
if env['PATH_INFO'] == regenerate_path
|
132
|
+
captcha_challenge = generate_challenge
|
133
|
+
json = { :captcha_challenge => captcha_challenge, :captcha_image_path => image_path(captcha_challenge) }.to_json
|
134
|
+
|
135
|
+
[200, { 'Content-Type' => 'application/json' }, [json]]
|
136
|
+
else
|
137
|
+
[404, { 'Content-Type' => 'text/html' }, ['Not Found']]
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Returns an array containing the paths to the available captcha images.
|
142
|
+
def images
|
143
|
+
@images ||= Dir[File.join(filesystem_dir, "*#{image_file_extension}")]
|
144
|
+
end
|
145
|
+
|
146
|
+
# Returns an array containing the available challenges (encrypted captcha codes).
|
147
|
+
def challenges
|
148
|
+
@challenges ||= images.map { |path| File.basename(path, image_file_extension) }
|
149
|
+
end
|
150
|
+
|
151
|
+
# Returns a captcha challenge.
|
152
|
+
def generate_challenge
|
153
|
+
raise("no captcha images found in #{filesystem_dir}") if challenges.empty?
|
154
|
+
|
155
|
+
challenges[rand(challenges.size)]
|
156
|
+
end
|
157
|
+
|
158
|
+
# Returns true if the captcha was solved using the given +challenge+ and +solution+,
|
159
|
+
# otherwise false.
|
160
|
+
def solved?(challenge, solution)
|
161
|
+
challenge == encrypt(solution)
|
162
|
+
end
|
163
|
+
|
164
|
+
# Returns an image tag with the source set to the url of the captcha image.
|
165
|
+
#
|
166
|
+
# Internally calls Rails' +image_tag+ helper method, passing the +options+ argument.
|
167
|
+
def render_challenge(sanitized_object_name, object, options = {})
|
168
|
+
src = image_path(object.captcha_challenge)
|
169
|
+
|
170
|
+
options[:alt] ||= 'CAPTCHA'
|
171
|
+
options[:id] = "#{sanitized_object_name}_captcha_image"
|
172
|
+
|
173
|
+
image_tag src, options
|
174
|
+
end
|
175
|
+
|
176
|
+
# Returns an anchor tag that makes an AJAX request to fetch a new captcha code and updates
|
177
|
+
# the captcha image after the request is complete.
|
178
|
+
#
|
179
|
+
# Internally calls Rails' +link_to_remote+ helper method, passing the +options+ and
|
180
|
+
# +html_options+ arguments. So it relies on the Prototype javascript framework
|
181
|
+
# to be available on the web page.
|
182
|
+
#
|
183
|
+
# The anchor text defaults to 'Regenerate Captcha'. You can set this to a custom value
|
184
|
+
# providing a +:text+ key in the +options+ hash.
|
185
|
+
def render_regenerate_challenge_link(sanitized_object_name, object, options = {}, html_options = {})
|
186
|
+
text = options.delete(:text) || 'Regenerate Captcha'
|
187
|
+
success = "var result = request.responseJSON; $('#{sanitized_object_name}_captcha_image').src = result.captcha_image_path; $('#{sanitized_object_name}_captcha_challenge').value = result.captcha_challenge; $('#{sanitized_object_name}_captcha_solution').value = '';"
|
188
|
+
|
189
|
+
link_to_remote text, options.reverse_merge(:url => regenerate_path, :method => :get, :success => success), html_options
|
190
|
+
end
|
191
|
+
|
192
|
+
private
|
193
|
+
def regenerate_path #:nodoc:
|
194
|
+
'/captchas/regenerate'
|
195
|
+
end
|
196
|
+
|
197
|
+
def image_file_extension #:nodoc:
|
198
|
+
self.class.image_generator.file_extension
|
199
|
+
end
|
200
|
+
|
201
|
+
def image_path(encrypted_code) #:nodoc:
|
202
|
+
File.join(web_dir, "#{encrypted_code}#{image_file_extension}")
|
203
|
+
end
|
204
|
+
|
205
|
+
def encrypt(code) #:nodoc:
|
206
|
+
self.class.encrypt code
|
207
|
+
end
|
208
|
+
|
209
|
+
def filesystem_dir #:nodoc:
|
210
|
+
self.class.filesystem_dir
|
211
|
+
end
|
212
|
+
|
213
|
+
def web_dir #:nodoc:
|
214
|
+
self.class.web_dir
|
215
|
+
end
|
216
|
+
|
217
|
+
# This is needed by +link_to_remote+ called in +render_regenerate_link+.
|
218
|
+
def protect_against_forgery? #:nodoc:
|
219
|
+
false
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
data/lib/validates_captcha.rb
CHANGED
@@ -38,6 +38,7 @@ module ValidatesCaptcha
|
|
38
38
|
module Provider
|
39
39
|
autoload :Question, 'validates_captcha/provider/question'
|
40
40
|
autoload :Image, 'validates_captcha/provider/image'
|
41
|
+
autoload :StaticImage, 'validates_captcha/provider/static_image'
|
41
42
|
end
|
42
43
|
|
43
44
|
module StringGenerator
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'validates_captcha'
|
2
|
+
|
3
|
+
namespace :validates_captcha do
|
4
|
+
desc "Create 3 static captcha images in RAILS_ROOT/public/images/captchas/, specify a different number with COUNT=n"
|
5
|
+
task :create_static_images => [:create_static_image_dir, :clear_static_image_dir] do
|
6
|
+
count = ENV['COUNT'] ? ENV['COUNT'].to_i : 3
|
7
|
+
|
8
|
+
count.times do
|
9
|
+
path, code = ValidatesCaptcha::Provider::StaticImage.create_image
|
10
|
+
puts "Created #{path} with code #{code}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
task :create_static_image_dir => :environment do
|
15
|
+
image_dir = ValidatesCaptcha::Provider::StaticImage.filesystem_dir
|
16
|
+
|
17
|
+
unless File.exist?(image_dir)
|
18
|
+
FileUtils.mkdir_p image_dir
|
19
|
+
puts "Created directory #{image_dir}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
task :clear_static_image_dir => :environment do
|
24
|
+
image_dir = ValidatesCaptcha::Provider::StaticImage.filesystem_dir
|
25
|
+
image_files = Dir[File.join(image_dir, '*')]
|
26
|
+
|
27
|
+
if image_files.any?
|
28
|
+
FileUtils.rm image_files
|
29
|
+
puts "Cleared directory #{image_dir}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
@@ -76,13 +76,21 @@ end
|
|
76
76
|
class ControllerValidationTest < ActionController::TestCase
|
77
77
|
tests WidgetsController
|
78
78
|
|
79
|
-
def
|
79
|
+
def with_dynamic_image_provider(&block)
|
80
80
|
old_provider = ValidatesCaptcha.provider
|
81
81
|
provider = ValidatesCaptcha.provider = ValidatesCaptcha::Provider::Image.new
|
82
82
|
yield provider
|
83
83
|
ValidatesCaptcha.provider = old_provider
|
84
84
|
end
|
85
85
|
|
86
|
+
def with_static_image_provider(&block)
|
87
|
+
old_provider = ValidatesCaptcha.provider
|
88
|
+
provider = ValidatesCaptcha.provider = ValidatesCaptcha::Provider::StaticImage.new
|
89
|
+
provider.instance_variable_set "@images", ["/path/to/#{provider.send(:encrypt, 'hello')}#{provider.send(:image_file_extension)}"]
|
90
|
+
yield provider
|
91
|
+
ValidatesCaptcha.provider = old_provider
|
92
|
+
end
|
93
|
+
|
86
94
|
def with_question_provider(&block)
|
87
95
|
old_provider = ValidatesCaptcha.provider
|
88
96
|
provider = ValidatesCaptcha.provider = ValidatesCaptcha::Provider::Question.new
|
@@ -109,7 +117,7 @@ class ControllerValidationTest < ActionController::TestCase
|
|
109
117
|
end
|
110
118
|
|
111
119
|
test "calling #save method of controller should not assign @invalid" do
|
112
|
-
|
120
|
+
with_dynamic_image_provider do |provider|
|
113
121
|
challenge = provider.generate_challenge
|
114
122
|
solution = provider.send(:decrypt, challenge)
|
115
123
|
|
@@ -117,6 +125,14 @@ class ControllerValidationTest < ActionController::TestCase
|
|
117
125
|
assert_nil assigns(:invalid)
|
118
126
|
end
|
119
127
|
|
128
|
+
with_static_image_provider do |provider|
|
129
|
+
challenge = provider.generate_challenge
|
130
|
+
solution = 'hello'
|
131
|
+
|
132
|
+
post :save, { 'widget' => { 'captcha_challenge' => challenge, 'captcha_solution' => solution } }
|
133
|
+
assert_nil assigns(:invalid)
|
134
|
+
end
|
135
|
+
|
120
136
|
with_question_provider do |provider|
|
121
137
|
challenge = provider.generate_challenge
|
122
138
|
solution = provider.send(:solve, challenge)
|
@@ -127,7 +143,7 @@ class ControllerValidationTest < ActionController::TestCase
|
|
127
143
|
end
|
128
144
|
|
129
145
|
test "calling #store method of controller should assign @invalid" do
|
130
|
-
|
146
|
+
with_dynamic_image_provider do |provider|
|
131
147
|
challenge = provider.generate_challenge
|
132
148
|
solution = provider.send(:decrypt, challenge).reverse
|
133
149
|
|
@@ -135,6 +151,14 @@ class ControllerValidationTest < ActionController::TestCase
|
|
135
151
|
assert_not_nil assigns(:invalid)
|
136
152
|
end
|
137
153
|
|
154
|
+
with_static_image_provider do |provider|
|
155
|
+
challenge = provider.generate_challenge
|
156
|
+
solution = '---'
|
157
|
+
|
158
|
+
post :store, { 'widget' => { 'captcha_challenge' => challenge, 'captcha_solution' => solution } }
|
159
|
+
assert_not_nil assigns(:invalid)
|
160
|
+
end
|
161
|
+
|
138
162
|
with_question_provider do |provider|
|
139
163
|
challenge = provider.generate_challenge
|
140
164
|
solution = '---'
|
@@ -145,7 +169,7 @@ class ControllerValidationTest < ActionController::TestCase
|
|
145
169
|
end
|
146
170
|
|
147
171
|
test "calling #persist method of controller should assign @invalid" do
|
148
|
-
|
172
|
+
with_dynamic_image_provider do |provider|
|
149
173
|
challenge = provider.generate_challenge
|
150
174
|
solution = provider.send(:decrypt, challenge).reverse
|
151
175
|
|
@@ -153,6 +177,14 @@ class ControllerValidationTest < ActionController::TestCase
|
|
153
177
|
assert_not_nil assigns(:invalid)
|
154
178
|
end
|
155
179
|
|
180
|
+
with_static_image_provider do |provider|
|
181
|
+
challenge = provider.generate_challenge
|
182
|
+
solution = '---'
|
183
|
+
|
184
|
+
post :persist, { 'widget' => { 'captcha_challenge' => challenge, 'captcha_solution' => solution } }
|
185
|
+
assert_not_nil assigns(:invalid)
|
186
|
+
end
|
187
|
+
|
156
188
|
with_question_provider do |provider|
|
157
189
|
challenge = provider.generate_challenge
|
158
190
|
solution = '---'
|
@@ -163,7 +195,7 @@ class ControllerValidationTest < ActionController::TestCase
|
|
163
195
|
end
|
164
196
|
|
165
197
|
test "calling #bingo method of controller should not assign @invalid" do
|
166
|
-
|
198
|
+
with_dynamic_image_provider do |provider|
|
167
199
|
challenge = provider.generate_challenge
|
168
200
|
solution = provider.send(:decrypt, challenge).reverse
|
169
201
|
|
@@ -171,6 +203,14 @@ class ControllerValidationTest < ActionController::TestCase
|
|
171
203
|
assert_nil assigns(:invalid)
|
172
204
|
end
|
173
205
|
|
206
|
+
with_static_image_provider do |provider|
|
207
|
+
challenge = provider.generate_challenge
|
208
|
+
solution = '---'
|
209
|
+
|
210
|
+
post :bingo, { 'widget' => { 'captcha_challenge' => challenge, 'captcha_solution' => solution } }
|
211
|
+
assert_nil assigns(:invalid)
|
212
|
+
end
|
213
|
+
|
174
214
|
with_question_provider do |provider|
|
175
215
|
challenge = provider.generate_challenge
|
176
216
|
solution = '---'
|
@@ -1,13 +1,21 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
3
|
class ModelValidationTest < ValidatesCaptcha::TestCase
|
4
|
-
def
|
4
|
+
def with_dynamic_image_provider(&block)
|
5
5
|
old_provider = ValidatesCaptcha.provider
|
6
6
|
provider = ValidatesCaptcha.provider = ValidatesCaptcha::Provider::Image.new
|
7
7
|
yield provider
|
8
8
|
ValidatesCaptcha.provider = old_provider
|
9
9
|
end
|
10
10
|
|
11
|
+
def with_static_image_provider(&block)
|
12
|
+
old_provider = ValidatesCaptcha.provider
|
13
|
+
provider = ValidatesCaptcha.provider = ValidatesCaptcha::Provider::StaticImage.new
|
14
|
+
provider.instance_variable_set "@images", ["/path/to/#{provider.send(:encrypt, 'hello')}#{provider.send(:image_file_extension)}"]
|
15
|
+
yield provider
|
16
|
+
ValidatesCaptcha.provider = old_provider
|
17
|
+
end
|
18
|
+
|
11
19
|
def with_question_provider(&block)
|
12
20
|
old_provider = ValidatesCaptcha.provider
|
13
21
|
provider = ValidatesCaptcha.provider = ValidatesCaptcha::Provider::Question.new
|
@@ -76,13 +84,20 @@ class ModelValidationTest < ValidatesCaptcha::TestCase
|
|
76
84
|
end
|
77
85
|
|
78
86
|
test "not within a #with_captcha_validation block, calling valid? should return true if an invalid captcha_solution is set" do
|
79
|
-
|
87
|
+
with_dynamic_image_provider do |provider|
|
80
88
|
widget = Widget.new
|
81
89
|
widget.captcha_solution = provider.send(:decrypt, widget.captcha_challenge).reverse
|
82
90
|
|
83
91
|
assert widget.valid?
|
84
92
|
end
|
85
93
|
|
94
|
+
with_static_image_provider do |provider|
|
95
|
+
widget = Widget.new
|
96
|
+
widget.captcha_solution = '---'
|
97
|
+
|
98
|
+
assert widget.valid?
|
99
|
+
end
|
100
|
+
|
86
101
|
with_question_provider do |provider|
|
87
102
|
widget = Widget.new
|
88
103
|
widget.captcha_solution = '---'
|
@@ -92,13 +107,20 @@ class ModelValidationTest < ValidatesCaptcha::TestCase
|
|
92
107
|
end
|
93
108
|
|
94
109
|
test "not within a #with_captcha_validation block, calling valid? should return true if a valid captcha_solution is set" do
|
95
|
-
|
110
|
+
with_dynamic_image_provider do |provider|
|
96
111
|
widget = Widget.new
|
97
112
|
widget.captcha_solution = provider.send(:decrypt, widget.captcha_challenge)
|
98
113
|
|
99
114
|
assert widget.valid?
|
100
115
|
end
|
101
116
|
|
117
|
+
with_static_image_provider do |provider|
|
118
|
+
widget = Widget.new
|
119
|
+
widget.captcha_solution = 'hello'
|
120
|
+
|
121
|
+
assert widget.valid?
|
122
|
+
end
|
123
|
+
|
102
124
|
with_question_provider do |provider|
|
103
125
|
widget = Widget.new
|
104
126
|
widget.captcha_solution = provider.send(:solve, widget.captcha_challenge)
|
@@ -130,7 +152,7 @@ class ModelValidationTest < ValidatesCaptcha::TestCase
|
|
130
152
|
end
|
131
153
|
|
132
154
|
test "within a #with_captcha_validation block, calling valid? should return false if an invalid captcha_solution is set" do
|
133
|
-
|
155
|
+
with_dynamic_image_provider do |provider|
|
134
156
|
Widget.with_captcha_validation do
|
135
157
|
widget = Widget.new
|
136
158
|
widget.captcha_solution = provider.send(:decrypt, widget.captcha_challenge).reverse
|
@@ -141,6 +163,17 @@ class ModelValidationTest < ValidatesCaptcha::TestCase
|
|
141
163
|
end
|
142
164
|
end
|
143
165
|
|
166
|
+
with_static_image_provider do |provider|
|
167
|
+
Widget.with_captcha_validation do
|
168
|
+
widget = Widget.new
|
169
|
+
widget.captcha_solution = '---'
|
170
|
+
|
171
|
+
assert !widget.valid?
|
172
|
+
assert_equal 1, Array.wrap(widget.errors[:captcha_solution]).size
|
173
|
+
assert Array.wrap(widget.errors[:captcha_solution]).first.include?('invalid')
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
144
177
|
with_question_provider do |provider|
|
145
178
|
Widget.with_captcha_validation do
|
146
179
|
widget = Widget.new
|
@@ -154,7 +187,7 @@ class ModelValidationTest < ValidatesCaptcha::TestCase
|
|
154
187
|
end
|
155
188
|
|
156
189
|
test "within a #with_captcha_validation block, calling valid? should return true if a valid captcha_solution is set" do
|
157
|
-
|
190
|
+
with_dynamic_image_provider do |provider|
|
158
191
|
Widget.with_captcha_validation do
|
159
192
|
widget = Widget.new
|
160
193
|
widget.captcha_solution = provider.send(:decrypt, widget.captcha_challenge)
|
@@ -163,6 +196,15 @@ class ModelValidationTest < ValidatesCaptcha::TestCase
|
|
163
196
|
end
|
164
197
|
end
|
165
198
|
|
199
|
+
with_static_image_provider do |provider|
|
200
|
+
Widget.with_captcha_validation do
|
201
|
+
widget = Widget.new
|
202
|
+
widget.captcha_solution = 'hello'
|
203
|
+
|
204
|
+
assert widget.valid?
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
166
208
|
with_question_provider do |provider|
|
167
209
|
Widget.with_captcha_validation do
|
168
210
|
widget = Widget.new
|
@@ -174,7 +216,7 @@ class ModelValidationTest < ValidatesCaptcha::TestCase
|
|
174
216
|
end
|
175
217
|
|
176
218
|
test "with #with_captcha_validation block, calling valid? before and after the block should return true if valid? returned false within block" do
|
177
|
-
|
219
|
+
with_dynamic_image_provider do |provider|
|
178
220
|
widget = Widget.new
|
179
221
|
widget.captcha_solution = provider.send(:decrypt, widget.captcha_challenge).reverse
|
180
222
|
|
@@ -187,6 +229,19 @@ class ModelValidationTest < ValidatesCaptcha::TestCase
|
|
187
229
|
assert widget.valid?
|
188
230
|
end
|
189
231
|
|
232
|
+
with_static_image_provider do |provider|
|
233
|
+
widget = Widget.new
|
234
|
+
widget.captcha_solution = '---'
|
235
|
+
|
236
|
+
assert widget.valid?
|
237
|
+
|
238
|
+
Widget.with_captcha_validation do
|
239
|
+
assert !widget.valid?
|
240
|
+
end
|
241
|
+
|
242
|
+
assert widget.valid?
|
243
|
+
end
|
244
|
+
|
190
245
|
with_question_provider do |provider|
|
191
246
|
widget = Widget.new
|
192
247
|
widget.captcha_solution = '---'
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
STATIC_IMAGE = ValidatesCaptcha::Provider::StaticImage
|
4
|
+
|
5
|
+
unless defined?(::Rails)
|
6
|
+
module Rails
|
7
|
+
def self.public_path
|
8
|
+
'public'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class StaticImageTest < ValidatesCaptcha::TestCase
|
14
|
+
test "defines a class level #string_generator method" do
|
15
|
+
assert_respond_to STATIC_IMAGE, :string_generator
|
16
|
+
end
|
17
|
+
|
18
|
+
test "defines a class level #string_generator= method" do
|
19
|
+
assert_respond_to STATIC_IMAGE, :string_generator=
|
20
|
+
end
|
21
|
+
|
22
|
+
test "#string_generator method's return value should equal the value set using the #string_generator= method" do
|
23
|
+
old_string_generator = STATIC_IMAGE.string_generator
|
24
|
+
|
25
|
+
STATIC_IMAGE.string_generator = 'abc'
|
26
|
+
assert_equal 'abc', STATIC_IMAGE.string_generator
|
27
|
+
|
28
|
+
STATIC_IMAGE.string_generator = old_string_generator
|
29
|
+
end
|
30
|
+
|
31
|
+
test "defines a class level #image_generator method" do
|
32
|
+
assert_respond_to STATIC_IMAGE, :image_generator
|
33
|
+
end
|
34
|
+
|
35
|
+
test "defines a class level #image_generator= method" do
|
36
|
+
assert_respond_to STATIC_IMAGE, :image_generator=
|
37
|
+
end
|
38
|
+
|
39
|
+
test "#image_generator method's return value should equal the value set using the #image_generator= method" do
|
40
|
+
old_image_generator = STATIC_IMAGE.image_generator
|
41
|
+
|
42
|
+
STATIC_IMAGE.image_generator = 'abc'
|
43
|
+
assert_equal 'abc', STATIC_IMAGE.image_generator
|
44
|
+
|
45
|
+
STATIC_IMAGE.image_generator = old_image_generator
|
46
|
+
end
|
47
|
+
|
48
|
+
test "defines a class level #salt method" do
|
49
|
+
assert_respond_to STATIC_IMAGE, :salt
|
50
|
+
end
|
51
|
+
|
52
|
+
test "defines a class level #salt= method" do
|
53
|
+
assert_respond_to STATIC_IMAGE, :salt=
|
54
|
+
end
|
55
|
+
|
56
|
+
test "#salt method's return value should equal the value set using the #salt= method" do
|
57
|
+
old_salt = STATIC_IMAGE.salt
|
58
|
+
|
59
|
+
STATIC_IMAGE.salt = 'abc'
|
60
|
+
assert_equal 'abc', STATIC_IMAGE.salt
|
61
|
+
|
62
|
+
STATIC_IMAGE.salt = old_salt
|
63
|
+
end
|
64
|
+
|
65
|
+
test "defines a class level #filesystem_dir method" do
|
66
|
+
assert_respond_to STATIC_IMAGE, :filesystem_dir
|
67
|
+
end
|
68
|
+
|
69
|
+
test "defines a class level #filesystem_dir= method" do
|
70
|
+
assert_respond_to STATIC_IMAGE, :filesystem_dir=
|
71
|
+
end
|
72
|
+
|
73
|
+
test "#filesystem_dir method's return value should equal the value set using the #filesystem_dir= method" do
|
74
|
+
old_filesystem_dir = STATIC_IMAGE.filesystem_dir
|
75
|
+
|
76
|
+
STATIC_IMAGE.filesystem_dir = 'abc'
|
77
|
+
assert_equal 'abc', STATIC_IMAGE.filesystem_dir
|
78
|
+
|
79
|
+
STATIC_IMAGE.filesystem_dir = old_filesystem_dir
|
80
|
+
end
|
81
|
+
|
82
|
+
test "defines a class level #web_dir method" do
|
83
|
+
assert_respond_to STATIC_IMAGE, :web_dir
|
84
|
+
end
|
85
|
+
|
86
|
+
test "defines a class level #web_dir= method" do
|
87
|
+
assert_respond_to STATIC_IMAGE, :web_dir=
|
88
|
+
end
|
89
|
+
|
90
|
+
test "#web_dir method's return value should equal the value set using the #web_dir= method" do
|
91
|
+
old_web_dir = STATIC_IMAGE.web_dir
|
92
|
+
|
93
|
+
STATIC_IMAGE.web_dir = 'abc'
|
94
|
+
assert_equal 'abc', STATIC_IMAGE.web_dir
|
95
|
+
|
96
|
+
STATIC_IMAGE.web_dir = old_web_dir
|
97
|
+
end
|
98
|
+
|
99
|
+
test "calling #call with unrecognized path should have response status 404" do
|
100
|
+
result = STATIC_IMAGE.new.call 'PATH_INFO' => '/unrecognized'
|
101
|
+
|
102
|
+
assert_equal 404, result.first
|
103
|
+
end
|
104
|
+
|
105
|
+
test "calling #call with regenerate path should have response status 200" do
|
106
|
+
si = STATIC_IMAGE.new
|
107
|
+
si.instance_variable_set "@challenges", ['abc']
|
108
|
+
|
109
|
+
result = si.call 'PATH_INFO' => si.send(:regenerate_path)
|
110
|
+
|
111
|
+
assert_equal 200, result.first
|
112
|
+
end
|
113
|
+
|
114
|
+
test "calling #call with regenerate path should have content type response header set to application/json" do
|
115
|
+
si = STATIC_IMAGE.new
|
116
|
+
si.instance_variable_set "@challenges", ['abc']
|
117
|
+
|
118
|
+
result = si.call 'PATH_INFO' => si.send(:regenerate_path)
|
119
|
+
|
120
|
+
assert result.second.key?('Content-Type')
|
121
|
+
assert_equal 'application/json', result.second['Content-Type']
|
122
|
+
end
|
123
|
+
|
124
|
+
test "calling #generate_challenge should raise runtime error if no images are available" do
|
125
|
+
si = STATIC_IMAGE.new
|
126
|
+
si.instance_variable_set "@images", []
|
127
|
+
|
128
|
+
assert_raises RuntimeError do
|
129
|
+
si.generate_challenge
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
test "calling #generate_challenge should not raise runtime error if an images is available" do
|
134
|
+
si = STATIC_IMAGE.new
|
135
|
+
si.instance_variable_set "@images", ['/path/to/an/image.gif']
|
136
|
+
|
137
|
+
assert_nothing_raised do
|
138
|
+
si.generate_challenge
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
test "calling #generate_challenge should return the image file basename without extension" do
|
143
|
+
si = STATIC_IMAGE.new
|
144
|
+
si.instance_variable_set "@images", ["/path/to/an/captcha-image#{si.send(:image_file_extension)}"]
|
145
|
+
|
146
|
+
assert_equal 'captcha-image', si.generate_challenge
|
147
|
+
end
|
148
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: validates_captcha
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Martin Andert
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-10-07 00:00:00 +02:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -55,16 +55,19 @@ files:
|
|
55
55
|
- lib/validates_captcha/model_validation.rb
|
56
56
|
- lib/validates_captcha/provider/image.rb
|
57
57
|
- lib/validates_captcha/provider/question.rb
|
58
|
+
- lib/validates_captcha/provider/static_image.rb
|
58
59
|
- lib/validates_captcha/reversible_encrypter/simple.rb
|
59
60
|
- lib/validates_captcha/string_generator/simple.rb
|
60
61
|
- lib/validates_captcha/test_case.rb
|
61
62
|
- lib/validates_captcha/version.rb
|
62
63
|
- rails/init.rb
|
64
|
+
- tasks/static_image_tasks.rake
|
63
65
|
- test/cases/controller_validation_test.rb
|
64
66
|
- test/cases/image_generator/simple_test.rb
|
65
67
|
- test/cases/model_validation_test.rb
|
66
68
|
- test/cases/provider/image_test.rb
|
67
69
|
- test/cases/provider/question_test.rb
|
70
|
+
- test/cases/provider/static_image_test.rb
|
68
71
|
- test/cases/reversible_encrypter/simple_test.rb
|
69
72
|
- test/cases/string_generator/simple_test.rb
|
70
73
|
- test/cases/validates_captcha_test.rb
|
@@ -76,7 +79,7 @@ licenses: []
|
|
76
79
|
post_install_message:
|
77
80
|
rdoc_options:
|
78
81
|
- --title
|
79
|
-
- Validates Captcha 0.9.
|
82
|
+
- Validates Captcha 0.9.4
|
80
83
|
- --main
|
81
84
|
- README.rdoc
|
82
85
|
- --line-numbers
|
@@ -110,6 +113,7 @@ test_files:
|
|
110
113
|
- test/cases/model_validation_test.rb
|
111
114
|
- test/cases/provider/image_test.rb
|
112
115
|
- test/cases/provider/question_test.rb
|
116
|
+
- test/cases/provider/static_image_test.rb
|
113
117
|
- test/cases/reversible_encrypter/simple_test.rb
|
114
118
|
- test/cases/string_generator/simple_test.rb
|
115
119
|
- test/cases/validates_captcha_test.rb
|