16470: Adds an explicit reload before every pending with_lock call.
[arvados.git] / services / api / app / models / user.rb
index cb7efe9cca72245f0be24c6fb218e42551b33d90..e1fd53e3def9f035e317660d29274f509f42f5d9 100644 (file)
@@ -23,35 +23,35 @@ class User < ArvadosModel
   validate :must_unsetup_to_deactivate
   before_update :prevent_privilege_escalation
   before_update :prevent_inactive_admin
-  before_update :verify_repositories_empty, :if => Proc.new { |user|
-    user.username.nil? and user.username_changed?
+  before_update :verify_repositories_empty, :if => Proc.new {
+    username.nil? and username_changed?
   }
   before_update :setup_on_activate
 
   before_create :check_auto_admin
-  before_create :set_initial_username, :if => Proc.new { |user|
-    user.username.nil? and user.email
+  before_create :set_initial_username, :if => Proc.new {
+    username.nil? and email
   }
   after_create :after_ownership_change
   after_create :setup_on_activate
   after_create :add_system_group_permission_link
-  after_create :auto_setup_new_user, :if => Proc.new { |user|
+  after_create :auto_setup_new_user, :if => Proc.new {
     Rails.configuration.Users.AutoSetupNewUsers and
-    (user.uuid != system_user_uuid) and
-    (user.uuid != anonymous_user_uuid)
+    (uuid != system_user_uuid) and
+    (uuid != anonymous_user_uuid)
   }
   after_create :send_admin_notifications
 
   before_update :before_ownership_change
   after_update :after_ownership_change
   after_update :send_profile_created_notification
-  after_update :sync_repository_names, :if => Proc.new { |user|
-    (user.uuid != system_user_uuid) and
-    user.username_changed? and
-    (not user.username_was.nil?)
+  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 :check_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
@@ -121,10 +121,12 @@ class User < ArvadosModel
 
       target_owner_uuid = target.owner_uuid if target.respond_to? :owner_uuid
 
+      user_uuids_subquery = USER_UUIDS_SUBQUERY_TEMPLATE % {user: "$1", perm_level: "$3"}
+
       unless ActiveRecord::Base.connection.
         exec_query(%{
 SELECT 1 FROM #{PERMISSION_VIEW}
-  WHERE user_uuid = $1 and
+  WHERE user_uuid in (#{user_uuids_subquery}) and
         ((target_uuid = $2 and perm_level >= $3)
          or (target_uuid = $4 and perm_level >= $3 and traverse_owned))
 },
@@ -144,32 +146,37 @@ SELECT 1 FROM #{PERMISSION_VIEW}
   def before_ownership_change
     if owner_uuid_changed? and !self.owner_uuid_was.nil?
       MaterializedPermission.where(user_uuid: owner_uuid_was, target_uuid: uuid).delete_all
-      update_permissions self.owner_uuid_was, self.uuid, 0
+      update_permissions self.owner_uuid_was, self.uuid, REVOKE_PERM
     end
   end
 
   def after_ownership_change
-    if owner_uuid_changed?
-      update_permissions self.owner_uuid, self.uuid, 3
+    if saved_change_to_owner_uuid?
+      update_permissions self.owner_uuid, self.uuid, CAN_MANAGE_PERM
     end
   end
 
   def clear_permissions
-    update_permissions self.owner_uuid, self.uuid, 0
-    MaterializedPermission.where("user_uuid = ? or target_uuid = ?", uuid, uuid).delete_all
+    MaterializedPermission.where("user_uuid = ? and target_uuid != ?", uuid, uuid).delete_all
   end
 
-  def check_permissions
+  def remove_self_from_permissions
+    MaterializedPermission.where("target_uuid = ?", uuid).delete_all
     check_permissions_against_full_refresh
   end
 
   # Return a hash of {user_uuid: group_perms}
+  #
+  # note: this does not account for permissions that a user gains by
+  # having can_manage on another user.
   def self.all_group_permissions
     all_perms = {}
     ActiveRecord::Base.connection.
-      exec_query("SELECT user_uuid, target_uuid, perm_level
+      exec_query(%{
+SELECT user_uuid, target_uuid, perm_level
                   FROM #{PERMISSION_VIEW}
-                  WHERE traverse_owned",
+                  WHERE traverse_owned
+},
                   # "name" arg is a query label that appears in logs:
                  "all_group_permissions").
       rows.each do |user_uuid, group_uuid, max_p_val|
@@ -184,14 +191,17 @@ SELECT 1 FROM #{PERMISSION_VIEW}
   # objects owned by group_uuid.
   def group_permissions(level=1)
     group_perms = {}
+
+    user_uuids_subquery = USER_UUIDS_SUBQUERY_TEMPLATE % {user: "$1", perm_level: "$2"}
+
     ActiveRecord::Base.connection.
       exec_query(%{
 SELECT target_uuid, perm_level
   FROM #{PERMISSION_VIEW}
-  WHERE user_uuid = $1 and perm_level >= $2
+  WHERE user_uuid in (#{user_uuids_subquery}) and perm_level >= $2
 },
                   # "name" arg is a query label that appears in logs:
-                  "group_permissions_for_user",
+                  "User.group_permissions",
                   # "binds" arg is an array of [col_id, value] for '$1' vars:
                   [[nil, uuid],
                    [nil, level]]).
@@ -361,6 +371,7 @@ update #{PERMISSION_VIEW} set target_uuid=$1 where target_uuid = $2
       raise "cannot merge to an already merged user" if new_user.redirect_to_user_uuid
 
       self.clear_permissions
+      new_user.clear_permissions
 
       # If 'self' is a remote user, don't transfer authorizations
       # (i.e. ability to access the account) to the new user, because
@@ -437,9 +448,11 @@ update #{PERMISSION_VIEW} set target_uuid=$1 where target_uuid = $2
         update_attributes!(redirect_to_user_uuid: new_user.uuid, username: nil)
       end
       skip_check_permissions_against_full_refresh do
-        update_permissions self.owner_uuid, self.uuid, 3
+        update_permissions self.uuid, self.uuid, CAN_MANAGE_PERM
+        update_permissions new_user.uuid, new_user.uuid, CAN_MANAGE_PERM
+        update_permissions new_user.owner_uuid, new_user.uuid, CAN_MANAGE_PERM
       end
-      update_permissions new_user.owner_uuid, new_user.uuid, 3
+      update_permissions self.owner_uuid, self.uuid, CAN_MANAGE_PERM
     end
   end
 
@@ -730,7 +743,7 @@ update #{PERMISSION_VIEW} set target_uuid=$1 where target_uuid = $2
   # Automatically setup if is_active flag turns on
   def setup_on_activate
     return if [system_user_uuid, anonymous_user_uuid].include?(self.uuid)
-    if is_active && (new_record? || is_active_changed?)
+    if is_active && (new_record? || saved_change_to_is_active?)
       setup
     end
   end
@@ -753,8 +766,8 @@ update #{PERMISSION_VIEW} set target_uuid=$1 where target_uuid = $2
 
   # Send notification if the user saved profile for the first time
   def send_profile_created_notification
-    if self.prefs_changed?
-      if self.prefs_was.andand.empty? || !self.prefs_was.andand['profile']
+    if saved_change_to_prefs?
+      if prefs_before_last_save.andand.empty? || !prefs_before_last_save.andand['profile']
         profile_notification_address = Rails.configuration.Users.UserProfileNotificationAddress
         ProfileNotifier.profile_created(self, profile_notification_address).deliver_now if profile_notification_address and !profile_notification_address.empty?
       end
@@ -769,7 +782,7 @@ update #{PERMISSION_VIEW} set target_uuid=$1 where target_uuid = $2
   end
 
   def sync_repository_names
-    old_name_re = /^#{Regexp.escape(username_was)}\//
+    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)