X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/b2979b0db0b6b5ee7ad3001ffcc8f76ff4d96f05..cea92754dfacf2b409d1f5b45dd0775fc44c842d:/services/api/app/models/arvados_model.rb diff --git a/services/api/app/models/arvados_model.rb b/services/api/app/models/arvados_model.rb index 2bf1376d6d..95fd055d49 100644 --- a/services/api/app/models/arvados_model.rb +++ b/services/api/app/models/arvados_model.rb @@ -1,4 +1,5 @@ -require 'assign_uuid' +require 'has_uuid' + class ArvadosModel < ActiveRecord::Base self.abstract_class = true @@ -14,12 +15,12 @@ class ArvadosModel < ActiveRecord::Base before_save :ensure_ownership_path_leads_to_user before_destroy :ensure_owner_uuid_is_permitted before_destroy :ensure_permission_to_destroy - before_create :update_modified_by_fields before_update :maybe_update_modified_by_fields after_create :log_create after_update :log_update after_destroy :log_destroy + after_find :convert_serialized_symbols_to_strings validate :ensure_serialized_attribute_type validate :normalize_collection_uuids validate :ensure_valid_uuids @@ -187,9 +188,13 @@ class ArvadosModel < ActiveRecord::Base def ensure_owner_uuid_is_permitted raise PermissionDeniedError if !current_user - self.owner_uuid ||= current_user.uuid + if respond_to? :owner_uuid= + self.owner_uuid ||= current_user.uuid + end if self.owner_uuid_changed? - if current_user.uuid == self.owner_uuid or + if new_record? + return true + elsif current_user.uuid == self.owner_uuid or current_user.can? write: self.owner_uuid # current_user is, or has :write permission on, the new owner else @@ -247,6 +252,7 @@ class ArvadosModel < ActiveRecord::Base def maybe_update_modified_by_fields update_modified_by_fields if self.changed? or self.new_record? + true end def update_modified_by_fields @@ -255,6 +261,39 @@ class ArvadosModel < ActiveRecord::Base self.modified_at = Time.now self.modified_by_user_uuid = current_user ? current_user.uuid : nil self.modified_by_client_uuid = current_api_client ? current_api_client.uuid : nil + true + end + + def self.has_symbols? x + if x.is_a? Hash + x.each do |k,v| + return true if has_symbols?(k) or has_symbols?(v) + end + false + elsif x.is_a? Array + x.each do |k| + return true if has_symbols?(k) + end + false + else + (x.class == Symbol) + end + end + + def self.recursive_stringify x + if x.is_a? Hash + Hash[x.collect do |k,v| + [recursive_stringify(k), recursive_stringify(v)] + end] + elsif x.is_a? Array + x.collect do |k| + recursive_stringify k + end + elsif x.is_a? Symbol + x.to_s + else + x + end end def ensure_serialized_attribute_type @@ -266,13 +305,31 @@ class ArvadosModel < ActiveRecord::Base # 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}" + if self.attributes[colname].class != attr.object_class + self.errors.add colname.to_sym, "must be a #{attr.object_class.to_s}, not a #{self.attributes[colname].class.to_s}" + elsif self.class.has_symbols? attributes[colname] + self.errors.add colname.to_sym, "must not contain symbols: #{attributes[colname].inspect}" end end end end + def convert_serialized_symbols_to_strings + # ensure_serialized_attribute_type should prevent symbols from + # getting into the database in the first place. If someone managed + # to get them into the database (perhaps using an older version) + # we'll convert symbols to strings when loading from the + # database. (Otherwise, loading and saving an object with existing + # symbols in a serialized field will crash.) + self.class.serialized_attributes.each do |colname, attr| + if self.class.has_symbols? attributes[colname] + attributes[colname] = self.class.recursive_stringify attributes[colname] + self.send(colname + '=', + self.class.recursive_stringify(attributes[colname])) + end + end + end + def foreign_key_attributes attributes.keys.select { |a| a.match /_uuid$/ } end