validates_captcha 0.9.3 → 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc CHANGED
@@ -1,3 +1,8 @@
1
+ == 0.9.4 (October 7, 2009)
2
+
3
+ * Include new StaticImage provider
4
+
5
+
1
6
  == 0.9.3 (September 29, 2009)
2
7
 
3
8
  * Change API, include new Question provider, make it the default one
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
- Validates Captcha uses question/answer captchas by default. But you can
11
- also use the built-in image captcha provider -- or implement your own.
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 the form of your view with the necessary captcha display
22
- and input logic.
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 challange captchas (default provider)
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
- == Image challenge captchas
175
+ == Dynamic image challenge captchas
174
176
 
175
- You can set the captcha provider to use image challenges with
176
- the code below. It is best to put this in a Rails initializer.
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
+
@@ -2,7 +2,7 @@ module ValidatesCaptcha #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 9
5
- TINY = 3
5
+ TINY = 4
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -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 with_image_provider(&block)
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
- with_image_provider do |provider|
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
- with_image_provider do |provider|
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
- with_image_provider do |provider|
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
- with_image_provider do |provider|
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 with_image_provider(&block)
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
- with_image_provider do |provider|
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
- with_image_provider do |provider|
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
- with_image_provider do |provider|
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
- with_image_provider do |provider|
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
- with_image_provider do |provider|
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.3
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-09-29 00:00:00 +02:00
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.3
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