summaryrefslogtreecommitdiff
path: root/app/controllers/sessions_controller.rb
diff options
context:
space:
mode:
authorMałgorzata Ksionek <mksionek@gitlab.com>2019-07-18 10:27:02 +0200
committerMałgorzata Ksionek <mksionek@gitlab.com>2019-07-31 11:47:55 +0200
commitdfcf4cf5f1e87a29f0d9fcc5ff2bba47258893bb (patch)
tree9343675b9a675bc87236e2b65f4b9d2af18762c2 /app/controllers/sessions_controller.rb
parentd93da8888bd54531e5f5df2047902ae90635b8d0 (diff)
downloadgitlab-ce-dfcf4cf5f1e87a29f0d9fcc5ff2bba47258893bb.tar.gz
Add captcha if there are multiple failed login attempts
Add method to store session ids by ip Add new specs for storing session ids Add cleaning up records after login Add retrieving anonymous sessions Add login recaptcha setting Add new setting to sessions controller Add conditions for showing captcha Add sessions controller specs Add admin settings specs for login protection Add new settings to api Add stub to devise spec Add new translation key Add cr remarks Rename class call Add cr remarks Change if-clause for consistency Add cr remarks Add code review remarks Refactor AnonymousSession class Add changelog entry Move AnonymousSession class to lib Move store unauthenticated sessions to sessions controller Move link to recaptcha info Regenerate text file Improve copy on the spam page Change action filter for storing anonymous sessions Fix rubocop offences Add code review remarks
Diffstat (limited to 'app/controllers/sessions_controller.rb')
-rw-r--r--app/controllers/sessions_controller.rb44
1 files changed, 41 insertions, 3 deletions
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index 1880bead3ee..7b682cc0cc5 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -21,10 +21,13 @@ class SessionsController < Devise::SessionsController
prepend_before_action :ensure_password_authentication_enabled!, if: -> { action_name == 'create' && password_based_login? }
before_action :auto_sign_in_with_provider, only: [:new]
+ before_action :store_unauthenticated_sessions, only: [:new]
+ before_action :save_failed_login, if: :action_new_and_failed_login?
before_action :load_recaptcha
- after_action :log_failed_login, if: -> { action_name == 'new' && failed_login? }
- helper_method :captcha_enabled?
+ after_action :log_failed_login, if: :action_new_and_failed_login?
+
+ helper_method :captcha_enabled?, :captcha_on_login_required?
# protect_from_forgery is already prepended in ApplicationController but
# authenticate_with_two_factor which signs in the user is prepended before
@@ -38,6 +41,7 @@ class SessionsController < Devise::SessionsController
protect_from_forgery with: :exception, prepend: true
CAPTCHA_HEADER = 'X-GitLab-Show-Login-Captcha'.freeze
+ MAX_FAILED_LOGIN_ATTEMPTS = 5
def new
set_minimum_password_length
@@ -81,10 +85,14 @@ class SessionsController < Devise::SessionsController
request.headers[CAPTCHA_HEADER] && Gitlab::Recaptcha.enabled?
end
+ def captcha_on_login_required?
+ Gitlab::Recaptcha.enabled_on_login? && unverified_anonymous_user?
+ end
+
# From https://github.com/plataformatec/devise/wiki/How-To:-Use-Recaptcha-with-Devise#devisepasswordscontroller
def check_captcha
return unless user_params[:password].present?
- return unless captcha_enabled?
+ return unless captcha_enabled? || captcha_on_login_required?
return unless Gitlab::Recaptcha.load_configurations!
if verify_recaptcha
@@ -126,10 +134,28 @@ class SessionsController < Devise::SessionsController
Gitlab::AppLogger.info("Failed Login: username=#{user_params[:login]} ip=#{request.remote_ip}")
end
+ def action_new_and_failed_login?
+ action_name == 'new' && failed_login?
+ end
+
+ def save_failed_login
+ session[:failed_login_attempts] ||= 0
+ session[:failed_login_attempts] += 1
+ end
+
def failed_login?
(options = request.env["warden.options"]) && options[:action] == "unauthenticated"
end
+ # storing sessions per IP lets us check if there are associated multiple
+ # anonymous sessions with one IP and prevent situations when there are
+ # multiple attempts of logging in
+ def store_unauthenticated_sessions
+ return if current_user
+
+ Gitlab::AnonymousSession.new(request.remote_ip, session_id: request.session.id).store_session_id_per_ip
+ end
+
# Handle an "initial setup" state, where there's only one user, it's an admin,
# and they require a password change.
# rubocop: disable CodeReuse/ActiveRecord
@@ -240,6 +266,18 @@ class SessionsController < Devise::SessionsController
@ldap_servers ||= Gitlab::Auth::LDAP::Config.available_servers
end
+ def unverified_anonymous_user?
+ exceeded_failed_login_attempts? || exceeded_anonymous_sessions?
+ end
+
+ def exceeded_failed_login_attempts?
+ session.fetch(:failed_login_attempts, 0) > MAX_FAILED_LOGIN_ATTEMPTS
+ end
+
+ def exceeded_anonymous_sessions?
+ Gitlab::AnonymousSession.new(request.remote_ip).stored_sessions >= MAX_FAILED_LOGIN_ATTEMPTS
+ end
+
def authentication_method
if user_params[:otp_attempt]
"two-factor"