From eb8279384a2e9641893e85427ebe24cbbcf30cbb Mon Sep 17 00:00:00 2001 From: Tom Hughes Date: Tue, 24 Oct 2023 19:35:09 +0100 Subject: [PATCH] Add support for using an cloudflare turnstile widget during signup --- app/controllers/users_controller.rb | 27 ++++++++++++++++ app/views/users/new.html.erb | 10 ++++++ config/locales/en.yml | 3 ++ config/settings.yml | 3 ++ test/controllers/users_controller_test.rb | 38 +++++++++++++++++++++++ 5 files changed, 81 insertions(+) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 29fae6491..c37d2e60f 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -20,6 +20,13 @@ class UsersController < ApplicationController allow_thirdparty_images :only => :show allow_social_login :only => :new + content_security_policy(:only => :new) do |policy| + if Settings.turnstile_site_key + policy.frame_src(*policy.frame_src, "challenges.cloudflare.com") + policy.script_src(*policy.script_src, "challenges.cloudflare.com") + end + end + def show @user = User.find_by(:display_name => params[:display_name]) @@ -75,6 +82,10 @@ class UsersController < ApplicationController if current_user.invalid? # Something is wrong with a new user, so rerender the form render :action => "new" + elsif Settings.turnstile_site_key && !valid_turnstile_response?(params["cf-turnstile-response"]) + # Invalid turnstile response, so rerender the form + flash.now[:error] = t ".not_human" + render :action => "new" else # Save the user record if save_new_user params[:email_hmac] @@ -257,4 +268,20 @@ class UsersController < ApplicationController !blocked end + + ## + # check if a turnstile response is valid + def valid_turnstile_response?(turnstile_response) + response = OSM.http_client.post("https://challenges.cloudflare.com/turnstile/v0/siteverify", { + :secret => Settings.turnstile_secret_key, + :response => turnstile_response, + :remoteip => request.remote_ip + }) + + return false unless response.success? + + parsed_response = JSON.parse(response.body) + + parsed_response["success"] + end end diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb index 824316151..59ce69b4b 100644 --- a/app/views/users/new.html.erb +++ b/app/views/users/new.html.erb @@ -1,6 +1,9 @@ <% content_for :head do %> <%= javascript_include_tag "user" %> <%= javascript_include_tag "auth_providers" %> + <% if Settings.turnstile_site_key -%> + <%= javascript_include_tag "https://challenges.cloudflare.com/turnstile/v0/api.js", :async => true, :defer => true %> + <% end -%> <% end %> <% content_for :heading_class, "pb-0" %> @@ -66,6 +69,13 @@ <% end %> + <% if Settings.turnstile_site_key -%> +
+ +
+
+ <% end %> +

<%= t(".by_signing_up.html", :tou_link => link_to(t(".by_signing_up.tou"), "https://wiki.osmfoundation.org/wiki/Terms_of_Use", diff --git a/config/locales/en.yml b/config/locales/en.yml index 26dd03e09..22139f942 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -3181,6 +3181,9 @@ en: html: 'Your address is not displayed publicly, see our %{privacy_policy_link} for more information.' or: "or" use external auth: "or sign up with a third party" + verify_human: Verify you're a human + create: + not_human: Unfortunately, we can't confirm that you are a human. This is required to create new accounts. no_such_user: title: "No such user" heading: "The user %{user} does not exist" diff --git a/config/settings.yml b/config/settings.yml index 7161452ab..c85eb7276 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -253,3 +253,6 @@ smtp_password: null # -----BEGIN PRIVATE KEY----- # ... # -----END PRIVATE KEY----- +# Credentials for optional cloudflare turnstile widget +#turnstile_site_key: +#turnstile_secret_key: diff --git a/test/controllers/users_controller_test.rb b/test/controllers/users_controller_test.rb index f4e744b87..7901bd967 100644 --- a/test/controllers/users_controller_test.rb +++ b/test/controllers/users_controller_test.rb @@ -191,6 +191,44 @@ class UsersControllerTest < ActionDispatch::IntegrationTest end end + def test_create_turnstile_success + user = build(:user, :pending) + + stub_request(:post, "https://challenges.cloudflare.com/turnstile/v0/siteverify") + .with(:body => { "remoteip" => "127.0.0.1", "response" => "turnstile_response", "secret" => "turnstile_secret" }) + .to_return(:body => JSON.generate(:success => true)) + + with_settings(:turnstile_site_key => "turnstile_site", + :turnstile_secret_key => "turnstile_secret") do + assert_difference "User.count", 1 do + assert_difference "ActionMailer::Base.deliveries.size", 1 do + perform_enqueued_jobs do + post users_path, :params => { :user => user.attributes, "cf-turnstile-response" => "turnstile_response" } + end + end + end + end + end + + def test_create_turnstile_failure + user = build(:user, :pending) + + stub_request(:post, "https://challenges.cloudflare.com/turnstile/v0/siteverify") + .with(:body => { "remoteip" => "127.0.0.1", "response" => "turnstile_response", "secret" => "turnstile_secret" }) + .to_return(:body => JSON.generate(:success => false)) + + with_settings(:turnstile_site_key => "turnstile_site", + :turnstile_secret_key => "turnstile_secret") do + assert_no_difference "User.count" do + assert_no_difference "ActionMailer::Base.deliveries.size" do + perform_enqueued_jobs do + post users_path, :params => { :user => user.attributes, "cf-turnstile-response" => "turnstile_response" } + end + end + end + end + end + def test_go_public user = create(:user, :data_public => false) session_for(user) -- 2.39.5