12626: Advertise required params for users#merge in discovery doc.
[arvados.git] / services / api / app / controllers / user_sessions_controller.rb
index a7391bd73266a2b0b52decec09fa48057f62db2d..87967a40a5a860a47c34c52d93ae0fcbf38a23fa 100644 (file)
@@ -1,6 +1,11 @@
+# Copyright (C) The Arvados Authors. All rights reserved.
+#
+# SPDX-License-Identifier: AGPL-3.0
+
 class UserSessionsController < ApplicationController
-  before_filter :require_auth_scope_all, :only => [ :destroy ]
+  before_filter :require_auth_scope, :only => [ :destroy ]
 
+  skip_before_filter :set_cors_headers
   skip_before_filter :find_object_by_uuid
   skip_before_filter :render_404_if_no_object
 
@@ -9,18 +14,21 @@ class UserSessionsController < ApplicationController
   # omniauth callback method
   def create
     omniauth = env['omniauth.auth']
-    #logger.debug "+++ #{omniauth}"
 
     identity_url_ok = (omniauth['info']['identity_url'].length > 0) rescue false
     unless identity_url_ok
       # Whoa. This should never happen.
       logger.error "UserSessionsController.create: omniauth object missing/invalid"
-      logger.error "omniauth.pretty_inspect():\n\n#{omniauth.pretty_inspect()}"
+      logger.error "omniauth: "+omniauth.pretty_inspect
 
       return redirect_to login_failure_url
     end
 
-    user = User.find_by_identity_url(omniauth['info']['identity_url'])
+    # Only local users can create sessions, hence uuid_like_pattern
+    # here.
+    user = User.unscoped.where('identity_url = ? and uuid like ?',
+                               omniauth['info']['identity_url'],
+                               User.uuid_like_pattern).first
     if not user
       # Check for permission to log in to an existing User record with
       # a different identity_url
@@ -37,13 +45,28 @@ class UserSessionsController < ApplicationController
         end
       end
     end
+
+    while (uuid = user.andand.redirect_to_user_uuid)
+      user = User.where(uuid: uuid).first
+      if !user
+        raise Exception.new("identity_url #{omniauth['info']['identity_url']} redirects to nonexistent uuid #{uuid}")
+      end
+    end
+
     if not user
       # New user registration
       user = User.new(:email => omniauth['info']['email'],
                       :first_name => omniauth['info']['first_name'],
                       :last_name => omniauth['info']['last_name'],
                       :identity_url => omniauth['info']['identity_url'],
-                      :is_active => Rails.configuration.new_users_are_active)
+                      :is_active => Rails.configuration.new_users_are_active,
+                      :owner_uuid => system_user_uuid)
+      if omniauth['info']['username']
+        user.set_initial_username(requested: omniauth['info']['username'])
+      end
+      act_as_system_user do
+        user.save or raise Exception.new(user.errors.messages)
+      end
     else
       user.email = omniauth['info']['email']
       user.first_name = omniauth['info']['first_name']
@@ -54,11 +77,14 @@ class UserSessionsController < ApplicationController
       end
     end
 
+    # For the benefit of functional and integration tests:
+    @user = user
+
     # prevent ArvadosModel#before_create and _update from throwing
     # "unauthorized":
     Thread.current[:user] = user
 
-    user.save!
+    user.save or raise Exception.new(user.errors.messages)
 
     omniauth.delete('extra')
 
@@ -86,13 +112,15 @@ class UserSessionsController < ApplicationController
 
     flash[:notice] = 'You have logged off'
     return_to = params[:return_to] || root_url
-    redirect_to "#{CUSTOM_PROVIDER_URL}/users/sign_out?redirect_uri=#{CGI.escape return_to}"
+    redirect_to "#{Rails.configuration.sso_provider_url}/users/sign_out?redirect_uri=#{CGI.escape return_to}"
   end
 
   # login - Just bounce to /auth/joshid. The only purpose of this function is
   # to save the return_to parameter (if it exists; see the application
   # controller). /auth/joshid bypasses the application controller.
   def login
+    auth_provider = if params[:auth_provider] then "auth_provider=#{CGI.escape(params[:auth_provider])}" else "" end
+
     if current_user and params[:return_to]
       # Already logged in; just need to send a token to the requesting
       # API client.
@@ -102,9 +130,9 @@ class UserSessionsController < ApplicationController
 
       send_api_token_to(params[:return_to], current_user)
     elsif params[:return_to]
-      redirect_to "/auth/joshid?return_to=#{CGI.escape(params[:return_to])}"
+      redirect_to "/auth/joshid?return_to=#{CGI.escape(params[:return_to])}&#{auth_provider}"
     else
-      redirect_to "/auth/joshid"
+      redirect_to "/auth/joshid?#{auth_provider}"
     end
   end
 
@@ -115,7 +143,8 @@ class UserSessionsController < ApplicationController
     # Stub: automatically register all new API clients
     api_client_url_prefix = callback_url.match(%r{^.*?://[^/]+})[0] + '/'
     act_as_system_user do
-      @api_client = ApiClient.find_or_create_by_url_prefix api_client_url_prefix
+      @api_client = ApiClient.
+        find_or_create_by(url_prefix: api_client_url_prefix)
     end
 
     api_client_auth = ApiClientAuthorization.
@@ -133,4 +162,8 @@ class UserSessionsController < ApplicationController
     callback_url += 'api_token=' + api_client_auth.api_token
     redirect_to callback_url
   end
+
+  def cross_origin_forbidden
+    send_error 'Forbidden', status: 403
+  end
 end