From 3d9f99a883adb0e6b578d6737bacb73aa76bccba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Vigui=C3=A9?= Date: Fri, 1 May 2020 13:56:54 +0200 Subject: [PATCH] add optional registration verification with hCaptcha --- .env.production.sample | 6 ++++++ Gemfile | 2 ++ Gemfile.lock | 8 ++++++++ app/controllers/admin/dashboard_controller.rb | 1 + app/controllers/api/v1/accounts_controller.rb | 11 ++++++----- app/controllers/auth/registrations_controller.rb | 10 ++++++++++ app/helpers/application_helper.rb | 1 + app/javascript/flavours/glitch/styles/forms.scss | 4 ++++ app/javascript/styles/mastodon/forms.scss | 4 ++++ app/views/about/_registration.html.haml | 4 ++++ app/views/admin/dashboard/index.html.haml | 2 ++ app/views/auth/registrations/new.html.haml | 4 ++++ config/initializers/content_security_policy.rb | 8 +++++--- config/initializers/hcaptcha.rb | 4 ++++ config/locales/en.yml | 3 +++ 15 files changed, 64 insertions(+), 8 deletions(-) create mode 100644 config/initializers/hcaptcha.rb diff --git a/.env.production.sample b/.env.production.sample index a752298e89..cd3889f08e 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -290,3 +290,9 @@ STREAMING_CLUSTER_NUM=1 # Only allow federation with whitelisted domains, see # https://docs.joinmastodon.org/admin/config/#whitelist_mode # WHITELIST_MODE=true + +# Optional hCaptcha verification on user signup +# If the feature is enabled and no keys are specified here, dummy keys will be used and no actual verification will be taking place. +# HCAPTCHA_ENABLED= +# HCAPTCHA_SITE_KEY= +# HCAPTCHA_SECRET_KEY= \ No newline at end of file diff --git a/Gemfile b/Gemfile index d59aa9d122..f17958b3d8 100644 --- a/Gemfile +++ b/Gemfile @@ -100,6 +100,8 @@ gem 'tzinfo-data', '~> 1.2019' gem 'webpacker', '~> 4.2' gem 'webpush' +gem 'hcaptcha', git: 'https://github.com/firstmoversadvantage/hcaptcha', ref: 'e65246bea49374566a9dbaead59fdb35b46b086d', require: 'hcaptcha/rails' + gem 'json-ld' gem 'json-ld-preloaded', '~> 3.1' gem 'rdf-normalize', '~> 0.4' diff --git a/Gemfile.lock b/Gemfile.lock index 13f6bed594..87a77f15c3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -27,6 +27,13 @@ GIT ref: fd184883048b922b176939f851338d0a4971a532 specs: nilsimsa (1.1.2) + +GIT + remote: https://github.com/firstmoversadvantage/hcaptcha + revision: e65246bea49374566a9dbaead59fdb35b46b086d + ref: e65246bea49374566a9dbaead59fdb35b46b086d + specs: + hcaptcha (6.0.1) GEM remote: https://rubygems.org/ @@ -704,6 +711,7 @@ DEPENDENCIES fuubar (~> 2.5) goldfinger (~> 2.1) hamlit-rails (~> 0.2) + hcaptcha health_check! hiredis (~> 0.6) htmlentities (~> 4.3) diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb index 4116f99f45..328e487822 100644 --- a/app/controllers/admin/dashboard_controller.rb +++ b/app/controllers/admin/dashboard_controller.rb @@ -37,6 +37,7 @@ module Admin @keybase_integration = Setting.enable_keybase @spam_check_enabled = Setting.spam_check_enabled @trends_enabled = Setting.trends + @hcaptcha_enabled = ENV['HCAPTCHA_ENABLED'] == 'true' end private diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb index 0080faf330..78b7fdb0d8 100644 --- a/app/controllers/api/v1/accounts_controller.rb +++ b/app/controllers/api/v1/accounts_controller.rb @@ -21,13 +21,14 @@ class Api::V1::AccountsController < Api::BaseController end def create - token = AppSignUpService.new.call(doorkeeper_token.application, account_params) - response = Doorkeeper::OAuth::TokenResponse.new(token) + not_found +# token = AppSignUpService.new.call(doorkeeper_token.application, account_params) +# response = Doorkeeper::OAuth::TokenResponse.new(token) - headers.merge!(response.headers) +# headers.merge!(response.headers) - self.response_body = Oj.dump(response.body) - self.status = response.status +# self.response_body = Oj.dump(response.body) +# self.status = response.status end def follow diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb index f6a85d87e1..563c3cdb99 100644 --- a/app/controllers/auth/registrations_controller.rb +++ b/app/controllers/auth/registrations_controller.rb @@ -12,6 +12,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController before_action :set_body_classes, only: [:new, :create, :edit, :update] before_action :require_not_suspended!, only: [:update] before_action :set_cache_headers, only: [:edit, :update] + before_action :check_captcha, only: [:create] skip_before_action :require_functional!, only: [:edit, :update] @@ -93,6 +94,15 @@ class Auth::RegistrationsController < Devise::RegistrationsController private + def check_captcha + if ENV['HCAPTCHA_ENABLED'] == 'true' && !verify_hcaptcha + self.resource = resource_class.new sign_up_params + resource.validate + flash[:alert] = Hcaptcha::Helpers.to_error_message(:verification_failed) + respond_with_navigational(resource) { render :new } + end + end + def set_pack use_pack %w(edit update).include?(action_name) ? 'admin' : 'auth' end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 40f914f1e9..d4e94fa9e3 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -148,4 +148,5 @@ module ApplicationHelper json = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(state_params), serializer: InitialStateSerializer).to_json content_tag(:script, json_escape(json).html_safe, id: 'initial-state', type: 'application/json') end + end diff --git a/app/javascript/flavours/glitch/styles/forms.scss b/app/javascript/flavours/glitch/styles/forms.scss index 396e87c6c1..3a67ee456a 100644 --- a/app/javascript/flavours/glitch/styles/forms.scss +++ b/app/javascript/flavours/glitch/styles/forms.scss @@ -230,6 +230,10 @@ code { .input:last-child { margin-bottom: 0; } + + .h-captcha { + text-align: center; + } } .fields-row { diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss index c9ad68f941..cb97526fe4 100644 --- a/app/javascript/styles/mastodon/forms.scss +++ b/app/javascript/styles/mastodon/forms.scss @@ -239,6 +239,10 @@ code { .input:last-child { margin-bottom: 0; } + + .h-captcha { + text-align: center; + } } .fields-row { diff --git a/app/views/about/_registration.html.haml b/app/views/about/_registration.html.haml index af28e21749..c1181bb43c 100644 --- a/app/views/about/_registration.html.haml +++ b/app/views/about/_registration.html.haml @@ -17,6 +17,10 @@ .fields-group = f.input :agreement, as: :boolean, wrapper: :with_label, label: t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path), disabled: closed_registrations? + + - if ENV['HCAPTCHA_ENABLED'] == 'true' + .fields-group + = hcaptcha_tags .actions = f.button :button, sign_up_message, type: :submit, class: 'button button-primary', disabled: closed_registrations? diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index 249e125634..255eb487fe 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -124,6 +124,8 @@ = feature_hint('PAM', @pam_enabled) %li = feature_hint(t('admin.dashboard.hidden_service'), @hidden_service) + %li + = feature_hint(t('admin.dashboard.feature_hcaptcha'), @hcaptcha_enabled) .dashboard__widgets__trends %div diff --git a/app/views/auth/registrations/new.html.haml b/app/views/auth/registrations/new.html.haml index bcd66fb8a0..338a2c27ad 100644 --- a/app/views/auth/registrations/new.html.haml +++ b/app/views/auth/registrations/new.html.haml @@ -35,6 +35,10 @@ .fields-group = f.input :agreement, as: :boolean, wrapper: :with_label, label: whitelist_mode? ? t('auth.checkbox_agreement_without_rules_html', terms_path: terms_path) : t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path) + - if ENV['HCAPTCHA_ENABLED'] == 'true' + .fields-group + = hcaptcha_tags + .actions = f.button :button, @invite.present? ? t('auth.register') : sign_up_message, type: :submit diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb index d1e6701e23..1ea29783a8 100644 --- a/config/initializers/content_security_policy.rb +++ b/config/initializers/content_security_policy.rb @@ -16,6 +16,8 @@ if Rails.env.production? attachments_host = nil end + hcaptcha_hosts = ["https://hcaptcha.com", "https://*.hcaptcha.com"] + data_hosts << attachments_host unless attachments_host.nil? if ENV['PAPERCLIP_ROOT_URL'] @@ -31,12 +33,12 @@ if Rails.env.production? p.base_uri :none p.default_src :none p.frame_ancestors :none - p.script_src :self, assets_host + p.script_src :self, assets_host, *hcaptcha_hosts p.font_src :self, assets_host p.img_src :self, :data, :blob, *data_hosts - p.style_src :self, :unsafe_inline, assets_host + p.style_src :self, :unsafe_inline, assets_host, *hcaptcha_hosts p.media_src :self, :data, *data_hosts - p.frame_src :self, :https + p.frame_src :self, :https, *hcaptcha_hosts p.child_src :self, :blob, assets_host p.worker_src :self, :blob, assets_host p.connect_src :self, :blob, :data, Rails.configuration.x.streaming_api_base_url, *data_hosts diff --git a/config/initializers/hcaptcha.rb b/config/initializers/hcaptcha.rb new file mode 100644 index 0000000000..b08b4341ff --- /dev/null +++ b/config/initializers/hcaptcha.rb @@ -0,0 +1,4 @@ +Hcaptcha.configure do |config| + config.site_key = ENV.fetch('HCAPTCHA_SITE_KEY') { '10000000-ffff-ffff-ffff-000000000001' } + config.secret_key = ENV.fetch('HCAPTCHA_SECRET_KEY') { '0x0000000000000000000000000000000000000000' } +end \ No newline at end of file diff --git a/config/locales/en.yml b/config/locales/en.yml index edb7bb6845..432d8e910f 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -324,6 +324,7 @@ en: backlog: backlogged jobs config: Configuration feature_deletions: Account deletions + feature_hcaptcha: hCaptcha feature_invites: Invite links feature_profile_directory: Profile directory feature_registrations: Registrations @@ -542,6 +543,8 @@ en: deletion: desc_html: Allow anyone to delete their account title: Open account deletion + errors: + captcha_fail: Captcha verification failed min_invite_role: disabled: No one title: Allow invitations by