18691: Return empty writable_by for items inside frozen projects.
[arvados.git] / services / api / app / models / arvados_model.rb
index 7d5bea8faeb791482be7be26cc47834c42d88db5..a979338e4313a8f7a091e8ffd6cee9b0d112b199 100644 (file)
@@ -246,6 +246,15 @@ class ArvadosModel < ApplicationRecord
   # If current user cannot write this object, just return
   # [self.owner_uuid].
   def writable_by
+    # Return [] if this is a frozen project and the current user can't
+    # unfreeze
+    return [] if respond_to?(:frozen_by_uuid) && frozen_by_uuid &&
+                 !(Rails.configuration.API.UnfreezeProjectRequiresAdmin ?
+                     current_user.andand.is_admin :
+                     current_user.can?(manage: uuid))
+    # Return [] if nobody can write because this object is inside a
+    # frozen project
+    return [] if FrozenGroup.where(uuid: owner_uuid).any?
     return [owner_uuid] if not current_user
     unless (owner_uuid == current_user.uuid or
             current_user.is_admin or
@@ -384,13 +393,27 @@ class ArvadosModel < ApplicationRecord
         direct_check = " OR " + direct_check
       end
 
+      if Rails.configuration.Users.RoleGroupsVisibleToAll &&
+         sql_table == "groups" &&
+         users_list.select { |u| u.is_active }.any?
+        # All role groups are readable (but we still need the other
+        # direct_check clauses to handle non-role groups).
+        direct_check += " OR #{sql_table}.group_class = 'role'"
+      end
+
       links_cond = ""
       if sql_table == "links"
-        # Match any permission link that gives one of the authorized
-        # users some permission _or_ gives anyone else permission to
-        # view one of the authorized users.
+        # 1) Match permission links incoming or outgoing on the
+        # user, i.e. granting permission on the user, or granting
+        # permission to the user.
+        #
+        # 2) Match permission links which grant permission on an
+        # object that this user can_manage.
+        #
         links_cond = "OR (#{sql_table}.link_class IN (:permission_link_classes) AND "+
-                       "(#{sql_table}.head_uuid IN (#{user_uuids_subquery}) OR #{sql_table}.tail_uuid IN (#{user_uuids_subquery})))"
+                     "   ((#{sql_table}.head_uuid IN (#{user_uuids_subquery}) OR #{sql_table}.tail_uuid IN (#{user_uuids_subquery})) OR " +
+                     "    #{sql_table}.head_uuid IN (SELECT target_uuid FROM #{PERMISSION_VIEW} "+
+                     "    WHERE user_uuid IN (#{user_uuids_subquery}) AND perm_level >= 3))) "
       end
 
       sql_conds = "(#{owner_check} #{direct_check} #{links_cond}) #{trashed_check.empty? ? "" : "AND"} #{trashed_check}"
@@ -408,7 +431,7 @@ class ArvadosModel < ApplicationRecord
 
     self.where(sql_conds,
                user_uuids: all_user_uuids.collect{|c| c["target_uuid"]},
-               permission_link_classes: ['permission', 'resources'])
+               permission_link_classes: ['permission'])
   end
 
   def save_with_unique_name!
@@ -629,7 +652,14 @@ class ArvadosModel < ApplicationRecord
   end
 
   def permission_to_create
-    current_user.andand.is_active
+    if !current_user.andand.is_active
+      return false
+    end
+    if self.respond_to?(:owner_uuid) && FrozenGroup.where(uuid: owner_uuid).any?
+      errors.add :owner_uuid, "#{owner_uuid} is frozen"
+      return false
+    end
+    return true
   end
 
   def permission_to_update
@@ -646,6 +676,13 @@ class ArvadosModel < ApplicationRecord
       logger.warn "User #{current_user.uuid} tried to change uuid of #{self.class.to_s} #{self.uuid_was} to #{self.uuid}"
       return false
     end
+    if self.respond_to?(:owner_uuid)
+      frozen = FrozenGroup.where(uuid: [owner_uuid, owner_uuid_in_database]).to_a
+      if frozen.any?
+        errors.add :uuid, "#{uuid} cannot be modified in frozen project #{frozen[0]}"
+        return false
+      end
+    end
     return true
   end
 
@@ -695,7 +732,7 @@ class ArvadosModel < ApplicationRecord
     false
   end
 
-  def self.where_serialized(colname, value, md5: false)
+  def self.where_serialized(colname, value, md5: false, multivalue: false)
     colsql = colname.to_s
     if md5
       colsql = "md5(#{colsql})"
@@ -708,7 +745,16 @@ class ArvadosModel < ApplicationRecord
       sql = "#{colsql} IN (?)"
       sorted = deep_sort_hash(value)
     end
-    params = [sorted.to_yaml, SafeJSON.dump(sorted)]
+    params = []
+    if multivalue
+      sorted.each do |v|
+        params << v.to_yaml
+        params << SafeJSON.dump(v)
+      end
+    else
+      params << sorted.to_yaml
+      params << SafeJSON.dump(sorted)
+    end
     if md5
       params = params.map { |x| Digest::MD5.hexdigest(x) }
     end
@@ -877,7 +923,12 @@ class ArvadosModel < ApplicationRecord
   # request.
   def fill_container_defaults
     self.runtime_constraints = {
-      'api' => false,
+      'API' => false,
+      'cuda' => {
+        'device_count' => 0,
+        'driver_version' => '',
+        'hardware_capability' => '',
+      },
       'keep_cache_ram' => 0,
       'ram' => 0,
       'vcpus' => 0,