validates_captcha 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|