Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix hcaptcha usage and provide as a setting #14266

Merged
merged 1 commit into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def validate_settings
end

def permitted_params
params.permit(:recaptcha_type, :website_key, :secret_key)
params.permit(:recaptcha_type, :website_key, :secret_key, :response_limit)
end

def default_breadcrumb
Expand Down
21 changes: 19 additions & 2 deletions modules/recaptcha/app/controllers/recaptcha/request_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,17 @@ class RequestController < ApplicationController
# Skip if user has confirmed already
before_action :skip_if_user_verified

# Ensure we set the correct configuration for rendering/verifying the captcha
around_action :set_captcha_settings

##
# Request verification form
def perform
use_content_security_policy_named_append(:recaptcha)
if OpenProject::Recaptcha::Configuration.use_hcaptcha?
use_content_security_policy_named_append(:hcaptcha)
else
use_content_security_policy_named_append(:recaptcha)
end
end

def verify
Expand All @@ -37,6 +44,16 @@ def verify

private

def set_captcha_settings(&block)
if OpenProject::Recaptcha::Configuration.use_hcaptcha?
Recaptcha.with_configuration(verify_url: OpenProject::Recaptcha.hcaptcha_verify_url,
api_server_url: OpenProject::Recaptcha.hcaptcha_api_server_url,
&block)
else
block.call
end
end

##
# Insert that the account was verified
def save_recaptcha_verification_success!
Expand All @@ -49,7 +66,7 @@ def recaptcha_version
case recaptcha_settings['recaptcha_type']
when ::OpenProject::Recaptcha::TYPE_DISABLED
0
when ::OpenProject::Recaptcha::TYPE_V2
when ::OpenProject::Recaptcha::TYPE_V2, ::OpenProject::Recaptcha::TYPE_HCAPTCHA
2
when ::OpenProject::Recaptcha::TYPE_V3
3
Expand Down
3 changes: 2 additions & 1 deletion modules/recaptcha/app/helpers/recaptcha_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ def recaptcha_available_options
[
[I18n.t('recaptcha.settings.type_disabled'), ::OpenProject::Recaptcha::TYPE_DISABLED],
[I18n.t('recaptcha.settings.type_v2'), ::OpenProject::Recaptcha::TYPE_V2],
[I18n.t('recaptcha.settings.type_v3'), ::OpenProject::Recaptcha::TYPE_V3]
[I18n.t('recaptcha.settings.type_v3'), ::OpenProject::Recaptcha::TYPE_V3],
[I18n.t('recaptcha.settings.type_hcaptcha'), ::OpenProject::Recaptcha::TYPE_HCAPTCHA]
]
end

Expand Down
11 changes: 11 additions & 0 deletions modules/recaptcha/app/views/recaptcha/admin/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
</div>
<div class="form--field-instructions">
<%= I18n.t('recaptcha.settings.recaptcha_description_html',
hcaptcha_link: link_to('https://docs.hcaptcha.com/switch/', 'https://docs.hcaptcha.com/switch/', target: '_blan'),
recaptcha_link: link_to('https://www.google.com/recaptcha', 'https://www.google.com/recaptcha', target: '_blank')).html_safe %>
</div>
</div>
Expand All @@ -40,6 +41,16 @@
<%= I18n.t('recaptcha.settings.secret_key_text') %>
</div>
</div>
<div class="form--field">
<label class="form--label" for='secret_key'><%= t('recaptcha.settings.response_limit') %></label>
<div class="form--field-container">
<%= styled_text_field_tag 'response_limit',
Setting.plugin_openproject_recaptcha['response_limit'] %>
</div>
<div class="form--field-instructions">
<%= I18n.t('recaptcha.settings.response_limit_text') %>
</div>
</div>
</fieldset>
<%= styled_submit_tag t(:button_apply), class: '-highlight' %>
<% end %>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
<div id="login-form" class="form -bordered">
<%= styled_form_tag({ action: :verify }, { :autocomplete => "off", :id => 'submit_captcha' }) do %>
<h2><%= t 'recaptcha.verify_account' %></h2>
<% if recaptcha_settings['recaptcha_type'] == ::OpenProject::Recaptcha::TYPE_V2 %>
<% if [OpenProject::Recaptcha::TYPE_V2, OpenProject::Recaptcha::TYPE_HCAPTCHA].include?(recaptcha_settings['recaptcha_type']) %>
<% input_name = "g-recaptcha-response" %>
<input type="hidden" name="<%= input_name %>" />
<%= recaptcha_tags(
nonce: content_security_policy_script_nonce,
callback: 'submitRecaptchaForm',
site_key: recaptcha_settings['website_key']
site_key: recaptcha_settings['website_key'],
) %>

<%= nonced_javascript_tag do %>
Expand Down
7 changes: 0 additions & 7 deletions modules/recaptcha/config/initializers/recaptcha.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
Recaptcha.configure do |config|
# site_key and secret_key are defined via ENV already (RECAPTCHA_SITE_KEY, RECAPTCHA_SECRET_KEY)

config.verify_url = OpenProject::Recaptcha.verify_url_override || config.verify_url
config.api_server_url = OpenProject::Recaptcha.api_server_url_override || config.api_server_url
end

module RecaptchaLimitOverride
def invalid_response?(resp)
return super unless OpenProject::Recaptcha::use_hcaptcha?
Expand Down
6 changes: 6 additions & 0 deletions modules/recaptcha/config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,22 @@ en:
error_captcha: "Your account could not be verified. Please contact an administrator."
settings:
website_key: 'Website key'
response_limit: 'Response limit for HCaptcha'
response_limit_text: 'The maximum number of characters to treat the HCaptcha response as valid.'
website_key_text: 'Enter the website key you created on the reCAPTCHA admin console for this domain.'
secret_key: 'Secret key'
secret_key_text: 'Enter the secret key you created on the reCAPTCHA admin console.'
type: 'Use reCAPTCHA'
type_disabled: 'Disable reCAPTCHA'
type_v2: 'reCAPTCHA v2'
type_v3: 'reCAPTCHA v3'
type_hcaptcha: 'HCaptcha'
recaptcha_description_html: >
reCAPTCHA is a free service by Google that can be enabled for your OpenProject instance.
If enabled, a captcha form will be rendered upon login for all users that have not verified a captcha yet.
<br/>
Please see the following link for more details on reCAPTCHA and their versions, and how
to create the website and secret keys: %{recaptcha_link}
<br/>
HCaptcha is a Google-free alternative that you can use if you do not want to use reCAPTCHA.
See this link for more information: %{hcaptcha_link}
1 change: 1 addition & 0 deletions modules/recaptcha/lib/open_project/recaptcha.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module Recaptcha
TYPE_DISABLED ||= 'disabled'
TYPE_V2 ||= 'v2'
TYPE_V3 ||= 'v3'
TYPE_HCAPTCHA ||= 'hcaptcha'

require "open_project/recaptcha/engine"
require "open_project/recaptcha/configuration"
Expand Down
16 changes: 8 additions & 8 deletions modules/recaptcha/lib/open_project/recaptcha/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@ module Configuration

extend self

def use_hcaptcha?
OpenProject::Configuration[CONFIG_KEY]
def enabled?
type.present? && type != ::OpenProject::Recaptcha::TYPE_DISABLED
end

def hcaptcha_response_limit
@hcaptcha_response_limit ||= (ENV["RECAPTCHA_RESPONSE_LIMIT"].presence || 5000).to_i
def use_hcaptcha?
type == ::OpenProject::Recaptcha::TYPE_HCAPTCHA
end

def api_server_url_override
ENV["RECAPTCHA_API_SERVER_URL"].presence || ((use_hcaptcha? || nil) && hcaptcha_api_server_url)
def type
@type ||= ::Setting.plugin_openproject_recaptcha['recaptcha_type']
end

def verify_url_override
ENV["RECAPTCHA_VERIFY_URL"].presence || ((use_hcaptcha? || nil) && hcaptcha_verify_url)
def hcaptcha_response_limit
(::Setting.plugin_openproject_recaptcha['response_limit'] || '5000').to_i
end

def hcaptcha_verify_url
Expand Down
29 changes: 13 additions & 16 deletions modules/recaptcha/lib/open_project/recaptcha/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ class Engine < ::Rails::Engine
author_url: 'https://www.openproject.org',
settings: {
default: {
recaptcha_type: ::OpenProject::Recaptcha::TYPE_DISABLED
recaptcha_type: ::OpenProject::Recaptcha::TYPE_DISABLED,
response_limit: 5000,
}
},
bundled: true do
Expand All @@ -28,27 +29,23 @@ class Engine < ::Rails::Engine

config.after_initialize do
SecureHeaders::Configuration.named_append(:recaptcha) do
if OpenProject::Recaptcha.use_hcaptcha?
value = %w(https://*.hcaptcha.com)
keys = %i(frame_src script_src style_src connect_src)

keys.index_with value
else
{
frame_src: %w[https://www.recaptcha.net/recaptcha/ https://www.gstatic.com/recaptcha/]
}
end
{
frame_src: %w[https://www.recaptcha.net/recaptcha/ https://www.gstatic.com/recaptcha/]
}
end

SecureHeaders::Configuration.named_append(:hcaptcha) do
value = %w(https://*.hcaptcha.com)
keys = %i(frame_src script_src style_src connect_src)

keys.index_with value
end

OpenProject::Authentication::Stage.register(
:recaptcha,
nil,
run_after_activation: true,
active: -> {
type = Setting.plugin_openproject_recaptcha['recaptcha_type']

type.present? && type.to_s != ::OpenProject::Recaptcha::TYPE_DISABLED
}
active: -> { OpenProject::Recaptcha.enabled? }
) do
recaptcha_request_path
end
Expand Down
Loading