Class: ValidatesCaptcha::Provider::StaticImage

Inherits:
Object
  • Object
show all
Includes:
ActionView::Helpers
Defined in:
lib/validates_captcha/provider/static_image.rb

Overview

An image captcha provider that relies on pre-created captcha images.

There is a Rake tast for creating the captcha images:

rake validates_captcha:create_static_images

This will create 3 images in #filesystem_dir. To create a different number of images, provide a COUNT argument:

rake validates_captcha:create_static_images COUNT=50

This class contains the getters and setters for the backend classes: image generator and string generator. This allows you to replace them with your custom implementations. For more information on how to bring the image provider to use your own implementation instead of the default one, consult the documentation for the specific default class.

The default captcha image generator uses ImageMagick’s convert command to create the captcha. So a recent and properly configured version of ImageMagick must be installed on the system. The version used while developing was 6.4.5. But you are not bound to ImageMagick. If you want to provide a custom image generator, take a look at the documentation for ValidatesCaptcha::ImageGenerator::Simple on how to create your own.

Constant Summary collapse

SALT =
"3f(61&831_fa0712d4a?b58-eb4b8$a2%.36378f".freeze
@@string_generator =
nil
@@image_generator =
nil
@@filesystem_dir =
nil
@@web_dir =
nil
@@salt =
nil

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.create_imageObject

Creates a captcha image in the #filesystem_dir and returns the path to it and the code displayed on the image.



107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/validates_captcha/provider/static_image.rb', line 107

def create_image
  code = string_generator.generate
  encrypted_code = encrypt(code)

  image_filename = "#{encrypted_code}#{image_generator.file_extension}"
  image_path = File.join(filesystem_dir, image_filename)
  image_bytes = image_generator.generate(code)

  File.open image_path, 'w' do |os|
    os.write image_bytes
  end

  return image_path, code
end

.encrypt(code) ⇒ Object

Return the encryption of the code using #salt.



101
102
103
# File 'lib/validates_captcha/provider/static_image.rb', line 101

def encrypt(code)
  ::Digest::SHA1.hexdigest "#{salt}--#{code}"
end

.filesystem_dirObject

Returns the current captcha image file system directory. Defaults to RAILS_ROOT/public/images/captchas.



67
68
69
# File 'lib/validates_captcha/provider/static_image.rb', line 67

def filesystem_dir
  @@filesystem_dir ||= ::File.join(::Rails.public_path, 'images', 'captchas')
end

.filesystem_dir=(dir) ⇒ Object

Sets the current captcha image file system directory. Used to set a custom image directory.



73
74
75
# File 'lib/validates_captcha/provider/static_image.rb', line 73

def filesystem_dir=(dir)
  @@filesystem_dir = dir
end

.image_generatorObject

Returns the current captcha image generator. Defaults to an instance of the ValidatesCaptcha::ImageGenerator::Simple class.



55
56
57
# File 'lib/validates_captcha/provider/static_image.rb', line 55

def image_generator
  @@image_generator ||= ValidatesCaptcha::ImageGenerator::Simple.new
end

.image_generator=(generator) ⇒ Object

Sets the current captcha image generator. Used to set a custom image generator.



61
62
63
# File 'lib/validates_captcha/provider/static_image.rb', line 61

def image_generator=(generator)
  @@image_generator = generator
end

.saltObject

Returns the current salt used for encryption.



90
91
92
# File 'lib/validates_captcha/provider/static_image.rb', line 90

def salt
  @@salt ||= SALT
end

.salt=(salt) ⇒ Object

Sets the current salt used for encryption. Used to set a custom salt.



96
97
98
# File 'lib/validates_captcha/provider/static_image.rb', line 96

def salt=(salt)
  @@salt = salt
end

.string_generatorObject

Returns the current captcha string generator. Defaults to an instance of the ValidatesCaptcha::StringGenerator::Simple class.



43
44
45
# File 'lib/validates_captcha/provider/static_image.rb', line 43

def string_generator
  @@string_generator ||= ValidatesCaptcha::StringGenerator::Simple.new
end

.string_generator=(generator) ⇒ Object

Sets the current captcha string generator. Used to set a custom string generator.



49
50
51
# File 'lib/validates_captcha/provider/static_image.rb', line 49

def string_generator=(generator)
  @@string_generator = generator
end

.web_dirObject

Returns the current captcha image web directory. Defaults to /images/captchas.



79
80
81
# File 'lib/validates_captcha/provider/static_image.rb', line 79

def web_dir
  @@web_dir ||= '/images/captchas'
end

.web_dir=(dir) ⇒ Object

Sets the current captcha image web directory. Used to set a custom image directory.



85
86
87
# File 'lib/validates_captcha/provider/static_image.rb', line 85

def web_dir=(dir)
  @@web_dir = dir
end

Instance Method Details

#call(env) ⇒ Object

This method is the one called by Rack.

It returns HTTP status 404 if the path is not recognized. If the path is recognized, it returns HTTP status 200 and delivers a new challenge in JSON format.

Please take a look at the source code if you want to learn more.



130
131
132
133
134
135
136
137
138
139
# File 'lib/validates_captcha/provider/static_image.rb', line 130

def call(env)
  if env['PATH_INFO'] == regenerate_path
    captcha_challenge = generate_challenge
    json = { :captcha_challenge => captcha_challenge, :captcha_image_path => image_path(captcha_challenge) }.to_json

    [200, { 'Content-Type' => 'application/json' }, [json]]
  else
    [404, { 'Content-Type' => 'text/html' }, ['Not Found']]
  end
end

#challengesObject

Returns an array containing the available challenges (encrypted captcha codes).



147
148
149
# File 'lib/validates_captcha/provider/static_image.rb', line 147

def challenges
  @challenges ||= images.map { |path| File.basename(path, image_file_extension) }
end

#generate_challengeObject

Returns a captcha challenge.



152
153
154
155
156
# File 'lib/validates_captcha/provider/static_image.rb', line 152

def generate_challenge
  raise("no captcha images found in #{filesystem_dir}") if challenges.empty?

  challenges[rand(challenges.size)]
end

#imagesObject

Returns an array containing the paths to the available captcha images.



142
143
144
# File 'lib/validates_captcha/provider/static_image.rb', line 142

def images
  @images ||= Dir[File.join(filesystem_dir, "*#{image_file_extension}")]
end

#render_challenge(sanitized_object_name, object, options = {}) ⇒ Object

Returns an image tag with the source set to the url of the captcha image.

Internally calls Rails’ image_tag helper method, passing the options argument.



167
168
169
170
171
172
173
174
# File 'lib/validates_captcha/provider/static_image.rb', line 167

def render_challenge(sanitized_object_name, object, options = {})
  src = image_path(object.captcha_challenge)

  options[:alt] ||= 'CAPTCHA'
  options[:id] = "#{sanitized_object_name}_captcha_image"

  image_tag src, options
end

Returns an anchor tag that makes an AJAX request to fetch a new captcha code and updates the captcha image after the request is complete.

Internally calls Rails’ link_to_remote helper method, passing the options and html_options arguments. So it relies on the Prototype javascript framework to be available on the web page. As of version 0.9.8 you can pass :jquery => true as an option to render Javascript that’s based on jQuery.

The anchor text defaults to ‘Regenerate Captcha’. You can set this to a custom value providing a :text key in the options hash.



186
187
188
189
190
191
192
193
194
195
# File 'lib/validates_captcha/provider/static_image.rb', line 186

def render_regenerate_challenge_link(sanitized_object_name, object, options = {}, html_options = {})
  text = options.delete(:text) || 'Regenerate Captcha'
  if options.delete(:jquery)
    onclick = "jQuery.getJSON('#{regenerate_path}', function(result){jQuery('##{sanitized_object_name}_captcha_image').attr('src',result.captcha_image_path); jQuery('##{sanitized_object_name}_captcha_challenge').val(result.captcha_challenge); jQuery('##{sanitized_object_name}_captcha_solution').val('');});return false;"
    link_to text, "#", options.reverse_merge(:onclick => onclick), html_options
  else
    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 = '';"
    link_to_remote text, options.reverse_merge(:url => regenerate_path, :method => :get, :success => success), html_options
  end
end

#solved?(challenge, solution) ⇒ Boolean

Returns true if the captcha was solved using the given challenge and solution, otherwise false.

Returns:

  • (Boolean)


160
161
162
# File 'lib/validates_captcha/provider/static_image.rb', line 160

def solved?(challenge, solution)
  challenge == encrypt(solution)
end