2762: Protect owner_uuid referential integrity when changing uuids and
[arvados.git] / services / api / lib / can_be_an_owner.rb
1 # Protect 
2
3 module CanBeAnOwner
4
5   def self.included(base)
6     # Rails' "has_many" can prevent us from destroying the owner
7     # record when other objects refer to it.
8     ActiveRecord::Base.connection.tables.each do |t|
9       next if t == base.table_name
10       next if t == 'schema_migrations'
11       klass = t.classify.constantize
12       next unless klass and 'owner_uuid'.in?(klass.columns.collect(&:name))
13       base.has_many(t.to_sym,
14                     foreign_key: :owner_uuid,
15                     primary_key: :uuid,
16                     dependent: :restrict)
17     end
18     # We need custom protection for changing an owner's primary
19     # key. (Apart from this restriction, admins are allowed to change
20     # UUIDs.)
21     base.validate :restrict_uuid_change_breaking_associations
22   end
23
24   protected
25
26   def restrict_uuid_change_breaking_associations
27     return true if new_record? or not uuid_changed?
28
29     # Check for objects that have my old uuid listed as their owner.
30     self.class.reflect_on_all_associations(:has_many).each do |assoc|
31       next unless assoc.foreign_key == :owner_uuid
32       if assoc.klass.where(owner_uuid: uuid_was).any?
33         errors.add(:uuid,
34                    "cannot be changed on a #{self.class} that owns objects")
35         return false
36       end
37     end
38
39     # if I owned myself before, I'll just continue to own myself with
40     # my new uuid.
41     if owner_uuid == uuid_was
42       self.owner_uuid = uuid
43     end
44   end
45
46 end