4570: Rename 'auth_method' parameter to 'auth_provider' to get the terminology right.
[arvados.git] / services / api / app / controllers / user_sessions_controller.rb
1 class UserSessionsController < ApplicationController
2   before_filter :require_auth_scope, :only => [ :destroy ]
3
4   skip_before_filter :find_object_by_uuid
5   skip_before_filter :render_404_if_no_object
6
7   respond_to :html
8
9   # omniauth callback method
10   def create
11     omniauth = env['omniauth.auth']
12
13     identity_url_ok = (omniauth['info']['identity_url'].length > 0) rescue false
14     unless identity_url_ok
15       # Whoa. This should never happen.
16       logger.error "UserSessionsController.create: omniauth object missing/invalid"
17       logger.error "omniauth.pretty_inspect():\n\n#{omniauth.pretty_inspect()}"
18
19       return redirect_to login_failure_url
20     end
21
22     user = User.find_by_identity_url(omniauth['info']['identity_url'])
23     if not user
24       # Check for permission to log in to an existing User record with
25       # a different identity_url
26       Link.where("link_class = ? and name = ? and tail_uuid = ? and head_uuid like ?",
27                  'permission',
28                  'can_login',
29                  omniauth['info']['email'],
30                  User.uuid_like_pattern).each do |link|
31         if prefix = link.properties['identity_url_prefix']
32           if prefix == omniauth['info']['identity_url'][0..prefix.size-1]
33             user = User.find_by_uuid(link.head_uuid)
34             break if user
35           end
36         end
37       end
38     end
39     if not user
40       # New user registration
41       user = User.new(:email => omniauth['info']['email'],
42                       :first_name => omniauth['info']['first_name'],
43                       :last_name => omniauth['info']['last_name'],
44                       :identity_url => omniauth['info']['identity_url'],
45                       :is_active => Rails.configuration.new_users_are_active,
46                       :owner_uuid => system_user_uuid)
47       act_as_system_user do
48         user.save or raise Exception.new(user.errors.messages)
49       end
50     else
51       user.email = omniauth['info']['email']
52       user.first_name = omniauth['info']['first_name']
53       user.last_name = omniauth['info']['last_name']
54       if user.identity_url.nil?
55         # First login to a pre-activated account
56         user.identity_url = omniauth['info']['identity_url']
57       end
58     end
59
60     # For the benefit of functional and integration tests:
61     @user = user
62
63     # prevent ArvadosModel#before_create and _update from throwing
64     # "unauthorized":
65     Thread.current[:user] = user
66
67     user.save or raise Exception.new(user.errors.messages)
68
69     omniauth.delete('extra')
70
71     # Give the authenticated user a cookie for direct API access
72     session[:user_id] = user.id
73     session[:api_client_uuid] = nil
74     session[:api_client_trusted] = true # full permission to see user's secrets
75
76     @redirect_to = root_path
77     if params.has_key?(:return_to)
78       return send_api_token_to(params[:return_to], user)
79     end
80     redirect_to @redirect_to
81   end
82
83   # Omniauth failure callback
84   def failure
85     flash[:notice] = params[:message]
86   end
87
88   # logout - Clear our rack session BUT essentially redirect to the provider
89   # to clean up the Devise session from there too !
90   def logout
91     session[:user_id] = nil
92
93     flash[:notice] = 'You have logged off'
94     return_to = params[:return_to] || root_url
95     redirect_to "#{CUSTOM_PROVIDER_URL}/users/sign_out?redirect_uri=#{CGI.escape return_to}"
96   end
97
98   # login - Just bounce to /auth/joshid. The only purpose of this function is
99   # to save the return_to parameter (if it exists; see the application
100   # controller). /auth/joshid bypasses the application controller.
101   def login
102     auth_provider = if params[:auth_provider] then "auth_provider=#{CGI.escape(params[:auth_provider])}" else "" end
103
104     if current_user and params[:return_to]
105       # Already logged in; just need to send a token to the requesting
106       # API client.
107       #
108       # FIXME: if current_user has never authorized this app before,
109       # ask for confirmation here!
110
111       send_api_token_to(params[:return_to], current_user)
112     elsif params[:return_to]
113       redirect_to "/auth/joshid?return_to=#{CGI.escape(params[:return_to])}&#{auth_provider}"
114     else
115       redirect_to "/auth/joshid?#{auth_provider}"
116     end
117   end
118
119   def send_api_token_to(callback_url, user)
120     # Give the API client a token for making API calls on behalf of
121     # the authenticated user
122
123     # Stub: automatically register all new API clients
124     api_client_url_prefix = callback_url.match(%r{^.*?://[^/]+})[0] + '/'
125     act_as_system_user do
126       @api_client = ApiClient.find_or_create_by_url_prefix api_client_url_prefix
127     end
128
129     api_client_auth = ApiClientAuthorization.
130       new(user: user,
131           api_client: @api_client,
132           created_by_ip_address: remote_ip,
133           scopes: ["all"])
134     api_client_auth.save!
135
136     if callback_url.index('?')
137       callback_url += '&'
138     else
139       callback_url += '?'
140     end
141     callback_url += 'api_token=' + api_client_auth.api_token
142     redirect_to callback_url
143   end
144 end