1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: AGPL-3.0
5 class UserSessionsController < ApplicationController
6 before_filter :require_auth_scope, :only => [ :destroy ]
8 skip_before_filter :set_cors_headers
9 skip_before_filter :find_object_by_uuid
10 skip_before_filter :render_404_if_no_object
14 # omniauth callback method
16 omniauth = env['omniauth.auth']
18 identity_url_ok = (omniauth['info']['identity_url'].length > 0) rescue false
19 unless identity_url_ok
20 # Whoa. This should never happen.
21 logger.error "UserSessionsController.create: omniauth object missing/invalid"
22 logger.error "omniauth: "+omniauth.pretty_inspect
24 return redirect_to login_failure_url
27 # Only local users can create sessions, hence uuid_like_pattern
29 user = User.unscoped.where('identity_url = ? and uuid like ?',
30 omniauth['info']['identity_url'],
31 User.uuid_like_pattern).first
33 # Check for permission to log in to an existing User record with
34 # a different identity_url
35 Link.where("link_class = ? and name = ? and tail_uuid = ? and head_uuid like ?",
38 omniauth['info']['email'],
39 User.uuid_like_pattern).each do |link|
40 if prefix = link.properties['identity_url_prefix']
41 if prefix == omniauth['info']['identity_url'][0..prefix.size-1]
42 user = User.find_by_uuid(link.head_uuid)
50 # New user registration
51 user = User.new(:email => omniauth['info']['email'],
52 :first_name => omniauth['info']['first_name'],
53 :last_name => omniauth['info']['last_name'],
54 :identity_url => omniauth['info']['identity_url'],
55 :is_active => Rails.configuration.new_users_are_active,
56 :owner_uuid => system_user_uuid)
57 if omniauth['info']['username']
58 user.set_initial_username(requested: omniauth['info']['username'])
61 user.save or raise Exception.new(user.errors.messages)
64 user.email = omniauth['info']['email']
65 user.first_name = omniauth['info']['first_name']
66 user.last_name = omniauth['info']['last_name']
67 if user.identity_url.nil?
68 # First login to a pre-activated account
69 user.identity_url = omniauth['info']['identity_url']
72 while (uuid = user.redirect_to_user_uuid)
73 user = User.unscoped.where(uuid: uuid).first
75 raise Exception.new("identity_url #{omniauth['info']['identity_url']} redirects to nonexistent uuid #{uuid}")
80 # For the benefit of functional and integration tests:
83 # prevent ArvadosModel#before_create and _update from throwing
85 Thread.current[:user] = user
87 user.save or raise Exception.new(user.errors.messages)
89 omniauth.delete('extra')
91 # Give the authenticated user a cookie for direct API access
92 session[:user_id] = user.id
93 session[:api_client_uuid] = nil
94 session[:api_client_trusted] = true # full permission to see user's secrets
96 @redirect_to = root_path
97 if params.has_key?(:return_to)
98 rt = params[:return_to]
99 # Extracts query params as {param1 => [value1], param2 => [value2], ...}
100 p = rt.index('?').nil? ? {} : CGI::parse(rt[rt.index('?')+1..-1])
101 remote = p["remote"] && p["remote"][0]
102 return send_api_token_to(params[:return_to], user, remote)
104 redirect_to @redirect_to
107 # Omniauth failure callback
109 flash[:notice] = params[:message]
112 # logout - Clear our rack session BUT essentially redirect to the provider
113 # to clean up the Devise session from there too !
115 session[:user_id] = nil
117 flash[:notice] = 'You have logged off'
118 return_to = params[:return_to] || root_url
119 redirect_to "#{Rails.configuration.sso_provider_url}/users/sign_out?redirect_uri=#{CGI.escape return_to}"
122 # login - Just bounce to /auth/joshid. The only purpose of this function is
123 # to save the return_to parameter (if it exists; see the application
124 # controller). /auth/joshid bypasses the application controller.
126 if current_user and params[:return_to]
127 # Already logged in; just need to send a token to the requesting
130 # FIXME: if current_user has never authorized this app before,
131 # ask for confirmation here!
133 return send_api_token_to(params[:return_to], current_user, params[:remote])
136 p << "auth_provider=#{CGI.escape(params[:auth_provider])}" if params[:auth_provider]
137 if params[:return_to]
140 # Encode remote param inside return_to, so that we'll get it on the
141 # callback after login
142 remote_param += if params[:return_to].include? '?' then '&' else '?' end
143 remote_param += "remote=#{params[:remote]}"
145 p << "return_to=#{CGI.escape(params[:return_to]+remote_param)}"
148 redirect_to "/auth/joshid?#{p.join('&')}"
151 def send_api_token_to(callback_url, user, remote=nil)
152 # Give the API client a token for making API calls on behalf of
153 # the authenticated user
155 # Stub: automatically register all new API clients
156 api_client_url_prefix = callback_url.match(%r{^.*?://[^/]+})[0] + '/'
157 act_as_system_user do
158 @api_client = ApiClient.
159 find_or_create_by(url_prefix: api_client_url_prefix)
162 @api_client_auth = ApiClientAuthorization.
164 api_client: @api_client,
165 created_by_ip_address: remote_ip,
167 @api_client_auth.save!
169 if callback_url.index('?')
175 token = @api_client_auth.token
177 token = @api_client_auth.salted_token(remote: remote)
179 callback_url += 'api_token=' + token
180 redirect_to callback_url
183 def cross_origin_forbidden
184 send_error 'Forbidden', status: 403