44b89adaa916ca25d8be43b74bd71f8274b035ac
[arvados.git] / services / api / app / controllers / arvados / v1 / users_controller.rb
1 class Arvados::V1::UsersController < ApplicationController
2   skip_before_filter :find_object_by_uuid, only:
3     [:activate, :event_stream, :current, :system]
4   skip_before_filter :render_404_if_no_object, only:
5     [:activate, :event_stream, :current, :system]
6
7   def current
8     @object = current_user
9     show
10   end
11   def system
12     @object = system_user
13     show
14   end
15
16   class ChannelStreamer
17     Q_UPDATE_INTERVAL = 12
18     def initialize(opts={})
19       @opts = opts
20     end
21     def each
22       return unless @opts[:channel]
23       @redis = Redis.new(:timeout => 0)
24       @redis.subscribe(@opts[:channel]) do |event|
25         event.message do |channel, msg|
26           yield msg + "\n"
27         end
28       end
29     end
30   end
31       
32   def event_stream
33     channel = current_user.andand.uuid
34     if current_user.andand.is_admin
35       channel = params[:uuid] || channel
36     end
37     if client_accepts_plain_text_stream
38       self.response.headers['Last-Modified'] = Time.now.ctime.to_s
39       self.response_body = ChannelStreamer.new(channel: channel)
40     else
41       render json: {
42         href: url_for(uuid: channel),
43         comment: ('To retrieve the event stream as plain text, ' +
44                   'use a request header like "Accept: text/plain"')
45       }
46     end
47   end
48
49   def activate
50     if current_user.andand.is_admin && params[:uuid]
51       @object = User.find params[:uuid]
52     else
53       @object = current_user
54     end
55     if not @object.is_active
56       if not (current_user.is_admin or @object.is_invited)
57         logger.warn "User #{@object.uuid} called users.activate " +
58           "but is not invited"
59         raise ArgumentError.new "Cannot activate without being invited."
60       end
61       act_as_system_user do
62         required_uuids = Link.where(owner_uuid: system_user_uuid,
63                                     link_class: 'signature',
64                                     name: 'require',
65                                     tail_uuid: system_user_uuid,
66                                     head_kind: 'arvados#collection').
67           collect(&:head_uuid)
68         signed_uuids = Link.where(owner_uuid: system_user_uuid,
69                                   link_class: 'signature',
70                                   name: 'click',
71                                   tail_kind: 'arvados#user',
72                                   tail_uuid: @object.uuid,
73                                   head_kind: 'arvados#collection',
74                                   head_uuid: required_uuids).
75           collect(&:head_uuid)
76         todo_uuids = required_uuids - signed_uuids
77         if todo_uuids == []
78           @object.update_attributes is_active: true
79           logger.info "User #{@object.uuid} activated"
80         else
81           logger.warn "User #{@object.uuid} called users.activate " +
82             "before signing agreements #{todo_uuids.inspect}"
83           raise ArvadosModel::PermissionDeniedError.new \
84           "Cannot activate without user agreements #{todo_uuids.inspect}."
85         end
86       end
87     end
88
89                 @object = User.find @object[:uuid]
90     show
91   end
92
93         # create user object and all the needed links
94   def create
95                 if params[:openid_prefix]               # check if default openid_prefix needs to be overridden
96                         openid_prefix = params[:openid_prefix]
97                 else 
98                         openid_prefix = 'https://www.google.com/accounts/o8/id'         # default openid prefix
99                 end
100                 login_perm_props = {identity_url_prefix: openid_prefix}
101
102                 # check if only to probe the given user parameter
103                 just_probe = (params[:just_probe] == 'true') ? true : false;
104
105 puts "\n*******************************\nparams = #{params}"
106 puts "\n*******************************\nlogin_perm_props = #{login_perm_props.inspect}"
107 puts "\n*******************************\njust_probe = #{just_probe}"
108
109                 @object = model_class.new resource_attrs
110
111                 # If user_param is passed, lookup for user. If exists, skip create and create any missing links. 
112                 if params[:user_param]
113                         begin 
114                                 @object_found = find_user_from_user_param params[:user_param]
115                   end
116
117                         if !@object_found
118                                 @object[:email] = params[:user_param]                           
119                         need_to_create = true
120                         else
121                                 @object = @object_found
122                         end
123                 else            # need to create user for the given :user data
124                         need_to_create = true
125                 end
126
127                 # if just probing, return any object found      
128                 if just_probe   
129                         show; return
130                 end
131
132                 # create if need be, and then create or update the links as needed 
133                 if need_to_create
134                         if @object.save
135                                 # create openid login permission
136               oid_login_perm = Link.create(link_class: 'permission',
137                                            name: 'can_login',
138                                          tail_kind: 'email',
139                                          tail_uuid: @object[:email],
140                                          head_kind: 'arvados#user',
141                                          head_uuid: @object[:uuid],
142                                          properties: login_perm_props
143                                         )
144                                 logger.info { "openid login permission: " + oid_login_perm[:uuid] }
145           else
146                 raise "Save failed"
147                 end
148                 end
149
150                 # create links
151                 link_repo params[:repo_name]
152                 vm_login_permission params[:vm_uuid]
153                 link_group 
154
155                 show
156   end
157
158         protected 
159
160         # find the user from the given user parameter
161         def find_user_from_user_param(user_param)
162                 found_object = User.find_by_uuid user_param
163                 puts "found by uuid = #{found_object.inspect}"
164                 if !found_object
165                         puts "didnt find by uuid. trying email"
166                         begin
167                                 if !user_param.match(/\w\@\w+\.\w+/)
168                                         logger.warn ("Given user param is not valid email format: #{user_param}")
169                                         raise ArgumentError.new "User param is not of valid email format. Stop"
170                                 else
171           found_objects = User.where('email=?', user_param)  
172        
173                                         if found_objects.size > 1
174                                                 logger.warn ("Found #{found_objects.size} users with email #{user_param}. Stop.")
175                                                 raise ArgumentError.new "Found #{found_objects.size} users with email #{user_param}. Stop."
176                                         elsif found_objects.size == 1
177                                                 found_object = found_objects.first
178                                         end
179         end
180                 end
181                 end
182
183                 return found_object
184         end
185         
186         # link the repo_name passed
187         def link_repo(repo_name)
188                 if !repo_name
189                         logger.warn ("Repository name not given for #{@object[:uuid]}. Skip creating the link")
190                         return
191                 end
192
193                 # Look for existing repository access (perhaps using a different repository/user name).
194                 repo_perms = Link.where(tail_uuid: @object[:uuid],
195                             head_kind: 'arvados#repository',
196                             link_class: 'permission',
197                             name: 'can_write')
198
199                 if [] != repo_perms
200                 logger.warn "User already has repository access " + repo_perms.collect { |p| p[:uuid] }.inspect
201                         return
202                 end
203
204                 # Check for an existing repository with the same name we're about to use.
205                 repo = Repository.where(name: repo_name).first
206                 if repo
207                 logger.warn "Repository already exists with name #{repo_name}: #{repo[:uuid]}"
208                         return
209                 end
210
211                 repo ||= Repository.create(name: repo_name)
212                 logger.info { "repo uuid: " + repo[:uuid] }
213
214                 repo_perm = Link.create(tail_kind: 'arvados#user',
215                             tail_uuid: @object[:uuid],
216                             head_kind: 'arvados#repository',
217                             head_uuid: repo[:uuid],
218                             link_class: 'permission',
219                             name: 'can_write')
220                 logger.info { "repo permission: " + repo_perm[:uuid] }
221         end
222
223         # create login permission for the given vm_uuid, if it does not already exist
224         def vm_login_permission(vm_uuid)
225                 # Look up the given virtual machine just to make sure it really exists.
226                 begin
227                 vm = VirtualMachine.get(uuid: vm_uuid)
228
229 # check vm exists 
230
231 # check vm is not already linked first
232
233
234                         logger.info { "vm uuid: " + vm[:uuid] }
235
236                         login_perm = Link.create(tail_kind: 'arvados#user',
237                                tail_uuid: @object[:uuid],
238                                head_kind: 'arvados#virtualMachine',
239                                head_uuid: vm[:uuid],
240                                link_class: 'permission',
241                                name: 'can_login',
242                                properties: {username: repo_name})
243                         logger.info { "login permission: " + login_perm[:uuid] }
244                 rescue
245                 logger.warn "Could not look up virtual machine with uuid #{vm_uuid.inspect}. Skip."
246                 end
247         end
248
249         # add the user to the 'All users' group
250         def link_group
251                 # Look up the "All users" group (we expect uuid *-*-fffffffffffffff).
252                 group = Group.where(name: 'All users').select do |g|
253                         g[:uuid].match /-f+$/
254                 end.first
255
256                 if not group
257                 logger.warn "Could not look up the 'All users' group with uuid '*-*-fffffffffffffff'. Skip."
258                 else
259                         logger.info { "\"All users\" group uuid: " + group[:uuid] }
260
261                         # link the user to 'All users' group, if not already linked
262
263 # check first
264
265                         group_perm = Link.create(tail_kind: 'arvados#user',
266                                tail_uuid: @object[:uuid],
267                                head_kind: 'arvados#group',
268                                head_uuid: group[:uuid],
269                                link_class: 'permission',
270                                name: 'can_read')
271                         logger.info { "group permission: " + group_perm[:uuid] }
272                 end
273         end
274
275 end