Merge branch 'master' into 12479-wb-structured-vocabulary
[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_filter :require_auth_scope, :only => [ :destroy ]
7
8   skip_before_filter :set_cors_headers
9   skip_before_filter :find_object_by_uuid
10   skip_before_filter :render_404_if_no_object
11
12   respond_to :html
13
14   # omniauth callback method
15   def create
16     omniauth = env['omniauth.auth']
17
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
23
24       return redirect_to login_failure_url
25     end
26
27     # Only local users can create sessions, hence uuid_like_pattern
28     # here.
29     user = User.where('identity_url = ? and uuid like ?',
30                       omniauth['info']['identity_url'],
31                       User.uuid_like_pattern).first
32     if not user
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 ?",
36                  'permission',
37                  'can_login',
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)
43             break if user
44           end
45         end
46       end
47     end
48     if not user
49       # New user registration
50       user = User.new(:email => omniauth['info']['email'],
51                       :first_name => omniauth['info']['first_name'],
52                       :last_name => omniauth['info']['last_name'],
53                       :identity_url => omniauth['info']['identity_url'],
54                       :is_active => Rails.configuration.new_users_are_active,
55                       :owner_uuid => system_user_uuid)
56       if omniauth['info']['username']
57         user.set_initial_username(requested: omniauth['info']['username'])
58       end
59       act_as_system_user do
60         user.save or raise Exception.new(user.errors.messages)
61       end
62     else
63       user.email = omniauth['info']['email']
64       user.first_name = omniauth['info']['first_name']
65       user.last_name = omniauth['info']['last_name']
66       if user.identity_url.nil?
67         # First login to a pre-activated account
68         user.identity_url = omniauth['info']['identity_url']
69       end
70     end
71
72     # For the benefit of functional and integration tests:
73     @user = user
74
75     # prevent ArvadosModel#before_create and _update from throwing
76     # "unauthorized":
77     Thread.current[:user] = user
78
79     user.save or raise Exception.new(user.errors.messages)
80
81     omniauth.delete('extra')
82
83     # Give the authenticated user a cookie for direct API access
84     session[:user_id] = user.id
85     session[:api_client_uuid] = nil
86     session[:api_client_trusted] = true # full permission to see user's secrets
87
88     @redirect_to = root_path
89     if params.has_key?(:return_to)
90       return send_api_token_to(params[:return_to], user)
91     end
92     redirect_to @redirect_to
93   end
94
95   # Omniauth failure callback
96   def failure
97     flash[:notice] = params[:message]
98   end
99
100   # logout - Clear our rack session BUT essentially redirect to the provider
101   # to clean up the Devise session from there too !
102   def logout
103     session[:user_id] = nil
104
105     flash[:notice] = 'You have logged off'
106     return_to = params[:return_to] || root_url
107     redirect_to "#{Rails.configuration.sso_provider_url}/users/sign_out?redirect_uri=#{CGI.escape return_to}"
108   end
109
110   # login - Just bounce to /auth/joshid. The only purpose of this function is
111   # to save the return_to parameter (if it exists; see the application
112   # controller). /auth/joshid bypasses the application controller.
113   def login
114     auth_provider = if params[:auth_provider] then "auth_provider=#{CGI.escape(params[:auth_provider])}" else "" end
115
116     if current_user and params[:return_to]
117       # Already logged in; just need to send a token to the requesting
118       # API client.
119       #
120       # FIXME: if current_user has never authorized this app before,
121       # ask for confirmation here!
122
123       send_api_token_to(params[:return_to], current_user)
124     elsif params[:return_to]
125       redirect_to "/auth/joshid?return_to=#{CGI.escape(params[:return_to])}&#{auth_provider}"
126     else
127       redirect_to "/auth/joshid?#{auth_provider}"
128     end
129   end
130
131   def send_api_token_to(callback_url, user)
132     # Give the API client a token for making API calls on behalf of
133     # the authenticated user
134
135     # Stub: automatically register all new API clients
136     api_client_url_prefix = callback_url.match(%r{^.*?://[^/]+})[0] + '/'
137     act_as_system_user do
138       @api_client = ApiClient.
139         find_or_create_by(url_prefix: api_client_url_prefix)
140     end
141
142     api_client_auth = ApiClientAuthorization.
143       new(user: user,
144           api_client: @api_client,
145           created_by_ip_address: remote_ip,
146           scopes: ["all"])
147     api_client_auth.save!
148
149     if callback_url.index('?')
150       callback_url += '&'
151     else
152       callback_url += '?'
153     end
154     callback_url += 'api_token=' + api_client_auth.api_token
155     redirect_to callback_url
156   end
157
158   def cross_origin_forbidden
159     send_error 'Forbidden', status: 403
160   end
161 end