turnstile-captcha 0.1.3
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 +7 -0
- data/README.md +130 -0
- data/lib/turnstile/configuration.rb +18 -0
- data/lib/turnstile/controller_methods.rb +20 -0
- data/lib/turnstile/railtie.rb +11 -0
- data/lib/turnstile/verification.rb +42 -0
- data/lib/turnstile/version.rb +5 -0
- data/lib/turnstile/view_helpers.rb +27 -0
- data/lib/turnstile.rb +19 -0
- metadata +139 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 90f0930df0270dc3176b2f4ffbb9313b67b6ec5e754e8dcaede32eb423d8b1d3
|
4
|
+
data.tar.gz: 588984312cb99edfe8a1ce598b97a6d8bf0595ba22e21218c41c5d1578d9da43
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3329bec1812bfa6716bc06f49c4e7ef5d470bf1b07d8e79853d468353a28a6d53f86203e092720c481e76e6f4be1d3efa930b6f3a87ea080bbd3454a53391c4b
|
7
|
+
data.tar.gz: 8e491f31f09d8f18f03cc4f53b5a42cb7b6b71e2850942835adcbbc297af8794dc5155fc4db42c8b7b3edaf0dfdbcac8cb099daa05e0371687c85954a524a4a5
|
data/README.md
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
# Turnstile
|
2
|
+
|
3
|
+
A gem to add [Cloudflare Turnstile](https://blog.cloudflare.com/turnstile-private-captcha-alternative/) to your Rails app.
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
gem 'turnstile-captcha', require: 'turnstile'
|
7
|
+
```
|
8
|
+
|
9
|
+
And then execute:
|
10
|
+
|
11
|
+
$ bundle install
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
Create an initializer and configure Turnstile with your Site Key and Secret Key:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
# config/initializers/turnstile.rb
|
19
|
+
|
20
|
+
Turnstile.configure do |config|
|
21
|
+
config.site_key = ...
|
22
|
+
config.secret_key = ...
|
23
|
+
end
|
24
|
+
```
|
25
|
+
|
26
|
+
You now have access to the Turnstile view and controller helpers.
|
27
|
+
|
28
|
+
To output the required tags, use `captcha_javascript_tag`, eg in your `<head>` tag:
|
29
|
+
|
30
|
+
```erb
|
31
|
+
# application.html.erb
|
32
|
+
|
33
|
+
<html>
|
34
|
+
<head>
|
35
|
+
...
|
36
|
+
<%= captcha_javascript_tag %>
|
37
|
+
...
|
38
|
+
</head>
|
39
|
+
...
|
40
|
+
</html>
|
41
|
+
```
|
42
|
+
|
43
|
+
And in your form, output the placeholder tag. You can provide an [`action`](https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/#configurations):
|
44
|
+
|
45
|
+
```erb
|
46
|
+
<%= form_for @user do |f| %>
|
47
|
+
..
|
48
|
+
<%= captcha_placeholder_tag action: "login" %>
|
49
|
+
..
|
50
|
+
<% end %>
|
51
|
+
```
|
52
|
+
|
53
|
+
You can also output both of these tags together, eg. if you only have a single form on the page:
|
54
|
+
|
55
|
+
```erb
|
56
|
+
<%= form_for @user do |f| %>
|
57
|
+
...
|
58
|
+
<%= captcha_tags action: "login" %>
|
59
|
+
...
|
60
|
+
<% end %>
|
61
|
+
```
|
62
|
+
|
63
|
+
Upon submission, you can now validate the request using `valid_captcha?`:
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
class SessionsController < ..
|
67
|
+
def create
|
68
|
+
if valid_captcha?
|
69
|
+
# Perform
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
```
|
74
|
+
|
75
|
+
Inspired by the [recaptcha gem](https://github.com/ambethia/recaptcha) you can provide a `model:` option.
|
76
|
+
In case of captcha failure, an `:invalid_captcha` error will be added to the provided model on the `:base`,
|
77
|
+
which can be localized using Rails I18n.
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
class UsersController < ..
|
81
|
+
def create
|
82
|
+
@user = User.new(..)
|
83
|
+
|
84
|
+
if valid_captcha?(model: @user)
|
85
|
+
# Perform
|
86
|
+
else
|
87
|
+
# @user.errors.details
|
88
|
+
# => {:base=>[{error: :invalid_captcha}]}
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
```
|
93
|
+
|
94
|
+
## Configuration
|
95
|
+
|
96
|
+
You can add an `on_failure` handler to for instance instrument failures.
|
97
|
+
The proc will be called with the verification result from Cloudflare:
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
Turnstile.configure do |config|
|
101
|
+
config.on_failure = ->(verification) { ErrorNotifier.notify("Captcha failure: #{verification.result}") }
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
105
|
+
It is also possible to globally disable/enable Turnstile captcha validation, eg. in CI:
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
Turnstile.configure do |config|
|
109
|
+
config.enabled = ENV["ENABLE_TURNSTILE"]
|
110
|
+
end
|
111
|
+
```
|
112
|
+
|
113
|
+
## Development
|
114
|
+
|
115
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests.
|
116
|
+
|
117
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
118
|
+
|
119
|
+
## Contributing
|
120
|
+
|
121
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/pfeiffer/turnstile-captcha. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/pfeiffer/turnstile-captcha/blob/master/CODE_OF_CONDUCT.md).
|
122
|
+
|
123
|
+
|
124
|
+
## License
|
125
|
+
|
126
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
127
|
+
|
128
|
+
## Code of Conduct
|
129
|
+
|
130
|
+
Everyone interacting in the Turnstile project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/pfeiffer/turnstile-captcha/blob/master/CODE_OF_CONDUCT.md).
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Turnstile::Configuration
|
4
|
+
DEFAULTS = {
|
5
|
+
enabled: true,
|
6
|
+
server_url: "https://challenges.cloudflare.com/turnstile/v0/siteverify",
|
7
|
+
script_url: "https://challenges.cloudflare.com/turnstile/v0/api.js"
|
8
|
+
}.freeze
|
9
|
+
|
10
|
+
attr_accessor :site_key, :secret_key, :enabled, :script_url, :server_url, :on_failure
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@enabled = DEFAULTS[:enabled]
|
14
|
+
@server_url = DEFAULTS[:server_url]
|
15
|
+
@script_url = DEFAULTS[:script_url]
|
16
|
+
@on_failure = nil
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Turnstile::ControllerMethods
|
4
|
+
def valid_captcha?(*args)
|
5
|
+
return true unless Turnstile.configuration.enabled
|
6
|
+
|
7
|
+
options = args.extract_options!
|
8
|
+
verification = Turnstile::Verification.new(response: params["cf-turnstile-response"], remote_ip: request.remote_ip)
|
9
|
+
|
10
|
+
return true if verification.success?
|
11
|
+
|
12
|
+
Turnstile.configuration.on_failure&.call(verification)
|
13
|
+
|
14
|
+
if options[:model].respond_to?(:errors)
|
15
|
+
options[:model].errors.add(:base, :invalid_captcha, message: "Captcha verification failed")
|
16
|
+
end
|
17
|
+
|
18
|
+
false
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "faraday"
|
4
|
+
require "hashie/mash"
|
5
|
+
|
6
|
+
class Turnstile::Verification
|
7
|
+
attr_reader :response, :remote_ip
|
8
|
+
|
9
|
+
def initialize(response:, remote_ip:)
|
10
|
+
@response = response
|
11
|
+
@remote_ip = remote_ip
|
12
|
+
end
|
13
|
+
|
14
|
+
def result
|
15
|
+
@result ||= begin
|
16
|
+
response = perform_verification
|
17
|
+
|
18
|
+
Hashie::Mash.new(response.body)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def success?
|
23
|
+
result.success?
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def perform_verification
|
29
|
+
client.post \
|
30
|
+
Turnstile.configuration.server_url,
|
31
|
+
response: response,
|
32
|
+
secret: Turnstile.configuration.secret_key,
|
33
|
+
remoteip: remote_ip
|
34
|
+
end
|
35
|
+
|
36
|
+
def client
|
37
|
+
@client ||= Faraday.new do |conn|
|
38
|
+
conn.request :url_encoded
|
39
|
+
conn.response :json
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Turnstile::ViewHelpers
|
4
|
+
def captcha_tags(options = {})
|
5
|
+
[
|
6
|
+
captcha_javascript_tag,
|
7
|
+
captcha_placeholder_tag(options)
|
8
|
+
].join.html_safe
|
9
|
+
end
|
10
|
+
|
11
|
+
def captcha_javascript_tag
|
12
|
+
javascript_include_tag Turnstile.configuration.script_url, async: true, defer: true
|
13
|
+
end
|
14
|
+
|
15
|
+
def captcha_placeholder_tag(options = {})
|
16
|
+
action = options.delete(:action)
|
17
|
+
attrs = {
|
18
|
+
class: "cf-turnstile",
|
19
|
+
data: {
|
20
|
+
action: action,
|
21
|
+
sitekey: Turnstile.configuration.site_key
|
22
|
+
}
|
23
|
+
}.deep_merge(options)
|
24
|
+
|
25
|
+
content_tag :div, "", attrs
|
26
|
+
end
|
27
|
+
end
|
data/lib/turnstile.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "turnstile/version"
|
4
|
+
require "turnstile/configuration"
|
5
|
+
require "turnstile/controller_methods"
|
6
|
+
require "turnstile/view_helpers"
|
7
|
+
require "turnstile/verification"
|
8
|
+
|
9
|
+
require "turnstile/railtie" if defined?(Rails)
|
10
|
+
|
11
|
+
module Turnstile
|
12
|
+
def self.configuration
|
13
|
+
@configuration ||= Configuration.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.configure
|
17
|
+
yield(configuration)
|
18
|
+
end
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: turnstile-captcha
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mattias Pfeiffer
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-11-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: webmock
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: faraday
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rails
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 6.1.0
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 6.1.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: hashie
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: Use Turnstile by Cloudflare to perform captcha validation in your Rails
|
98
|
+
app
|
99
|
+
email:
|
100
|
+
- mattias@pfeiffer.dk
|
101
|
+
executables: []
|
102
|
+
extensions: []
|
103
|
+
extra_rdoc_files: []
|
104
|
+
files:
|
105
|
+
- README.md
|
106
|
+
- lib/turnstile.rb
|
107
|
+
- lib/turnstile/configuration.rb
|
108
|
+
- lib/turnstile/controller_methods.rb
|
109
|
+
- lib/turnstile/railtie.rb
|
110
|
+
- lib/turnstile/verification.rb
|
111
|
+
- lib/turnstile/version.rb
|
112
|
+
- lib/turnstile/view_helpers.rb
|
113
|
+
homepage: https://github.com/pfeiffer/turnstile-captcha
|
114
|
+
licenses:
|
115
|
+
- MIT
|
116
|
+
metadata:
|
117
|
+
homepage_uri: https://github.com/pfeiffer/turnstile-captcha
|
118
|
+
source_code_uri: https://github.com/pfeiffer/turnstile-captcha/
|
119
|
+
changelog_uri: https://github.com/pfeiffer/turnstile-captcha/blog/main/CHANGELOG.md
|
120
|
+
post_install_message:
|
121
|
+
rdoc_options: []
|
122
|
+
require_paths:
|
123
|
+
- lib
|
124
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: 2.7.0
|
129
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
requirements: []
|
135
|
+
rubygems_version: 3.1.6
|
136
|
+
signing_key:
|
137
|
+
specification_version: 4
|
138
|
+
summary: Use Turnstile by Cloudflare to perform captcha validation in your Rails app
|
139
|
+
test_files: []
|