1 class Link < ArvadosModel
4 include CommonApiTemplate
5 serialize :properties, Hash
6 before_create :permission_to_attach_to_objects
7 before_update :permission_to_attach_to_objects
8 after_update :maybe_invalidate_permissions_cache
9 after_create :maybe_invalidate_permissions_cache
10 after_destroy :maybe_invalidate_permissions_cache
11 attr_accessor :head_kind, :tail_kind
12 validate :name_link_has_valid_name
13 validate :name_link_owner_is_tail
15 api_accessible :user, extend: :common do |t|
26 @properties ||= Hash.new
31 if k = ArvadosModel::resource_class_for_uuid(head_uuid)
37 if k = ArvadosModel::resource_class_for_uuid(tail_uuid)
44 def permission_to_attach_to_objects
45 # Anonymous users cannot write links
46 return false if !current_user
48 # All users can write links that don't affect permissions
49 return true if self.link_class != 'permission'
51 # Administrators can grant permissions
52 return true if current_user.is_admin
54 # All users can grant permissions on objects they own or can manage
55 head_obj = ArvadosModel.find_by_uuid(head_uuid)
56 return true if current_user.can?(manage: head_obj)
62 def maybe_invalidate_permissions_cache
63 if self.link_class == 'permission'
64 # Clearing the entire permissions cache can generate many
65 # unnecessary queries if many active users are not affected by
66 # this change. In such cases it would be better to search cached
67 # permissions for head_uuid and tail_uuid, and invalidate the
68 # cache for only those users. (This would require a browseable
70 User.invalidate_permissions_cache
74 def name_link_has_valid_name
75 if link_class == 'name'
76 unless name.is_a? String and !name.empty?
77 errors.add('name', 'must be a non-empty string')
84 def name_link_owner_is_tail
85 if link_class == 'name'
86 self.owner_uuid = tail_uuid
87 ensure_owner_uuid_is_permitted
91 # A user is permitted to create, update or modify a permission link
92 # if and only if they have "manage" permission on the destination
94 # All other links are treated as regular ArvadosModel objects.
96 def ensure_owner_uuid_is_permitted
97 if link_class == 'permission'
98 ob = ArvadosModel.find_by_uuid(head_uuid)
99 raise PermissionDeniedError unless current_user.can?(manage: ob)
100 # All permission links should be owned by the system user.
101 self.owner_uuid = system_user_uuid
108 # A user can give all other users permissions on projects.
109 def skip_uuid_read_permission_check
110 skipped_attrs = super
111 if link_class == "permission" and
112 (ArvadosModel.resource_class_for_uuid(head_uuid) == Group) and
113 (ArvadosModel.resource_class_for_uuid(tail_uuid) == User)
114 skipped_attrs << "tail_uuid"