Merge branch '21535-multi-wf-delete'
[arvados.git] / services / api / app / models / user.rb
index cc5df2bd61cbd67e91a7fe28c2ba2d3db464c626..c104ac6fda4f432714a8ff33d8bebc0b651c55f3 100644 (file)
@@ -25,14 +25,11 @@ class User < ArvadosModel
   before_update :prevent_privilege_escalation
   before_update :prevent_inactive_admin
   before_update :prevent_nonadmin_system_root
-  before_update :verify_repositories_empty, :if => Proc.new {
-    username.nil? and username_changed?
-  }
   after_update :setup_on_activate
 
   before_create :check_auto_admin
-  before_create :set_initial_username, :if => Proc.new {
-    username.nil? and email
+  before_validation :set_initial_username, :if => Proc.new {
+    new_record? && email
   }
   before_create :active_is_not_nil
   after_create :after_ownership_change
@@ -49,16 +46,10 @@ class User < ArvadosModel
   before_update :before_ownership_change
   after_update :after_ownership_change
   after_update :send_profile_created_notification
-  after_update :sync_repository_names, :if => Proc.new {
-    (uuid != system_user_uuid) and
-    saved_change_to_username? and
-    (not username_before_last_save.nil?)
-  }
   before_destroy :clear_permissions
   after_destroy :remove_self_from_permissions
 
   has_many :authorized_keys, foreign_key: 'authorized_user_uuid', primary_key: 'uuid'
-  has_many :repositories, foreign_key: 'owner_uuid', primary_key: 'uuid'
 
   default_scope { where('redirect_to_user_uuid is null') }
 
@@ -261,23 +252,16 @@ SELECT target_uuid, perm_level
   end
 
   # create links
-  def setup(repo_name: nil, vm_uuid: nil, send_notification_email: nil)
+  def setup(vm_uuid: nil, send_notification_email: nil)
     newly_invited = Link.where(tail_uuid: self.uuid,
                               head_uuid: all_users_group_uuid,
-                              link_class: 'permission',
-                              name: 'can_read').empty?
+                              link_class: 'permission').empty?
 
     # Add can_read link from this user to "all users" which makes this
     # user "invited", and (depending on config) a link in the opposite
     # direction which makes this user visible to other users.
     group_perms = add_to_all_users_group
 
-    # Add git repo
-    repo_perm = if (!repo_name.nil? || Rails.configuration.Users.AutoSetupNewUsersWithRepository) and !username.nil?
-                  repo_name ||= "#{username}/#{username}"
-                  create_user_repo_link repo_name
-                end
-
     # Add virtual machine
     if vm_uuid.nil? and !Rails.configuration.Users.AutoSetupNewUsersWithVmUUID.empty?
       vm_uuid = Rails.configuration.Users.AutoSetupNewUsersWithVmUUID
@@ -302,10 +286,10 @@ SELECT target_uuid, perm_level
 
     forget_cached_group_perms
 
-    return [repo_perm, vm_login_perm, *group_perms, self].compact
+    return [vm_login_perm, *group_perms, self].compact
   end
 
-  # delete user signatures, login, repo, and vm perms, and mark as inactive
+  # delete user signatures, login, and vm perms, and mark as inactive
   def unsetup
     if self.uuid == system_user_uuid
       raise "System root user cannot be deactivated"
@@ -387,6 +371,10 @@ SELECT target_uuid, perm_level
   end
 
   def set_initial_username(requested: false)
+    if new_record? and requested == false and self.username != nil and self.username != ""
+      requested = self.username
+    end
+
     if (!requested.is_a?(String) || requested.empty?) and email
       email_parts = email.partition("@")
       local_parts = email_parts.first.partition("+")
@@ -480,30 +468,13 @@ SELECT target_uuid, perm_level
         klass.where(column => uuid).update_all(column => new_user.uuid)
       end
 
-      # Need to update repository names to new username
-      if username
-        old_repo_name_re = /^#{Regexp.escape(username)}\//
-        Repository.where(:owner_uuid => uuid).each do |repo|
-          repo.owner_uuid = new_user.uuid
-          repo_name_sub = "#{new_user.username}/"
-          name = repo.name.sub(old_repo_name_re, repo_name_sub)
-          while (conflict = Repository.where(:name => name).first) != nil
-            repo_name_sub += "migrated"
-            name = repo.name.sub(old_repo_name_re, repo_name_sub)
-          end
-          repo.name = name
-          repo.save!
-        end
-      end
-
       # References to the merged user's "home project" are updated to
       # point to new_owner_uuid.
       ActiveRecord::Base.descendants.reject(&:abstract_class?).each do |klass|
         next if [ApiClientAuthorization,
                  AuthorizedKey,
                  Link,
-                 Log,
-                 Repository].include?(klass)
+                 Log].include?(klass)
         next if !klass.columns.collect(&:name).include?('owner_uuid')
         klass.where(owner_uuid: uuid).update_all(owner_uuid: new_owner_uuid)
       end
@@ -511,7 +482,6 @@ SELECT target_uuid, perm_level
       if redirect_to_new_user
         update!(redirect_to_user_uuid: new_user.uuid, username: nil)
       end
-      puts "self #{self.uuid} new #{new_user.uuid} owner #{new_user.owner_uuid}"
       skip_check_permissions_against_full_refresh do
         update_permissions self.uuid, self.uuid, CAN_MANAGE_PERM, nil, true
         update_permissions new_user.uuid, new_user.uuid, CAN_MANAGE_PERM, nil, true
@@ -607,13 +577,67 @@ SELECT target_uuid, perm_level
 
   def self.update_remote_user remote_user
     remote_user = remote_user.symbolize_keys
+    remote_user_prefix = remote_user[:uuid][0..4]
+
+    # interaction between is_invited and is_active
+    #
+    # either can flag can be nil, true or false
+    #
+    # in all cases, we create the user if they don't exist.
+    #
+    # invited nil, active nil: don't call setup or unsetup.
+    #
+    # invited nil, active false: call unsetup
+    #
+    # invited nil, active true: call setup and activate them.
+    #
+    #
+    # invited false, active nil: call unsetup
+    #
+    # invited false, active false: call unsetup
+    #
+    # invited false, active true: call unsetup
+    #
+    #
+    # invited true, active nil: call setup but don't change is_active
+    #
+    # invited true, active false: call setup but don't change is_active
+    #
+    # invited true, active true: call setup and activate them.
+
+    should_setup = (remote_user_prefix == Rails.configuration.Login.LoginCluster or
+                    Rails.configuration.Users.AutoSetupNewUsers or
+                    Rails.configuration.Users.NewUsersAreActive or
+                    Rails.configuration.RemoteClusters[remote_user_prefix].andand["ActivateUsers"])
+
+    should_activate = (remote_user_prefix == Rails.configuration.Login.LoginCluster or
+                       Rails.configuration.Users.NewUsersAreActive or
+                       Rails.configuration.RemoteClusters[remote_user_prefix].andand["ActivateUsers"])
+
+    remote_should_be_unsetup = (remote_user[:is_invited] == nil && remote_user[:is_active] == false) ||
+                               (remote_user[:is_invited] == false)
+
+    remote_should_be_setup = should_setup && (
+      (remote_user[:is_invited] == nil && remote_user[:is_active] == true) ||
+      (remote_user[:is_invited] == false && remote_user[:is_active] == true) ||
+      (remote_user[:is_invited] == true))
+
+    remote_should_be_active = should_activate && remote_user[:is_invited] != false && remote_user[:is_active] == true
+
+    # Make sure blank username is nil
+    remote_user[:username] = nil if remote_user[:username] == ""
+
     begin
-      user = User.find_or_create_by(uuid: remote_user[:uuid])
+      user = User.create_with(email: remote_user[:email],
+                              username: remote_user[:username],
+                              first_name: remote_user[:first_name],
+                              last_name: remote_user[:last_name],
+                              is_active: remote_should_be_active,
+                             ).find_or_create_by(uuid: remote_user[:uuid])
     rescue ActiveRecord::RecordNotUnique
       retry
     end
 
-    remote_user_prefix = user.uuid[0..4]
     user.with_lock do
       needupdate = {}
       [:email, :username, :first_name, :last_name, :prefs].each do |k|
@@ -627,8 +651,9 @@ SELECT target_uuid, perm_level
 
       loginCluster = Rails.configuration.Login.LoginCluster
       if user.username.nil? || user.username == ""
-        # Don't have a username yet, set one
-        needupdate[:username] = user.set_initial_username(requested: remote_user[:username])
+        # Don't have a username yet, try to set one
+        initial_username = user.set_initial_username(requested: remote_user[:username])
+        needupdate[:username] = initial_username if !initial_username.nil?
       elsif remote_user_prefix != loginCluster
         # Upstream is not login cluster, don't try to change the
         # username once set.
@@ -657,31 +682,29 @@ SELECT target_uuid, perm_level
           end
           raise # Not the issue we're handling above
         end
+      elsif user.new_record?
+        begin
+          user.save!
+        rescue => e
+          Rails.logger.debug "Error saving user record: #{$!}"
+          Rails.logger.debug "Backtrace:\n\t#{e.backtrace.join("\n\t")}"
+          raise
+        end
       end
 
-      if user.is_invited && remote_user[:is_invited] == false
-        # Remote user is not "invited" state, they should be unsetup, which
-        # also makes them inactive.
+      if remote_should_be_unsetup
+        # Remote user is not "invited" or "active" state on their home
+        # cluster, so they should be unsetup, which also makes them
+        # inactive.
         user.unsetup
       else
-        if !user.is_invited && remote_user[:is_invited] and
-          (remote_user_prefix == Rails.configuration.Login.LoginCluster or
-           Rails.configuration.Users.AutoSetupNewUsers or
-           Rails.configuration.Users.NewUsersAreActive or
-           Rails.configuration.RemoteClusters[remote_user_prefix].andand["ActivateUsers"])
-          # Remote user is 'invited' and should be set up
+        if !user.is_invited && remote_should_be_setup
           user.setup
         end
 
-        if !user.is_active && remote_user[:is_active] && user.is_invited and
-          (remote_user_prefix == Rails.configuration.Login.LoginCluster or
-           Rails.configuration.Users.NewUsersAreActive or
-           Rails.configuration.RemoteClusters[remote_user_prefix].andand["ActivateUsers"])
+        if !user.is_active && remote_should_be_active
           # remote user is active and invited, we need to activate them
           user.update!(is_active: true)
-        elsif user.is_active && remote_user[:is_active] == false
-          # remote user is not active, we need to de-activate them
-          user.update!(is_active: false)
         end
 
         if remote_user_prefix == Rails.configuration.Login.LoginCluster and
@@ -834,24 +857,8 @@ SELECT target_uuid, perm_level
     merged
   end
 
-  def create_user_repo_link(repo_name)
-    # repo_name is optional
-    if not repo_name
-      logger.warn ("Repository name not given for #{self.uuid}.")
-      return
-    end
-
-    repo = Repository.where(owner_uuid: uuid, name: repo_name).first_or_create!
-    logger.info { "repo uuid: " + repo[:uuid] }
-    repo_perm = Link.where(tail_uuid: uuid, head_uuid: repo.uuid,
-                           link_class: "permission",
-                           name: "can_manage").first_or_create!
-    logger.info { "repo permission: " + repo_perm[:uuid] }
-    return repo_perm
-  end
-
   # create login permission for the given vm_uuid, if it does not already exist
-  def create_vm_login_permission_link(vm_uuid, repo_name)
+  def create_vm_login_permission_link(vm_uuid, username)
     # vm uuid is optional
     return if vm_uuid == ""
 
@@ -869,11 +876,11 @@ SELECT target_uuid, perm_level
 
     login_perm = Link.
       where(login_attrs).
-      select { |link| link.properties["username"] == repo_name }.
+      select { |link| link.properties["username"] == username }.
       first
 
     login_perm ||= Link.
-      create(login_attrs.merge(properties: {"username" => repo_name}))
+      create(login_attrs.merge(properties: {"username" => username}))
 
     logger.info { "login permission: " + login_perm[:uuid] }
     login_perm
@@ -915,8 +922,9 @@ SELECT target_uuid, perm_level
 
   # Send admin notifications
   def send_admin_notifications
-    AdminNotifier.new_user(self).deliver_now
-    if not self.is_active then
+    if self.is_invited then
+      AdminNotifier.new_user(self).deliver_now
+    else
       AdminNotifier.new_inactive_user(self).deliver_now
     end
   end
@@ -945,22 +953,6 @@ SELECT target_uuid, perm_level
     end
   end
 
-  def verify_repositories_empty
-    unless repositories.first.nil?
-      errors.add(:username, "can't be unset when the user owns repositories")
-      throw(:abort)
-    end
-  end
-
-  def sync_repository_names
-    old_name_re = /^#{Regexp.escape(username_before_last_save)}\//
-    name_sub = "#{username}/"
-    repositories.find_each do |repo|
-      repo.name = repo.name.sub(old_name_re, name_sub)
-      repo.save!
-    end
-  end
-
   def identity_url_nil_if_empty
     if identity_url == ""
       self.identity_url = nil