1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: AGPL-3.0
5 class UserSessionsController < ApplicationController
6 before_action :require_auth_scope, :only => [ :destroy ]
8 skip_before_action :set_cors_headers
9 skip_before_action :find_object_by_uuid
10 skip_before_action :render_404_if_no_object
14 # omniauth callback method
16 if !Rails.configuration.Login.LoginCluster.empty? and Rails.configuration.Login.LoginCluster != Rails.configuration.ClusterID
17 raise "Local login disabled when LoginCluster is set"
20 omniauth = request.env['omniauth.auth']
23 user = User.register omniauth['info']
26 return redirect_to login_failure_url
29 # For the benefit of functional and integration tests:
32 if user.uuid[0..4] != Rails.configuration.ClusterID
33 # Actually a remote user
34 # Send them to their home cluster's login
35 rh = Rails.configuration.RemoteClusters[user.uuid[0..4]]
36 remote, return_to_url = params[:return_to].split(',', 2)
37 @remotehomeurl = "#{rh.Scheme || "https"}://#{rh.Host}/login?remote=#{Rails.configuration.ClusterID}&return_to=#{return_to_url}"
42 # prevent ArvadosModel#before_create and _update from throwing
44 Thread.current[:user] = user
46 user.save or raise Exception.new(user.errors.messages)
48 omniauth.delete('extra')
50 # Give the authenticated user a cookie for direct API access
51 session[:user_id] = user.id
52 session[:api_client_uuid] = nil
53 session[:api_client_trusted] = true # full permission to see user's secrets
55 @redirect_to = root_path
56 if params.has_key?(:return_to)
57 # return_to param's format is 'remote,return_to_url'. This comes from login()
58 # encoding the remote=zbbbb parameter passed by a client asking for a salted
60 remote, return_to_url = params[:return_to].split(',', 2)
61 if remote !~ /^[0-9a-z]{5}$/ && remote != ""
62 return send_error 'Invalid remote cluster id', status: 400
64 remote = nil if remote == ''
65 return send_api_token_to(return_to_url, user, remote)
67 redirect_to @redirect_to
70 # Omniauth failure callback
72 flash[:notice] = params[:message]
75 # logout - Clear our rack session BUT essentially redirect to the provider
76 # to clean up the Devise session from there too !
78 session[:user_id] = nil
80 flash[:notice] = 'You have logged off'
81 return_to = params[:return_to] || root_url
82 redirect_to "#{Rails.configuration.Services.SSO.ExternalURL}/users/sign_out?redirect_uri=#{CGI.escape return_to}"
85 # login - Just bounce to /auth/joshid. The only purpose of this function is
86 # to save the return_to parameter (if it exists; see the application
87 # controller). /auth/joshid bypasses the application controller.
89 if params[:remote] !~ /^[0-9a-z]{5}$/ && !params[:remote].nil?
90 return send_error 'Invalid remote cluster id', status: 400
92 if current_user and params[:return_to]
93 # Already logged in; just need to send a token to the requesting
96 # FIXME: if current_user has never authorized this app before,
97 # ask for confirmation here!
99 return send_api_token_to(params[:return_to], current_user, params[:remote])
102 p << "auth_provider=#{CGI.escape(params[:auth_provider])}" if params[:auth_provider]
104 if !Rails.configuration.Login.LoginCluster.empty? and Rails.configuration.Login.LoginCluster != Rails.configuration.ClusterID
105 host = ApiClientAuthorization.remote_host(uuid_prefix: Rails.configuration.Login.LoginCluster)
107 raise "LoginCluster #{Rails.configuration.Login.LoginCluster} missing from RemoteClusters"
110 cluster = Rails.configuration.RemoteClusters[Rails.configuration.Login.LoginCluster]
111 if cluster and cluster['Scheme'] and !cluster['Scheme'].empty?
112 scheme = cluster['Scheme']
114 login_cluster = "#{scheme}://#{host}"
115 p << "remote=#{CGI.escape(params[:remote])}" if params[:remote]
116 p << "return_to=#{CGI.escape(params[:return_to])}" if params[:return_to]
117 redirect_to "#{login_cluster}/login?#{p.join('&')}"
119 if params[:return_to]
120 # Encode remote param inside callback's return_to, so that we'll get it on
121 # create() after login.
122 remote_param = params[:remote].nil? ? '' : params[:remote]
123 p << "return_to=#{CGI.escape(remote_param + ',' + params[:return_to])}"
125 redirect_to "/auth/joshid?#{p.join('&')}"
129 def send_api_token_to(callback_url, user, remote=nil)
130 # Give the API client a token for making API calls on behalf of
131 # the authenticated user
133 # Stub: automatically register all new API clients
134 api_client_url_prefix = callback_url.match(%r{^.*?://[^/]+})[0] + '/'
135 act_as_system_user do
136 @api_client = ApiClient.
137 find_or_create_by(url_prefix: api_client_url_prefix)
140 @api_client_auth = ApiClientAuthorization.
142 api_client: @api_client,
143 created_by_ip_address: remote_ip,
145 @api_client_auth.save!
147 if callback_url.index('?')
153 token = @api_client_auth.token
155 token = @api_client_auth.salted_token(remote: remote)
157 callback_url += 'api_token=' + token
158 redirect_to callback_url
161 def cross_origin_forbidden
162 send_error 'Forbidden', status: 403