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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
|
# frozen_string_literal: true
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
include AuthenticatesWithTwoFactor
include Devise::Controllers::Rememberable
include AuthHelper
protect_from_forgery except: [:kerberos, :saml, :cas3, :failure], with: :exception, prepend: true
def handle_omniauth
omniauth_flow(Gitlab::Auth::OAuth)
end
AuthHelper.providers_for_base_controller.each do |provider|
alias_method provider, :handle_omniauth
end
# Extend the standard implementation to also increment
# the number of failed sign in attempts
def failure
if params[:username].present? && AuthHelper.form_based_provider?(failed_strategy.name)
user = User.by_login(params[:username])
user&.increment_failed_attempts!
end
super
end
# Extend the standard message generation to accept our custom exception
def failure_message
exception = request.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 ||= request.env["omniauth.error.type"].to_s
error.to_s.humanize if error
end
def saml
omniauth_flow(Gitlab::Auth::Saml)
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
def auth0
if oauth['uid'].blank?
fail_auth0_login
else
handle_omniauth
end
end
private
def omniauth_flow(auth_module, identity_linker: nil)
if fragment = request.env.dig('omniauth.params', 'redirect_fragment').presence
store_redirect_fragment(fragment)
end
if current_user
return render_403 unless link_provider_allowed?(oauth['provider'])
log_audit_event(current_user, with: oauth['provider'])
identity_linker ||= auth_module::IdentityLinker.new(current_user, oauth)
link_identity(identity_linker)
if identity_linker.changed?
redirect_identity_linked
elsif identity_linker.failed?
redirect_identity_link_failed(identity_linker.error_message)
else
redirect_identity_exists
end
else
sign_in_user_flow(auth_module::User)
end
end
def link_identity(identity_linker)
identity_linker.link
end
def redirect_identity_exists
redirect_to after_sign_in_path_for(current_user)
end
def redirect_identity_link_failed(error_message)
redirect_to profile_account_path, notice: _("Authentication failed: %{error_message}") % { error_message: error_message }
end
def redirect_identity_linked
redirect_to profile_account_path, notice: _('Authentication method updated')
end
def handle_service_ticket(provider, ticket)
Gitlab::Auth::OAuth::Session.create provider, ticket
session[:service_tickets] ||= {}
session[:service_tickets][provider] = ticket
end
def build_auth_user(auth_user_class)
auth_user_class.new(oauth)
end
def sign_in_user_flow(auth_user_class)
auth_user = build_auth_user(auth_user_class)
user = auth_user.find_and_update!
if auth_user.valid_sign_in?
log_audit_event(user, with: oauth['provider'])
set_remember_me(user)
if user.two_factor_enabled? && !auth_user.bypass_two_factor?
prompt_for_two_factor(user)
else
sign_in_and_redirect(user, event: :authentication)
end
else
fail_login(user)
end
rescue Gitlab::Auth::OAuth::User::SigninDisabledForProviderError
handle_disabled_provider
rescue Gitlab::Auth::OAuth::User::SignupDisabledError
handle_signup_error
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.") % { label: label }]
if Gitlab::CurrentSettings.allow_signup?
message << _("Create a GitLab account first, and then connect it to your %{label} account.") % { label: label }
end
flash[:notice] = message.join(' ')
redirect_to new_user_session_path
end
def oauth
@oauth ||= request.env['omniauth.auth']
end
def fail_login(user)
error_message = user.errors.full_messages.to_sentence
return redirect_to omniauth_error_path(oauth['provider'], error: error_message)
end
def fail_auth0_login
flash[:alert] = _('Wrong extern UID provided. Make sure Auth0 is configured correctly.')
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") % { label: label }
redirect_to new_user_session_path
end
def log_audit_event(user, options = {})
AuditEventService.new(user, user, options)
.for_authentication.security_event
end
def set_remember_me(user)
return unless remember_me?
if user.two_factor_enabled?
params[:remember_me] = '1'
else
remember_me(user)
end
end
def remember_me?
request_params = request.env['omniauth.params']
(request_params['remember_me'] == '1') if request_params.present?
end
def store_redirect_fragment(redirect_fragment)
key = stored_location_key_for(:user)
location = session[key]
if uri = parse_uri(location)
uri.fragment = redirect_fragment
store_location_for(:user, uri.to_s)
end
end
end
|