diff options
author | Małgorzata Ksionek <mksionek@gitlab.com> | 2019-07-18 10:27:02 +0200 |
---|---|---|
committer | Małgorzata Ksionek <mksionek@gitlab.com> | 2019-07-31 11:47:55 +0200 |
commit | dfcf4cf5f1e87a29f0d9fcc5ff2bba47258893bb (patch) | |
tree | 9343675b9a675bc87236e2b65f4b9d2af18762c2 /app/controllers/sessions_controller.rb | |
parent | d93da8888bd54531e5f5df2047902ae90635b8d0 (diff) | |
download | gitlab-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.rb | 44 |
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" |