new_google_recaptcha 1.0.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +113 -5
- data/lib/new_google_recaptcha/validator.rb +19 -4
- data/lib/new_google_recaptcha/version.rb +1 -1
- data/lib/new_google_recaptcha/view_ext.rb +36 -15
- data/lib/new_google_recaptcha.rb +32 -2
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a187e51b64407ccc6cae4186568b1f0deac59c601e6b095424dcb5aad7243edd
|
4
|
+
data.tar.gz: 5660c73c953982985bd3b926071e38040e64615cf9bfed9342cb71d2eed05ddf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f4032d4cfa438d70622a2f7b49f4af670b52f6e67fe81638cbb50396fdf240fa356fc23a3417d8b57ba78d2e2fef294f59bcd62037352fcda7d801066837d712
|
7
|
+
data.tar.gz: bd804e4ace434e6dab65d07eef3988c9d6880671a69ee04d8fe081c964ca6b4b94818506165af14aa9a09ded729f0806e77ff0cd637cf36401f4268f4169971d
|
data/README.md
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
# Google Recaptcha v3 + Rails
|
2
2
|
|
3
|
-
|
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
|
+
[![https://www.patreon.com/igorkasyanchuk](https://github.com/igorkasyanchuk/rails_time_travel/blob/main/docs/patron.svg?raw=true)](https://www.patreon.com/igorkasyanchuk)
|
6
|
+
|
7
|
+
Integrate Google Recaptcha v3 with Rails app.
|
4
8
|
|
5
9
|
Google Recaptcha console: https://www.google.com/recaptcha/admin#list
|
6
10
|
|
@@ -44,6 +48,31 @@ Recaptcha v3 documentation: https://developers.google.com/recaptcha/docs/v3
|
|
44
48
|
render :new
|
45
49
|
end
|
46
50
|
end
|
51
|
+
|
52
|
+
|
53
|
+
# or
|
54
|
+
# if you need to capture a humanity `score` from Google
|
55
|
+
# before you need to add a column for example `humanity_score` (type: float) where this score will be saved.
|
56
|
+
|
57
|
+
|
58
|
+
def create
|
59
|
+
@post = Post.new(post_params)
|
60
|
+
humanity_details =
|
61
|
+
NewGoogleRecaptcha.get_humanity_detailed(
|
62
|
+
params[:new_google_recaptcha_token],
|
63
|
+
"checkout",
|
64
|
+
NewGoogleRecaptcha.minimum_score,
|
65
|
+
@post
|
66
|
+
)
|
67
|
+
|
68
|
+
@post.humanity_score = humanity_details[:score]
|
69
|
+
|
70
|
+
if humanity_details[:is_human] && @post.save
|
71
|
+
redirect_to @post, notice: 'Post was successfully created.'
|
72
|
+
else
|
73
|
+
render :new
|
74
|
+
end
|
75
|
+
end
|
47
76
|
```
|
48
77
|
|
49
78
|
There are two mandatory arguments for `human?` method:
|
@@ -67,6 +96,16 @@ like this:
|
|
67
96
|
NewGoogleRecaptcha.human?(params[:new_google_recaptcha_token], "checkout")
|
68
97
|
```
|
69
98
|
|
99
|
+
### Saving humanity score from Google in your model
|
100
|
+
|
101
|
+
`get_humanity_detailed` method acts like `human?` method, the only difference is that it returns following hash with three key-value pairs:
|
102
|
+
|
103
|
+
- `is_human` - whether actor is a human or not (same as result of `human?` method)
|
104
|
+
- `score` - actual humanity score from recaptcha response
|
105
|
+
- `model` - model which you trying to save
|
106
|
+
|
107
|
+
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.
|
108
|
+
|
70
109
|
Add to your navigation links `data-turbolinks="false"` to make it works with `turbolinks`.
|
71
110
|
|
72
111
|
## Installation
|
@@ -90,20 +129,71 @@ And edit new_google_recaptcha.rb and enter your site_key and secret_key.
|
|
90
129
|
|
91
130
|
## API
|
92
131
|
|
93
|
-
|
132
|
+
`NewGoogleRecaptcha.human?(token, model)` or `NewGoogleRecaptcha.get_humanity_detailed(token, model)` in contoller
|
94
133
|
|
95
134
|
- token is received from google, must be sent to backend
|
96
135
|
- model optional parameter. if you want to add error to model.
|
97
136
|
|
98
|
-
|
137
|
+
`<%= include_recaptcha_js %>` in layout (by using yield)
|
99
138
|
|
100
139
|
Include Google Recaptcha v3 JS into your Rails app. In head, right before `</head>`.
|
101
140
|
|
102
|
-
|
141
|
+
`<%= recaptcha_action(action_name) %>` in view
|
103
142
|
|
104
143
|
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.
|
105
144
|
|
145
|
+
## How to add to the Devise
|
146
|
+
|
147
|
+
Generate Devise controllers and views, and edit "create" method.
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
class Users::RegistrationsController < Devise::RegistrationsController
|
151
|
+
...
|
152
|
+
def create
|
153
|
+
build_resource(sign_up_params)
|
154
|
+
|
155
|
+
NewGoogleRecaptcha.human?(
|
156
|
+
params[:new_google_recaptcha_token],
|
157
|
+
"user",
|
158
|
+
NewGoogleRecaptcha.minimum_score,
|
159
|
+
resource) && resource.save
|
160
|
+
|
161
|
+
yield resource if block_given?
|
162
|
+
if resource.persisted?
|
163
|
+
if resource.active_for_authentication?
|
164
|
+
set_flash_message! :notice, :signed_up
|
165
|
+
sign_up(resource_name, resource)
|
166
|
+
respond_with resource, location: after_sign_up_path_for(resource)
|
167
|
+
else
|
168
|
+
set_flash_message! :notice, :"signed_up_but_#{resource.inactive_message}"
|
169
|
+
expire_data_after_sign_in!
|
170
|
+
respond_with resource, location: after_inactive_sign_up_path_for(resource)
|
171
|
+
end
|
172
|
+
else
|
173
|
+
clean_up_passwords resource
|
174
|
+
set_minimum_password_length
|
175
|
+
respond_with resource
|
176
|
+
end
|
177
|
+
end
|
178
|
+
```
|
179
|
+
|
180
|
+
## How to use in test or specs
|
181
|
+
|
182
|
+
At the end of the spec/rails_helper.rb put:
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
module NewGoogleRecaptcha
|
186
|
+
def self.human?(*attrs)
|
187
|
+
true
|
188
|
+
end
|
189
|
+
end
|
190
|
+
```
|
191
|
+
|
192
|
+
Tests are located in `specs` folder and `test/dummy/tests` folder.
|
193
|
+
|
194
|
+
|
106
195
|
## I18n support
|
196
|
+
|
107
197
|
reCAPTCHA passes one types of error explanation to a linked model. It will use the I18n gem
|
108
198
|
to translate the default error message if I18n is available. To customize the messages to your locale,
|
109
199
|
add these keys to your I18n backend:
|
@@ -119,11 +209,21 @@ en:
|
|
119
209
|
verification_human: 'Fail'
|
120
210
|
```
|
121
211
|
|
212
|
+
## Badge position
|
213
|
+
|
214
|
+
NewGoogleRecaptcha allows you to render badge in different positions
|
215
|
+
|
216
|
+
```ruby
|
217
|
+
<%= include_recaptcha_js badge: "bottomleft" %>
|
218
|
+
```
|
219
|
+
|
220
|
+
Three of existing `badge` values are `bottomright`, `bottomleft` and `inline`.
|
221
|
+
'inline' lets you position it with CSS.
|
222
|
+
|
122
223
|
## TODO
|
123
224
|
|
124
225
|
- check everything works with turbolinks
|
125
226
|
- allow custom ID for input
|
126
|
-
- return score ?
|
127
227
|
- more tests
|
128
228
|
- handle exceptions with timeouts, json is not parsed
|
129
229
|
- add support for non-Rails apps
|
@@ -137,7 +237,15 @@ You are welcome to contribute.
|
|
137
237
|
* [gilcierweb](https://github.com/gilcierweb)
|
138
238
|
* [RoRElessar](https://github.com/RoRElessar)
|
139
239
|
* [rubyconvict](https://github.com/rubyconvict)
|
240
|
+
* [adelnabiullin](https://github.com/adelnabiullin)
|
241
|
+
* [jhill](https://github.com/jhill)
|
242
|
+
* [dachinat](https://github.com/dachinat)
|
243
|
+
* [edugonch](https://github.com/edugonch)
|
140
244
|
|
141
245
|
## License
|
142
246
|
|
143
247
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
248
|
+
|
249
|
+
|
250
|
+
[<img src="https://github.com/igorkasyanchuk/rails_time_travel/blob/main/docs/more_gems.png?raw=true"
|
251
|
+
/>](https://www.railsjazz.com/)
|
@@ -2,9 +2,24 @@ require 'net/http'
|
|
2
2
|
|
3
3
|
module NewGoogleRecaptcha
|
4
4
|
class Validator
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
+
|
8
23
|
conditions = []
|
9
24
|
conditions << !!result['success']
|
10
25
|
conditions << (result['score'].to_f >= minimum_score)
|
@@ -12,4 +27,4 @@ module NewGoogleRecaptcha
|
|
12
27
|
conditions.none?(&:!)
|
13
28
|
end
|
14
29
|
end
|
15
|
-
end
|
30
|
+
end
|
@@ -2,24 +2,45 @@ module NewGoogleRecaptcha
|
|
2
2
|
module ViewExt
|
3
3
|
include ActionView::Helpers::TagHelper
|
4
4
|
|
5
|
-
def include_recaptcha_js
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
def include_recaptcha_js(opts = {})
|
6
|
+
badge = opts[:badge] ? "&badge=#{opts[:badge]}" : ""
|
7
|
+
generate_recaptcha_callback +
|
8
|
+
javascript_include_tag(
|
9
|
+
"https://www.google.com/recaptcha/api.js?render=#{NewGoogleRecaptcha.site_key}&onload=newGoogleRecaptchaCallback#{badge}",
|
10
|
+
defer: true
|
11
|
+
)
|
9
12
|
end
|
10
13
|
|
11
14
|
def recaptcha_action(action)
|
12
15
|
id = "new_google_recaptcha_token_#{SecureRandom.hex(10)}"
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
16
|
+
hidden_field_tag(
|
17
|
+
'new_google_recaptcha_token',
|
18
|
+
nil,
|
19
|
+
readonly: true,
|
20
|
+
'data-google-recaptcha-action' => action,
|
21
|
+
id: id
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def generate_recaptcha_callback
|
28
|
+
javascript_tag %(
|
29
|
+
function newGoogleRecaptchaCallback () {
|
30
|
+
grecaptcha.ready(function () {
|
31
|
+
var elements = document.querySelectorAll('[data-google-recaptcha-action]')
|
32
|
+
Array.prototype.slice.call(elements).forEach(function (el) {
|
33
|
+
var action = el.dataset.googleRecaptchaAction
|
34
|
+
if (!action) return
|
35
|
+
grecaptcha
|
36
|
+
.execute("#{NewGoogleRecaptcha.site_key}", { action: action })
|
37
|
+
.then(function (token) {
|
38
|
+
el.value = token
|
39
|
+
})
|
40
|
+
})
|
41
|
+
})
|
42
|
+
}
|
43
|
+
)
|
23
44
|
end
|
24
45
|
end
|
25
|
-
end
|
46
|
+
end
|
data/lib/new_google_recaptcha.rb
CHANGED
@@ -10,13 +10,37 @@ module NewGoogleRecaptcha
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.human?(token, action, minimum_score = self.minimum_score, model = nil)
|
13
|
-
is_valid =
|
13
|
+
is_valid =
|
14
|
+
NewGoogleRecaptcha::Validator.new(
|
15
|
+
token: token,
|
16
|
+
action: action,
|
17
|
+
minimum_score: minimum_score
|
18
|
+
).call
|
19
|
+
|
14
20
|
if model && !is_valid
|
15
21
|
model.errors.add(:base, self.i18n("new_google_recaptcha.errors.verification_human", "Looks like you are not a human"))
|
16
22
|
end
|
23
|
+
|
17
24
|
is_valid
|
18
25
|
end
|
19
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
|
+
|
20
44
|
def self.i18n(key, default)
|
21
45
|
if defined?(I18n)
|
22
46
|
I18n.translate(key, default: default)
|
@@ -25,7 +49,13 @@ module NewGoogleRecaptcha
|
|
25
49
|
end
|
26
50
|
end
|
27
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
|
28
58
|
end
|
29
59
|
|
30
60
|
require_relative "new_google_recaptcha/view_ext"
|
31
|
-
require_relative "new_google_recaptcha/validator"
|
61
|
+
require_relative "new_google_recaptcha/validator"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: new_google_recaptcha
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Igor Kasyanchuk
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2022-01-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -123,7 +123,7 @@ dependencies:
|
|
123
123
|
- - ">="
|
124
124
|
- !ruby/object:Gem::Version
|
125
125
|
version: '0'
|
126
|
-
description: Google
|
126
|
+
description: Google reCAPTCHA v3 + Rails (integration)
|
127
127
|
email:
|
128
128
|
- igorkasyanchuk@gmail.com
|
129
129
|
executables: []
|
@@ -161,8 +161,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
161
161
|
- !ruby/object:Gem::Version
|
162
162
|
version: '0'
|
163
163
|
requirements: []
|
164
|
-
rubygems_version: 3.
|
164
|
+
rubygems_version: 3.3.3
|
165
165
|
signing_key:
|
166
166
|
specification_version: 4
|
167
|
-
summary: Google
|
167
|
+
summary: Google reCAPTCHA v3 + Rails
|
168
168
|
test_files: []
|