summaryrefslogtreecommitdiff
path: root/app/controllers/otp_challenges_controller.rb
blob: 5a834ed03c4c5d22b52c72832228f6c863b1071f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# The second half of a two-step login. A pending marker (set by
# sessions#create after a correct password) plus deadline and attempt
# counter live in the session; the real user_id is only written after a
# valid code, through a fresh session.
class OtpChallengesController < ApplicationController

  layout 'admin'

  MAX_ATTEMPTS = 5

  def new
    redirect_to login_path unless pending_user
  end

  def create
    user = pending_user
    return redirect_to login_path unless user

    session[:otp_attempts] = session[:otp_attempts].to_i + 1
    if session[:otp_attempts] > MAX_ATTEMPTS
      clear_pending
      flash[:error] = t("flash.otp.too_many_attempts")
      return redirect_to login_path
    end

    if user.verify_otp!(params[:code])
      return_to = session[:return_to]
      reset_session
      self.current_user = user
      session[:logged_in_at] = Time.now.to_i
      user.update_column(:last_login_at, Time.now)

      # an admin who logs in and goes straight to user management
      # is already elevated
      elevate! if user.is_admin?
      flash[:notice] = if user.is_admin?
        t("flash.elevation.granted_at_login",
          :minutes => AuthenticatedSystem::ELEVATION_MAX_AGE.in_minutes.to_i)
      else
        t("flash.common.logged_in")
      end
      redirect_to safe_return_to(return_to, :default => admin_path)
    else
      flash.now[:error] = t("flash.otp.code_mismatch")
      render :new
    end
  end

  private

    def pending_user
      return nil if session[:pending_otp_user_id].blank?
      if session[:otp_deadline].to_i < Time.now.to_i
        clear_pending
        return nil
      end
      @pending_user ||= User.find_by(:id => session[:pending_otp_user_id])
    end

    def clear_pending
      session.delete(:pending_otp_user_id)
      session.delete(:otp_deadline)
      session.delete(:otp_attempts)
    end
end