12032: Make queries to groups table a special case, streamlines permission logic.
[arvados.git] / services / api / test / unit / repository_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 require 'helpers/git_test_helper'
7
8 class RepositoryTest < ActiveSupport::TestCase
9   include GitTestHelper
10
11   def new_repo(owner_key, attrs={})
12     set_user_from_auth owner_key
13     owner = users(owner_key)
14     Repository.new({owner_uuid: owner.uuid}.merge(attrs))
15   end
16
17   def changed_repo(repo_key, changes)
18     repo = repositories(repo_key)
19     changes.each_pair { |attr, value| repo.send("#{attr}=".to_sym, value) }
20     repo
21   end
22
23   def default_git_url(repo_name, user_name=nil)
24     if user_name
25       "git@git.%s.arvadosapi.com:%s/%s.git" %
26         [Rails.configuration.uuid_prefix, user_name, repo_name]
27     else
28       "git@git.%s.arvadosapi.com:%s.git" %
29         [Rails.configuration.uuid_prefix, repo_name]
30     end
31   end
32
33   def assert_server_path(path_tail, repo_sym)
34     assert_equal(File.join(Rails.configuration.git_repositories_dir, path_tail),
35                  repositories(repo_sym).server_path)
36   end
37
38   ### name validation
39
40   {active: "active/", admin: "admin/", system_user: ""}.
41       each_pair do |user_sym, name_prefix|
42     test "valid names for #{user_sym} repo" do
43       %w(a aa a0 aA Aa AA A0).each do |name|
44         repo = new_repo(user_sym, name: name_prefix + name)
45         assert(repo.valid?)
46       end
47     end
48
49     test "name is required for #{user_sym} repo" do
50       refute(new_repo(user_sym).valid?)
51     end
52
53     test "repo name beginning with numeral is invalid for #{user_sym}" do
54       repo = new_repo(user_sym, name: "#{name_prefix}0a")
55       refute(repo.valid?)
56     end
57
58     test "name containing bad char is invalid for #{user_sym}" do
59       "\\.-_/!@#$%^&*()[]{}".each_char do |bad_char|
60         repo = new_repo(user_sym, name: "#{name_prefix}bad#{bad_char}reponame")
61         refute(repo.valid?)
62       end
63     end
64   end
65
66   test "admin can create valid repo for other user with correct name prefix" do
67     owner = users(:active)
68     repo = new_repo(:admin, name: "#{owner.username}/validnametest",
69                     owner_uuid: owner.uuid)
70     assert(repo.valid?)
71   end
72
73   test "admin can create valid system repo without name prefix" do
74     repo = new_repo(:admin, name: "validnametest",
75                     owner_uuid: users(:system_user).uuid)
76     assert(repo.valid?)
77   end
78
79   test "repo name prefix must match owner_uuid username" do
80     repo = new_repo(:admin, name: "admin/badusernametest",
81                     owner_uuid: users(:active).uuid)
82     refute(repo.valid?)
83   end
84
85   test "repo name prefix must be empty for system repo" do
86     repo = new_repo(:admin, name: "root/badprefixtest",
87                     owner_uuid: users(:system_user).uuid)
88     refute(repo.valid?)
89   end
90
91   ### owner validation
92
93   test "name must be unique per user" do
94     repo = new_repo(:active, name: repositories(:foo).name)
95     refute(repo.valid?)
96   end
97
98   test "name can be duplicated across users" do
99     repo = new_repo(:active, name: "active/#{repositories(:arvados).name}")
100     assert(repo.valid?)
101   end
102
103   test "repository cannot be owned by a group" do
104     set_user_from_auth :active
105     repo = Repository.new(owner_uuid: groups(:all_users).uuid,
106                           name: "ownedbygroup")
107     refute(repo.valid?)
108     refute_empty(repo.errors[:owner_uuid] || [])
109   end
110
111   ### URL generation
112
113   test "fetch_url" do
114     repo = new_repo(:active, name: "active/fetchtest")
115     repo.save
116     assert_equal(default_git_url("fetchtest", "active"), repo.fetch_url)
117   end
118
119   test "fetch_url owned by system user" do
120     set_user_from_auth :admin
121     repo = Repository.new(owner_uuid: users(:system_user).uuid,
122                           name: "fetchtest")
123     repo.save
124     assert_equal(default_git_url("fetchtest"), repo.fetch_url)
125   end
126
127   test "push_url" do
128     repo = new_repo(:active, name: "active/pushtest")
129     repo.save
130     assert_equal(default_git_url("pushtest", "active"), repo.push_url)
131   end
132
133   test "push_url owned by system user" do
134     set_user_from_auth :admin
135     repo = Repository.new(owner_uuid: users(:system_user).uuid,
136                           name: "pushtest")
137     repo.save
138     assert_equal(default_git_url("pushtest"), repo.push_url)
139   end
140
141   ### Path generation
142
143   test "disk path stored by UUID" do
144     assert_server_path("zzzzz-s0uqq-382brsig8rp3666/.git", :foo)
145   end
146
147   test "disk path stored by name" do
148     assert_server_path("arvados/.git", :arvados)
149   end
150
151   test "disk path for repository not on disk" do
152     assert_nil(Repository.new.server_path)
153   end
154
155   ### Repository creation
156
157   test "non-admin can create a repository for themselves" do
158     repo = new_repo(:active, name: "active/newtestrepo")
159     assert(repo.save)
160   end
161
162   test "non-admin can't create a repository for another visible user" do
163     repo = new_repo(:active, name: "repoforanon",
164                     owner_uuid: users(:anonymous).uuid)
165     assert_not_allowed { repo.save }
166   end
167
168   test "admin can create a repository for themselves" do
169     repo = new_repo(:admin, name: "admin/newtestrepo")
170     assert(repo.save)
171   end
172
173   test "admin can create a repository for others" do
174     repo = new_repo(:admin, name: "active/repoforactive",
175                     owner_uuid: users(:active).uuid)
176     assert(repo.save)
177   end
178
179   test "admin can create a system repository" do
180     repo = new_repo(:admin, name: "repoforsystem",
181                     owner_uuid: users(:system_user).uuid)
182     assert(repo.save)
183   end
184
185   ### Repository destruction
186
187   test "non-admin can destroy their own repository" do
188     set_user_from_auth :active
189     assert(repositories(:foo).destroy)
190   end
191
192   test "non-admin can't destroy others' repository" do
193     set_user_from_auth :active
194     assert_not_allowed { repositories(:repository3).destroy }
195   end
196
197   test "non-admin can't destroy system repository" do
198     set_user_from_auth :active
199     assert_not_allowed { repositories(:arvados).destroy }
200   end
201
202   test "admin can destroy their own repository" do
203     set_user_from_auth :admin
204     assert(repositories(:repository3).destroy)
205   end
206
207   test "admin can destroy others' repository" do
208     set_user_from_auth :admin
209     assert(repositories(:foo).destroy)
210   end
211
212   test "admin can destroy system repository" do
213     set_user_from_auth :admin
214     assert(repositories(:arvados).destroy)
215   end
216
217   ### Changing ownership
218
219   test "non-admin can't make their repository a system repository" do
220     set_user_from_auth :active
221     repo = changed_repo(:foo, owner_uuid: users(:system_user).uuid)
222     assert_not_allowed { repo.save }
223   end
224
225   test "admin can give their repository to someone else" do
226     set_user_from_auth :admin
227     repo = changed_repo(:repository3, owner_uuid: users(:active).uuid,
228                         name: "active/foo3")
229     assert(repo.save)
230   end
231
232   test "admin can make their repository a system repository" do
233     set_user_from_auth :admin
234     repo = changed_repo(:repository3, owner_uuid: users(:system_user).uuid,
235                         name: "foo3")
236     assert(repo.save)
237   end
238
239   test 'write permission allows changing modified_at' do
240     act_as_user users(:active) do
241       r = repositories(:foo)
242       modtime_was = r.modified_at
243       r.modified_at = Time.now
244       assert r.save
245       assert_operator modtime_was, :<, r.modified_at
246     end
247   end
248
249   test 'write permission necessary for changing modified_at' do
250     act_as_user users(:spectator) do
251       r = repositories(:foo)
252       modtime_was = r.modified_at
253       r.modified_at = Time.now
254       assert_raises ArvadosModel::PermissionDeniedError do
255         r.save!
256       end
257       r.reload
258       assert_equal modtime_was, r.modified_at
259     end
260   end
261
262   ### Renaming
263
264   test "non-admin can rename own repo" do
265     act_as_user users(:active) do
266       assert repositories(:foo).update_attributes(name: 'active/foo12345')
267     end
268   end
269
270   test "top level repo can be touched by non-admin with can_manage" do
271     add_permission_link users(:active), repositories(:arvados), 'can_manage'
272     act_as_user users(:active) do
273       assert changed_repo(:arvados, modified_at: Time.now).save
274     end
275   end
276
277   test "top level repo cannot be renamed by non-admin with can_manage" do
278     add_permission_link users(:active), repositories(:arvados), 'can_manage'
279     act_as_user users(:active) do
280       assert_not_allowed { changed_repo(:arvados, name: 'xarvados').save }
281     end
282   end
283 end