relocate permissions code, use resulting :read permissions in #index. refs #1415
[arvados.git] / app / models / orvos_model.rb
index c26ac7e0baa2e82bc277a153e504d653ce148d9f..262d755426e8d8a7bd52dc75cffd90f6e5d05149 100644 (file)
@@ -7,9 +7,11 @@ class OrvosModel < ActiveRecord::Base
   attr_protected :modified_by_user
   attr_protected :modified_by_client
   attr_protected :modified_at
-  before_update :permission_to_update
+  before_create :ensure_permission_to_create
+  before_update :ensure_permission_to_update
   before_create :update_modified_by_fields
-  before_update :update_modified_by_fields
+  before_update :maybe_update_modified_by_fields
+  validate :ensure_serialized_attribute_type
 
   def self.kind_class(kind)
     kind.match(/^orvos\#(.+?)(_list|List)?$/)[1].pluralize.classify.constantize rescue nil
@@ -30,34 +32,76 @@ class OrvosModel < ActiveRecord::Base
 
   protected
 
+  def ensure_permission_to_create
+    raise "Permission denied" unless permission_to_create
+  end
+
+  def permission_to_create
+    current_user
+  end
+
+  def ensure_permission_to_update
+    raise "Permission denied" unless permission_to_update
+  end
+
   def permission_to_update
-    return false unless current_user
+    if !current_user
+      logger.warn "Anonymous user tried to update #{self.class.to_s} #{self.uuid_was}"
+      return false
+    end
+    if self.uuid_changed?
+      logger.warn "User #{current_user.uuid} tried to change uuid of #{self.class.to_s} #{self.uuid_was} to #{self.uuid}"
+      return false
+    end
     return true if current_user.is_admin
     if self.owner_changed? and
         self.owner_was != current_user.uuid and
         0 == Link.where(link_class: 'permission',
                         name: 'can_pillage',
-                        tail_uuid: self.owner,
+                        tail_uuid: self.owner_was,
                         head_uuid: current_user.uuid).count
       logger.warn "User #{current_user.uuid} tried to change owner of #{self.class.to_s} #{self.uuid} to #{self.owner}"
       return false
     end
-    self.owner == current_user.uuid or
+    if self.owner_was == current_user.uuid or
       current_user.is_admin or
       current_user.uuid == self.uuid or
       Link.where(link_class: 'permission',
                  name: 'can_write',
-                 tail_uuid: self.owner,
+                 tail_uuid: self.owner_was,
                  head_uuid: current_user.uuid).count > 0
+      return true
+    else
+      logger.warn "User #{current_user.uuid} tried to modify #{self.class.to_s} #{self.uuid} but does not have can_write permission and owner is #{self.owner_was}"
+      return false
+    end
+  end
+
+  def maybe_update_modified_by_fields
+    update_modified_by_fields if self.changed?
   end
 
   def update_modified_by_fields
-    if self.changed?
-      self.created_at ||= Time.now
-      self.owner ||= current_user.uuid
-      self.modified_at = Time.now
-      self.modified_by_user = current_user.uuid
-      self.modified_by_client = current_api_client ? current_api_client.uuid : nil
+    self.created_at ||= Time.now
+    self.owner ||= current_user.uuid if current_user
+    self.modified_at = Time.now
+    self.modified_by_user = current_user ? current_user.uuid : nil
+    self.modified_by_client = current_api_client ? current_api_client.uuid : nil
+  end
+
+  def ensure_serialized_attribute_type
+    # Specifying a type in the "serialize" declaration causes rails to
+    # raise an exception if a different data type is retrieved from
+    # the database during load().  The validation preventing such
+    # crash-inducing records from being inserted in the database in
+    # the first place seems to have been left as an exercise to the
+    # developer.
+    self.class.serialized_attributes.each do |colname, attr|
+      if attr.object_class
+        unless self.attributes[colname].is_a? attr.object_class
+          self.errors.add colname.to_sym, "must be a #{attr.object_class.to_s}"
+        end
+      end
     end
   end
 end