When auto_admin_first_user is set to true (default false), the first
[arvados.git] / services / api / app / models / user.rb
index 19e84dca7cf4d37288882cc46037c3494a6e8c34..3b201b5babd90ee9244263737e499b8a381b35a6 100644 (file)
@@ -12,6 +12,7 @@ class User < ArvadosModel
   before_update :prevent_inactive_admin
   before_create :check_auto_admin
   after_create :add_system_group_permission_link
+  after_create :auto_setup_new_user
   after_create :send_admin_notifications
   after_update :send_profile_created_notification
 
@@ -28,6 +29,7 @@ class User < ArvadosModel
     t.add :is_admin
     t.add :is_invited
     t.add :prefs
+    t.add :writable_by
   end
 
   ALL_PERMISSIONS = {read: true, write: true, manage: true}
@@ -69,6 +71,30 @@ class User < ArvadosModel
         next if (group_permissions[target.owner_uuid] and
                  group_permissions[target.owner_uuid][action])
       end
+      sufficient_perms = case action
+                         when :manage
+                           ['can_manage']
+                         when :write
+                           ['can_manage', 'can_write']
+                         when :read
+                           ['can_manage', 'can_write', 'can_read']
+                         else
+                           # (Skip this kind of permission opportunity
+                           # if action is an unknown permission type)
+                         end
+      if sufficient_perms
+        # Check permission links with head_uuid pointing directly at
+        # the target object. If target is a Group, this is redundant
+        # and will fail except [a] if permission caching is broken or
+        # [b] during a race condition, where a permission link has
+        # *just* been added.
+        if Link.where(link_class: 'permission',
+                      name: sufficient_perms,
+                      tail_uuid: groups_i_can(action) + [self.uuid],
+                      head_uuid: target_uuid).any?
+          next
+        end
+      end
       return false
     end
     true
@@ -103,12 +129,13 @@ class User < ArvadosModel
         Group.where('owner_uuid in (?)', lookup_uuids).each do |group|
           newgroups << [group.owner_uuid, group.uuid, 'can_manage']
         end
-        # add any permission links from the current lookup_uuids to a
-        # User or Group.
-        Link.where('tail_uuid in (?) and link_class = ? and (head_uuid like ? or head_uuid like ?)',
-                   lookup_uuids,
+        # add any permission links from the current lookup_uuids to a Group.
+        Link.where('link_class = ? and tail_uuid in (?) and ' \
+                   '(head_uuid like ? or (name = ? and head_uuid like ?))',
                    'permission',
+                   lookup_uuids,
                    Group.uuid_like_pattern,
+                   'can_manage',
                    User.uuid_like_pattern).each do |link|
           newgroups << [link.tail_uuid, link.head_uuid, link.name]
         end
@@ -207,8 +234,10 @@ class User < ArvadosModel
   end
 
   def check_auto_admin
-    if User.where("uuid not like '%-000000000000000'").where(:is_admin => true).count == 0 and Rails.configuration.auto_admin_user
-      if self.email == Rails.configuration.auto_admin_user
+    if not self.uuid.end_with?('anonymouspublic') and
+       User.where("uuid not like '%-000000000000000'").where(:is_admin => true).count == 0
+      if (Rails.configuration.auto_admin_user and self.email == Rails.configuration.auto_admin_user) or
+         Rails.configuration.auto_admin_first_user
         self.is_admin = true
         self.is_active = true
       end
@@ -420,6 +449,49 @@ class User < ArvadosModel
     end
   end
 
+  # Automatically setup new user during creation
+  def auto_setup_new_user
+    return true if !Rails.configuration.auto_setup_new_users
+    return true if !self.email
+    return true if self.uuid == system_user_uuid
+    return true if self.uuid == anonymous_user_uuid
+
+    if Rails.configuration.auto_setup_new_users_with_vm_uuid ||
+       Rails.configuration.auto_setup_new_users_with_repository
+      username = self.email.partition('@')[0] if self.email
+      return true if !username
+
+      blacklisted_usernames = Rails.configuration.auto_setup_name_blacklist
+      if blacklisted_usernames.include?(username)
+        return true
+      elsif !(/^[a-zA-Z][-._a-zA-Z0-9]{0,30}[a-zA-Z0-9]$/.match(username))
+        return true
+      else
+        return true if !(username = derive_unique_username username)
+      end
+    end
+
+    # setup user
+    setup_repo_vm_links(username,
+                        Rails.configuration.auto_setup_new_users_with_vm_uuid,
+                        Rails.configuration.default_openid_prefix)
+  end
+
+  # Find a username that starts with the given string and does not collide
+  # with any existing repository name or VM login name
+  def derive_unique_username username
+    while true
+      if Repository.where(name: username).empty?
+        login_collisions = Link.where(link_class: 'permission',
+                                      name: 'can_login').select do |perm|
+          perm.properties['username'] == username
+        end
+        return username if login_collisions.empty?
+      end
+      username = username + SecureRandom.random_number(10).to_s
+    end
+  end
+
   # Send notification if the user saved profile for the first time
   def send_profile_created_notification
     if self.prefs_changed?