]> git.arvados.org - arvados.git/blob - services/api/app/models/link.rb
16007: Roles are owned by system user
[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   validate :name_links_are_obsolete
15   validate :permission_to_attach_to_objects
16   before_update :cannot_alter_permissions
17   after_update :call_update_permissions
18   after_create :call_update_permissions
19   before_destroy :clear_permissions
20   after_destroy :check_permissions
21
22   api_accessible :user, extend: :common do |t|
23     t.add :tail_uuid
24     t.add :link_class
25     t.add :name
26     t.add :head_uuid
27     t.add :head_kind
28     t.add :tail_kind
29     t.add :properties
30   end
31
32   def head_kind
33     if k = ArvadosModel::resource_class_for_uuid(head_uuid)
34       k.kind
35     end
36   end
37
38   def tail_kind
39     if k = ArvadosModel::resource_class_for_uuid(tail_uuid)
40       k.kind
41     end
42   end
43
44   protected
45
46   def permission_to_attach_to_objects
47     # Anonymous users cannot write links
48     return false if !current_user
49
50     # All users can write links that don't affect permissions
51     return true if self.link_class != 'permission'
52
53     rsc_class = ArvadosModel::resource_class_for_uuid tail_uuid
54     if rsc_class == Group
55       tail_obj = Group.find_by_uuid(tail_uuid)
56       if tail_obj.nil?
57         errors.add(:tail_uuid, "does not exist")
58         return false
59       end
60       if tail_obj.group_class != "role"
61         errors.add(:tail_uuid, "must be a role, was #{tail_obj.group_class}")
62         return false
63       end
64     elsif rsc_class != User
65       errors.add(:tail_uuid, "must be a user or role")
66       return false
67     end
68
69     # Administrators can grant permissions
70     return true if current_user.is_admin
71
72     head_obj = ArvadosModel.find_by_uuid(head_uuid)
73
74     # No permission links can be pointed to past collection versions
75     if head_obj.is_a?(Collection) && head_obj.current_version_uuid != head_uuid
76       errors.add(:head_uuid, "cannot point to a past version of a collection")
77       return false
78     end
79
80     # All users can grant permissions on objects they own or can manage
81     return true if current_user.can?(manage: head_obj)
82
83     # Default = deny.
84     false
85   end
86
87   def cannot_alter_permissions
88     return true if self.link_class != 'permission' && self.link_class_was != 'permission'
89
90     return true if current_user.andand.uuid == system_user.uuid
91
92     if link_class_changed? || name_changed? || tail_uuid_changed? || head_uuid_changed?
93       raise "Cannot alter a permission link"
94     end
95   end
96
97   PERM_LEVEL = {
98     'can_read' => 1,
99     'can_login' => 1,
100     'can_write' => 2,
101     'can_manage' => 3,
102   }
103
104   def call_update_permissions
105     if self.link_class == 'permission'
106       update_permissions tail_uuid, head_uuid, PERM_LEVEL[name], self.uuid
107     end
108   end
109
110   def clear_permissions
111     if self.link_class == 'permission'
112       update_permissions tail_uuid, head_uuid, REVOKE_PERM, self.uuid
113     end
114   end
115
116   def check_permissions
117     if self.link_class == 'permission'
118       check_permissions_against_full_refresh
119     end
120   end
121
122   def name_links_are_obsolete
123     if link_class == 'name'
124       errors.add('name', 'Name links are obsolete')
125       false
126     else
127       true
128     end
129   end
130
131   # A user is permitted to create, update or modify a permission link
132   # if and only if they have "manage" permission on the object
133   # indicated by the permission link's head_uuid.
134   #
135   # All other links are treated as regular ArvadosModel objects.
136   #
137   def ensure_owner_uuid_is_permitted
138     if link_class == 'permission'
139       ob = ArvadosModel.find_by_uuid(head_uuid)
140       raise PermissionDeniedError unless current_user.can?(manage: ob)
141       # All permission links should be owned by the system user.
142       self.owner_uuid = system_user_uuid
143       return true
144     else
145       super
146     end
147   end
148 end