CWL spec -> CWL standards
[arvados.git] / services / api / app / models / link.rb
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 class Link < ArvadosModel
6   include HasUuid
7   include KindAndEtag
8   include CommonApiTemplate
9
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: {}
13
14   before_create :permission_to_attach_to_objects
15   before_update :permission_to_attach_to_objects
16   after_update :maybe_invalidate_permissions_cache
17   after_create :maybe_invalidate_permissions_cache
18   after_destroy :maybe_invalidate_permissions_cache
19   validate :name_links_are_obsolete
20
21   api_accessible :user, extend: :common do |t|
22     t.add :tail_uuid
23     t.add :link_class
24     t.add :name
25     t.add :head_uuid
26     t.add :head_kind
27     t.add :tail_kind
28     t.add :properties
29   end
30
31   def head_kind
32     if k = ArvadosModel::resource_class_for_uuid(head_uuid)
33       k.kind
34     end
35   end
36
37   def tail_kind
38     if k = ArvadosModel::resource_class_for_uuid(tail_uuid)
39       k.kind
40     end
41   end
42
43   protected
44
45   def permission_to_attach_to_objects
46     # Anonymous users cannot write links
47     return false if !current_user
48
49     # All users can write links that don't affect permissions
50     return true if self.link_class != 'permission'
51
52     # Administrators can grant permissions
53     return true if current_user.is_admin
54
55     head_obj = ArvadosModel.find_by_uuid(head_uuid)
56
57     # No permission links can be pointed to past collection versions
58     return false if head_obj.is_a?(Collection) && head_obj.current_version_uuid != head_uuid
59
60     # All users can grant permissions on objects they own or can manage
61     return true if current_user.can?(manage: head_obj)
62
63     # Default = deny.
64     false
65   end
66
67   def maybe_invalidate_permissions_cache
68     if self.link_class == 'permission'
69       # Clearing the entire permissions cache can generate many
70       # unnecessary queries if many active users are not affected by
71       # this change. In such cases it would be better to search cached
72       # permissions for head_uuid and tail_uuid, and invalidate the
73       # cache for only those users. (This would require a browseable
74       # cache.)
75       User.invalidate_permissions_cache
76     end
77   end
78
79   def name_links_are_obsolete
80     if link_class == 'name'
81       errors.add('name', 'Name links are obsolete')
82       false
83     else
84       true
85     end
86   end
87
88   # A user is permitted to create, update or modify a permission link
89   # if and only if they have "manage" permission on the object
90   # indicated by the permission link's head_uuid.
91   #
92   # All other links are treated as regular ArvadosModel objects.
93   #
94   def ensure_owner_uuid_is_permitted
95     if link_class == 'permission'
96       ob = ArvadosModel.find_by_uuid(head_uuid)
97       raise PermissionDeniedError unless current_user.can?(manage: ob)
98       # All permission links should be owned by the system user.
99       self.owner_uuid = system_user_uuid
100       return true
101     else
102       super
103     end
104   end
105 end