From 9f7d379c2a018c86671bfc157fe1f0cf4e31e25e Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 27 Dec 2015 09:03:06 -0800 Subject: [PATCH 1/4] Add support for Google reCAPTCHA in user registration to prevent spammers --- CHANGELOG | 1 + Gemfile | 3 +++ Gemfile.lock | 3 +++ app/controllers/registrations_controller.rb | 23 +++++++++++++++++++ app/controllers/sessions_controller.rb | 13 ++++++----- app/views/devise/shared/_signup_box.html.haml | 3 +++ config/gitlab.yml.example | 6 +++++ config/initializers/1_settings.rb | 7 ++++++ config/initializers/recaptcha.rb | 6 +++++ 9 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 config/initializers/recaptcha.rb diff --git a/CHANGELOG b/CHANGELOG index a20c3978a11..e54a6669080 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.4.0 (unreleased) + - Add support for Google reCAPTCHA in user registration to prevent spammers (Stan Hu) - Implement new UI for group page - Implement search inside emoji picker - Add API support for looking up a user by username (Stan Hu) diff --git a/Gemfile b/Gemfile index db54bf2f186..a63c84fe94b 100644 --- a/Gemfile +++ b/Gemfile @@ -35,6 +35,9 @@ gem 'omniauth-twitter', '~> 1.2.0' gem 'omniauth_crowd' gem 'rack-oauth2', '~> 1.2.1' +# reCAPTCHA protection +gem 'recaptcha', require: 'recaptcha/rails' + # Two-factor authentication gem 'devise-two-factor', '~> 2.0.0' gem 'rqrcode-rails3', '~> 0.1.7' diff --git a/Gemfile.lock b/Gemfile.lock index 4f4b10c0fb7..664a02c4bab 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -566,6 +566,8 @@ GEM trollop rdoc (3.12.2) json (~> 1.4) + recaptcha (1.0.2) + json redcarpet (3.3.3) redis (3.2.2) redis-actionpack (4.0.1) @@ -924,6 +926,7 @@ DEPENDENCIES raphael-rails (~> 2.1.2) rblineprof rdoc (~> 3.6) + recaptcha redcarpet (~> 3.3.3) redis-namespace redis-rails (~> 4.0.0) diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 3b3dc86cb68..283831f8149 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -1,10 +1,21 @@ class RegistrationsController < Devise::RegistrationsController before_action :signup_enabled? + include Recaptcha::Verify def new redirect_to(new_user_session_path) end + def create + if !Gitlab.config.recaptcha.enabled || verify_recaptcha + super + else + flash[:alert] = "There was an error with the reCAPTCHA code below. Please re-enter the code." + flash.delete :recaptcha_error + render action: 'new' + end + end + def destroy DeleteUserService.new(current_user).execute(current_user) @@ -38,4 +49,16 @@ class RegistrationsController < Devise::RegistrationsController def sign_up_params params.require(:user).permit(:username, :email, :name, :password, :password_confirmation) end + + def resource_name + :user + end + + def resource + @resource ||= User.new + end + + def devise_mapping + @devise_mapping ||= Devise.mappings[:user] + end end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 1b60d3e27d0..da4b35d322b 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,5 +1,6 @@ class SessionsController < Devise::SessionsController include AuthenticatesWithTwoFactor + include Recaptcha::ClientHelper prepend_before_action :authenticate_with_two_factor, only: [:create] prepend_before_action :store_redirect_path, only: [:new] @@ -40,7 +41,7 @@ class SessionsController < Devise::SessionsController User.find(session[:otp_user_id]) end end - + def store_redirect_path redirect_path = if request.referer.present? && (params['redirect_to_referer'] == 'yes') @@ -87,14 +88,14 @@ class SessionsController < Devise::SessionsController provider = Gitlab.config.omniauth.auto_sign_in_with_provider return unless provider.present? - # Auto sign in with an Omniauth provider only if the standard "you need to sign-in" alert is - # registered or no alert at all. In case of another alert (such as a blocked user), it is safer + # Auto sign in with an Omniauth provider only if the standard "you need to sign-in" alert is + # registered or no alert at all. In case of another alert (such as a blocked user), it is safer # to do nothing to prevent redirection loops with certain Omniauth providers. return unless flash[:alert].blank? || flash[:alert] == I18n.t('devise.failure.unauthenticated') - + # Prevent alert from popping up on the first page shown after authentication. - flash[:alert] = nil - + flash[:alert] = nil + redirect_to user_omniauth_authorize_path(provider.to_sym) end diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml index 9dc6aeffd59..3c079d7e2f8 100644 --- a/app/views/devise/shared/_signup_box.html.haml +++ b/app/views/devise/shared/_signup_box.html.haml @@ -17,6 +17,9 @@ = f.email_field :email, class: "form-control middle", placeholder: "Email", required: true .form-group.append-bottom-20#password-strength = f.password_field :password, class: "form-control bottom", id: "user_password_sign_up", placeholder: "Password", required: true + %div + - if Gitlab.config.recaptcha.enabled + = recaptcha_tags %div = f.submit "Sign up", class: "btn-create btn" diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index db68b5512b8..79fc7423b31 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -346,6 +346,12 @@ production: &base # cas3: # session_duration: 28800 + # reCAPTCHA settings. See: http://www.google.com/recaptcha + recaptcha: + enabled: false + public_key: 'YOUR_PUBLIC_KEY' + private_key: 'YOUR_PRIVATE_KEY' + # Shared file storage settings shared: # path: /mnt/gitlab # Default: shared diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 816cb0c02a9..0dc4838fec1 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -131,6 +131,13 @@ Settings.omniauth.cas3['session_duration'] ||= 8.hours Settings.omniauth['session_tickets'] ||= Settingslogic.new({}) Settings.omniauth.session_tickets['cas3'] = 'ticket' +# ReCAPTCHA settings +Settings['recaptcha'] ||= Settingslogic.new({}) +Settings.recaptcha['enabled'] = false if Settings.recaptcha['enabled'].nil? +Settings.recaptcha['public_key'] ||= Settings.recaptcha['public_key'] +Settings.recaptcha['private_key'] ||= Settings.recaptcha['private_key'] + + Settings['shared'] ||= Settingslogic.new({}) Settings.shared['path'] = File.expand_path(Settings.shared['path'] || "shared", Rails.root) diff --git a/config/initializers/recaptcha.rb b/config/initializers/recaptcha.rb new file mode 100644 index 00000000000..7509e327ae1 --- /dev/null +++ b/config/initializers/recaptcha.rb @@ -0,0 +1,6 @@ +if Gitlab.config.recaptcha.enabled + Recaptcha.configure do |config| + config.public_key = Gitlab.config.recaptcha['public_key'] + config.private_key = Gitlab.config.recaptcha['private_key'] + end +end From 6f0ee5c9089d469a59879fbc0ffd6a2f3d69687e Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 27 Dec 2015 19:47:10 -0800 Subject: [PATCH 2/4] Fix failed spec --- app/controllers/registrations_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 283831f8149..ee1006dea49 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -55,7 +55,7 @@ class RegistrationsController < Devise::RegistrationsController end def resource - @resource ||= User.new + @resource ||= User.new(sign_up_params) end def devise_mapping From 4c6591c9220676c97ddf2dda36e8e855d3196a74 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 27 Dec 2015 19:47:21 -0800 Subject: [PATCH 3/4] Make sign-up form retain fields after failed CAPTCHA test --- app/views/devise/shared/_signup_box.html.haml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml index 3c079d7e2f8..49fab016bfa 100644 --- a/app/views/devise/shared/_signup_box.html.haml +++ b/app/views/devise/shared/_signup_box.html.haml @@ -6,17 +6,18 @@ .login-heading %h3 Create an account .login-body + - user = params[:user].present? ? params[:user] : {} = form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| .devise-errors = devise_error_messages! %div - = f.text_field :name, class: "form-control top", placeholder: "Name", required: true + = f.text_field :name, class: "form-control top", value: user[:name], placeholder: "Name", required: true %div - = f.text_field :username, class: "form-control middle", placeholder: "Username", required: true + = f.text_field :username, class: "form-control middle", value: user[:username], placeholder: "Username", required: true %div - = f.email_field :email, class: "form-control middle", placeholder: "Email", required: true + = f.email_field :email, class: "form-control middle", value: user[:email], placeholder: "Email", required: true .form-group.append-bottom-20#password-strength - = f.password_field :password, class: "form-control bottom", id: "user_password_sign_up", placeholder: "Password", required: true + = f.password_field :password, class: "form-control bottom", value: user[:password], id: "user_password_sign_up", placeholder: "Password", required: true %div - if Gitlab.config.recaptcha.enabled = recaptcha_tags From 9e0f532f3eca474bbb4bdf49ea744afb23178b82 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 27 Dec 2015 20:36:33 -0800 Subject: [PATCH 4/4] Add documentation for using reCAPTCHA --- config/initializers/1_settings.rb | 2 +- doc/integration/README.md | 1 + doc/integration/recaptcha.md | 56 +++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 doc/integration/recaptcha.md diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 0dc4838fec1..fb7adc77284 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -133,7 +133,7 @@ Settings.omniauth.session_tickets['cas3'] = 'ticket' # ReCAPTCHA settings Settings['recaptcha'] ||= Settingslogic.new({}) -Settings.recaptcha['enabled'] = false if Settings.recaptcha['enabled'].nil? +Settings.recaptcha['enabled'] = false if Settings.recaptcha['enabled'].nil? Settings.recaptcha['public_key'] ||= Settings.recaptcha['public_key'] Settings.recaptcha['private_key'] ||= Settings.recaptcha['private_key'] diff --git a/doc/integration/README.md b/doc/integration/README.md index 6263353851f..2a9f76533b7 100644 --- a/doc/integration/README.md +++ b/doc/integration/README.md @@ -13,6 +13,7 @@ See the documentation below for details on how to configure these services. - [Slack](slack.md) Integrate with the Slack chat service - [OAuth2 provider](oauth_provider.md) OAuth2 application creation - [Gmail actions buttons](gmail_action_buttons_for_gitlab.md) Adds GitLab actions to messages +- [reCAPTCHA](recaptcha.md) Configure GitLab to use Google reCAPTCHA for new users GitLab Enterprise Edition contains [advanced JIRA support](http://doc.gitlab.com/ee/integration/jira.html) and [advanced Jenkins support](http://doc.gitlab.com/ee/integration/jenkins.html). diff --git a/doc/integration/recaptcha.md b/doc/integration/recaptcha.md new file mode 100644 index 00000000000..7e6f7e7e30a --- /dev/null +++ b/doc/integration/recaptcha.md @@ -0,0 +1,56 @@ +# reCAPTCHA + +GitLab leverages [Google's reCAPTCHA](https://www.google.com/recaptcha/intro/index.html) +to protect against spam and abuse. GitLab displays the CAPTCHA form on the sign-up page +to confirm that a real user, not a bot, is attempting to create an account. + +## Configuration + +To use reCAPTCHA, first you must create a public and private key. + +1. Go to the URL: https://www.google.com/recaptcha/admin + +1. Fill out the form necessary to obtain reCAPTCHA keys. + +1. On your GitLab server, open the configuration file. + + For omnibus package: + + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` + + For installations from source: + + ```sh + cd /home/git/gitlab + + sudo -u git -H editor config/gitlab.yml + ``` + +1. Enable reCAPTCHA and add the settings: + + For omnibus package: + + ```ruby + gitlab_rails['recaptcha_enabled'] = true + gitlab_rails['recaptcha_public_key'] = 'YOUR_PUBLIC_KEY' + gitlab_rails['recaptcha_private_key'] = 'YOUR_PUBLIC_KEY' + ``` + + For installation from source: + + ``` + recaptcha: + enabled: true + public_key: 'YOUR_PUBLIC_KEY' + private_key: 'YOUR_PRIVATE_KEY' + ``` + +1. Change 'YOUR_PUBLIC_KEY' to the public key from step 2. + +1. Change 'YOUR_PRIVATE_KEY' to the private key from step 2. + +1. Save the configuration file. + +1. Restart GitLab.