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