Commit 9dd69c82 authored by Thong Kuah's avatar Thong Kuah

Merge branch 'ashmckenzie/new-user-signup-ab-test' into 'master'

New user signup with/without reCAPTCHA A/B test

See merge request gitlab-org/gitlab-ee!14019
parents a54b874e 94a55900
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
class RegistrationsController < Devise::RegistrationsController class RegistrationsController < Devise::RegistrationsController
include Recaptcha::Verify include Recaptcha::Verify
include AcceptsPendingInvitations include AcceptsPendingInvitations
include RecaptchaExperimentHelper
prepend_before_action :check_captcha, only: :create prepend_before_action :check_captcha, only: :create
before_action :whitelist_query_limiting, only: [:destroy] before_action :whitelist_query_limiting, only: [:destroy]
...@@ -15,13 +16,6 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -15,13 +16,6 @@ class RegistrationsController < Devise::RegistrationsController
end end
def create def create
# To avoid duplicate form fields on the login page, the registration form
# names fields using `new_user`, but Devise still wants the params in
# `user`.
if params["new_#{resource_name}"].present? && params[resource_name].blank?
params[resource_name] = params.delete(:"new_#{resource_name}")
end
accept_pending_invitations accept_pending_invitations
super do |new_user| super do |new_user|
...@@ -74,19 +68,35 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -74,19 +68,35 @@ class RegistrationsController < Devise::RegistrationsController
end end
def after_sign_up_path_for(user) def after_sign_up_path_for(user)
Gitlab::AppLogger.info("User Created: username=#{user.username} email=#{user.email} ip=#{request.remote_ip} confirmed:#{user.confirmed?}") Gitlab::AppLogger.info(user_created_message(confirmed: user.confirmed?))
user.confirmed? ? stored_location_for(user) || dashboard_projects_path : users_almost_there_path user.confirmed? ? stored_location_for(user) || dashboard_projects_path : users_almost_there_path
end end
def after_inactive_sign_up_path_for(resource) def after_inactive_sign_up_path_for(resource)
Gitlab::AppLogger.info("User Created: username=#{resource.username} email=#{resource.email} ip=#{request.remote_ip} confirmed:false") Gitlab::AppLogger.info(user_created_message)
users_almost_there_path users_almost_there_path
end end
private private
def user_created_message(confirmed: false)
"User Created: username=#{resource.username} email=#{resource.email} ip=#{request.remote_ip} confirmed:#{confirmed}"
end
def ensure_correct_params!
# To avoid duplicate form fields on the login page, the registration form
# names fields using `new_user`, but Devise still wants the params in
# `user`.
if params["new_#{resource_name}"].present? && params[resource_name].blank?
params[resource_name] = params.delete(:"new_#{resource_name}")
end
end
def check_captcha def check_captcha
return unless Feature.enabled?(:registrations_recaptcha, default_enabled: true) ensure_correct_params!
return unless Feature.enabled?(:registrations_recaptcha, default_enabled: true) # reCAPTCHA on the UI will still display however
return unless show_recaptcha_sign_up?
return unless Gitlab::Recaptcha.load_configurations! return unless Gitlab::Recaptcha.load_configurations!
return if verify_recaptcha return if verify_recaptcha
......
# frozen_string_literal: true
module RecaptchaExperimentHelper
def show_recaptcha_sign_up?
!!Gitlab::Recaptcha.enabled?
end
end
RecaptchaExperimentHelper.prepend(EE::RecaptchaExperimentHelper)
...@@ -7,7 +7,10 @@ ...@@ -7,7 +7,10 @@
= f.check_box :recaptcha_enabled, class: 'form-check-input' = f.check_box :recaptcha_enabled, class: 'form-check-input'
= f.label :recaptcha_enabled, class: 'form-check-label' do = f.label :recaptcha_enabled, class: 'form-check-label' do
Enable reCAPTCHA Enable reCAPTCHA
%span.form-text.text-muted#recaptcha_help_block Helps prevent bots from creating accounts - recaptcha_v2_link_url = 'https://developers.google.com/recaptcha/docs/versions'
- recaptcha_v2_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: recaptcha_v2_link_url }
%span.form-text.text-muted#recaptcha_help_block
= _('Helps prevent bots from creating accounts. We currently only support %{recaptcha_v2_link_start}reCAPTCHA v2%{recaptcha_v2_link_end}').html_safe % { recaptcha_v2_link_start: recaptcha_v2_link_start, recaptcha_v2_link_end: '</a>'.html_safe }
.form-group .form-group
= f.label :recaptcha_site_key, 'reCAPTCHA Site Key', class: 'label-bold' = f.label :recaptcha_site_key, 'reCAPTCHA Site Key', class: 'label-bold'
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
= accept_terms_label.html_safe = accept_terms_label.html_safe
= render_if_exists 'devise/shared/email_opted_in', f: f = render_if_exists 'devise/shared/email_opted_in', f: f
%div %div
- if Gitlab::Recaptcha.enabled? - if show_recaptcha_sign_up?
= recaptcha_tags = recaptcha_tags
.submit-container .submit-container
= f.submit _("Register"), class: "btn-register btn qa-new-user-register-button" = f.submit _("Register"), class: "btn-register btn qa-new-user-register-button"
...@@ -3,9 +3,17 @@ ...@@ -3,9 +3,17 @@
module EE module EE
module RegistrationsController module RegistrationsController
extend ActiveSupport::Concern extend ActiveSupport::Concern
extend ::Gitlab::Utils::Override
private private
override :user_created_message
def user_created_message(confirmed: false)
experiments = "experiment_growth_recaptcha?#{show_recaptcha_sign_up?}"
super(confirmed: confirmed) + ", experiments:#{experiments}"
end
def sign_up_params def sign_up_params
clean_params = params.require(:user).permit(:username, :email, :email_confirmation, :name, :password, :email_opted_in) clean_params = params.require(:user).permit(:username, :email, :email_confirmation, :name, :password, :email_opted_in)
......
# frozen_string_literal: true
module EE
module RecaptchaExperimentHelper
include FlipperSessionHelper
extend ::Gitlab::Utils::Override
EXPERIMENT_GROWTH_RECAPTCHA_FEATURE_NAME = :experiment_growth_recaptcha
override :show_recaptcha_sign_up?
def show_recaptcha_sign_up?
super && experiment_enabled_for_session?
end
private
def experiment_enabled_for_session?
# If EXPERIMENT_GROWTH_RECAPTCHA_FEATURE_NAME is not set, we should show
# reCAPTCHA on the sign_up page
return true unless recaptcha_sign_up_experiment_set?
::Feature.enabled?(EXPERIMENT_GROWTH_RECAPTCHA_FEATURE_NAME, flipper_session)
end
def recaptcha_sign_up_experiment_set?
::Feature.persisted?(recaptcha_sign_up_experiment_feature)
end
def recaptcha_sign_up_experiment_feature
::Feature.get(EXPERIMENT_GROWTH_RECAPTCHA_FEATURE_NAME)
end
end
end
...@@ -27,5 +27,16 @@ describe RegistrationsController do ...@@ -27,5 +27,16 @@ describe RegistrationsController do
expect(user.email_opted_in_at).to be_nil expect(user.email_opted_in_at).to be_nil
end end
end end
context 'when recaptcha experiment enabled' do
it "logs a 'User Created' message including the experiment state" do
user_params = { user: attributes_for(:user) }
allow_any_instance_of(EE::RecaptchaExperimentHelper).to receive(:show_recaptcha_sign_up?).and_return(true)
expect(Gitlab::AppLogger).to receive(:info).with(/\AUser Created: .+experiment_growth_recaptcha\?true\z/).and_call_original
post :create, params: user_params
end
end
end end
end end
# frozen_string_literal: true
require 'spec_helper'
describe RecaptchaExperimentHelper, type: :helper do
using RSpec::Parameterized::TableSyntax
describe '.show_recaptcha_sign_up?' do
context 'when reCAPTCHA is disabled' do
it 'returns false' do
stub_application_setting(recaptcha_enabled: false)
expect(helper.show_recaptcha_sign_up?).to be(false)
end
end
context 'when reCAPTCHA is enabled' do
before do
stub_application_setting(recaptcha_enabled: true)
end
context 'and experiment_growth_recaptcha has not been set' do
it 'returns true' do
expect(helper.show_recaptcha_sign_up?).to be(true)
end
end
context 'and experiment_growth_recaptcha has been set' do
let(:feature_name) { described_class::EXPERIMENT_GROWTH_RECAPTCHA_FEATURE_NAME }
before(:all) do
# We need to create a '50%' of actors feature flag before _any_ test
# runs and need to continue to use the same feature throughout the
# test duration.
fifty_percent = ::Feature.flipper.actors(50)
::Feature.flipper[described_class::EXPERIMENT_GROWTH_RECAPTCHA_FEATURE_NAME].enable(fifty_percent)
end
after(:all) do
Feature.flipper.remove(described_class::EXPERIMENT_GROWTH_RECAPTCHA_FEATURE_NAME)
end
where(:flipper_session_id, :expected_result) do
'00687625-667c-480c-ae2a-9bf861ddd909' | true
'b8b78156-f7b8-4bf4-b906-06a899b84ea3' | false
'e622a262-6e48-41ba-b19f-2d91c24f17a3' | true
'd2c0aae1-bc08-4000-bbb2-0dd2802f67e2' | false
end
with_them do
it "returns expected_result" do
allow(Feature).to receive(:enabled?).and_call_original # Allow Feature to _really_ work.
allow(helper).to receive(:flipper_session).and_return(FlipperSession.new(flipper_session_id))
expect(helper.show_recaptcha_sign_up?).to eq(expected_result)
end
end
end
end
end
end
...@@ -6803,6 +6803,9 @@ msgstr "" ...@@ -6803,6 +6803,9 @@ msgstr ""
msgid "Help page text and support page url." msgid "Help page text and support page url."
msgstr "" msgstr ""
msgid "Helps prevent bots from creating accounts. We currently only support %{recaptcha_v2_link_start}reCAPTCHA v2%{recaptcha_v2_link_end}"
msgstr ""
msgid "Hide archived projects" msgid "Hide archived projects"
msgstr "" msgstr ""
......
...@@ -6,7 +6,8 @@ describe RegistrationsController do ...@@ -6,7 +6,8 @@ describe RegistrationsController do
include TermsHelper include TermsHelper
describe '#create' do describe '#create' do
let(:user_params) { { user: { name: 'new_user', username: 'new_username', email: 'new@user.com', password: 'Any_password' } } } let(:base_user_params) { { name: 'new_user', username: 'new_username', email: 'new@user.com', password: 'Any_password' } }
let(:user_params) { { user: base_user_params } }
context 'email confirmation' do context 'email confirmation' do
around do |example| around do |example|
...@@ -105,6 +106,20 @@ describe RegistrationsController do ...@@ -105,6 +106,20 @@ describe RegistrationsController do
expect(subject.current_user.terms_accepted?).to be(true) expect(subject.current_user.terms_accepted?).to be(true)
end end
end end
it "logs a 'User Created' message" do
stub_feature_flags(registrations_recaptcha: false)
expect(Gitlab::AppLogger).to receive(:info).with(/\AUser Created: username=new_username email=new@user.com.+\z/).and_call_original
post(:create, params: user_params)
end
it 'handles when params are new_user' do
post(:create, params: { new_user: base_user_params })
expect(subject.current_user).not_to be_nil
end
end end
describe '#destroy' do describe '#destroy' do
......
# frozen_string_literal: true
require 'spec_helper'
describe RecaptchaExperimentHelper, type: :helper do
describe '.show_recaptcha_sign_up?' do
context 'when reCAPTCHA is disabled' do
it 'returns false' do
stub_application_setting(recaptcha_enabled: false)
expect(helper.show_recaptcha_sign_up?).to be(false)
end
end
context 'when reCAPTCHA is enabled' do
it 'returns true' do
stub_application_setting(recaptcha_enabled: true)
expect(helper.show_recaptcha_sign_up?).to be(true)
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment