CWL spec -> CWL standards
[arvados.git] / services / api / test / unit / group_test.rb
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 require 'test_helper'
6
7 class GroupTest < ActiveSupport::TestCase
8
9   test "cannot set owner_uuid to object with existing ownership cycle" do
10     set_user_from_auth :active_trustedclient
11
12     # First make sure we have lots of permission on the bad group by
13     # renaming it to "{current name} is mine all mine"
14     g = groups(:bad_group_has_ownership_cycle_b)
15     g.name += " is mine all mine"
16     assert g.save, "active user should be able to modify group #{g.uuid}"
17
18     # Use the group as the owner of a new object
19     s = Specimen.
20       create(owner_uuid: groups(:bad_group_has_ownership_cycle_b).uuid)
21     assert s.valid?, "ownership should pass validation #{s.errors.messages}"
22     assert_equal false, s.save, "should not save object with #{g.uuid} as owner"
23
24     # Use the group as the new owner of an existing object
25     s = specimens(:in_aproject)
26     s.owner_uuid = groups(:bad_group_has_ownership_cycle_b).uuid
27     assert s.valid?, "ownership should pass validation"
28     assert_equal false, s.save, "should not save object with #{g.uuid} as owner"
29   end
30
31   test "cannot create a new ownership cycle" do
32     set_user_from_auth :active_trustedclient
33
34     g_foo = Group.create!(name: "foo")
35     g_bar = Group.create!(name: "bar")
36
37     g_foo.owner_uuid = g_bar.uuid
38     assert g_foo.save, lambda { g_foo.errors.messages }
39     g_bar.owner_uuid = g_foo.uuid
40     assert g_bar.valid?, "ownership cycle should not prevent validation"
41     assert_equal false, g_bar.save, "should not create an ownership loop"
42     assert g_bar.errors.messages[:owner_uuid].join(" ").match(/ownership cycle/)
43   end
44
45   test "cannot create a single-object ownership cycle" do
46     set_user_from_auth :active_trustedclient
47
48     g_foo = Group.create!(name: "foo")
49     assert g_foo.save
50
51     # Ensure I have permission to manage this group even when its owner changes
52     perm_link = Link.create!(tail_uuid: users(:active).uuid,
53                             head_uuid: g_foo.uuid,
54                             link_class: 'permission',
55                             name: 'can_manage')
56     assert perm_link.save
57
58     g_foo.owner_uuid = g_foo.uuid
59     assert_equal false, g_foo.save, "should not create an ownership loop"
60     assert g_foo.errors.messages[:owner_uuid].join(" ").match(/ownership cycle/)
61   end
62
63   test "trash group hides contents" do
64     set_user_from_auth :active_trustedclient
65
66     g_foo = Group.create!(name: "foo")
67     col = Collection.create!(owner_uuid: g_foo.uuid)
68
69     assert Collection.readable_by(users(:active)).where(uuid: col.uuid).any?
70     g_foo.update! is_trashed: true
71     assert Collection.readable_by(users(:active)).where(uuid: col.uuid).empty?
72     assert Collection.readable_by(users(:active), {:include_trash => true}).where(uuid: col.uuid).any?
73     g_foo.update! is_trashed: false
74     assert Collection.readable_by(users(:active)).where(uuid: col.uuid).any?
75   end
76
77   test "trash group" do
78     set_user_from_auth :active_trustedclient
79
80     g_foo = Group.create!(name: "foo")
81     g_bar = Group.create!(name: "bar", owner_uuid: g_foo.uuid)
82     g_baz = Group.create!(name: "baz", owner_uuid: g_bar.uuid)
83
84     assert Group.readable_by(users(:active)).where(uuid: g_foo.uuid).any?
85     assert Group.readable_by(users(:active)).where(uuid: g_bar.uuid).any?
86     assert Group.readable_by(users(:active)).where(uuid: g_baz.uuid).any?
87     g_foo.update! is_trashed: true
88     assert Group.readable_by(users(:active)).where(uuid: g_foo.uuid).empty?
89     assert Group.readable_by(users(:active)).where(uuid: g_bar.uuid).empty?
90     assert Group.readable_by(users(:active)).where(uuid: g_baz.uuid).empty?
91
92     assert Group.readable_by(users(:active), {:include_trash => true}).where(uuid: g_foo.uuid).any?
93     assert Group.readable_by(users(:active), {:include_trash => true}).where(uuid: g_bar.uuid).any?
94     assert Group.readable_by(users(:active), {:include_trash => true}).where(uuid: g_baz.uuid).any?
95   end
96
97
98   test "trash subgroup" do
99     set_user_from_auth :active_trustedclient
100
101     g_foo = Group.create!(name: "foo")
102     g_bar = Group.create!(name: "bar", owner_uuid: g_foo.uuid)
103     g_baz = Group.create!(name: "baz", owner_uuid: g_bar.uuid)
104
105     assert Group.readable_by(users(:active)).where(uuid: g_foo.uuid).any?
106     assert Group.readable_by(users(:active)).where(uuid: g_bar.uuid).any?
107     assert Group.readable_by(users(:active)).where(uuid: g_baz.uuid).any?
108     g_bar.update! is_trashed: true
109
110     assert Group.readable_by(users(:active)).where(uuid: g_foo.uuid).any?
111     assert Group.readable_by(users(:active)).where(uuid: g_bar.uuid).empty?
112     assert Group.readable_by(users(:active)).where(uuid: g_baz.uuid).empty?
113
114     assert Group.readable_by(users(:active), {:include_trash => true}).where(uuid: g_bar.uuid).any?
115     assert Group.readable_by(users(:active), {:include_trash => true}).where(uuid: g_baz.uuid).any?
116   end
117
118   test "trash subsubgroup" do
119     set_user_from_auth :active_trustedclient
120
121     g_foo = Group.create!(name: "foo")
122     g_bar = Group.create!(name: "bar", owner_uuid: g_foo.uuid)
123     g_baz = Group.create!(name: "baz", owner_uuid: g_bar.uuid)
124
125     assert Group.readable_by(users(:active)).where(uuid: g_foo.uuid).any?
126     assert Group.readable_by(users(:active)).where(uuid: g_bar.uuid).any?
127     assert Group.readable_by(users(:active)).where(uuid: g_baz.uuid).any?
128     g_baz.update! is_trashed: true
129     assert Group.readable_by(users(:active)).where(uuid: g_foo.uuid).any?
130     assert Group.readable_by(users(:active)).where(uuid: g_bar.uuid).any?
131     assert Group.readable_by(users(:active)).where(uuid: g_baz.uuid).empty?
132     assert Group.readable_by(users(:active), {:include_trash => true}).where(uuid: g_baz.uuid).any?
133   end
134
135
136   test "trash group propagates to subgroups" do
137     set_user_from_auth :active_trustedclient
138
139     g_foo = groups(:trashed_project)
140     g_bar = groups(:trashed_subproject)
141     g_baz = groups(:trashed_subproject3)
142     col = collections(:collection_in_trashed_subproject)
143
144     assert Group.readable_by(users(:active)).where(uuid: g_foo.uuid).empty?
145     assert Group.readable_by(users(:active)).where(uuid: g_bar.uuid).empty?
146     assert Group.readable_by(users(:active)).where(uuid: g_baz.uuid).empty?
147     assert Collection.readable_by(users(:active)).where(uuid: col.uuid).empty?
148
149     set_user_from_auth :admin
150     assert Group.readable_by(users(:active)).where(uuid: g_foo.uuid).empty?
151     assert Group.readable_by(users(:active)).where(uuid: g_bar.uuid).empty?
152     assert Group.readable_by(users(:active)).where(uuid: g_baz.uuid).empty?
153     assert Collection.readable_by(users(:active)).where(uuid: col.uuid).empty?
154
155     set_user_from_auth :active_trustedclient
156     g_foo.update! is_trashed: false
157     assert Group.readable_by(users(:active)).where(uuid: g_foo.uuid).any?
158     assert Group.readable_by(users(:active)).where(uuid: g_bar.uuid).any?
159     assert Collection.readable_by(users(:active)).where(uuid: col.uuid).any?
160
161     # this one should still be trashed.
162     assert Group.readable_by(users(:active)).where(uuid: g_baz.uuid).empty?
163
164     g_baz.update! is_trashed: false
165     assert Group.readable_by(users(:active)).where(uuid: g_baz.uuid).any?
166   end
167
168   test "trashed does not propagate across permission links" do
169     set_user_from_auth :admin
170
171     g_foo = Group.create!(name: "foo")
172     u_bar = User.create!(first_name: "bar")
173
174     assert Group.readable_by(users(:admin)).where(uuid: g_foo.uuid).any?
175     assert User.readable_by(users(:admin)).where(uuid:  u_bar.uuid).any?
176     g_foo.update! is_trashed: true
177
178     assert Group.readable_by(users(:admin)).where(uuid: g_foo.uuid).empty?
179     assert User.readable_by(users(:admin)).where(uuid:  u_bar.uuid).any?
180
181     g_foo.update! is_trashed: false
182     ln = Link.create!(tail_uuid: g_foo.uuid,
183                       head_uuid: u_bar.uuid,
184                       link_class: "permission",
185                       name: "can_read")
186     g_foo.update! is_trashed: true
187
188     assert Group.readable_by(users(:admin)).where(uuid: g_foo.uuid).empty?
189     assert User.readable_by(users(:admin)).where(uuid:  u_bar.uuid).any?
190   end
191
192   test "move projects to trash in SweepTrashedObjects" do
193     p = groups(:trashed_on_next_sweep)
194     assert_empty Group.where('uuid=? and is_trashed=true', p.uuid)
195     SweepTrashedObjects.sweep_now
196     assert_not_empty Group.where('uuid=? and is_trashed=true', p.uuid)
197   end
198
199   test "delete projects and their contents in SweepTrashedObjects" do
200     g_foo = groups(:trashed_project)
201     g_bar = groups(:trashed_subproject)
202     g_baz = groups(:trashed_subproject3)
203     col = collections(:collection_in_trashed_subproject)
204     job = jobs(:job_in_trashed_project)
205     cr = container_requests(:cr_in_trashed_project)
206     # Save how many objects were before the sweep
207     user_nr_was = User.all.length
208     coll_nr_was = Collection.all.length
209     group_nr_was = Group.where('group_class<>?', 'project').length
210     project_nr_was = Group.where(group_class: 'project').length
211     cr_nr_was = ContainerRequest.all.length
212     job_nr_was = Job.all.length
213     assert_not_empty Group.where(uuid: g_foo.uuid)
214     assert_not_empty Group.where(uuid: g_bar.uuid)
215     assert_not_empty Group.where(uuid: g_baz.uuid)
216     assert_not_empty Collection.where(uuid: col.uuid)
217     assert_not_empty Job.where(uuid: job.uuid)
218     assert_not_empty ContainerRequest.where(uuid: cr.uuid)
219     SweepTrashedObjects.sweep_now
220     assert_empty Group.where(uuid: g_foo.uuid)
221     assert_empty Group.where(uuid: g_bar.uuid)
222     assert_empty Group.where(uuid: g_baz.uuid)
223     assert_empty Collection.where(uuid: col.uuid)
224     assert_empty Job.where(uuid: job.uuid)
225     assert_empty ContainerRequest.where(uuid: cr.uuid)
226     # No unwanted deletions should have happened
227     assert_equal user_nr_was, User.all.length
228     assert_equal coll_nr_was-2,        # collection_in_trashed_subproject
229                  Collection.all.length # & deleted_on_next_sweep collections
230     assert_equal group_nr_was, Group.where('group_class<>?', 'project').length
231     assert_equal project_nr_was-3, Group.where(group_class: 'project').length
232     assert_equal cr_nr_was-1, ContainerRequest.all.length
233     assert_equal job_nr_was-1, Job.all.length
234   end
235
236   test "project names must be displayable in a filesystem" do
237     set_user_from_auth :active
238     ["", "{SOLIDUS}"].each do |subst|
239       Rails.configuration.Collections.ForwardSlashNameSubstitution = subst
240       g = Group.create
241       [[nil, true],
242        ["", true],
243        [".", false],
244        ["..", false],
245        ["...", true],
246        ["..z..", true],
247        ["foo/bar", subst != ""],
248        ["../..", subst != ""],
249        ["/", subst != ""],
250       ].each do |name, valid|
251         g.name = name
252         g.group_class = "role"
253         assert_equal true, g.valid?
254         g.group_class = "project"
255         assert_equal valid, g.valid?, "#{name.inspect} should be #{valid ? "valid" : "invalid"}"
256       end
257     end
258   end
259 end