Remove -n aka just probe option from the setup-new-user script as per Ward's core...
[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     show
89   end
90
91         # create user object and all the needed links
92   def create
93                 if params[:openid_prefix]               # check if default openid_prefix needs to be overridden
94                         openid_prefix = params[:openid_prefix]
95                 else 
96                         openid_prefix = 'https://www.google.com/accounts/o8/id'         # default openid prefix
97                 end
98                 login_perm_props = {identity_url_prefix: openid_prefix}
99
100                 @object = model_class.new resource_attrs
101
102                 # If user_param is passed, lookup for user. If exists, skip create and only create any missing links. 
103                 if params[:user_param]
104                         begin
105                                 @object_found = find_user_from_input params[:user_param], params[:user_param]
106                   end
107                         if !@object_found
108                                 @object = User.new              # when user_param is used, it will be used as user object
109                                 @object[:email] = params[:user_param]                           
110                         need_to_create = true
111                         else
112                                 @object = @object_found
113                         end
114                 else            # need to create user for the given user data
115                         @object_found = find_user_from_input @object[:uuid], @object[:email]
116                         if !@object_found
117                         need_to_create = true           # use the user object sent in to create with the user
118                         else
119                                 @object = @object_found
120                         end
121                 end
122
123                 # create if need be, and then create or update the links as needed 
124                 if need_to_create
125                         if @object.save
126                                 oid_login_perm = Link.where(tail_uuid: @object[:email],
127                                                         head_kind: 'arvados#user',
128                                                         link_class: 'permission',
129                                                         name: 'can_login')
130
131                                 if [] == oid_login_perm
132                                         # create openid login permission
133                 oid_login_perm = Link.create(link_class: 'permission',
134                                                  name: 'can_login',
135                                                  tail_kind: 'email',
136                                            tail_uuid: @object[:email],
137                                            head_kind: 'arvados#user',
138                                            head_uuid: @object[:uuid],
139                                            properties: login_perm_props
140                                                 )
141                                         logger.info { "openid login permission: " + oid_login_perm[:uuid] }
142                                 end
143           else
144                 raise "Save failed"
145                 end
146                 end
147
148                 # create links
149                 create_user_repo_link params[:repo_name]
150                 create_vm_login_permission_link params[:vm_uuid], params[:repo_name]
151                 create_user_group_link 
152
153                 show
154   end
155
156         protected 
157
158         # find the user from the given user parameters
159         def find_user_from_input(user_uuid, user_email)
160                 if user_uuid
161                         found_object = User.find_by_uuid user_uuid
162                 end
163
164                 if !found_object
165                         begin
166                                 if !user_email
167                                         return
168                                 end
169
170                                 if !user_email.match(/\w\@\w+\.\w+/)
171                                         logger.warn ("Given user param is not valid email format: #{user_email}")
172                                         raise ArgumentError.new "User param is not of valid email format. Stop"
173                                 else
174           found_objects = User.where('email=?', user_email)  
175                                         if found_objects.size > 1
176                                                 logger.warn ("Found #{found_objects.size} users with email #{user_email}. Stop.")
177                                                 raise ArgumentError.new "Found #{found_objects.size} users with email #{user_email}. Stop."
178                                         elsif found_objects.size == 1
179                                                 found_object = found_objects.first
180                                         end
181         end
182                 end
183                 end
184
185                 return found_object
186         end
187         
188         # link the repo_name passed
189         def create_user_repo_link(repo_name)
190                 if not repo_name
191                         logger.warn ("Repository name not given for #{@object[:uuid]}. Skip creating the link")
192                         return
193                 end
194
195                 # Check for an existing repository with the same name we're about to use.
196                 repo = (repositories = Repository.where(name: repo_name)) != nil ? repositories.first : nil
197                 if repo
198                 logger.warn "Repository already exists with name #{repo_name}: #{repo[:uuid]}. Will link to user."
199
200                         # Look for existing repository access (perhaps using a different repository/user name).
201                         repo_perms = Link.where(tail_uuid: @object[:uuid],
202                                 head_kind: 'arvados#repository',
203                                 head_uuid: repo[:uuid],
204                                 link_class: 'permission',
205                                 name: 'can_write')
206                         if [] != repo_perms
207                         logger.warn "User already has repository access " + repo_perms.collect { |p| p[:uuid] }.inspect
208                                 return
209                         end
210                 end
211
212                 repo ||= Repository.create(name: repo_name)             # create repo, if does not already exist
213                 logger.info { "repo uuid: " + repo[:uuid] }
214
215                 repo_perm = Link.create(tail_kind: 'arvados#user',
216                             tail_uuid: @object[:uuid],
217                             head_kind: 'arvados#repository',
218                             head_uuid: repo[:uuid],
219                             link_class: 'permission',
220                             name: 'can_write')
221                 logger.info { "repo permission: " + repo_perm[:uuid] }
222         end
223
224         # create login permission for the given vm_uuid, if it does not already exist
225         def create_vm_login_permission_link(vm_uuid, repo_name)
226                 # Look up the given virtual machine just to make sure it really exists.
227                 begin
228                         vm = (vms = VirtualMachine.where(uuid: vm_uuid)) != nil ? vms.first : nil
229                         if not vm
230                           logger.warn "Could not look up virtual machine with uuid #{vm_uuid.inspect}"
231                                 return
232                         end
233
234                         logger.info { "vm uuid: " + vm[:uuid] }
235
236                         login_perm = Link.where(tail_uuid: @object[:uuid],
237                               head_uuid: vm[:uuid],
238                                 head_kind: 'arvados#virtualMachine',
239                                 link_class: 'permission',
240                                 name: 'can_login')
241                         if [] == login_perm
242                                 login_perm = Link.create(tail_kind: 'arvados#user',
243                                        tail_uuid: @object[:uuid],
244                                        head_kind: 'arvados#virtualMachine',
245                                        head_uuid: vm[:uuid],
246                                        link_class: 'permission',
247                                        name: 'can_login',
248                                        properties: {username: repo_name})
249                                 logger.info { "login permission: " + login_perm[:uuid] }
250                         end
251                 end
252         end
253
254         # add the user to the 'All users' group
255         def create_user_group_link
256                 # Look up the "All users" group (we expect uuid *-*-fffffffffffffff).
257                 group = Group.where(name: 'All users').select do |g|
258                         g[:uuid].match /-f+$/
259                 end.first
260
261                 if not group
262                 logger.warn "Could not look up the 'All users' group with uuid '*-*-fffffffffffffff'. Skip."
263                         return
264                 else
265                         logger.info { "\"All users\" group uuid: " + group[:uuid] }
266
267                         group_perm = Link.where(tail_uuid: @object[:uuid],
268                                                                                                                         head_uuid: group[:uuid],
269                                 head_kind: 'arvados#group',
270                                 link_class: 'permission',
271                                 name: 'can_read')
272
273                         if [] == group_perm
274                                 group_perm = Link.create(tail_kind: 'arvados#user',
275                                      tail_uuid: @object[:uuid],
276                                      head_kind: 'arvados#group',
277                                      head_uuid: group[:uuid],
278                                      link_class: 'permission',
279                                      name: 'can_read')
280                                 logger.info { "group permission: " + group_perm[:uuid] }
281                         end
282                 end
283         end
284
285 end