Merge branch '17074-optimize-itemsavailable' into main. Closes #17074
[arvados.git] / services / api / app / controllers / user_sessions_controller.rb
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 class UserSessionsController < ApplicationController
6   before_action :require_auth_scope, :only => [ :destroy ]
7
8   skip_before_action :set_cors_headers
9   skip_before_action :find_object_by_uuid
10   skip_before_action :render_404_if_no_object
11
12   respond_to :html
13
14   def login
15     return send_error "Legacy code path no longer supported", status: 404
16   end
17
18   def logout
19     return send_error "Legacy code path no longer supported", status: 404
20   end
21
22   # create a new session
23   def create
24     remote, return_to_url = params[:return_to].split(',', 2)
25     if params[:provider] != 'controller' ||
26        return_to_url != 'https://controller.api.client.invalid'
27       return send_error "Legacy code path no longer supported", status: 404
28     end
29     if request.headers['Authorization'] != 'Bearer ' + Rails.configuration.SystemRootToken
30       return send_error('Invalid authorization header', status: 401)
31     end
32     if remote == ''
33       remote = nil
34     elsif remote !~ /^[0-9a-z]{5}$/
35       return send_error 'Invalid remote cluster id', status: 400
36     end
37     # arvados-controller verified the user and is passing auth_info
38     # in request params.
39     authinfo = SafeJSON.load(params[:auth_info])
40     max_expires_at = authinfo["expires_at"]
41
42     if !authinfo['user_uuid'].blank?
43       user = User.find_by_uuid(authinfo['user_uuid'])
44       if !user
45         Rails.logger.warn "Nonexistent user_uuid in authinfo #{authinfo.inspect}"
46         return redirect_to login_failure_url
47       end
48     else
49       begin
50         user = User.register(authinfo)
51       rescue => e
52         Rails.logger.warn "User.register error #{e}"
53         Rails.logger.warn "authinfo was #{authinfo.inspect}"
54         return redirect_to login_failure_url
55       end
56     end
57
58     # For the benefit of functional and integration tests:
59     @user = user
60
61     # prevent ArvadosModel#before_create and _update from throwing
62     # "unauthorized":
63     Thread.current[:user] = user
64
65     user.save or raise Exception.new(user.errors.messages)
66
67     return send_api_token_to(return_to_url, user, remote, max_expires_at)
68   end
69
70   # Omniauth failure callback
71   def failure
72     flash[:notice] = params[:message]
73   end
74
75   def send_api_token_to(callback_url, user, remote=nil, token_expiration=nil)
76     # Give the API client a token for making API calls on behalf of
77     # the authenticated user
78
79     if Rails.configuration.Login.TokenLifetime > 0
80       if token_expiration == nil
81         token_expiration = db_current_time + Rails.configuration.Login.TokenLifetime
82       else
83         token_expiration = [token_expiration, db_current_time + Rails.configuration.Login.TokenLifetime].min
84       end
85     end
86
87     @api_client_auth = ApiClientAuthorization.
88       new(user: user,
89           created_by_ip_address: remote_ip,
90           expires_at: token_expiration,
91           scopes: ["all"])
92     @api_client_auth.save!
93
94     if callback_url.index('?')
95       callback_url += '&'
96     else
97       callback_url += '?'
98     end
99     if remote.nil?
100       token = @api_client_auth.token
101     else
102       token = @api_client_auth.salted_token(remote: remote)
103     end
104     callback_url += 'api_token=' + token
105     redirect_to callback_url, allow_other_host: true
106   end
107
108   def cross_origin_forbidden
109     send_error 'Forbidden', status: 403
110   end
111 end