validates_captcha 0.9.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/CHANGELOG.rdoc +3 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +243 -0
- data/Rakefile +105 -0
- data/lib/validates_captcha/controller_validation.rb +63 -0
- data/lib/validates_captcha/form_builder.rb +16 -0
- data/lib/validates_captcha/form_helper.rb +51 -0
- data/lib/validates_captcha/image_generator/simple.rb +87 -0
- data/lib/validates_captcha/middleware/simple.rb +108 -0
- data/lib/validates_captcha/model_validation.rb +66 -0
- data/lib/validates_captcha/reversible_encrypter/simple.rb +54 -0
- data/lib/validates_captcha/string_generator/simple.rb +77 -0
- data/lib/validates_captcha/test_case.rb +12 -0
- data/lib/validates_captcha/version.rb +9 -0
- data/lib/validates_captcha.rb +170 -0
- data/rails/init.rb +29 -0
- data/test/cases/controller_validation_test.rb +125 -0
- data/test/cases/image_generator_test.rb +33 -0
- data/test/cases/middleware_test.rb +71 -0
- data/test/cases/model_validation_test.rb +130 -0
- data/test/cases/reversible_encrypter_test.rb +27 -0
- data/test/cases/string_generator_test.rb +114 -0
- data/test/cases/validates_captcha_test.rb +135 -0
- data/test/test_helper.rb +26 -0
- metadata +113 -0
data/CHANGELOG.rdoc
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2009 Martin Andert
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
data/README.rdoc
ADDED
@@ -0,0 +1,243 @@
|
|
1
|
+
= Validates Captcha
|
2
|
+
|
3
|
+
An image captcha verification approach for Rails apps, directly integrated into
|
4
|
+
ActiveRecord's validation mechanism and providing helpers for ActionController
|
5
|
+
and ActionView.
|
6
|
+
|
7
|
+
RDoc documentation (including this README as start page) can be found at
|
8
|
+
http://m4n.github.com/validates_captcha
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
== Basic Usage
|
13
|
+
|
14
|
+
Validates Captcha extends ActiveRecord, ActionController and ActionView with
|
15
|
+
helper methods that make it a snap to integrate captcha verification in your
|
16
|
+
Rails application.
|
17
|
+
|
18
|
+
Step #1: Extend the form of your view with the necessary captcha code display
|
19
|
+
and input logic.
|
20
|
+
|
21
|
+
# app/views/comments/new.html.erb
|
22
|
+
<% form_for @comment do |f| %>
|
23
|
+
<%= f.error_messages %>
|
24
|
+
|
25
|
+
<!-- standard input fields: -->
|
26
|
+
<p>
|
27
|
+
<%= f.label :name %><br />
|
28
|
+
<%= f.text_field :name %>
|
29
|
+
</p>
|
30
|
+
<!-- ... -->
|
31
|
+
|
32
|
+
<!-- now something new: -->
|
33
|
+
<p>
|
34
|
+
<%= f.label :captcha %><br />
|
35
|
+
<%= f.captcha_image %>
|
36
|
+
<%= f.captcha_field %>
|
37
|
+
</p>
|
38
|
+
|
39
|
+
<p>
|
40
|
+
<%= f.submit 'Create' %>
|
41
|
+
</p>
|
42
|
+
<% end %>
|
43
|
+
|
44
|
+
Step #2: Tell the controller that you want to validate
|
45
|
+
captchas.
|
46
|
+
|
47
|
+
class CommentsController < ApplicationController
|
48
|
+
validates_captcha
|
49
|
+
|
50
|
+
def create
|
51
|
+
# scaffold comment creation code ...
|
52
|
+
end
|
53
|
+
|
54
|
+
# more actions here ...
|
55
|
+
end
|
56
|
+
|
57
|
+
This activates captcha validation in every action of the controller
|
58
|
+
whenever an instance of class +Comment+ is saved.
|
59
|
+
|
60
|
+
Step #3: There's no step three!
|
61
|
+
|
62
|
+
To summarize: Put the following in your view.
|
63
|
+
|
64
|
+
<%= f.captcha_image %>
|
65
|
+
<%= f.captcha_field %>
|
66
|
+
|
67
|
+
And what you see below in the corresponding controller.
|
68
|
+
|
69
|
+
validates_captcha
|
70
|
+
|
71
|
+
Done.
|
72
|
+
|
73
|
+
== Customization
|
74
|
+
|
75
|
+
Because the +validates_captcha+ controller method internally creates an
|
76
|
+
around filter, you can (de)activate captcha validation for specific actions.
|
77
|
+
|
78
|
+
validates_captcha :only => [:create, :update]
|
79
|
+
validates_captcha :except => :reset
|
80
|
+
|
81
|
+
The class for which captcha validation is activated is derived from
|
82
|
+
the name of the controller. So putting +validates_captcha+ in a
|
83
|
+
+UsersController+ validates instances of the +User+ class.
|
84
|
+
|
85
|
+
You can customize the validated class using the +validates_captcha_of+ method.
|
86
|
+
|
87
|
+
class ArticlesController < ApplicationController
|
88
|
+
validates_captcha_of Post
|
89
|
+
validates_captcha_of :blog_entries, :except => :persist
|
90
|
+
validates_captcha_of 'users', :only => :store
|
91
|
+
end
|
92
|
+
|
93
|
+
Two kinds of errors are added to the model if captcha validation fails:
|
94
|
+
+:blank+ if no captcha code is submitted and +:invalid+ if a captcha code
|
95
|
+
is submitted but does not match the code displayed on the captcha image.
|
96
|
+
You can localize the error messages for the captcha as you usually do
|
97
|
+
for the other attributes.
|
98
|
+
|
99
|
+
models:
|
100
|
+
comment:
|
101
|
+
attributes:
|
102
|
+
captcha:
|
103
|
+
blank: 'must not be empty'
|
104
|
+
invalid: 'does not match the code displayed on the image'
|
105
|
+
|
106
|
+
What if the captcha's text is unreadable? There's also a form helper
|
107
|
+
method for captcha regeneration available. You can call it like this.
|
108
|
+
|
109
|
+
<p>
|
110
|
+
Captcha code unreadable? <%= f.regenerate_captcha_link %>
|
111
|
+
</p>
|
112
|
+
|
113
|
+
This generates an anchor tag that, when clicked, generates a new
|
114
|
+
captcha and updates the image. It makes an AJAX request to fetch a
|
115
|
+
new captcha code and updates the captcha image after the request is complete.
|
116
|
+
|
117
|
+
+regenerate_captcha_link+ internally calls Rails' #link_to_remote helper
|
118
|
+
method. So it relies on the Prototype javascript framework to be available
|
119
|
+
on the page.
|
120
|
+
|
121
|
+
The anchor's text defaults to 'Regenerate Captcha'. You can set this to
|
122
|
+
a custom value by providing a +:text+ key in the options hash.
|
123
|
+
|
124
|
+
<%= f.regenerate_captcha_link :text => 'Another captcha, please' %>
|
125
|
+
|
126
|
+
By default, captchas have a length of 6 characters and the text displayed
|
127
|
+
on the captcha image is created by randomly selecting characters from a
|
128
|
+
predefined alphabet constisting of visually distinguishable letters and digits.
|
129
|
+
|
130
|
+
The number of characters and the alphabet used when generating strings can
|
131
|
+
be customized. Just put the following in a Rails initializer and adjust the
|
132
|
+
values to your needs.
|
133
|
+
|
134
|
+
ValidatesCaptcha::StringGenerator::Simple.alphabet = '01'
|
135
|
+
ValidatesCaptcha::StringGenerator::Simple.length = 8
|
136
|
+
|
137
|
+
Apart from controllers, you can activate captcha validation for a model
|
138
|
+
using the class level +with_captcha_validation+ method added to
|
139
|
+
ActiveRecord::Base.
|
140
|
+
|
141
|
+
Comment.with_captcha_validation do
|
142
|
+
@comment = Comment.new(...)
|
143
|
+
@comment.save
|
144
|
+
end
|
145
|
+
|
146
|
+
This activates captcha validation on entering the block and deactivates it
|
147
|
+
on leaving the block.
|
148
|
+
|
149
|
+
Two new attribute like methods are added to ActiveRecord: +captcha+ and
|
150
|
+
+encrypted_captcha+. Those are made +attr_accessible+. The latter is
|
151
|
+
initialized to a randomly generated and encrypted captcha code on
|
152
|
+
instantation.
|
153
|
+
|
154
|
+
For a record to be valid, the value assigned to +captcha=+ must match the
|
155
|
+
decryption of the return value of +encrypted_captcha+. Within a
|
156
|
+
+with_captcha_validation+ block, calling +valid?+ (as is done by +save+,
|
157
|
+
+update_attributes+, etc.) will also validate the value of +captcha+
|
158
|
+
against the +encrypted_captcha+. Outside +with_captcha_validation+, no
|
159
|
+
captcha validation is performed.
|
160
|
+
|
161
|
+
|
162
|
+
|
163
|
+
== Extensibility
|
164
|
+
|
165
|
+
Validates Captcha delegates tasks like string and image generation,
|
166
|
+
encryption/decryption of captcha codes, and responding to captcha requests
|
167
|
+
to dedicated backend classes.
|
168
|
+
|
169
|
+
Those classes can easily be replaced by your custom implementations. So
|
170
|
+
you can achieve stronger encryption, can use a word list as captcha text
|
171
|
+
generation source, or can replace the captcha image generator with one
|
172
|
+
that creates images that are harder to crack.
|
173
|
+
|
174
|
+
Please see the documentation of the following classes for further information.
|
175
|
+
|
176
|
+
* ValidatesCaptcha::StringGenerator::Simple
|
177
|
+
* ValidatesCaptcha::ReversibleEncrypter::Simple
|
178
|
+
* ValidatesCaptcha::ImageGenerator::Simple
|
179
|
+
* ValidatesCaptcha::Middleware::Simple
|
180
|
+
|
181
|
+
|
182
|
+
|
183
|
+
== Dependencies
|
184
|
+
|
185
|
+
Using a Rack middleware to speed up the request/response cycle when fetching
|
186
|
+
captcha images, Validates Captcha requires Rails version 2.3 or greater.
|
187
|
+
|
188
|
+
The default captcha image generator uses ImageMagick's +convert+ command to
|
189
|
+
create the captcha. So a recent and properly configured version of ImageMagick
|
190
|
+
must be installed on the system. The version used while developing was 6.4.5.
|
191
|
+
But you are not bound to ImageMagick. If you want to provide a custom image
|
192
|
+
generator, take a look at the documentation for
|
193
|
+
ValidatesCaptcha::ImageGenerator::Simple on how to create your own.
|
194
|
+
|
195
|
+
|
196
|
+
|
197
|
+
== Download
|
198
|
+
|
199
|
+
The latest version of Validates Captcha can be found at
|
200
|
+
http://github.com/m4n/validates_captcha
|
201
|
+
|
202
|
+
Documentation can be generated from its distribution directory with the
|
203
|
+
following command.
|
204
|
+
|
205
|
+
% [sudo] rake rdoc
|
206
|
+
|
207
|
+
Tests can be executed from its distribution directory with the
|
208
|
+
following command.
|
209
|
+
|
210
|
+
% [sudo] rake test
|
211
|
+
|
212
|
+
|
213
|
+
|
214
|
+
== Installation
|
215
|
+
|
216
|
+
You can install Validates Captcha as a Rails plugin with the following command.
|
217
|
+
|
218
|
+
% ./script/plugin install git://github.com/m4n/validates_captcha.git
|
219
|
+
|
220
|
+
Or you can install it as a Gem with
|
221
|
+
|
222
|
+
% [sudo] gem install m4n-validates_captcha --source http://gems.github.com
|
223
|
+
|
224
|
+
and then configure it in your +environment.rb+ file as shown below.
|
225
|
+
|
226
|
+
Rails::Initializer.run do |config|
|
227
|
+
# ...
|
228
|
+
config.gem 'm4n-validates_captcha', :lib => 'validates_captcha'
|
229
|
+
# ...
|
230
|
+
end
|
231
|
+
|
232
|
+
|
233
|
+
|
234
|
+
== License
|
235
|
+
|
236
|
+
Validates Captcha is released under the MIT license.
|
237
|
+
|
238
|
+
|
239
|
+
|
240
|
+
== Copyright
|
241
|
+
|
242
|
+
Copyright (c) 2009 Martin Andert
|
243
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rake/packagetask'
|
5
|
+
require 'rake/gempackagetask'
|
6
|
+
|
7
|
+
require File.join(File.dirname(__FILE__), 'lib', 'validates_captcha', 'version')
|
8
|
+
|
9
|
+
|
10
|
+
|
11
|
+
PKG_NAME = 'validates_captcha'
|
12
|
+
PKG_VERSION = ValidatesCaptcha::VERSION::STRING
|
13
|
+
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
14
|
+
|
15
|
+
RELEASE_NAME = "REL #{PKG_VERSION}"
|
16
|
+
|
17
|
+
RUBY_FORGE_PROJECT = "validatecaptcha"
|
18
|
+
RUBY_FORGE_USER = "m4n"
|
19
|
+
|
20
|
+
PKG_FILES = FileList['[A-Z]*', 'lib/**/*', 'test/**/*', 'rails/*'].exclude(/\bCVS\b|~$/)
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
begin
|
25
|
+
require 'hanna/rdoctask'
|
26
|
+
rescue LoadError
|
27
|
+
require 'rake/rdoctask'
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'Generate documentation'
|
31
|
+
Rake::RDocTask.new do |rdoc|
|
32
|
+
rdoc.rdoc_dir = 'doc'
|
33
|
+
rdoc.title = "Validates Captcha"
|
34
|
+
rdoc.main = "README.rdoc"
|
35
|
+
|
36
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
37
|
+
rdoc.options << '--charset' << 'utf-8'
|
38
|
+
|
39
|
+
rdoc.rdoc_files.include 'README.rdoc'
|
40
|
+
rdoc.rdoc_files.include 'MIT-LICENSE'
|
41
|
+
rdoc.rdoc_files.include 'CHANGELOG.rdoc'
|
42
|
+
rdoc.rdoc_files.include 'lib/**/*.rb'
|
43
|
+
rdoc.rdoc_files.exclude 'lib/validates_captcha/test_case.rb'
|
44
|
+
rdoc.rdoc_files.exclude 'lib/validates_captcha/version.rb'
|
45
|
+
end
|
46
|
+
|
47
|
+
namespace :rdoc do
|
48
|
+
desc 'Show documentation in Firefox'
|
49
|
+
task :show do
|
50
|
+
sh 'firefox doc/index.html'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
desc 'Run tests by default'
|
57
|
+
task :default => :test
|
58
|
+
|
59
|
+
Rake::TestTask.new do |t|
|
60
|
+
t.libs << 'test'
|
61
|
+
t.test_files = FileList['test/**/*_test.rb']
|
62
|
+
#t.verbose = true
|
63
|
+
#t.warning = true
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
|
68
|
+
spec = eval(File.read('validates_captcha.gemspec'))
|
69
|
+
|
70
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
71
|
+
pkg.gem_spec = spec
|
72
|
+
pkg.need_tar = true
|
73
|
+
pkg.need_zip = true
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
desc 'Publish the release files to RubyForge'
|
79
|
+
task :release => [:package] do
|
80
|
+
require 'rubyforge'
|
81
|
+
require 'rake/contrib/rubyforgepublisher'
|
82
|
+
|
83
|
+
packages = %w( gem tgz zip ).collect{ |ext| "pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}" }
|
84
|
+
|
85
|
+
rubyforge = RubyForge.new(YAML.load(File.read(RubyForge::CONFIG_F)), YAML.load(File.read('/home/andert/.rubyforge/auto-config.yml')))
|
86
|
+
rubyforge.login
|
87
|
+
rubyforge.add_release(9020, 12371, "REL #{PKG_VERSION}", *packages)
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
|
92
|
+
desc 'Uninstall local gem'
|
93
|
+
task :uninstall do
|
94
|
+
system "sudo gem uninstall #{PKG_NAME}"
|
95
|
+
end
|
96
|
+
|
97
|
+
desc 'Install local gem'
|
98
|
+
task :install => [:uninstall, :gem] do
|
99
|
+
system "sudo gem install pkg/#{PKG_NAME}-#{PKG_VERSION}.gem --no-ri --no-rdoc"
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
|
104
|
+
Dir['tasks/**/*.rake'].each { |tasks_file| load tasks_file }
|
105
|
+
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module ValidatesCaptcha
|
2
|
+
module ControllerValidation
|
3
|
+
def self.included(base) #:nodoc:
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
# This module extends ActionController::Base with methods for captcha
|
8
|
+
# verification.
|
9
|
+
module ClassMethods
|
10
|
+
# This method is the one Validates Captcha got its name from. It
|
11
|
+
# internally calls #validates_captcha_of with the name of the controller
|
12
|
+
# as first argument and passing the conditions hash.
|
13
|
+
#
|
14
|
+
# Usage Example:
|
15
|
+
#
|
16
|
+
# class UsersController < ApplicationController
|
17
|
+
# # Whenever a User gets saved, validate the captcha.
|
18
|
+
# validates_captcha
|
19
|
+
#
|
20
|
+
# def create
|
21
|
+
# # ... user creation code ...
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# # ... more actions ...
|
25
|
+
# end
|
26
|
+
def validates_captcha(conditions = {})
|
27
|
+
validates_captcha_of controller_name, conditions
|
28
|
+
end
|
29
|
+
|
30
|
+
# Activates captcha validation for the specified model.
|
31
|
+
#
|
32
|
+
# The +model+ argument can be a Class, a string, or a symbol.
|
33
|
+
#
|
34
|
+
# This method internally creates an around filter, passing the
|
35
|
+
# +conditions+ argument to it. So you can (de)activate captcha
|
36
|
+
# validation for specific actions.
|
37
|
+
#
|
38
|
+
# Usage examples:
|
39
|
+
#
|
40
|
+
# class UsersController < ApplicationController
|
41
|
+
# validates_captcha_of User
|
42
|
+
# validates_captcha_of :users, :only => [:create, :update]
|
43
|
+
# validates_captcha_of 'user', :except => :persist
|
44
|
+
#
|
45
|
+
# # ... actions go here ...
|
46
|
+
# end
|
47
|
+
def validates_captcha_of(model, conditions = {})
|
48
|
+
model = model.is_a?(Class) ? model : model.to_s.classify.constantize
|
49
|
+
without_formats = Array.wrap(conditions.delete(:without)).map(&:to_sym)
|
50
|
+
|
51
|
+
around_filter(conditions) do |controller, action|
|
52
|
+
if without_formats.include?(controller.request.format.to_sym)
|
53
|
+
action.call
|
54
|
+
else
|
55
|
+
model.with_captcha_validation do
|
56
|
+
action.call
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ValidatesCaptcha
|
2
|
+
module FormBuilder #:nodoc:
|
3
|
+
def captcha_image(options = {}) #:nodoc:
|
4
|
+
@template.captcha_image @object_name, options.merge(:object => @object)
|
5
|
+
end
|
6
|
+
|
7
|
+
def captcha_field(options = {}) #:nodoc:
|
8
|
+
@template.captcha_field @object_name, options.merge(:object => @object)
|
9
|
+
end
|
10
|
+
|
11
|
+
def regenerate_captcha_link(options = {}, html_options = {}) #:nodoc:
|
12
|
+
@template.regenerate_captcha_link @object_name, options.merge(:object => @object), html_options
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module ValidatesCaptcha
|
2
|
+
module FormHelper
|
3
|
+
# Returns an img tag with the src attribute pointing to the captcha image url.
|
4
|
+
#
|
5
|
+
# Internally calls Rails' #image_tag helper method, passing the +options+
|
6
|
+
# argument.
|
7
|
+
def captcha_image(object_name, options = {})
|
8
|
+
object = options.delete(:object)
|
9
|
+
src = ValidatesCaptcha.captcha_image_path(object.encrypted_captcha)
|
10
|
+
sanitized_object_name = object_name.to_s.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
|
11
|
+
|
12
|
+
options[:alt] ||= 'CAPTCHA'
|
13
|
+
options[:id] = "#{sanitized_object_name}_captcha_image"
|
14
|
+
|
15
|
+
image_tag src, options
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns an input tag of the "text" type tailored for entering the captcha code.
|
19
|
+
#
|
20
|
+
# Internally calls Rails' #text_field helper method, passing the +object_name+ and
|
21
|
+
# +options+ arguments.
|
22
|
+
def captcha_field(object_name, options = {})
|
23
|
+
options.delete(:id)
|
24
|
+
|
25
|
+
hidden_field(object_name, :encrypted_captcha, options) + text_field(object_name, :captcha, options)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns an anchor tag that makes an AJAX request to fetch a new captcha code and updates
|
29
|
+
# the captcha image after the request is complete.
|
30
|
+
#
|
31
|
+
# Internally calls Rails' #link_to_remote helper method, passing the +options+ and
|
32
|
+
# +html_options+ arguments. So it relies on the Prototype javascript framework
|
33
|
+
# to be available on the web page.
|
34
|
+
#
|
35
|
+
# The anchor text defaults to 'Regenerate Captcha'. You can set this to a custom value
|
36
|
+
# providing a +:text+ key in the +options+ hash.
|
37
|
+
def regenerate_captcha_link(object_name, options = {}, html_options = {})
|
38
|
+
options.symbolize_keys!
|
39
|
+
|
40
|
+
object = options.delete(:object)
|
41
|
+
text = options.delete(:text) || 'Regenerate Captcha'
|
42
|
+
sanitized_object_name = object_name.to_s.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
|
43
|
+
|
44
|
+
url = ValidatesCaptcha.regenerate_captcha_path
|
45
|
+
success = "var result = request.responseJSON; $('#{sanitized_object_name}_captcha_image').src = result.captcha_image_path; $('#{sanitized_object_name}_encrypted_captcha').value = result.encrypted_captcha_code;"
|
46
|
+
|
47
|
+
link_to_remote text, options.reverse_merge(:url => url, :method => :get, :success => success), html_options
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module ValidatesCaptcha
|
2
|
+
module ImageGenerator
|
3
|
+
# This class is responsible for creating the captcha image. It internally
|
4
|
+
# uses ImageMagick's +convert+ command to generate the image bytes. So
|
5
|
+
# ImageMagick must be installed on the system for it to work properly.
|
6
|
+
#
|
7
|
+
# In order to deliver the captcha image to the user's browser,
|
8
|
+
# Validate Captcha's Rack middleware calls the methods of this class
|
9
|
+
# to create the image, to retrieve its mime type, and to construct the
|
10
|
+
# path to it.
|
11
|
+
#
|
12
|
+
# The image generation process is no rocket science. The chars are just
|
13
|
+
# laid out next to each other with varying vertical positions, font sizes,
|
14
|
+
# and weights. Then a slight rotation is performed and some randomly
|
15
|
+
# positioned lines are rendered on the canvas.
|
16
|
+
#
|
17
|
+
# Sure, the created captcha can easily be cracked by intelligent
|
18
|
+
# bots. As the name of the class suggests, it's rather a starting point
|
19
|
+
# for your own implementations.
|
20
|
+
#
|
21
|
+
# You can implement your own (better) image generator by creating a
|
22
|
+
# class that conforms to the method definitions of the example below and
|
23
|
+
# assign an instance of it to ValidatesCaptcha#image_generator=.
|
24
|
+
#
|
25
|
+
# Example for a custom image generator:
|
26
|
+
#
|
27
|
+
# class AdvancedImageGenerator
|
28
|
+
# def generate(captcha_text)
|
29
|
+
# # ... do your magic here ...
|
30
|
+
#
|
31
|
+
# return string_containing_image_bytes
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# def image_mime_type
|
35
|
+
# 'image/png'
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# def image_file_extension
|
39
|
+
# '.png'
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# ValidatesCaptcha.image_generator = AdvancedImageGenerator.new
|
44
|
+
#
|
45
|
+
class Simple
|
46
|
+
MIME_TYPE = 'image/gif'.freeze
|
47
|
+
FILE_EXTENSION = '.gif'.freeze
|
48
|
+
|
49
|
+
# Returns a string containing the image bytes of the captcha.
|
50
|
+
# As the only argument, the cleartext captcha text must be passed.
|
51
|
+
def generate(captcha_code)
|
52
|
+
image_width = captcha_code.length * 20 + 10
|
53
|
+
|
54
|
+
cmd = []
|
55
|
+
cmd << "convert -size #{image_width}x40 xc:grey84 -background grey84 -fill black "
|
56
|
+
|
57
|
+
captcha_code.split(//).each_with_index do |char, i|
|
58
|
+
cmd << " -pointsize #{rand(8) + 15} "
|
59
|
+
cmd << " -weight #{rand(2) == 0 ? '4' : '8'}00 "
|
60
|
+
cmd << " -draw 'text #{5 + 20 * i},#{rand(10) + 20} \"#{char}\"' "
|
61
|
+
end
|
62
|
+
|
63
|
+
cmd << " -rotate #{rand(2) == 0 ? '-' : ''}5 -fill grey40 "
|
64
|
+
|
65
|
+
captcha_code.size.times do
|
66
|
+
cmd << " -draw 'line #{rand(image_width)},0 #{rand(image_width)},60' "
|
67
|
+
end
|
68
|
+
|
69
|
+
cmd << " gif:-"
|
70
|
+
|
71
|
+
image_magick_command = cmd.join
|
72
|
+
|
73
|
+
`#{image_magick_command}`
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns the image mime type. This is always 'image/gif'.
|
77
|
+
def image_mime_type
|
78
|
+
MIME_TYPE
|
79
|
+
end
|
80
|
+
|
81
|
+
# Returns the image file extension. This is always '.gif'.
|
82
|
+
def image_file_extension
|
83
|
+
FILE_EXTENSION
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|