cloudflare-turnstile-rails 1.2.0 → 1.3.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.
- checksums.yaml +4 -4
- data/README.md +15 -0
- data/lib/cloudflare/turnstile/rails/configuration.rb +5 -1
- data/lib/cloudflare/turnstile/rails/constants/cloudflare.rb +3 -0
- data/lib/cloudflare/turnstile/rails/verification.rb +29 -3
- data/lib/cloudflare/turnstile/rails/version.rb +1 -1
- data/lib/generators/cloudflare_turnstile/templates/cloudflare_turnstile.rb +6 -0
- metadata +6 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7bdf250ea2814264e89921898dcc1c38b968596fe59e9423914b2d245ee8607c
|
|
4
|
+
data.tar.gz: e72026fff680a00cccfdb6134fbe9dafe0658abc8c135b0c5a4bc73404a1a104
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dfe05337cc155cd855b9297c8512d1c173069862a33f3061b685e41db3ec4ac7c20e86466d2c90d82d10795ccb0560b64177a344a5098885506efc53f614872d
|
|
7
|
+
data.tar.gz: aa3d5dacc7e140dedf475881f61522480b30c87e39bf1b228999ed5244fbe33cf1f02af3ffa6575bc2e0a09acb78221375b2fe82863dfcf13274398bac631d3d
|
data/README.md
CHANGED
|
@@ -11,6 +11,9 @@ Cloudflare Turnstile gem for Ruby on Rails with built-in Turbo and Turbolinks su
|
|
|
11
11
|
|
|
12
12
|
Supports **Rails 5.0 → latest** and **Ruby 2.6 → latest**, with the full Rails/Ruby matrix tested daily in CI.
|
|
13
13
|
|
|
14
|
+
> [!TIP]
|
|
15
|
+
> **Using Devise?** [devise-cloudflare-turnstile](https://github.com/vkononov/devise-cloudflare-turnstile) protects every Devise form (sign in, sign up, password reset, and more) with no changes to your Devise views or controllers. It is built on this gem and shares its configuration, so you can use both to protect Devise and non-Devise forms in the same app.
|
|
16
|
+
|
|
14
17
|
[](https://www.buymeacoffee.com/vkononov)
|
|
15
18
|
|
|
16
19
|
## Features
|
|
@@ -184,6 +187,16 @@ Supports **Rails 5.0 → latest** and **Ruby 2.6 → latest**, with the full Rai
|
|
|
184
187
|
end
|
|
185
188
|
```
|
|
186
189
|
|
|
190
|
+
* Requests to Cloudflare time out after 5 seconds to connect, read, or write, so a slow Cloudflare can't tie up your app's request threads for long. A timeout raises `Cloudflare::Turnstile::Rails::ConfigurationError`. To change the limits:
|
|
191
|
+
|
|
192
|
+
```ruby
|
|
193
|
+
Cloudflare::Turnstile::Rails.configure do |config|
|
|
194
|
+
config.open_timeout = 5 # connecting to siteverify
|
|
195
|
+
config.read_timeout = 5 # reading the response
|
|
196
|
+
config.write_timeout = 5 # sending the verification POST
|
|
197
|
+
end
|
|
198
|
+
```
|
|
199
|
+
|
|
187
200
|
#### Advanced Validation
|
|
188
201
|
|
|
189
202
|
* To inspect the entire verification payload, use `verify_turnstile`. It returns a `VerificationResponse` object with detailed information:
|
|
@@ -203,6 +216,8 @@ Supports **Rails 5.0 → latest** and **Ruby 2.6 → latest**, with the full Rai
|
|
|
203
216
|
end
|
|
204
217
|
```
|
|
205
218
|
|
|
219
|
+
* A missing, malformed, or oversized token fails immediately with `missing-input-response` or `invalid-input-response`, without a request to Cloudflare. The `VerificationResponse` for these failures only has `success` and `error-codes`; it doesn't carry `hostname`, `challenge_ts`, or the other fields a real siteverify call returns.
|
|
220
|
+
|
|
206
221
|
* The `VerificationResponse` object contains the raw response from Cloudflare:
|
|
207
222
|
|
|
208
223
|
```ruby
|
|
@@ -3,7 +3,8 @@ module Cloudflare
|
|
|
3
3
|
module Rails
|
|
4
4
|
class Configuration
|
|
5
5
|
attr_writer :script_url
|
|
6
|
-
attr_accessor :site_key, :secret_key, :render, :onload, :default_data, :auto_populate_response_in_test_env
|
|
6
|
+
attr_accessor :site_key, :secret_key, :render, :onload, :default_data, :auto_populate_response_in_test_env,
|
|
7
|
+
:open_timeout, :read_timeout, :write_timeout
|
|
7
8
|
|
|
8
9
|
def initialize
|
|
9
10
|
@script_url = Cloudflare::SCRIPT_URL
|
|
@@ -13,6 +14,9 @@ module Cloudflare
|
|
|
13
14
|
@onload = nil
|
|
14
15
|
@default_data = {}
|
|
15
16
|
@auto_populate_response_in_test_env = true
|
|
17
|
+
@open_timeout = 5
|
|
18
|
+
@read_timeout = 5
|
|
19
|
+
@write_timeout = 5
|
|
16
20
|
end
|
|
17
21
|
|
|
18
22
|
# Dynamically build the URL every time, so that
|
|
@@ -11,6 +11,9 @@ module Cloudflare
|
|
|
11
11
|
# Default hidden input field name for Turnstile token submission
|
|
12
12
|
RESPONSE_FIELD_NAME = 'cf-turnstile-response'.freeze
|
|
13
13
|
|
|
14
|
+
# Longest token siteverify accepts; anything longer is rejected without a request
|
|
15
|
+
MAX_RESPONSE_LENGTH = 2048
|
|
16
|
+
|
|
14
17
|
# Default CSS class applied to Turnstile widget containers
|
|
15
18
|
WIDGET_CLASS = 'cf-turnstile'.freeze
|
|
16
19
|
end
|
|
@@ -2,6 +2,7 @@ require 'net/http'
|
|
|
2
2
|
require 'openssl'
|
|
3
3
|
|
|
4
4
|
require_relative 'constants/cloudflare'
|
|
5
|
+
require_relative 'constants/error_code'
|
|
5
6
|
|
|
6
7
|
module Cloudflare
|
|
7
8
|
module Turnstile
|
|
@@ -48,6 +49,10 @@ module Cloudflare
|
|
|
48
49
|
|
|
49
50
|
module Verification
|
|
50
51
|
def self.verify(response: nil, secret: nil, remoteip: nil, idempotency_key: nil) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
52
|
+
if response.is_a?(String) && !response.valid_encoding?
|
|
53
|
+
return VerificationResponse.new('success' => false, 'error-codes' => [ErrorCode::INVALID_INPUT_RESPONSE])
|
|
54
|
+
end
|
|
55
|
+
|
|
51
56
|
response = 'dummy-response' if auto_populate_test_response?(response)
|
|
52
57
|
|
|
53
58
|
secret ||= Rails.configuration.secret_key
|
|
@@ -55,6 +60,14 @@ module Cloudflare
|
|
|
55
60
|
raise ConfigurationError, ErrorMessage.for(ErrorCode::MISSING_INPUT_SECRET)
|
|
56
61
|
end
|
|
57
62
|
|
|
63
|
+
local_error = if missing_response?(response)
|
|
64
|
+
ErrorCode::MISSING_INPUT_RESPONSE
|
|
65
|
+
elsif invalid_response?(response)
|
|
66
|
+
ErrorCode::INVALID_INPUT_RESPONSE
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
return VerificationResponse.new('success' => false, 'error-codes' => [local_error]) if local_error
|
|
70
|
+
|
|
58
71
|
body = { 'secret' => secret, 'response' => response }
|
|
59
72
|
body['remoteip'] = remoteip if remoteip
|
|
60
73
|
body['idempotency_key'] = idempotency_key if idempotency_key
|
|
@@ -65,17 +78,20 @@ module Cloudflare
|
|
|
65
78
|
|
|
66
79
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
67
80
|
http.use_ssl = true
|
|
81
|
+
http.open_timeout = Rails.configuration.open_timeout
|
|
82
|
+
http.read_timeout = Rails.configuration.read_timeout
|
|
83
|
+
http.write_timeout = Rails.configuration.write_timeout
|
|
68
84
|
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
69
85
|
http.cert_store = OpenSSL::X509::Store.new
|
|
70
86
|
http.cert_store.set_default_paths
|
|
71
87
|
|
|
72
88
|
begin
|
|
73
89
|
res = http.request(request)
|
|
74
|
-
rescue Net::OpenTimeout, Net::ReadTimeout => e
|
|
90
|
+
rescue Net::OpenTimeout, Net::ReadTimeout, Net::WriteTimeout, Errno::ETIMEDOUT => e
|
|
75
91
|
raise ConfigurationError, "Turnstile verification timed out: #{e.message}"
|
|
76
92
|
rescue OpenSSL::SSL::SSLError => e
|
|
77
93
|
raise ConfigurationError, "SSL verification failed: #{e.message}"
|
|
78
|
-
rescue SocketError, Errno::ECONNREFUSED => e
|
|
94
|
+
rescue SocketError, EOFError, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::EHOSTUNREACH => e
|
|
79
95
|
raise ConfigurationError, "Network error during Turnstile verification: #{e.message}"
|
|
80
96
|
end
|
|
81
97
|
|
|
@@ -89,11 +105,21 @@ module Cloudflare
|
|
|
89
105
|
end
|
|
90
106
|
|
|
91
107
|
def self.auto_populate_test_response?(response)
|
|
92
|
-
(response.nil? || response.strip.empty?) &&
|
|
108
|
+
(response.nil? || (response.is_a?(String) && response.strip.empty?)) &&
|
|
93
109
|
::Rails.env.test? &&
|
|
94
110
|
Rails.configuration.auto_populate_response_in_test_env
|
|
95
111
|
end
|
|
96
112
|
private_class_method :auto_populate_test_response?
|
|
113
|
+
|
|
114
|
+
def self.missing_response?(response)
|
|
115
|
+
response.nil? || response == ''
|
|
116
|
+
end
|
|
117
|
+
private_class_method :missing_response?
|
|
118
|
+
|
|
119
|
+
def self.invalid_response?(response)
|
|
120
|
+
!response.is_a?(String) || response.strip.empty? || response.length > Cloudflare::MAX_RESPONSE_LENGTH
|
|
121
|
+
end
|
|
122
|
+
private_class_method :invalid_response?
|
|
97
123
|
end
|
|
98
124
|
end
|
|
99
125
|
end
|
|
@@ -25,6 +25,12 @@ Cloudflare::Turnstile::Rails.configure do |config|
|
|
|
25
25
|
# language: -> { I18n.locale }
|
|
26
26
|
# }
|
|
27
27
|
|
|
28
|
+
# Optional: Seconds to wait when connecting to, and reading from, Cloudflare's siteverify endpoint.
|
|
29
|
+
# Keep these short so a slow Cloudflare can't hold your app's request threads for long.
|
|
30
|
+
# config.open_timeout = 5 # connecting to siteverify
|
|
31
|
+
# config.read_timeout = 5 # reading the response
|
|
32
|
+
# config.write_timeout = 5 # sending the verification POST
|
|
33
|
+
|
|
28
34
|
# In the Rails Test environment, automatically fill in a dummy response if none was provided.
|
|
29
35
|
# This lets you keep existing controller tests without having to add
|
|
30
36
|
# params["cf-turnstile-response"] manually in every test.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cloudflare-turnstile-rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vadim Kononov
|
|
@@ -23,8 +23,9 @@ dependencies:
|
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '5.0'
|
|
26
|
-
description:
|
|
27
|
-
|
|
26
|
+
description: Cloudflare Turnstile CAPTCHA, a reCAPTCHA alternative, for Ruby on Rails.
|
|
27
|
+
View and controller helpers with Turbo, Turbo Streams, Turbolinks, CSP nonce and
|
|
28
|
+
i18n support.
|
|
28
29
|
email:
|
|
29
30
|
- vadim@konoson.com
|
|
30
31
|
executables: []
|
|
@@ -83,8 +84,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
83
84
|
- !ruby/object:Gem::Version
|
|
84
85
|
version: '0'
|
|
85
86
|
requirements: []
|
|
86
|
-
rubygems_version:
|
|
87
|
+
rubygems_version: 4.0.20
|
|
87
88
|
specification_version: 4
|
|
88
|
-
summary: Cloudflare Turnstile
|
|
89
|
-
and CSP compliance
|
|
89
|
+
summary: Cloudflare Turnstile CAPTCHA for Rails with Turbo, Turbolinks and CSP support
|
|
90
90
|
test_files: []
|