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 ApiClientAuthorization.remote_host(uuid_prefix: attr_value[0..4]) &&
52 ArvadosModel::resource_class_for_uuid(attr_value) == User
53 # Permission link tail is a remote user (the user permissions
54 # are being granted to), so bypass the standard check that a
55 # referenced object uuid is readable by current user.
57 # We could do a call to the remote cluster to check if the user
58 # in tail_uuid exists. This would detect copy-and-paste errors,
59 # but add another way for the request to fail, and I don't think
60 # it would improve security. It doesn't seem to be worth the
61 # complexity tradeoff.
68 def permission_to_attach_to_objects
69 # Anonymous users cannot write links
70 return false if !current_user
72 # All users can write links that don't affect permissions
73 return true if self.link_class != 'permission'
75 if PERM_LEVEL[self.name].nil?
76 errors.add(:name, "is invalid permission, must be one of 'can_read', 'can_write', 'can_manage', 'can_login'")
80 rsc_class = ArvadosModel::resource_class_for_uuid tail_uuid
82 tail_obj = Group.find_by_uuid(tail_uuid)
84 errors.add(:tail_uuid, "does not exist")
87 if tail_obj.group_class != "role"
88 errors.add(:tail_uuid, "must be a user or role, was group with group_class #{tail_obj.group_class}")
91 elsif rsc_class != User
92 errors.add(:tail_uuid, "must be a user or role")
96 # Administrators can grant permissions
97 return true if current_user.is_admin
99 head_obj = ArvadosModel.find_by_uuid(head_uuid)
102 errors.add(:head_uuid, "does not exist")
106 # No permission links can be pointed to past collection versions
107 if head_obj.is_a?(Collection) && head_obj.current_version_uuid != head_uuid
108 errors.add(:head_uuid, "cannot point to a past version of a collection")
112 # All users can grant permissions on objects they own or can manage
113 return true if current_user.can?(manage: head_obj)
119 def restrict_alter_permissions
120 return true if self.link_class != 'permission' && self.link_class_was != 'permission'
122 return true if current_user.andand.uuid == system_user.uuid
124 if link_class_changed? || tail_uuid_changed? || head_uuid_changed?
125 raise "Can only alter permission link level"
136 def call_update_permissions
137 if self.link_class == 'permission'
138 update_permissions tail_uuid, head_uuid, PERM_LEVEL[name], self.uuid
139 current_user.forget_cached_group_perms
143 def clear_permissions
144 if self.link_class == 'permission'
145 update_permissions tail_uuid, head_uuid, REVOKE_PERM, self.uuid
146 current_user.forget_cached_group_perms
150 def check_permissions
151 if self.link_class == 'permission'
152 check_permissions_against_full_refresh
156 def name_links_are_obsolete
157 if link_class == 'name'
158 errors.add('name', 'Name links are obsolete')
165 # A user is permitted to create, update or modify a permission link
166 # if and only if they have "manage" permission on the object
167 # indicated by the permission link's head_uuid.
169 # All other links are treated as regular ArvadosModel objects.
171 def ensure_owner_uuid_is_permitted
172 if link_class == 'permission'
173 ob = ArvadosModel.find_by_uuid(head_uuid)
174 raise PermissionDeniedError unless current_user.can?(manage: ob)
175 # All permission links should be owned by the system user.
176 self.owner_uuid = system_user_uuid