resource_attrs.delete :head_kind
resource_attrs.delete :tail_kind
+
+ if resource_attrs[:link_class] == 'permission' && Link::PermLevel[resource_attrs[:name]]
+ existing = Link.where(link_class: 'permission',
+ tail_uuid: resource_attrs[:tail_uuid],
+ head_uuid: resource_attrs[:head_uuid],
+ name: Link::PermLevel.keys).first
+ if existing
+ @object = existing
+ if Link::PermLevel[resource_attrs[:name]] > Link::PermLevel[existing.name]
+ # upgrade existing permission link to the requested level.
+ return update
+ else
+ # no-op: existing permission is already greater or equal to
+ # the newly requested permission.
+ return show
+ end
+ end
+ end
+
super
end
validate :name_links_are_obsolete
validate :permission_to_attach_to_objects
before_update :restrict_alter_permissions
+ before_update :apply_max_overlapping_permissions
+ after_update :delete_overlapping_permissions
after_update :call_update_permissions
after_create :call_update_permissions
before_destroy :clear_permissions
+ after_destroy :delete_overlapping_permissions
after_destroy :check_permissions
api_accessible :user, extend: :common do |t|
t.add :properties
end
+ PermLevel = {
+ 'can_read' => 0,
+ 'can_write' => 1,
+ 'can_manage' => 2,
+ }
+
+ def apply_max_overlapping_permissions
+ return if self.link_class != 'permission' || !PermLevel[self.name]
+ Link.where(link_class: 'permission',
+ tail_uuid: self.tail_uuid,
+ head_uuid: self.head_uuid,
+ name: PermLevel.keys).
+ where('uuid <> ?', self.uuid).each do |other|
+ if PermLevel[other.name] > PermLevel[self.name]
+ self.name = other.name
+ end
+ end
+ end
+
+ def delete_overlapping_permissions
+ return if self.link_class != 'permission'
+ Link.where(link_class: 'permission',
+ tail_uuid: self.tail_uuid,
+ head_uuid: self.head_uuid,
+ name: PermLevel.keys).
+ where('uuid <> ?', self.uuid).
+ delete_all
+ end
+
def head_kind
if k = ArvadosModel::resource_class_for_uuid(head_uuid)
k.kind
assert_response :success
assert_empty json_response['manifest_text'], "empty collection manifest_text is not empty"
end
+
+ [['can_write', 'can_read', 'can_write'],
+ ['can_manage', 'can_write', 'can_manage'],
+ ['can_manage', 'can_read', 'can_manage'],
+ ['can_read', 'can_write', 'can_write'],
+ ['can_read', 'can_manage', 'can_manage'],
+ ['can_write', 'can_manage', 'can_manage'],
+ ].each do |perm1, perm2, expect|
+ test "creating #{perm2} permission returns existing #{perm1} link as #{expect}" do
+ link1 = act_as_system_user do
+ Link.create!({
+ link_class: "permission",
+ tail_uuid: users(:active).uuid,
+ head_uuid: collections(:baz_file).uuid,
+ name: perm1,
+ })
+ end
+ post "/arvados/v1/links",
+ params: {
+ link: {
+ link_class: "permission",
+ tail_uuid: users(:active).uuid,
+ head_uuid: collections(:baz_file).uuid,
+ name: perm2,
+ },
+ },
+ headers: auth(:admin)
+ assert_response :success
+ assert_equal link1.uuid, json_response["uuid"]
+ assert_equal expect, json_response["name"]
+ link1.reload
+ assert_equal expect, link1.name
+ end
+ end
end
refute new_active_link_valid?(tail_uuid: groups(:public).uuid,
head_uuid: collections(:w_a_z_file_version_1).uuid)
end
+
+ def create_overlapping_permissions(names=[], attrs={})
+ names.map do |name|
+ link = Link.create!({
+ link_class: "tmp",
+ tail_uuid: users(:active).uuid,
+ head_uuid: collections(:baz_file).uuid,
+ name: name,
+ }.merge(attrs).merge({name: name}))
+ ActiveRecord::Base.connection.execute "update links set link_class='permission' where uuid='#{link.uuid}'"
+ link.uuid
+ end
+ end
+
+ test "updating permission causes any conflicting links to be deleted" do
+ link1, link2 = create_overlapping_permissions(['can_read', 'can_manage'])
+ Link.find_by_uuid(link2).update_attributes!(name: 'can_write')
+ assert_empty Link.where(uuid: link1)
+ end
+
+ test "deleting permission causes any conflicting links to be deleted" do
+ rlink, wlink = create_overlapping_permissions(['can_read', 'can_write'])
+ Link.find_by_uuid(wlink).destroy
+ assert_empty Link.where(uuid: rlink)
+ end
end