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