1 # Copyright (C) The Arvados Authors. All rights reserved.
3 # SPDX-License-Identifier: AGPL-3.0
5 class Link < ArvadosModel
8 include CommonApiTemplate
10 # Posgresql JSONB columns should NOT be declared as serialized, Rails 5
11 # already know how to properly treat them.
12 attribute :properties, :jsonbHash, default: {}
14 validate :name_links_are_obsolete
15 validate :permission_to_attach_to_objects
16 before_update :restrict_alter_permissions
17 after_update :call_update_permissions
18 after_create :call_update_permissions
19 before_destroy :clear_permissions
20 after_destroy :check_permissions
22 api_accessible :user, extend: :common do |t|
33 if k = ArvadosModel::resource_class_for_uuid(head_uuid)
39 if k = ArvadosModel::resource_class_for_uuid(tail_uuid)
46 def check_readable_uuid attr, attr_value
47 if attr == 'tail_uuid' &&
49 self.link_class == 'permission' &&
50 attr_value[0..4] != Rails.configuration.ClusterID &&
51 ArvadosModel::resource_class_for_uuid(attr_value) == User
52 # Permission link tail is a remote user (the user permissions
53 # are being granted to), so bypass the standard check that a
54 # referenced object uuid is readable by current user.
56 # We could do a call to the remote cluster to check if the user
57 # in tail_uuid exists. This would detect copy-and-paste errors,
58 # but add another way for the request to fail, and I don't think
59 # it would improve security. It doesn't seem to be worth the
60 # complexity tradeoff.
67 def permission_to_attach_to_objects
68 # Anonymous users cannot write links
69 return false if !current_user
71 # All users can write links that don't affect permissions
72 return true if self.link_class != 'permission'
74 if PERM_LEVEL[self.name].nil?
75 errors.add(:name, "is invalid permission, must be one of 'can_read', 'can_write', 'can_manage', 'can_login'")
79 rsc_class = ArvadosModel::resource_class_for_uuid tail_uuid
81 tail_obj = Group.find_by_uuid(tail_uuid)
83 errors.add(:tail_uuid, "does not exist")
86 if tail_obj.group_class != "role"
87 errors.add(:tail_uuid, "must be a user or role, was group with group_class #{tail_obj.group_class}")
90 elsif rsc_class != User
91 errors.add(:tail_uuid, "must be a user or role")
95 # Administrators can grant permissions
96 return true if current_user.is_admin
98 head_obj = ArvadosModel.find_by_uuid(head_uuid)
101 errors.add(:head_uuid, "does not exist")
105 # No permission links can be pointed to past collection versions
106 if head_obj.is_a?(Collection) && head_obj.current_version_uuid != head_uuid
107 errors.add(:head_uuid, "cannot point to a past version of a collection")
111 # All users can grant permissions on objects they own or can manage
112 return true if current_user.can?(manage: head_obj)
118 def restrict_alter_permissions
119 return true if self.link_class != 'permission' && self.link_class_was != 'permission'
121 return true if current_user.andand.uuid == system_user.uuid
123 if link_class_changed? || tail_uuid_changed? || head_uuid_changed?
124 raise "Can only alter permission link level"
135 def call_update_permissions
136 if self.link_class == 'permission'
137 update_permissions tail_uuid, head_uuid, PERM_LEVEL[name], self.uuid
141 def clear_permissions
142 if self.link_class == 'permission'
143 update_permissions tail_uuid, head_uuid, REVOKE_PERM, self.uuid
147 def check_permissions
148 if self.link_class == 'permission'
149 check_permissions_against_full_refresh
153 def name_links_are_obsolete
154 if link_class == 'name'
155 errors.add('name', 'Name links are obsolete')
162 # A user is permitted to create, update or modify a permission link
163 # if and only if they have "manage" permission on the object
164 # indicated by the permission link's head_uuid.
166 # All other links are treated as regular ArvadosModel objects.
168 def ensure_owner_uuid_is_permitted
169 if link_class == 'permission'
170 ob = ArvadosModel.find_by_uuid(head_uuid)
171 raise PermissionDeniedError unless current_user.can?(manage: ob)
172 # All permission links should be owned by the system user.
173 self.owner_uuid = system_user_uuid