7709: Merge branch 'master' into 7709-api-rails4
[arvados.git] / services / api / lib / has_uuid.rb
index 3bd330edca59808bd3fbbe21f49725c687e5e7c7..74d09e94e9122998cd376d867b88ae9816875978 100644 (file)
@@ -1,11 +1,24 @@
 module HasUuid
 
+  UUID_REGEX = /^[0-9a-z]{5}-([0-9a-z]{5})-[0-9a-z]{15}$/
+
   def self.included(base)
     base.extend(ClassMethods)
+    base.validate :validate_uuid
     base.before_create :assign_uuid
     base.before_destroy :destroy_permission_links
-    base.has_many :links_via_head, class_name: 'Link', foreign_key: :head_uuid, primary_key: :uuid, conditions: "not (link_class = 'permission')", dependent: :restrict
-    base.has_many :links_via_tail, class_name: 'Link', foreign_key: :tail_uuid, primary_key: :uuid, conditions: "not (link_class = 'permission')", dependent: :restrict
+    base.has_many(:links_via_head,
+                  -> { where("not (link_class = 'permission')") },
+                  class_name: 'Link',
+                  foreign_key: :head_uuid,
+                  primary_key: :uuid,
+                  dependent: :destroy)
+    base.has_many(:links_via_tail,
+                  -> { where("not (link_class = 'permission')") },
+                  class_name: 'Link',
+                  foreign_key: :tail_uuid,
+                  primary_key: :uuid,
+                  dependent: :destroy)
   end
 
   module ClassMethods
@@ -26,17 +39,44 @@ module HasUuid
     self.respond_to? :uuid
   end
 
-  def assign_uuid
-    return true if !self.respond_to_uuid?
-    if (uuid.is_a?(String) and uuid.length>0 and
-        current_user and current_user.is_admin)
+  def validate_uuid
+    if self.respond_to_uuid? and self.uuid_changed?
+      if current_user.andand.is_admin and self.uuid.is_a?(String)
+        if (re = self.uuid.match HasUuid::UUID_REGEX)
+          if re[1] == self.class.uuid_prefix
+            return true
+          else
+            self.errors.add(:uuid, "type field is '#{re[1]}', expected '#{self.class.uuid_prefix}'")
+            return false
+          end
+        else
+          self.errors.add(:uuid, "not a valid Arvados uuid '#{self.uuid}'")
+          return false
+        end
+      else
+        if self.new_record?
+          self.errors.add(:uuid, "assignment not permitted")
+        else
+          self.errors.add(:uuid, "change not permitted")
+        end
+        return false
+      end
+    else
       return true
     end
-    self.uuid = self.class.generate_uuid
+  end
+
+  def assign_uuid
+    if self.respond_to_uuid? and self.uuid.nil? or self.uuid.empty?
+      self.uuid = self.class.generate_uuid
+    end
+    true
   end
 
   def destroy_permission_links
-    Link.destroy_all(['link_class=? and (head_uuid=? or tail_uuid=?)',
-                      'permission', uuid, uuid])
+    if uuid
+      Link.destroy_all(['link_class=? and (head_uuid=? or tail_uuid=?)',
+                        'permission', uuid, uuid])
+    end
   end
 end