new_google_recaptcha_turbo 1.2.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3c5cd7372d8cb66ef663b5d79f4c252e949189f9f60be5ea0a5d32691f5a8aa0
4
+ data.tar.gz: cc6f097e61eb5be159f6ac0e165f2f037e2ef94ac9e0a3e549532a7d743ba880
5
+ SHA512:
6
+ metadata.gz: e0ce6734d58c2e7f9b4d9c53a24fd9754b6aeb09b0688bc657989aceacd5c1ce76638b967367148cde449bd2ff7af8ef1e6770963d7d51b7291b20c0f1394f2b
7
+ data.tar.gz: 84570ddaeb15737ab5f3c2645ea1cd9e32a5c54d09ff378df6a3f2606a285297a56fb77fe37bb16e42bee837a592100240b8f1523da23687e60ee713cef03610
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2018 Igor Kasyanchuk
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,237 @@
1
+ # Google Recaptcha v3 + Rails
2
+
3
+ [![Build Status](https://travis-ci.org/igorkasyanchuk/new_google_recaptcha.svg?branch=master)](https://travis-ci.org/igorkasyanchuk/new_google_recaptcha)
4
+ [![RailsJazz](https://github.com/igorkasyanchuk/rails_time_travel/blob/main/docs/my_other.svg?raw=true)](https://www.railsjazz.com)
5
+
6
+ Integrate Google Recaptcha v3 with Rails app.
7
+
8
+ Google Recaptcha console: https://www.google.com/recaptcha/admin#list
9
+
10
+ Recaptcha v3 documentation: https://developers.google.com/recaptcha/docs/v3
11
+
12
+ ## Usage
13
+
14
+ - Open https://www.google.com/recaptcha/admin#list
15
+ - register a new site
16
+ - copy `site_key` and `secret_key` and put into config/initializers/new_google_recaptcha.rb
17
+ - optionally, change the `minimum_score` in the initializer to a preferred float value (from 0.0 to 1.0)
18
+ - in layout:
19
+ ```erb
20
+ <head>
21
+ ...
22
+ <%= yield :recaptcha_js %>
23
+ </head>
24
+ ```
25
+ - in view where you for example you have a form:
26
+ ```erb
27
+ <%= content_for :recaptcha_js do %>
28
+ <%= include_recaptcha_js %>
29
+ <% end %>
30
+ <form ...>
31
+ <%#= 'checkout' is action name to be verified later %>
32
+ <%= recaptcha_action('checkout') %>
33
+ </form>
34
+ ```
35
+ - in controller:
36
+ ```ruby
37
+ def create
38
+ @post = Post.new(post_params)
39
+ if NewGoogleRecaptcha.human?(
40
+ params[:new_google_recaptcha_token],
41
+ "checkout",
42
+ NewGoogleRecaptcha.minimum_score,
43
+ @post
44
+ ) && @post.save
45
+ redirect_to @post, notice: 'Post was successfully created.'
46
+ else
47
+ render :new
48
+ end
49
+ end
50
+
51
+
52
+ # or
53
+ # if you need to capture a humanity `score` from Google
54
+ # before you need to add a column for example `humanity_score` (type: float) where this score will be saved.
55
+
56
+
57
+ def create
58
+ @post = Post.new(post_params)
59
+ humanity_details =
60
+ NewGoogleRecaptcha.get_humanity_detailed(
61
+ params[:new_google_recaptcha_token],
62
+ "checkout",
63
+ NewGoogleRecaptcha.minimum_score,
64
+ @post
65
+ )
66
+
67
+ @post.humanity_score = humanity_details[:score]
68
+
69
+ if humanity_details[:is_human] && @post.save
70
+ redirect_to @post, notice: 'Post was successfully created.'
71
+ else
72
+ render :new
73
+ end
74
+ end
75
+ ```
76
+
77
+ There are two mandatory arguments for `human?` method:
78
+
79
+ - `token` - token valid for your site
80
+ - `action` - the action name for this request
81
+ (the gem checks if it is the same as the name used with the token,
82
+ otherwise a hacker could replace it on frontend to some another action used,
83
+ but with lower score requirement and thus pass the verification)
84
+
85
+ You can verify recaptcha without using these arguments:
86
+
87
+ - `minimum_score` - defaults to value set in the initializer
88
+ (reCAPTCHA recommends using 0.5 as default)
89
+ - `model` - defaults to `nil` which will result in not adding an error to model;
90
+ any custom failure handling is applicable here
91
+
92
+ like this:
93
+
94
+ ```ruby
95
+ NewGoogleRecaptcha.human?(params[:new_google_recaptcha_token], "checkout")
96
+ ```
97
+
98
+ ### Saving humanity score from Google in your model
99
+
100
+ `get_humanity_detailed` method acts like `human?` method, the only difference is that it returns following hash with three key-value pairs:
101
+
102
+ - `is_human` - whether actor is a human or not (same as result of `human?` method)
103
+ - `score` - actual humanity score from recaptcha response
104
+ - `model` - model which you trying to save
105
+
106
+ It could be handy if you want to store score in db or put it into logs or smth else. Real example is above in the code samples.
107
+
108
+ Add to your navigation links `data-turbolinks="false"` to make it works with `turbolinks`.
109
+
110
+ ## Installation
111
+
112
+ ```ruby
113
+ gem 'new_google_recaptcha'
114
+ ```
115
+
116
+ And then execute:
117
+ ```bash
118
+ $ bundle
119
+ ```
120
+
121
+ And then run:
122
+
123
+ ```bash
124
+ $ rails generate new_google_recaptcha initializer
125
+ ```
126
+
127
+ And edit new_google_recaptcha.rb and enter your site_key and secret_key.
128
+
129
+ ## API
130
+
131
+ `NewGoogleRecaptcha.human?(token, model)` or `NewGoogleRecaptcha.get_humanity_detailed(token, model)` in contoller
132
+
133
+ - token is received from google, must be sent to backend
134
+ - model optional parameter. if you want to add error to model.
135
+
136
+ `<%= include_recaptcha_js %>` in layout (by using yield)
137
+
138
+ Include Google Recaptcha v3 JS into your Rails app. In head, right before `</head>`.
139
+
140
+ `<%= recaptcha_action(action_name) %>` in view
141
+
142
+ Action where recaptcha action was executed. Actions could be viewed in Admin console. More docs: https://developers.google.com/recaptcha/docs/v3. Action name could be "comments", "checkout", etc. Put any name and check scores in console.
143
+
144
+ ## How to add to the Devise
145
+
146
+ Generate Devise controllers and views, and edit "create" method.
147
+
148
+ ```ruby
149
+ class Users::RegistrationsController < Devise::RegistrationsController
150
+ ...
151
+ def create
152
+ build_resource(sign_up_params)
153
+
154
+ NewGoogleRecaptcha.human?(
155
+ params[:new_google_recaptcha_token],
156
+ "user",
157
+ NewGoogleRecaptcha.minimum_score,
158
+ resource) && resource.save
159
+
160
+ yield resource if block_given?
161
+ if resource.persisted?
162
+ if resource.active_for_authentication?
163
+ set_flash_message! :notice, :signed_up
164
+ sign_up(resource_name, resource)
165
+ respond_with resource, location: after_sign_up_path_for(resource)
166
+ else
167
+ set_flash_message! :notice, :"signed_up_but_#{resource.inactive_message}"
168
+ expire_data_after_sign_in!
169
+ respond_with resource, location: after_inactive_sign_up_path_for(resource)
170
+ end
171
+ else
172
+ clean_up_passwords resource
173
+ set_minimum_password_length
174
+ respond_with resource
175
+ end
176
+ end
177
+ ```
178
+
179
+ ## How to use in test or specs
180
+
181
+ At the end of the spec/rails_helper.rb put:
182
+
183
+ ```ruby
184
+ module NewGoogleRecaptcha
185
+ def self.human?(*attrs)
186
+ true
187
+ end
188
+ end
189
+ ```
190
+
191
+ Tests are located in `specs` folder and `test/dummy/tests` folder.
192
+
193
+
194
+ ## I18n support
195
+
196
+ reCAPTCHA passes one types of error explanation to a linked model. It will use the I18n gem
197
+ to translate the default error message if I18n is available. To customize the messages to your locale,
198
+ add these keys to your I18n backend:
199
+
200
+ `new_google_recaptcha.errors.verification_human` error message displayed when it is something like a robot, or a suspicious action
201
+
202
+ Also you can translate API response errors to human friendly by adding translations to the locale (`config/locales/en.yml`):
203
+
204
+ ```Yaml
205
+ en:
206
+ new_google_recaptcha:
207
+ errors:
208
+ verification_human: 'Fail'
209
+ ```
210
+
211
+ ## TODO
212
+
213
+ - check everything works with turbolinks
214
+ - allow custom ID for input
215
+ - more tests
216
+ - handle exceptions with timeouts, json is not parsed
217
+ - add support for non-Rails apps
218
+ - add support for older Rails (should be easy since code is very simple)
219
+
220
+ ## Contributors
221
+
222
+ You are welcome to contribute.
223
+
224
+ * [Igor Kasyanchuk](https://github.com/igorkasyanchuk) (maintainer)
225
+ * [gilcierweb](https://github.com/gilcierweb)
226
+ * [RoRElessar](https://github.com/RoRElessar)
227
+ * [rubyconvict](https://github.com/rubyconvict)
228
+ * [adelnabiullin](https://github.com/adelnabiullin)
229
+ * [jhill](https://github.com/jhill)
230
+
231
+ ## License
232
+
233
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
234
+
235
+
236
+ [<img src="https://github.com/igorkasyanchuk/rails_time_travel/blob/main/docs/more_gems.png?raw=true"
237
+ />](https://www.railsjazz.com/)
data/Rakefile ADDED
@@ -0,0 +1,27 @@
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 = 'NewGoogleRecaptcha'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ require 'bundler/gem_tasks'
18
+
19
+ require 'rake/testtask'
20
+
21
+ Rake::TestTask.new(:test) do |t|
22
+ t.libs << 'test'
23
+ t.pattern = 'test/**/*_test.rb'
24
+ t.verbose = false
25
+ end
26
+
27
+ task default: :test
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Generate configuration file.
3
+
4
+ Example:
5
+ rails generate new_google_recaptcha initializer
6
+
7
+ This will create:
8
+ config/new_google_recaptcha.rb
@@ -0,0 +1,6 @@
1
+ class NewGoogleRecaptchaGenerator < Rails::Generators::NamedBase
2
+ source_root File.expand_path('../templates', __FILE__)
3
+ def copy_initializer
4
+ template 'new_google_recaptcha.rb', 'config/initializers/new_google_recaptcha.rb'
5
+ end
6
+ end
@@ -0,0 +1,7 @@
1
+ if Object.const_defined?('NewGoogleRecaptcha')
2
+ NewGoogleRecaptcha.setup do |config|
3
+ config.site_key = "SITE_KEY"
4
+ config.secret_key = "SECRET_KEY"
5
+ config.minimum_score = 0.5
6
+ end
7
+ end
@@ -0,0 +1,61 @@
1
+ require "new_google_recaptcha/railtie"
2
+
3
+ module NewGoogleRecaptcha
4
+ mattr_accessor :site_key
5
+ mattr_accessor :secret_key
6
+ mattr_accessor :minimum_score
7
+
8
+ def self.setup
9
+ yield(self)
10
+ end
11
+
12
+ def self.human?(token, action, minimum_score = self.minimum_score, model = nil)
13
+ is_valid =
14
+ NewGoogleRecaptcha::Validator.new(
15
+ token: token,
16
+ action: action,
17
+ minimum_score: minimum_score
18
+ ).call
19
+
20
+ if model && !is_valid
21
+ model.errors.add(:base, self.i18n("new_google_recaptcha.errors.verification_human", "Looks like you are not a human"))
22
+ end
23
+
24
+ is_valid
25
+ end
26
+
27
+ def self.get_humanity_detailed(token, action, minimum_score = self.minimum_score, model = nil)
28
+ validator =
29
+ NewGoogleRecaptcha::Validator.new(
30
+ token: token,
31
+ action: action,
32
+ minimum_score: minimum_score
33
+ )
34
+
35
+ is_valid = validator.call
36
+
37
+ if model && !is_valid
38
+ model.errors.add(:base, self.i18n("new_google_recaptcha.errors.verification_human", "Looks like you are not a human"))
39
+ end
40
+
41
+ { is_human: is_valid, score: validator.score, model: model }
42
+ end
43
+
44
+ def self.i18n(key, default)
45
+ if defined?(I18n)
46
+ I18n.translate(key, default: default)
47
+ else
48
+ default
49
+ end
50
+ end
51
+
52
+ def self.compose_uri(token)
53
+ URI(
54
+ "https://www.google.com/recaptcha/api/siteverify?"\
55
+ "secret=#{self.secret_key}&response=#{token}"
56
+ )
57
+ end
58
+ end
59
+
60
+ require_relative "new_google_recaptcha/view_ext"
61
+ require_relative "new_google_recaptcha/validator"
@@ -0,0 +1,9 @@
1
+ module NewGoogleRecaptcha
2
+ class Railtie < ::Rails::Railtie
3
+ initializer 'new_google_recaptcha.helpers' do
4
+ ActiveSupport.on_load :action_view do
5
+ ActionView::Base.send :include, NewGoogleRecaptcha::ViewExt
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,30 @@
1
+ require 'net/http'
2
+
3
+ module NewGoogleRecaptcha
4
+ class Validator
5
+ attr_reader :score
6
+ attr_reader :token, :action, :minimum_score
7
+
8
+ def initialize(token:, action:, minimum_score:)
9
+ @token = token
10
+ @action = action
11
+ @minimum_score = minimum_score
12
+ end
13
+
14
+ def call
15
+ uri = NewGoogleRecaptcha.compose_uri(token)
16
+
17
+ http = Net::HTTP.new(uri.host, uri.port)
18
+ http.use_ssl = true if uri.scheme == 'https'
19
+ result = JSON.parse(http.request(Net::HTTP::Get.new(uri)).body)
20
+
21
+ @score = result['score'].to_f
22
+
23
+ conditions = []
24
+ conditions << !!result['success']
25
+ conditions << (result['score'].to_f >= minimum_score)
26
+ conditions << (result['action'].to_s == action.to_s)
27
+ conditions.none?(&:!)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,3 @@
1
+ module NewGoogleRecaptcha
2
+ VERSION = '1.2.1'
3
+ end
@@ -0,0 +1,41 @@
1
+ module NewGoogleRecaptcha
2
+ module ViewExt
3
+ include ActionView::Helpers::TagHelper
4
+
5
+ def include_recaptcha_js
6
+ generate_recaptcha_callback + javascript_include_tag("https://www.google.com/recaptcha/api.js?render=#{NewGoogleRecaptcha.site_key}&onload=newGoogleRecaptchaCallback", defer: true, 'data-turbolinks-track': "reload")
7
+ end
8
+
9
+ def recaptcha_action(action)
10
+ id = "new_google_recaptcha_token_#{SecureRandom.hex(10)}"
11
+ hidden_field_tag(
12
+ "new_recaptcha_token",
13
+ nil,
14
+ :readonly => true,
15
+ "data-google-recaptcha-action" => action,
16
+ :id => id
17
+ )
18
+ end
19
+
20
+ private
21
+
22
+ def generate_recaptcha_callback
23
+ javascript_tag %(
24
+ function newGoogleRecaptchaCallback () {
25
+ grecaptcha.ready(function () {
26
+ var elements = document.querySelectorAll('[data-google-recaptcha-action]')
27
+ Array.prototype.slice.call(elements).forEach(function (el) {
28
+ var action = el.dataset.googleRecaptchaAction
29
+ if (!action) return
30
+ grecaptcha
31
+ .execute("#{NewGoogleRecaptcha.site_key}", { action: action })
32
+ .then(function (token) {
33
+ el.value = token
34
+ })
35
+ })
36
+ })
37
+ }
38
+ )
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :new_google_recaptcha do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,168 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: new_google_recaptcha_turbo
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Igor Kasyanchuk
8
+ - rubyconvict
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2021-02-01 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: 4.2.0
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: 4.2.0
28
+ - !ruby/object:Gem::Dependency
29
+ name: sqlite3
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '1.3'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '1.3'
42
+ - !ruby/object:Gem::Dependency
43
+ name: byebug
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '10.0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '10.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: webmock
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '3.5'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '3.5'
70
+ - !ruby/object:Gem::Dependency
71
+ name: mocha
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: 0.14.0
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: 0.14.0
84
+ - !ruby/object:Gem::Dependency
85
+ name: redis-store-testing
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: connection_pool
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: 1.2.0
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: 1.2.0
112
+ - !ruby/object:Gem::Dependency
113
+ name: appraisal
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ description: Google reCAPTCHA v3 + Rails (integration)
127
+ email:
128
+ - igorkasyanchuk@gmail.com
129
+ executables: []
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - MIT-LICENSE
134
+ - README.md
135
+ - Rakefile
136
+ - lib/generators/USAGE
137
+ - lib/generators/new_google_recaptcha_generator.rb
138
+ - lib/generators/templates/new_google_recaptcha.rb
139
+ - lib/new_google_recaptcha.rb
140
+ - lib/new_google_recaptcha/railtie.rb
141
+ - lib/new_google_recaptcha/validator.rb
142
+ - lib/new_google_recaptcha/version.rb
143
+ - lib/new_google_recaptcha/view_ext.rb
144
+ - lib/tasks/new_google_recaptcha_tasks.rake
145
+ homepage: https://github.com/igorkasyanchuk/new_google_recaptcha
146
+ licenses:
147
+ - MIT
148
+ metadata: {}
149
+ post_install_message:
150
+ rdoc_options: []
151
+ require_paths:
152
+ - lib
153
+ required_ruby_version: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ required_rubygems_version: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ version: '0'
163
+ requirements: []
164
+ rubygems_version: 3.1.4
165
+ signing_key:
166
+ specification_version: 4
167
+ summary: Google reCAPTCHA v3 + Rails
168
+ test_files: []