captcher 0.1.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.
Files changed (89) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +210 -0
  4. data/Rakefile +22 -0
  5. data/app/controllers/captcher/captchas_controller.rb +22 -0
  6. data/app/controllers/concerns/captcher/captcha_aware.rb +30 -0
  7. data/app/helpers/captcher/application_helper.rb +13 -0
  8. data/app/views/layouts/captcher/application.html.erb +16 -0
  9. data/config/routes.rb +10 -0
  10. data/lib/captcher.rb +57 -0
  11. data/lib/captcher/base_captcha.rb +46 -0
  12. data/lib/captcher/captchas/awesome_captcha.rb +7 -0
  13. data/lib/captcher/captchas/code_captcha.rb +40 -0
  14. data/lib/captcher/captchas/math_captcha.rb +7 -0
  15. data/lib/captcher/config.rb +41 -0
  16. data/lib/captcher/engine.rb +5 -0
  17. data/lib/captcher/text_image.rb +48 -0
  18. data/lib/captcher/version.rb +3 -0
  19. data/lib/fonts/Bangers-Regular.ttf +0 -0
  20. data/lib/fonts/CarterOne.ttf +0 -0
  21. data/lib/fonts/FrederickatheGreat-Regular.ttf +0 -0
  22. data/lib/fonts/IndieFlower-Regular.ttf +0 -0
  23. data/lib/fonts/LobsterTwo-BoldItalic.ttf +0 -0
  24. data/lib/fonts/SigmarOne-Regular.ttf +0 -0
  25. data/lib/tasks/captcher_tasks.rake +4 -0
  26. data/spec/dummy/Rakefile +6 -0
  27. data/spec/dummy/app/assets/config/manifest.js +4 -0
  28. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  29. data/spec/dummy/app/assets/javascripts/cable.js +13 -0
  30. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  31. data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
  32. data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
  33. data/spec/dummy/app/controllers/application_controller.rb +2 -0
  34. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  35. data/spec/dummy/app/jobs/application_job.rb +2 -0
  36. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  37. data/spec/dummy/app/models/application_record.rb +3 -0
  38. data/spec/dummy/app/views/layouts/application.html.erb +15 -0
  39. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  40. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  41. data/spec/dummy/bin/bundle +3 -0
  42. data/spec/dummy/bin/rails +4 -0
  43. data/spec/dummy/bin/rake +4 -0
  44. data/spec/dummy/bin/setup +36 -0
  45. data/spec/dummy/bin/update +31 -0
  46. data/spec/dummy/bin/yarn +11 -0
  47. data/spec/dummy/config.ru +5 -0
  48. data/spec/dummy/config/application.rb +30 -0
  49. data/spec/dummy/config/boot.rb +5 -0
  50. data/spec/dummy/config/cable.yml +10 -0
  51. data/spec/dummy/config/database.yml +25 -0
  52. data/spec/dummy/config/environment.rb +5 -0
  53. data/spec/dummy/config/environments/development.rb +61 -0
  54. data/spec/dummy/config/environments/production.rb +94 -0
  55. data/spec/dummy/config/environments/test.rb +46 -0
  56. data/spec/dummy/config/initializers/application_controller_renderer.rb +8 -0
  57. data/spec/dummy/config/initializers/assets.rb +14 -0
  58. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  59. data/spec/dummy/config/initializers/content_security_policy.rb +25 -0
  60. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  61. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  62. data/spec/dummy/config/initializers/inflections.rb +16 -0
  63. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  64. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  65. data/spec/dummy/config/locales/en.yml +33 -0
  66. data/spec/dummy/config/puma.rb +34 -0
  67. data/spec/dummy/config/routes.rb +3 -0
  68. data/spec/dummy/config/spring.rb +6 -0
  69. data/spec/dummy/config/storage.yml +34 -0
  70. data/spec/dummy/db/development.sqlite3 +0 -0
  71. data/spec/dummy/db/test.sqlite3 +0 -0
  72. data/spec/dummy/log/development.log +6608 -0
  73. data/spec/dummy/log/test.log +6492 -0
  74. data/spec/dummy/package.json +5 -0
  75. data/spec/dummy/public/404.html +67 -0
  76. data/spec/dummy/public/422.html +67 -0
  77. data/spec/dummy/public/500.html +66 -0
  78. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  79. data/spec/dummy/public/apple-touch-icon.png +0 -0
  80. data/spec/dummy/public/favicon.ico +0 -0
  81. data/spec/dummy/tmp/development_secret.txt +1 -0
  82. data/spec/helpers.rb +23 -0
  83. data/spec/lib/captcher/config_spec.rb +41 -0
  84. data/spec/models/captchas/code_captcha_spec.rb +45 -0
  85. data/spec/rails_helper.rb +72 -0
  86. data/spec/requests/captcha_management_spec.rb +48 -0
  87. data/spec/spec_helper.rb +96 -0
  88. data/spec/tmp/test.png +0 -0
  89. metadata +291 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 42a4bd27e904e52012256226988e854b5d21cf73a69af147f3407de4cd3e8965
4
+ data.tar.gz: 8effe1d5469292eece64a258f89dd604a2c0389473af70dd1553e80d820ae052
5
+ SHA512:
6
+ metadata.gz: 117d2b310d1ec56e1474f92c5a28ab09340df48e53e6514581f35febfe7e63ad2a6d9c5b767a9224a3209678eb90dd648d686c4c9412bbd93957cf82a0e566fd
7
+ data.tar.gz: c7a699acdf006c866d389a28caf0143db664919a1811fc039c1f79c54502ffc2c00c569e82bdbe741218af43ee338b132a2f5f8a4b393072b432c80a7ba072bc
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2019 Ivan Zinovyev
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.
data/README.md ADDED
@@ -0,0 +1,210 @@
1
+ # Captcher
2
+
3
+ Easy to use classical captcha for Rails apps
4
+
5
+ ![Example of captcha field](docs/captcha_field.png "Captcher captcha")
6
+
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+
14
+ gem 'captcher'
15
+
16
+ ```
17
+
18
+ And then execute:
19
+
20
+ ```bash
21
+
22
+ $ bundle
23
+
24
+ ```
25
+
26
+ Or install it yourself as:
27
+
28
+ ```bash
29
+
30
+ $ gem install captcher
31
+
32
+ ```
33
+
34
+ Mount the engine inside your application by adding this line to your application's routes file at `config/routes.rb`:
35
+
36
+ ```ruby
37
+
38
+ mount Captcher::Engine => "/captcher"
39
+
40
+ ```
41
+
42
+
43
+ ## Usage
44
+
45
+ ### Render on page
46
+
47
+ 1. Include the concern with helper methods to your ApplicationController:
48
+
49
+ ```ruby
50
+
51
+ class ApplicationController < ActionController::Base
52
+ include Captcher::CaptchaAware
53
+ end
54
+
55
+ ```
56
+
57
+ 2. Use helper methods in your controller:
58
+
59
+ ```ruby
60
+
61
+ class MyController < ApplicationController
62
+ def index
63
+ reload_captcha(session) # Reload the captcha
64
+ # render response with success code ...
65
+ end
66
+
67
+ def create
68
+ @comment = Comment.new(comment_params)
69
+ captcha_check = confirm_captcha?(session, params[:captcha])
70
+ if @comment.valid? && captcha_check && @comment.save
71
+ # render response with success code ...
72
+ else
73
+ @comment.errors[:captcha] << "Captcha verification failed" unless captcha_check
74
+ # render response with error code ...
75
+ end
76
+ end
77
+
78
+ # ... some other code
79
+ end
80
+
81
+ ```
82
+
83
+ 3. An example bootstrap-based html/erb code:
84
+
85
+
86
+ ```html
87
+
88
+ <%= simple_form_for(some_form) do |f| %>
89
+ <!-- Some html/erb code for all form fields -->
90
+ <!-- ... -->
91
+ <!-- /Some html/erb code for all form fields -->
92
+
93
+ <div class="input-group">
94
+ <%= text_field_tag :captcha, "",
95
+ type: :text,
96
+ label: false,
97
+ class: "form-control",
98
+ placeholder: "Enter the captcha" %>
99
+
100
+ <div class="input-group-append">
101
+ <div class="input-group-text" style="padding: 0">
102
+ <%= image_tag(captcher.captcha_path(format: :png), style: "height: 35px;",
103
+ id: "captcha-image") %>
104
+ </div>
105
+ <button class="btn btn-outline-secondary" type="button" id="captcha-reload">
106
+ <i class="fa fa-refresh"></i>
107
+ </button>
108
+ </div>
109
+ </div>
110
+
111
+ <% end %>
112
+
113
+ ```
114
+
115
+ 4. Javascript code to refresh the capture:
116
+
117
+
118
+ ```javascript
119
+
120
+ function reloadCaptcha() {
121
+ $.ajax({
122
+ type: 'GET',
123
+ url: '/captcher/captcha/reload.png',
124
+ success: function() {
125
+ var timestamp = (new Date()).getTime();
126
+ $('#captcha-image').attr("src", "/captcher/captcha.png?" + timestamp);
127
+ },
128
+ });
129
+ }
130
+
131
+ $('#captcha-reload').click(function() {
132
+ reloadCaptcha();
133
+ });
134
+
135
+ ```
136
+
137
+
138
+ ### API endpoints
139
+
140
+ These endpoints are available by default (as soon as you've mounted the `Captcher` engine to your `routes.rb` file) and can be used for some async requests:
141
+
142
+ * `http://your-application.com/captcher/captcha` - Load the captcha image
143
+
144
+ * `http://your-application.com/captcher/captcha/reload` - Reload the captcha
145
+
146
+ * `http://your-application.com/captcher/captcha/confirm?confirmation=code` - Confirm captcha code
147
+
148
+
149
+ ## Configuration
150
+
151
+ ```ruby
152
+
153
+ # config/initialiers/captcher.rb
154
+
155
+ Captcher.configure do |c|
156
+ c.mode = :code_captcha
157
+
158
+ c.code_captcha do |cc|
159
+ cc.fonts Dir[Captcher::Engine.root.join("lib/fonts/**")]
160
+ cc.font_size 50
161
+ cc.font_color "black"
162
+ cc.count 5
163
+ cc.background "#999999"
164
+ cc.format "png"
165
+ end
166
+ end
167
+
168
+ ```
169
+
170
+
171
+ ## TODO
172
+
173
+ 1. Implement some other types of captcha
174
+
175
+ 2. Integrate with Travis to test the gem against different versions of Ruby/ROR
176
+
177
+ 3. Improve code style
178
+
179
+ 4. Improve documentation
180
+
181
+
182
+ ## Contributing
183
+
184
+ Contribution directions go here.
185
+
186
+
187
+ ## Fonts
188
+
189
+ The fonts wich are shiped by default with this repo
190
+ are taken from https://github.com/google/fonts
191
+ and use the SIL Open Font License, v1.1
192
+
193
+ **There's a list of the origin paths of the fonts**:
194
+
195
+ * https://github.com/google/fonts/tree/master/ofl/bangers
196
+
197
+ * https://github.com/google/fonts/tree/master/ofl/carterone
198
+
199
+ * https://github.com/google/fonts/tree/master/ofl/frederickathegreat
200
+
201
+ * https://github.com/google/fonts/tree/master/ofl/indieflower
202
+
203
+ * https://github.com/google/fonts/tree/master/ofl/lobstertwo
204
+
205
+ * https://github.com/google/fonts/tree/master/ofl/sigmarone
206
+
207
+
208
+ ## License
209
+
210
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Captcher'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+ load 'rails/tasks/statistics.rake'
21
+
22
+ require 'bundler/gem_tasks'
@@ -0,0 +1,22 @@
1
+ module Captcher
2
+ class CaptchasController < ActionController::Base
3
+ include Captcher::CaptchaAware
4
+
5
+ def show
6
+ render_captcha(load_captcha(session))
7
+ end
8
+
9
+ def reload
10
+ render_captcha(reload_captcha(session))
11
+ end
12
+ alias refresh reload
13
+
14
+ def confirm
15
+ if confirm_captcha?(session, params[:confirmation])
16
+ render json: { success: true }, status: 200
17
+ else
18
+ render json: { success: false }, status: 422
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ module Captcher
2
+ module CaptchaAware
3
+ extend ActiveSupport::Concern
4
+
5
+ def load_captcha(session)
6
+ Captcher.captcha_class.restore_or_create(Captcher.config, session)
7
+ end
8
+
9
+ def reload_captcha(session)
10
+ captcha = Captcher.captcha_class.new(config: Captcher.config)
11
+ captcha.store(session)
12
+ captcha
13
+ end
14
+
15
+ def confirm_captcha(session, confirmation)
16
+ captcha = load_captcha(session)
17
+ captcha.validate(confirmation)
18
+ end
19
+ alias confirm_captcha? confirm_captcha
20
+
21
+ private
22
+
23
+ def render_captcha(captcha)
24
+ format = params[:format] || captcha.own_config[:format]
25
+ filename = "captcha.#{format}"
26
+ type = "image/#{format}"
27
+ send_data captcha.represent, filename: filename, type: type, disposition: :inline
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,13 @@
1
+ module Captcher
2
+ module ApplicationHelper
3
+ def captcha_tag(options)
4
+ options = options.symbolize_keys
5
+
6
+ src = options[:src] = captcha_path
7
+
8
+ options[:alt] = options.fetch(:alt) { image_alt(src) }
9
+ options[:width], options[:height] = extract_dimensions(options.delete(:size)) if options[:size]
10
+ tag("img", options)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Captcher</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= stylesheet_link_tag "captcher/application", media: "all" %>
9
+ <%= javascript_include_tag "captcher/application" %>
10
+ </head>
11
+ <body>
12
+
13
+ <%= yield %>
14
+
15
+ </body>
16
+ </html>
data/config/routes.rb ADDED
@@ -0,0 +1,10 @@
1
+ Captcher::Engine.routes.draw do
2
+ resource :captcha, only: [:show] do
3
+ member do
4
+ %i[reload refresh confirm].each do |route|
5
+ send :post, route
6
+ send :get, route
7
+ end
8
+ end
9
+ end
10
+ end
data/lib/captcher.rb ADDED
@@ -0,0 +1,57 @@
1
+ require "mini_magick"
2
+ require "captcher/engine"
3
+ require "captcher/config"
4
+ require "captcher/text_image"
5
+ require "captcher/base_captcha"
6
+ require "captcher/captchas/awesome_captcha"
7
+ require "captcher/captchas/math_captcha"
8
+ require "captcher/captchas/code_captcha"
9
+ require "pry" if Rails.env.in?(%w[development test])
10
+
11
+ module Captcher
12
+ extend self
13
+
14
+ def configure
15
+ @config = Captcher::Config.new
16
+ yield(@config)
17
+ self
18
+ end
19
+
20
+ def config
21
+ default_config.merge(@config)
22
+ end
23
+
24
+ def captcha_class
25
+ return @captcha_class if @captcha_class
26
+
27
+ klass = Captcher.config[:mode].to_s.camelize
28
+ @captcha_class = "Captcher::Captchas::#{klass}".constantize
29
+ end
30
+
31
+ private
32
+
33
+ # rubocop:disable Metrics/MethodLength
34
+ def default_config
35
+ @default_config ||= Captcher::Config.new do |c|
36
+ c.mode = :code_captcha
37
+
38
+ c.code_captcha do |cc|
39
+ cc.fonts Dir[Captcher::Engine.root.join("lib/fonts/**")]
40
+ cc.font_size 50
41
+ cc.font_color "black"
42
+ cc.count 5
43
+ cc.background "#999999"
44
+ cc.format "png"
45
+ end
46
+
47
+ c.math_captcha do |mc|
48
+
49
+ end
50
+
51
+ c.awesome_captcha do |ac|
52
+
53
+ end
54
+ end
55
+ end
56
+ # rubocop:enable Metrics/MethodLength
57
+ end
@@ -0,0 +1,46 @@
1
+ module Captcher
2
+ class BaseCaptcha
3
+ SESSION_KEY = "captcha_state".freeze
4
+
5
+ class << self
6
+ attr_accessor :name
7
+
8
+ def restore(session)
9
+ state = session[SESSION_KEY]
10
+ new(state) if state
11
+ end
12
+
13
+ def restore_or_create(config, session)
14
+ restore(session) || new(config: config).store(session)
15
+ end
16
+ end
17
+
18
+ attr_accessor :config, :payload
19
+
20
+ def initialize(options = {})
21
+ options = options.with_indifferent_access
22
+ @config = options[:config] if options[:config]
23
+ @payload = options[:payload] if options[:payload]
24
+ after_initialize
25
+ end
26
+
27
+ def after_initialize; end
28
+
29
+ def store(session)
30
+ session[SESSION_KEY] = { payload: payload, config: config }
31
+ self
32
+ end
33
+
34
+ def own_config
35
+ @own_config ||= @config[self.class.name.to_sym]
36
+ end
37
+
38
+ def represent(format = :html, options = {})
39
+ raise NotImplementedError
40
+ end
41
+
42
+ def validate(confirmation)
43
+ raise NotImplementedError
44
+ end
45
+ end
46
+ end