1# frozen_string_literal: true
2
3class Admin::SessionsController < ApplicationController
4  include AuthenticatesWithTwoFactorForAdminMode
5  include InternalRedirect
6  include RendersLdapServers
7
8  before_action :user_is_admin!
9
10  feature_category :authentication_and_authorization
11
12  def new
13    if current_user_mode.admin_mode?
14      redirect_to redirect_path, notice: _('Admin mode already enabled')
15    else
16      current_user_mode.request_admin_mode! unless current_user_mode.admin_mode_requested?
17      store_location_for(:redirect, redirect_path)
18    end
19  end
20
21  def create
22    if two_factor_enabled_for_user?
23      admin_mode_authenticate_with_two_factor
24    elsif current_user_mode.enable_admin_mode!(password: user_params[:password])
25      redirect_to redirect_path, notice: _('Admin mode enabled')
26    else
27      flash.now[:alert] = _('Invalid login or password')
28      render :new
29    end
30  rescue Gitlab::Auth::CurrentUserMode::NotRequestedError
31    redirect_to new_admin_session_path, alert: _('Re-authentication period expired or never requested. Please try again')
32  end
33
34  def destroy
35    current_user_mode.disable_admin_mode!
36
37    redirect_to root_path, status: :found, notice: _('Admin mode disabled')
38  end
39
40  private
41
42  def user_is_admin!
43    render_404 unless current_user&.admin?
44  end
45
46  def two_factor_enabled_for_user?
47    current_user&.two_factor_enabled?
48  end
49
50  def redirect_path
51    redirect_to_path = safe_redirect_path(stored_location_for(:redirect)) || safe_redirect_path_for_url(request.referer)
52
53    if redirect_to_path &&
54        excluded_redirect_paths.none? { |excluded| redirect_to_path.include?(excluded) }
55      redirect_to_path
56    else
57      admin_root_path
58    end
59  end
60
61  def excluded_redirect_paths
62    [new_admin_session_path, admin_session_path]
63  end
64
65  def user_params
66    params.fetch(:user, {}).permit(:password, :otp_attempt, :device_response)
67  end
68
69  def valid_otp_attempt?(user)
70    otp_validation_result =
71      ::Users::ValidateOtpService.new(user).execute(user_params[:otp_attempt])
72    valid_otp_attempt = otp_validation_result[:status] == :success
73
74    return valid_otp_attempt if Gitlab::Database.read_only?
75
76    valid_otp_attempt || user.invalidate_otp_backup_code!(user_params[:otp_attempt])
77  end
78end
79