summaryrefslogtreecommitdiff
path: root/app/controllers/omniauth_callbacks_controller.rb
blob: 8440945ab43c3ee7b30508fbf473c8f1653d8f55 (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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
  include AuthenticatesWithTwoFactor
  include Devise::Controllers::Rememberable

  protect_from_forgery except: [:kerberos, :saml, :cas3]

  Gitlab.config.omniauth.providers.each do |provider|
    define_method provider['name'] do
      handle_omniauth
    end
  end

  if Gitlab::Auth::LDAP::Config.enabled?
    Gitlab::Auth::LDAP::Config.available_servers.each do |server|
      define_method server['provider_name'] do
        ldap
      end
    end
  end

  # Extend the standard message generation to accept our custom exception
  def failure_message
    exception = env["omniauth.error"]
    error   = exception.error_reason if exception.respond_to?(:error_reason)
    error ||= exception.error        if exception.respond_to?(:error)
    error ||= exception.message      if exception.respond_to?(:message)
    error ||= env["omniauth.error.type"].to_s
    error.to_s.humanize if error
  end

  # We only find ourselves here
  # if the authentication to LDAP was successful.
  def ldap
    ldap_user = Gitlab::Auth::LDAP::User.new(oauth)
    ldap_user.save if ldap_user.changed? # will also save new users

    @user = ldap_user.gl_user
    @user.remember_me = params[:remember_me] if ldap_user.persisted?

    # Do additional LDAP checks for the user filter and EE features
    if ldap_user.allowed?
      if @user.two_factor_enabled?
        prompt_for_two_factor(@user)
      else
        log_audit_event(@user, with: oauth['provider'])
        sign_in_and_redirect(@user)
      end
    else
      fail_ldap_login
    end
  end

  def saml
    if current_user
      log_audit_event(current_user, with: :saml)
      # Update SAML identity if data has changed.
      identity = current_user.identities.with_extern_uid(:saml, oauth['uid']).take
      if identity.nil?
        current_user.identities.create(extern_uid: oauth['uid'], provider: :saml)
        redirect_to profile_account_path, notice: 'Authentication method updated'
      else
        redirect_to after_sign_in_path_for(current_user)
      end
    else
      saml_user = Gitlab::Auth::Saml::User.new(oauth)
      saml_user.save if saml_user.changed?
      @user = saml_user.gl_user

      continue_login_process
    end
  rescue Gitlab::Auth::OAuth::User::SignupDisabledError
    handle_signup_error
  end

  def omniauth_error
    @provider = params[:provider]
    @error = params[:error]
    render 'errors/omniauth_error', layout: "oauth_error", status: 422
  end

  def cas3
    ticket = params['ticket']
    if ticket
      handle_service_ticket oauth['provider'], ticket
    end

    handle_omniauth
  end

  def authentiq
    if params['sid']
      handle_service_ticket oauth['provider'], params['sid']
    end

    handle_omniauth
  end

  private

  def handle_omniauth
    if current_user
      # Add new authentication method
      current_user.identities
                  .with_extern_uid(oauth['provider'], oauth['uid'])
                  .first_or_create(extern_uid: oauth['uid'])
      log_audit_event(current_user, with: oauth['provider'])
      redirect_to profile_account_path, notice: 'Authentication method updated'
    else
      oauth_user = Gitlab::Auth::OAuth::User.new(oauth)
      oauth_user.save
      @user = oauth_user.gl_user

      continue_login_process
    end
  rescue Gitlab::Auth::OAuth::User::SigninDisabledForProviderError
    handle_disabled_provider
  rescue Gitlab::Auth::OAuth::User::SignupDisabledError
    handle_signup_error
  end

  def handle_service_ticket(provider, ticket)
    Gitlab::Auth::OAuth::Session.create provider, ticket
    session[:service_tickets] ||= {}
    session[:service_tickets][provider] = ticket
  end

  def continue_login_process
    # Only allow properly saved users to login.
    if @user.persisted? && @user.valid?
      log_audit_event(@user, with: oauth['provider'])

      if @user.two_factor_enabled?
        params[:remember_me] = '1' if remember_me?
        prompt_for_two_factor(@user)
      else
        remember_me(@user) if remember_me?
        sign_in_and_redirect(@user)
      end
    else
      fail_login
    end
  end

  def handle_signup_error
    label = Gitlab::Auth::OAuth::Provider.label_for(oauth['provider'])
    message = "Signing in using your #{label} account without a pre-existing GitLab account is not allowed."

    if Gitlab::CurrentSettings.allow_signup?
      message << " Create a GitLab account first, and then connect it to your #{label} account."
    end

    flash[:notice] = message

    redirect_to new_user_session_path
  end

  def oauth
    @oauth ||= request.env['omniauth.auth']
  end

  def fail_login
    error_message = @user.errors.full_messages.to_sentence

    return redirect_to omniauth_error_path(oauth['provider'], error: error_message)
  end

  def fail_ldap_login
    flash[:alert] = 'Access denied for your LDAP account.'

    redirect_to new_user_session_path
  end

  def handle_disabled_provider
    label = Gitlab::Auth::OAuth::Provider.label_for(oauth['provider'])
    flash[:alert] = "Signing in using #{label} has been disabled"

    redirect_to new_user_session_path
  end

  def log_audit_event(user, options = {})
    AuditEventService.new(user, user, options)
      .for_authentication.security_event
  end

  def remember_me?
    request_params = request.env['omniauth.params']
    (request_params['remember_me'] == '1') if request_params.present?
  end
end